@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/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
- return async (request) => {
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,