@vercel/queue 0.0.0-alpha.13 → 0.0.0-alpha.14
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/README.md +3 -3
- package/dist/index.js +304 -106
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +304 -106
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -791,111 +791,6 @@ var ConsumerGroup = class {
|
|
|
791
791
|
}
|
|
792
792
|
};
|
|
793
793
|
|
|
794
|
-
// src/topic.ts
|
|
795
|
-
var Topic = class {
|
|
796
|
-
client;
|
|
797
|
-
topicName;
|
|
798
|
-
transport;
|
|
799
|
-
/**
|
|
800
|
-
* Create a new Topic instance
|
|
801
|
-
* @param client QueueClient instance to use for API calls
|
|
802
|
-
* @param topicName Name of the topic to work with
|
|
803
|
-
* @param transport Optional serializer/deserializer for the payload (defaults to JSON)
|
|
804
|
-
*/
|
|
805
|
-
constructor(client, topicName, transport) {
|
|
806
|
-
this.client = client;
|
|
807
|
-
this.topicName = topicName;
|
|
808
|
-
this.transport = transport || new JsonTransport();
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Publish a message to the topic
|
|
812
|
-
* @param payload The data to publish
|
|
813
|
-
* @param options Optional publish options
|
|
814
|
-
* @returns An object containing the message ID
|
|
815
|
-
* @throws {BadRequestError} When request parameters are invalid
|
|
816
|
-
* @throws {UnauthorizedError} When authentication fails
|
|
817
|
-
* @throws {ForbiddenError} When access is denied (environment mismatch)
|
|
818
|
-
* @throws {InternalServerError} When server encounters an error
|
|
819
|
-
*/
|
|
820
|
-
async publish(payload, options) {
|
|
821
|
-
const result = await this.client.sendMessage(
|
|
822
|
-
{
|
|
823
|
-
queueName: this.topicName,
|
|
824
|
-
payload,
|
|
825
|
-
idempotencyKey: options?.idempotencyKey,
|
|
826
|
-
retentionSeconds: options?.retentionSeconds
|
|
827
|
-
},
|
|
828
|
-
this.transport
|
|
829
|
-
);
|
|
830
|
-
return { messageId: result.messageId };
|
|
831
|
-
}
|
|
832
|
-
/**
|
|
833
|
-
* Create a consumer group for this topic
|
|
834
|
-
* @param consumerGroupName Name of the consumer group
|
|
835
|
-
* @param options Optional configuration for the consumer group
|
|
836
|
-
* @returns A ConsumerGroup instance
|
|
837
|
-
*/
|
|
838
|
-
consumerGroup(consumerGroupName, options) {
|
|
839
|
-
const consumerOptions = {
|
|
840
|
-
...options,
|
|
841
|
-
transport: options?.transport || this.transport
|
|
842
|
-
};
|
|
843
|
-
return new ConsumerGroup(
|
|
844
|
-
this.client,
|
|
845
|
-
this.topicName,
|
|
846
|
-
consumerGroupName,
|
|
847
|
-
consumerOptions
|
|
848
|
-
);
|
|
849
|
-
}
|
|
850
|
-
/**
|
|
851
|
-
* Get the topic name
|
|
852
|
-
*/
|
|
853
|
-
get name() {
|
|
854
|
-
return this.topicName;
|
|
855
|
-
}
|
|
856
|
-
/**
|
|
857
|
-
* Get the transport used by this topic
|
|
858
|
-
*/
|
|
859
|
-
get serializer() {
|
|
860
|
-
return this.transport;
|
|
861
|
-
}
|
|
862
|
-
};
|
|
863
|
-
|
|
864
|
-
// src/factory.ts
|
|
865
|
-
async function send(topicName, payload, options) {
|
|
866
|
-
const transport = options?.transport || new JsonTransport();
|
|
867
|
-
const client = new QueueClient();
|
|
868
|
-
const result = await client.sendMessage(
|
|
869
|
-
{
|
|
870
|
-
queueName: topicName,
|
|
871
|
-
payload,
|
|
872
|
-
idempotencyKey: options?.idempotencyKey,
|
|
873
|
-
retentionSeconds: options?.retentionSeconds
|
|
874
|
-
},
|
|
875
|
-
transport
|
|
876
|
-
);
|
|
877
|
-
return { messageId: result.messageId };
|
|
878
|
-
}
|
|
879
|
-
async function receive(topicName, consumerGroup, handler, options) {
|
|
880
|
-
const transport = options?.transport || new JsonTransport();
|
|
881
|
-
const client = new QueueClient();
|
|
882
|
-
const topic = new Topic(client, topicName, transport);
|
|
883
|
-
const { messageId, skipPayload, ...consumerGroupOptions } = options || {};
|
|
884
|
-
const consumer = topic.consumerGroup(consumerGroup, consumerGroupOptions);
|
|
885
|
-
if (messageId) {
|
|
886
|
-
if (skipPayload) {
|
|
887
|
-
return consumer.consume(handler, {
|
|
888
|
-
messageId,
|
|
889
|
-
skipPayload: true
|
|
890
|
-
});
|
|
891
|
-
} else {
|
|
892
|
-
return consumer.consume(handler, { messageId });
|
|
893
|
-
}
|
|
894
|
-
} else {
|
|
895
|
-
return consumer.consume(handler);
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
|
|
899
794
|
// src/callback.ts
|
|
900
795
|
function validateWildcardPattern(pattern) {
|
|
901
796
|
const firstIndex = pattern.indexOf("*");
|
|
@@ -975,7 +870,7 @@ function handleCallback(handlers) {
|
|
|
975
870
|
}
|
|
976
871
|
}
|
|
977
872
|
}
|
|
978
|
-
|
|
873
|
+
const routeHandler = async (request) => {
|
|
979
874
|
try {
|
|
980
875
|
const { queueName, consumerGroup, messageId } = await parseCallback(request);
|
|
981
876
|
const topicHandler = findTopicHandler(queueName, handlers);
|
|
@@ -1016,6 +911,309 @@ function handleCallback(handlers) {
|
|
|
1016
911
|
);
|
|
1017
912
|
}
|
|
1018
913
|
};
|
|
914
|
+
if (isDevMode()) {
|
|
915
|
+
registerDevRouteHandler(routeHandler, handlers);
|
|
916
|
+
}
|
|
917
|
+
return routeHandler;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// src/dev.ts
|
|
921
|
+
var devRouteHandlers = /* @__PURE__ */ new Map();
|
|
922
|
+
var wildcardRouteHandlers = /* @__PURE__ */ new Map();
|
|
923
|
+
var routeHandlerKeys = /* @__PURE__ */ new WeakMap();
|
|
924
|
+
function cleanupDeadRefs(key, refs) {
|
|
925
|
+
const aliveRefs = refs.filter((ref) => ref.deref() !== void 0);
|
|
926
|
+
if (aliveRefs.length === 0) {
|
|
927
|
+
wildcardRouteHandlers.delete(key);
|
|
928
|
+
} else if (aliveRefs.length < refs.length) {
|
|
929
|
+
wildcardRouteHandlers.set(key, aliveRefs);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
function isDevMode() {
|
|
933
|
+
return process.env.NODE_ENV === "development";
|
|
934
|
+
}
|
|
935
|
+
function registerDevRouteHandler(routeHandler, handlers) {
|
|
936
|
+
const existingKeys = routeHandlerKeys.get(routeHandler);
|
|
937
|
+
if (existingKeys) {
|
|
938
|
+
const newKeys = /* @__PURE__ */ new Set();
|
|
939
|
+
for (const topicName in handlers) {
|
|
940
|
+
for (const consumerGroup in handlers[topicName]) {
|
|
941
|
+
newKeys.add(`${topicName}:${consumerGroup}`);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
for (const key of existingKeys) {
|
|
945
|
+
if (!newKeys.has(key)) {
|
|
946
|
+
const [topicPattern] = key.split(":");
|
|
947
|
+
if (topicPattern.includes("*")) {
|
|
948
|
+
const refs = wildcardRouteHandlers.get(key);
|
|
949
|
+
if (refs) {
|
|
950
|
+
const filteredRefs = refs.filter(
|
|
951
|
+
(ref) => ref.deref() !== routeHandler
|
|
952
|
+
);
|
|
953
|
+
if (filteredRefs.length === 0) {
|
|
954
|
+
wildcardRouteHandlers.delete(key);
|
|
955
|
+
} else {
|
|
956
|
+
wildcardRouteHandlers.set(key, filteredRefs);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
} else {
|
|
960
|
+
devRouteHandlers.delete(key);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
const keys = /* @__PURE__ */ new Set();
|
|
966
|
+
for (const topicName in handlers) {
|
|
967
|
+
for (const consumerGroup in handlers[topicName]) {
|
|
968
|
+
const key = `${topicName}:${consumerGroup}`;
|
|
969
|
+
keys.add(key);
|
|
970
|
+
if (topicName.includes("*")) {
|
|
971
|
+
const weakRef = new WeakRef(routeHandler);
|
|
972
|
+
const existing = wildcardRouteHandlers.get(key) || [];
|
|
973
|
+
cleanupDeadRefs(key, existing);
|
|
974
|
+
const cleanedRefs = wildcardRouteHandlers.get(key) || [];
|
|
975
|
+
cleanedRefs.push(weakRef);
|
|
976
|
+
wildcardRouteHandlers.set(key, cleanedRefs);
|
|
977
|
+
} else {
|
|
978
|
+
devRouteHandlers.set(key, {
|
|
979
|
+
routeHandler,
|
|
980
|
+
topicPattern: topicName
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
routeHandlerKeys.set(routeHandler, keys);
|
|
986
|
+
}
|
|
987
|
+
function findRouteHandlersForTopic(topicName) {
|
|
988
|
+
const handlersMap = /* @__PURE__ */ new Map();
|
|
989
|
+
for (const [
|
|
990
|
+
key,
|
|
991
|
+
{ routeHandler, topicPattern }
|
|
992
|
+
] of devRouteHandlers.entries()) {
|
|
993
|
+
const [_, consumerGroup] = key.split(":");
|
|
994
|
+
if (topicPattern === topicName) {
|
|
995
|
+
if (!handlersMap.has(routeHandler)) {
|
|
996
|
+
handlersMap.set(routeHandler, /* @__PURE__ */ new Set());
|
|
997
|
+
}
|
|
998
|
+
handlersMap.get(routeHandler).add(consumerGroup);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
for (const [key, refs] of wildcardRouteHandlers.entries()) {
|
|
1002
|
+
const [pattern, consumerGroup] = key.split(":");
|
|
1003
|
+
if (matchesWildcardPattern(topicName, pattern)) {
|
|
1004
|
+
cleanupDeadRefs(key, refs);
|
|
1005
|
+
const cleanedRefs = wildcardRouteHandlers.get(key) || [];
|
|
1006
|
+
for (const ref of cleanedRefs) {
|
|
1007
|
+
const routeHandler = ref.deref();
|
|
1008
|
+
if (routeHandler) {
|
|
1009
|
+
if (!handlersMap.has(routeHandler)) {
|
|
1010
|
+
handlersMap.set(routeHandler, /* @__PURE__ */ new Set());
|
|
1011
|
+
}
|
|
1012
|
+
handlersMap.get(routeHandler).add(consumerGroup);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return handlersMap;
|
|
1018
|
+
}
|
|
1019
|
+
function createMockCloudEventRequest(topicName, consumerGroup, messageId) {
|
|
1020
|
+
const cloudEvent = {
|
|
1021
|
+
type: "com.vercel.queue.v1beta",
|
|
1022
|
+
source: `/topic/${topicName}/consumer/${consumerGroup}`,
|
|
1023
|
+
id: messageId,
|
|
1024
|
+
datacontenttype: "application/json",
|
|
1025
|
+
data: {
|
|
1026
|
+
messageId,
|
|
1027
|
+
queueName: topicName,
|
|
1028
|
+
consumerGroup
|
|
1029
|
+
},
|
|
1030
|
+
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1031
|
+
specversion: "1.0"
|
|
1032
|
+
};
|
|
1033
|
+
return new Request("https://localhost/api/queue/callback", {
|
|
1034
|
+
method: "POST",
|
|
1035
|
+
headers: {
|
|
1036
|
+
"Content-Type": "application/cloudevents+json"
|
|
1037
|
+
},
|
|
1038
|
+
body: JSON.stringify(cloudEvent)
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
var DEV_CALLBACK_DELAY = 1e3;
|
|
1042
|
+
function triggerDevCallbacks(topicName, messageId) {
|
|
1043
|
+
const handlersMap = findRouteHandlersForTopic(topicName);
|
|
1044
|
+
if (handlersMap.size === 0) {
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
const consumerGroups = Array.from(
|
|
1048
|
+
new Set(
|
|
1049
|
+
Array.from(handlersMap.values()).flatMap((groups) => Array.from(groups))
|
|
1050
|
+
)
|
|
1051
|
+
);
|
|
1052
|
+
console.log(
|
|
1053
|
+
`[Dev Mode] Triggering local callbacks for topic "${topicName}" \u2192 consumers: ${consumerGroups.join(", ")}`
|
|
1054
|
+
);
|
|
1055
|
+
setTimeout(async () => {
|
|
1056
|
+
for (const [routeHandler, consumerGroups2] of handlersMap.entries()) {
|
|
1057
|
+
for (const consumerGroup of consumerGroups2) {
|
|
1058
|
+
try {
|
|
1059
|
+
const request = createMockCloudEventRequest(
|
|
1060
|
+
topicName,
|
|
1061
|
+
consumerGroup,
|
|
1062
|
+
messageId
|
|
1063
|
+
);
|
|
1064
|
+
const response = await routeHandler(request);
|
|
1065
|
+
if (response.ok) {
|
|
1066
|
+
try {
|
|
1067
|
+
const responseData = await response.json();
|
|
1068
|
+
if (responseData.status === "success") {
|
|
1069
|
+
console.log(
|
|
1070
|
+
`[Dev Mode] Message processed for ${topicName}/${consumerGroup}`
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
} catch (jsonError) {
|
|
1074
|
+
console.error(
|
|
1075
|
+
`[Dev Mode] Failed to parse success response for ${topicName}/${consumerGroup}:`,
|
|
1076
|
+
jsonError
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
} else {
|
|
1080
|
+
try {
|
|
1081
|
+
const errorData = await response.json();
|
|
1082
|
+
console.error(
|
|
1083
|
+
`[Dev Mode] Failed to process message for ${topicName}/${consumerGroup}:`,
|
|
1084
|
+
errorData.error || response.statusText
|
|
1085
|
+
);
|
|
1086
|
+
} catch (jsonError) {
|
|
1087
|
+
console.error(
|
|
1088
|
+
`[Dev Mode] Failed to process message for ${topicName}/${consumerGroup}:`,
|
|
1089
|
+
response.statusText
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
console.error(
|
|
1095
|
+
`[Dev Mode] Error triggering callback for ${topicName}/${consumerGroup}:`,
|
|
1096
|
+
error
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}, DEV_CALLBACK_DELAY);
|
|
1102
|
+
}
|
|
1103
|
+
function clearDevHandlers() {
|
|
1104
|
+
devRouteHandlers.clear();
|
|
1105
|
+
wildcardRouteHandlers.clear();
|
|
1106
|
+
}
|
|
1107
|
+
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
1108
|
+
globalThis.__clearDevHandlers = clearDevHandlers;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// src/topic.ts
|
|
1112
|
+
var Topic = class {
|
|
1113
|
+
client;
|
|
1114
|
+
topicName;
|
|
1115
|
+
transport;
|
|
1116
|
+
/**
|
|
1117
|
+
* Create a new Topic instance
|
|
1118
|
+
* @param client QueueClient instance to use for API calls
|
|
1119
|
+
* @param topicName Name of the topic to work with
|
|
1120
|
+
* @param transport Optional serializer/deserializer for the payload (defaults to JSON)
|
|
1121
|
+
*/
|
|
1122
|
+
constructor(client, topicName, transport) {
|
|
1123
|
+
this.client = client;
|
|
1124
|
+
this.topicName = topicName;
|
|
1125
|
+
this.transport = transport || new JsonTransport();
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Publish a message to the topic
|
|
1129
|
+
* @param payload The data to publish
|
|
1130
|
+
* @param options Optional publish options
|
|
1131
|
+
* @returns An object containing the message ID
|
|
1132
|
+
* @throws {BadRequestError} When request parameters are invalid
|
|
1133
|
+
* @throws {UnauthorizedError} When authentication fails
|
|
1134
|
+
* @throws {ForbiddenError} When access is denied (environment mismatch)
|
|
1135
|
+
* @throws {InternalServerError} When server encounters an error
|
|
1136
|
+
*/
|
|
1137
|
+
async publish(payload, options) {
|
|
1138
|
+
const result = await this.client.sendMessage(
|
|
1139
|
+
{
|
|
1140
|
+
queueName: this.topicName,
|
|
1141
|
+
payload,
|
|
1142
|
+
idempotencyKey: options?.idempotencyKey,
|
|
1143
|
+
retentionSeconds: options?.retentionSeconds
|
|
1144
|
+
},
|
|
1145
|
+
this.transport
|
|
1146
|
+
);
|
|
1147
|
+
if (isDevMode()) {
|
|
1148
|
+
triggerDevCallbacks(this.topicName, result.messageId);
|
|
1149
|
+
}
|
|
1150
|
+
return { messageId: result.messageId };
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Create a consumer group for this topic
|
|
1154
|
+
* @param consumerGroupName Name of the consumer group
|
|
1155
|
+
* @param options Optional configuration for the consumer group
|
|
1156
|
+
* @returns A ConsumerGroup instance
|
|
1157
|
+
*/
|
|
1158
|
+
consumerGroup(consumerGroupName, options) {
|
|
1159
|
+
const consumerOptions = {
|
|
1160
|
+
...options,
|
|
1161
|
+
transport: options?.transport || this.transport
|
|
1162
|
+
};
|
|
1163
|
+
return new ConsumerGroup(
|
|
1164
|
+
this.client,
|
|
1165
|
+
this.topicName,
|
|
1166
|
+
consumerGroupName,
|
|
1167
|
+
consumerOptions
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
/**
|
|
1171
|
+
* Get the topic name
|
|
1172
|
+
*/
|
|
1173
|
+
get name() {
|
|
1174
|
+
return this.topicName;
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Get the transport used by this topic
|
|
1178
|
+
*/
|
|
1179
|
+
get serializer() {
|
|
1180
|
+
return this.transport;
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
// src/factory.ts
|
|
1185
|
+
async function send(topicName, payload, options) {
|
|
1186
|
+
const transport = options?.transport || new JsonTransport();
|
|
1187
|
+
const client = new QueueClient();
|
|
1188
|
+
const result = await client.sendMessage(
|
|
1189
|
+
{
|
|
1190
|
+
queueName: topicName,
|
|
1191
|
+
payload,
|
|
1192
|
+
idempotencyKey: options?.idempotencyKey,
|
|
1193
|
+
retentionSeconds: options?.retentionSeconds
|
|
1194
|
+
},
|
|
1195
|
+
transport
|
|
1196
|
+
);
|
|
1197
|
+
return { messageId: result.messageId };
|
|
1198
|
+
}
|
|
1199
|
+
async function receive(topicName, consumerGroup, handler, options) {
|
|
1200
|
+
const transport = options?.transport || new JsonTransport();
|
|
1201
|
+
const client = new QueueClient();
|
|
1202
|
+
const topic = new Topic(client, topicName, transport);
|
|
1203
|
+
const { messageId, skipPayload, ...consumerGroupOptions } = options || {};
|
|
1204
|
+
const consumer = topic.consumerGroup(consumerGroup, consumerGroupOptions);
|
|
1205
|
+
if (messageId) {
|
|
1206
|
+
if (skipPayload) {
|
|
1207
|
+
return consumer.consume(handler, {
|
|
1208
|
+
messageId,
|
|
1209
|
+
skipPayload: true
|
|
1210
|
+
});
|
|
1211
|
+
} else {
|
|
1212
|
+
return consumer.consume(handler, { messageId });
|
|
1213
|
+
}
|
|
1214
|
+
} else {
|
|
1215
|
+
return consumer.consume(handler);
|
|
1216
|
+
}
|
|
1019
1217
|
}
|
|
1020
1218
|
export {
|
|
1021
1219
|
BadRequestError,
|