@vercel/queue 0.0.0-alpha.15 → 0.0.0-alpha.16
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 +219 -201
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +219 -201
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -632,207 +632,6 @@ var QueueClient = class {
|
|
|
632
632
|
}
|
|
633
633
|
};
|
|
634
634
|
|
|
635
|
-
// src/consumer-group.ts
|
|
636
|
-
var ConsumerGroup = class {
|
|
637
|
-
client;
|
|
638
|
-
topicName;
|
|
639
|
-
consumerGroupName;
|
|
640
|
-
visibilityTimeout;
|
|
641
|
-
refreshInterval;
|
|
642
|
-
transport;
|
|
643
|
-
/**
|
|
644
|
-
* Create a new ConsumerGroup instance
|
|
645
|
-
* @param client QueueClient instance to use for API calls
|
|
646
|
-
* @param topicName Name of the topic to consume from
|
|
647
|
-
* @param consumerGroupName Name of the consumer group
|
|
648
|
-
* @param options Optional configuration
|
|
649
|
-
*/
|
|
650
|
-
constructor(client, topicName, consumerGroupName, options = {}) {
|
|
651
|
-
this.client = client;
|
|
652
|
-
this.topicName = topicName;
|
|
653
|
-
this.consumerGroupName = consumerGroupName;
|
|
654
|
-
this.visibilityTimeout = options.visibilityTimeoutSeconds || 30;
|
|
655
|
-
this.refreshInterval = options.refreshInterval || 10;
|
|
656
|
-
this.transport = options.transport || new JsonTransport();
|
|
657
|
-
}
|
|
658
|
-
/**
|
|
659
|
-
* Starts a background loop that periodically extends the visibility timeout for a message.
|
|
660
|
-
* This prevents the message from becoming visible to other consumers while it's being processed.
|
|
661
|
-
*
|
|
662
|
-
* The extension loop runs every `refreshInterval` seconds and updates the message's
|
|
663
|
-
* visibility timeout to `visibilityTimeout` seconds from the current time.
|
|
664
|
-
*
|
|
665
|
-
* @param messageId - The unique identifier of the message to extend visibility for
|
|
666
|
-
* @param ticket - The receipt ticket that proves ownership of the message
|
|
667
|
-
* @returns A function that when called will stop the extension loop
|
|
668
|
-
*
|
|
669
|
-
* @remarks
|
|
670
|
-
* - The first extension attempt occurs after `refreshInterval` seconds, not immediately
|
|
671
|
-
* - If an extension fails, the loop terminates with an error logged to console
|
|
672
|
-
* - The returned stop function is idempotent - calling it multiple times is safe
|
|
673
|
-
* - By default, the stop function returns immediately without waiting for in-flight
|
|
674
|
-
* - Pass `true` to the stop function to wait for any in-flight extension to complete
|
|
675
|
-
*/
|
|
676
|
-
startVisibilityExtension(messageId, ticket) {
|
|
677
|
-
let isRunning = true;
|
|
678
|
-
let resolveLifecycle;
|
|
679
|
-
let timeoutId = null;
|
|
680
|
-
const lifecyclePromise = new Promise((resolve) => {
|
|
681
|
-
resolveLifecycle = resolve;
|
|
682
|
-
});
|
|
683
|
-
const extend = async () => {
|
|
684
|
-
if (!isRunning) {
|
|
685
|
-
resolveLifecycle();
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
688
|
-
try {
|
|
689
|
-
await this.client.changeVisibility({
|
|
690
|
-
queueName: this.topicName,
|
|
691
|
-
consumerGroup: this.consumerGroupName,
|
|
692
|
-
messageId,
|
|
693
|
-
ticket,
|
|
694
|
-
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
695
|
-
});
|
|
696
|
-
if (isRunning) {
|
|
697
|
-
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
698
|
-
} else {
|
|
699
|
-
resolveLifecycle();
|
|
700
|
-
}
|
|
701
|
-
} catch (error) {
|
|
702
|
-
console.error(
|
|
703
|
-
`Failed to extend visibility for message ${messageId}:`,
|
|
704
|
-
error
|
|
705
|
-
);
|
|
706
|
-
resolveLifecycle();
|
|
707
|
-
}
|
|
708
|
-
};
|
|
709
|
-
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
710
|
-
return async (waitForCompletion = false) => {
|
|
711
|
-
isRunning = false;
|
|
712
|
-
if (timeoutId) {
|
|
713
|
-
clearTimeout(timeoutId);
|
|
714
|
-
timeoutId = null;
|
|
715
|
-
}
|
|
716
|
-
if (waitForCompletion) {
|
|
717
|
-
await lifecyclePromise;
|
|
718
|
-
} else {
|
|
719
|
-
resolveLifecycle();
|
|
720
|
-
}
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Process a single message with the given handler
|
|
725
|
-
* @param message The message to process
|
|
726
|
-
* @param handler Function to process the message
|
|
727
|
-
*/
|
|
728
|
-
async processMessage(message, handler) {
|
|
729
|
-
const stopExtension = this.startVisibilityExtension(
|
|
730
|
-
message.messageId,
|
|
731
|
-
message.ticket
|
|
732
|
-
);
|
|
733
|
-
try {
|
|
734
|
-
const result = await handler(message.payload, {
|
|
735
|
-
messageId: message.messageId,
|
|
736
|
-
deliveryCount: message.deliveryCount,
|
|
737
|
-
createdAt: message.createdAt,
|
|
738
|
-
topicName: this.topicName,
|
|
739
|
-
consumerGroup: this.consumerGroupName
|
|
740
|
-
});
|
|
741
|
-
await stopExtension();
|
|
742
|
-
if (result && "timeoutSeconds" in result) {
|
|
743
|
-
await this.client.changeVisibility({
|
|
744
|
-
queueName: this.topicName,
|
|
745
|
-
consumerGroup: this.consumerGroupName,
|
|
746
|
-
messageId: message.messageId,
|
|
747
|
-
ticket: message.ticket,
|
|
748
|
-
visibilityTimeoutSeconds: result.timeoutSeconds
|
|
749
|
-
});
|
|
750
|
-
} else {
|
|
751
|
-
await this.client.deleteMessage({
|
|
752
|
-
queueName: this.topicName,
|
|
753
|
-
consumerGroup: this.consumerGroupName,
|
|
754
|
-
messageId: message.messageId,
|
|
755
|
-
ticket: message.ticket
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
} catch (error) {
|
|
759
|
-
await stopExtension();
|
|
760
|
-
if (this.transport.finalize && message.payload !== void 0 && message.payload !== null) {
|
|
761
|
-
try {
|
|
762
|
-
await this.transport.finalize(message.payload);
|
|
763
|
-
} catch (finalizeError) {
|
|
764
|
-
console.warn("Failed to finalize message payload:", finalizeError);
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
throw error;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
async consume(handler, options) {
|
|
771
|
-
if (options?.messageId) {
|
|
772
|
-
if (options.skipPayload) {
|
|
773
|
-
const response = await this.client.receiveMessageById(
|
|
774
|
-
{
|
|
775
|
-
queueName: this.topicName,
|
|
776
|
-
consumerGroup: this.consumerGroupName,
|
|
777
|
-
messageId: options.messageId,
|
|
778
|
-
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
779
|
-
skipPayload: true
|
|
780
|
-
},
|
|
781
|
-
this.transport
|
|
782
|
-
);
|
|
783
|
-
await this.processMessage(
|
|
784
|
-
response.message,
|
|
785
|
-
handler
|
|
786
|
-
);
|
|
787
|
-
} else {
|
|
788
|
-
const response = await this.client.receiveMessageById(
|
|
789
|
-
{
|
|
790
|
-
queueName: this.topicName,
|
|
791
|
-
consumerGroup: this.consumerGroupName,
|
|
792
|
-
messageId: options.messageId,
|
|
793
|
-
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
794
|
-
},
|
|
795
|
-
this.transport
|
|
796
|
-
);
|
|
797
|
-
await this.processMessage(
|
|
798
|
-
response.message,
|
|
799
|
-
handler
|
|
800
|
-
);
|
|
801
|
-
}
|
|
802
|
-
} else {
|
|
803
|
-
let messageFound = false;
|
|
804
|
-
for await (const message of this.client.receiveMessages(
|
|
805
|
-
{
|
|
806
|
-
queueName: this.topicName,
|
|
807
|
-
consumerGroup: this.consumerGroupName,
|
|
808
|
-
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
809
|
-
limit: 1
|
|
810
|
-
},
|
|
811
|
-
this.transport
|
|
812
|
-
)) {
|
|
813
|
-
messageFound = true;
|
|
814
|
-
await this.processMessage(message, handler);
|
|
815
|
-
break;
|
|
816
|
-
}
|
|
817
|
-
if (!messageFound) {
|
|
818
|
-
throw new Error("No messages available");
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
/**
|
|
823
|
-
* Get the consumer group name
|
|
824
|
-
*/
|
|
825
|
-
get name() {
|
|
826
|
-
return this.consumerGroupName;
|
|
827
|
-
}
|
|
828
|
-
/**
|
|
829
|
-
* Get the topic name this consumer group is subscribed to
|
|
830
|
-
*/
|
|
831
|
-
get topic() {
|
|
832
|
-
return this.topicName;
|
|
833
|
-
}
|
|
834
|
-
};
|
|
835
|
-
|
|
836
635
|
// src/callback.ts
|
|
837
636
|
function validateWildcardPattern(pattern) {
|
|
838
637
|
const firstIndex = pattern.indexOf("*");
|
|
@@ -1081,6 +880,17 @@ function createMockCloudEventRequest(topicName, consumerGroup, messageId) {
|
|
|
1081
880
|
});
|
|
1082
881
|
}
|
|
1083
882
|
var DEV_CALLBACK_DELAY = 1e3;
|
|
883
|
+
function scheduleDevTimeout(topicName, messageId, timeoutSeconds) {
|
|
884
|
+
console.log(
|
|
885
|
+
`[Dev Mode] Message ${messageId} timed out for ${timeoutSeconds}s, will re-trigger`
|
|
886
|
+
);
|
|
887
|
+
setTimeout(() => {
|
|
888
|
+
console.log(
|
|
889
|
+
`[Dev Mode] Re-triggering callback for timed-out message ${messageId}`
|
|
890
|
+
);
|
|
891
|
+
triggerDevCallbacks(topicName, messageId);
|
|
892
|
+
}, timeoutSeconds * 1e3);
|
|
893
|
+
}
|
|
1084
894
|
function triggerDevCallbacks(topicName, messageId) {
|
|
1085
895
|
const handlersMap = findRouteHandlersForTopic(topicName);
|
|
1086
896
|
if (handlersMap.size === 0) {
|
|
@@ -1150,6 +960,214 @@ if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
|
1150
960
|
globalThis.__clearDevHandlers = clearDevHandlers;
|
|
1151
961
|
}
|
|
1152
962
|
|
|
963
|
+
// src/consumer-group.ts
|
|
964
|
+
var ConsumerGroup = class {
|
|
965
|
+
client;
|
|
966
|
+
topicName;
|
|
967
|
+
consumerGroupName;
|
|
968
|
+
visibilityTimeout;
|
|
969
|
+
refreshInterval;
|
|
970
|
+
transport;
|
|
971
|
+
/**
|
|
972
|
+
* Create a new ConsumerGroup instance
|
|
973
|
+
* @param client QueueClient instance to use for API calls
|
|
974
|
+
* @param topicName Name of the topic to consume from
|
|
975
|
+
* @param consumerGroupName Name of the consumer group
|
|
976
|
+
* @param options Optional configuration
|
|
977
|
+
*/
|
|
978
|
+
constructor(client, topicName, consumerGroupName, options = {}) {
|
|
979
|
+
this.client = client;
|
|
980
|
+
this.topicName = topicName;
|
|
981
|
+
this.consumerGroupName = consumerGroupName;
|
|
982
|
+
this.visibilityTimeout = options.visibilityTimeoutSeconds || 30;
|
|
983
|
+
this.refreshInterval = options.refreshInterval || 10;
|
|
984
|
+
this.transport = options.transport || new JsonTransport();
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Starts a background loop that periodically extends the visibility timeout for a message.
|
|
988
|
+
* This prevents the message from becoming visible to other consumers while it's being processed.
|
|
989
|
+
*
|
|
990
|
+
* The extension loop runs every `refreshInterval` seconds and updates the message's
|
|
991
|
+
* visibility timeout to `visibilityTimeout` seconds from the current time.
|
|
992
|
+
*
|
|
993
|
+
* @param messageId - The unique identifier of the message to extend visibility for
|
|
994
|
+
* @param ticket - The receipt ticket that proves ownership of the message
|
|
995
|
+
* @returns A function that when called will stop the extension loop
|
|
996
|
+
*
|
|
997
|
+
* @remarks
|
|
998
|
+
* - The first extension attempt occurs after `refreshInterval` seconds, not immediately
|
|
999
|
+
* - If an extension fails, the loop terminates with an error logged to console
|
|
1000
|
+
* - The returned stop function is idempotent - calling it multiple times is safe
|
|
1001
|
+
* - By default, the stop function returns immediately without waiting for in-flight
|
|
1002
|
+
* - Pass `true` to the stop function to wait for any in-flight extension to complete
|
|
1003
|
+
*/
|
|
1004
|
+
startVisibilityExtension(messageId, ticket) {
|
|
1005
|
+
let isRunning = true;
|
|
1006
|
+
let resolveLifecycle;
|
|
1007
|
+
let timeoutId = null;
|
|
1008
|
+
const lifecyclePromise = new Promise((resolve) => {
|
|
1009
|
+
resolveLifecycle = resolve;
|
|
1010
|
+
});
|
|
1011
|
+
const extend = async () => {
|
|
1012
|
+
if (!isRunning) {
|
|
1013
|
+
resolveLifecycle();
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
try {
|
|
1017
|
+
await this.client.changeVisibility({
|
|
1018
|
+
queueName: this.topicName,
|
|
1019
|
+
consumerGroup: this.consumerGroupName,
|
|
1020
|
+
messageId,
|
|
1021
|
+
ticket,
|
|
1022
|
+
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
1023
|
+
});
|
|
1024
|
+
if (isRunning) {
|
|
1025
|
+
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
1026
|
+
} else {
|
|
1027
|
+
resolveLifecycle();
|
|
1028
|
+
}
|
|
1029
|
+
} catch (error) {
|
|
1030
|
+
console.error(
|
|
1031
|
+
`Failed to extend visibility for message ${messageId}:`,
|
|
1032
|
+
error
|
|
1033
|
+
);
|
|
1034
|
+
resolveLifecycle();
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
1038
|
+
return async (waitForCompletion = false) => {
|
|
1039
|
+
isRunning = false;
|
|
1040
|
+
if (timeoutId) {
|
|
1041
|
+
clearTimeout(timeoutId);
|
|
1042
|
+
timeoutId = null;
|
|
1043
|
+
}
|
|
1044
|
+
if (waitForCompletion) {
|
|
1045
|
+
await lifecyclePromise;
|
|
1046
|
+
} else {
|
|
1047
|
+
resolveLifecycle();
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Process a single message with the given handler
|
|
1053
|
+
* @param message The message to process
|
|
1054
|
+
* @param handler Function to process the message
|
|
1055
|
+
*/
|
|
1056
|
+
async processMessage(message, handler) {
|
|
1057
|
+
const stopExtension = this.startVisibilityExtension(
|
|
1058
|
+
message.messageId,
|
|
1059
|
+
message.ticket
|
|
1060
|
+
);
|
|
1061
|
+
try {
|
|
1062
|
+
const result = await handler(message.payload, {
|
|
1063
|
+
messageId: message.messageId,
|
|
1064
|
+
deliveryCount: message.deliveryCount,
|
|
1065
|
+
createdAt: message.createdAt,
|
|
1066
|
+
topicName: this.topicName,
|
|
1067
|
+
consumerGroup: this.consumerGroupName
|
|
1068
|
+
});
|
|
1069
|
+
await stopExtension();
|
|
1070
|
+
if (result && "timeoutSeconds" in result) {
|
|
1071
|
+
await this.client.changeVisibility({
|
|
1072
|
+
queueName: this.topicName,
|
|
1073
|
+
consumerGroup: this.consumerGroupName,
|
|
1074
|
+
messageId: message.messageId,
|
|
1075
|
+
ticket: message.ticket,
|
|
1076
|
+
visibilityTimeoutSeconds: result.timeoutSeconds
|
|
1077
|
+
});
|
|
1078
|
+
if (isDevMode()) {
|
|
1079
|
+
scheduleDevTimeout(
|
|
1080
|
+
this.topicName,
|
|
1081
|
+
message.messageId,
|
|
1082
|
+
result.timeoutSeconds
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1085
|
+
} else {
|
|
1086
|
+
await this.client.deleteMessage({
|
|
1087
|
+
queueName: this.topicName,
|
|
1088
|
+
consumerGroup: this.consumerGroupName,
|
|
1089
|
+
messageId: message.messageId,
|
|
1090
|
+
ticket: message.ticket
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
await stopExtension();
|
|
1095
|
+
if (this.transport.finalize && message.payload !== void 0 && message.payload !== null) {
|
|
1096
|
+
try {
|
|
1097
|
+
await this.transport.finalize(message.payload);
|
|
1098
|
+
} catch (finalizeError) {
|
|
1099
|
+
console.warn("Failed to finalize message payload:", finalizeError);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
throw error;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
async consume(handler, options) {
|
|
1106
|
+
if (options?.messageId) {
|
|
1107
|
+
if (options.skipPayload) {
|
|
1108
|
+
const response = await this.client.receiveMessageById(
|
|
1109
|
+
{
|
|
1110
|
+
queueName: this.topicName,
|
|
1111
|
+
consumerGroup: this.consumerGroupName,
|
|
1112
|
+
messageId: options.messageId,
|
|
1113
|
+
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
1114
|
+
skipPayload: true
|
|
1115
|
+
},
|
|
1116
|
+
this.transport
|
|
1117
|
+
);
|
|
1118
|
+
await this.processMessage(
|
|
1119
|
+
response.message,
|
|
1120
|
+
handler
|
|
1121
|
+
);
|
|
1122
|
+
} else {
|
|
1123
|
+
const response = await this.client.receiveMessageById(
|
|
1124
|
+
{
|
|
1125
|
+
queueName: this.topicName,
|
|
1126
|
+
consumerGroup: this.consumerGroupName,
|
|
1127
|
+
messageId: options.messageId,
|
|
1128
|
+
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
1129
|
+
},
|
|
1130
|
+
this.transport
|
|
1131
|
+
);
|
|
1132
|
+
await this.processMessage(
|
|
1133
|
+
response.message,
|
|
1134
|
+
handler
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
} else {
|
|
1138
|
+
let messageFound = false;
|
|
1139
|
+
for await (const message of this.client.receiveMessages(
|
|
1140
|
+
{
|
|
1141
|
+
queueName: this.topicName,
|
|
1142
|
+
consumerGroup: this.consumerGroupName,
|
|
1143
|
+
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
1144
|
+
limit: 1
|
|
1145
|
+
},
|
|
1146
|
+
this.transport
|
|
1147
|
+
)) {
|
|
1148
|
+
messageFound = true;
|
|
1149
|
+
await this.processMessage(message, handler);
|
|
1150
|
+
break;
|
|
1151
|
+
}
|
|
1152
|
+
if (!messageFound) {
|
|
1153
|
+
throw new Error("No messages available");
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Get the consumer group name
|
|
1159
|
+
*/
|
|
1160
|
+
get name() {
|
|
1161
|
+
return this.consumerGroupName;
|
|
1162
|
+
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Get the topic name this consumer group is subscribed to
|
|
1165
|
+
*/
|
|
1166
|
+
get topic() {
|
|
1167
|
+
return this.topicName;
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1153
1171
|
// src/topic.ts
|
|
1154
1172
|
var Topic = class {
|
|
1155
1173
|
client;
|