@vercel/queue 0.0.0-alpha.15 → 0.0.0-alpha.17
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 +52 -9
- package/bin/local-discover.js +179 -0
- 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 +5 -3
package/dist/index.mjs
CHANGED
|
@@ -590,207 +590,6 @@ var QueueClient = class {
|
|
|
590
590
|
}
|
|
591
591
|
};
|
|
592
592
|
|
|
593
|
-
// src/consumer-group.ts
|
|
594
|
-
var ConsumerGroup = class {
|
|
595
|
-
client;
|
|
596
|
-
topicName;
|
|
597
|
-
consumerGroupName;
|
|
598
|
-
visibilityTimeout;
|
|
599
|
-
refreshInterval;
|
|
600
|
-
transport;
|
|
601
|
-
/**
|
|
602
|
-
* Create a new ConsumerGroup instance
|
|
603
|
-
* @param client QueueClient instance to use for API calls
|
|
604
|
-
* @param topicName Name of the topic to consume from
|
|
605
|
-
* @param consumerGroupName Name of the consumer group
|
|
606
|
-
* @param options Optional configuration
|
|
607
|
-
*/
|
|
608
|
-
constructor(client, topicName, consumerGroupName, options = {}) {
|
|
609
|
-
this.client = client;
|
|
610
|
-
this.topicName = topicName;
|
|
611
|
-
this.consumerGroupName = consumerGroupName;
|
|
612
|
-
this.visibilityTimeout = options.visibilityTimeoutSeconds || 30;
|
|
613
|
-
this.refreshInterval = options.refreshInterval || 10;
|
|
614
|
-
this.transport = options.transport || new JsonTransport();
|
|
615
|
-
}
|
|
616
|
-
/**
|
|
617
|
-
* Starts a background loop that periodically extends the visibility timeout for a message.
|
|
618
|
-
* This prevents the message from becoming visible to other consumers while it's being processed.
|
|
619
|
-
*
|
|
620
|
-
* The extension loop runs every `refreshInterval` seconds and updates the message's
|
|
621
|
-
* visibility timeout to `visibilityTimeout` seconds from the current time.
|
|
622
|
-
*
|
|
623
|
-
* @param messageId - The unique identifier of the message to extend visibility for
|
|
624
|
-
* @param ticket - The receipt ticket that proves ownership of the message
|
|
625
|
-
* @returns A function that when called will stop the extension loop
|
|
626
|
-
*
|
|
627
|
-
* @remarks
|
|
628
|
-
* - The first extension attempt occurs after `refreshInterval` seconds, not immediately
|
|
629
|
-
* - If an extension fails, the loop terminates with an error logged to console
|
|
630
|
-
* - The returned stop function is idempotent - calling it multiple times is safe
|
|
631
|
-
* - By default, the stop function returns immediately without waiting for in-flight
|
|
632
|
-
* - Pass `true` to the stop function to wait for any in-flight extension to complete
|
|
633
|
-
*/
|
|
634
|
-
startVisibilityExtension(messageId, ticket) {
|
|
635
|
-
let isRunning = true;
|
|
636
|
-
let resolveLifecycle;
|
|
637
|
-
let timeoutId = null;
|
|
638
|
-
const lifecyclePromise = new Promise((resolve) => {
|
|
639
|
-
resolveLifecycle = resolve;
|
|
640
|
-
});
|
|
641
|
-
const extend = async () => {
|
|
642
|
-
if (!isRunning) {
|
|
643
|
-
resolveLifecycle();
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
try {
|
|
647
|
-
await this.client.changeVisibility({
|
|
648
|
-
queueName: this.topicName,
|
|
649
|
-
consumerGroup: this.consumerGroupName,
|
|
650
|
-
messageId,
|
|
651
|
-
ticket,
|
|
652
|
-
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
653
|
-
});
|
|
654
|
-
if (isRunning) {
|
|
655
|
-
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
656
|
-
} else {
|
|
657
|
-
resolveLifecycle();
|
|
658
|
-
}
|
|
659
|
-
} catch (error) {
|
|
660
|
-
console.error(
|
|
661
|
-
`Failed to extend visibility for message ${messageId}:`,
|
|
662
|
-
error
|
|
663
|
-
);
|
|
664
|
-
resolveLifecycle();
|
|
665
|
-
}
|
|
666
|
-
};
|
|
667
|
-
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
668
|
-
return async (waitForCompletion = false) => {
|
|
669
|
-
isRunning = false;
|
|
670
|
-
if (timeoutId) {
|
|
671
|
-
clearTimeout(timeoutId);
|
|
672
|
-
timeoutId = null;
|
|
673
|
-
}
|
|
674
|
-
if (waitForCompletion) {
|
|
675
|
-
await lifecyclePromise;
|
|
676
|
-
} else {
|
|
677
|
-
resolveLifecycle();
|
|
678
|
-
}
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
/**
|
|
682
|
-
* Process a single message with the given handler
|
|
683
|
-
* @param message The message to process
|
|
684
|
-
* @param handler Function to process the message
|
|
685
|
-
*/
|
|
686
|
-
async processMessage(message, handler) {
|
|
687
|
-
const stopExtension = this.startVisibilityExtension(
|
|
688
|
-
message.messageId,
|
|
689
|
-
message.ticket
|
|
690
|
-
);
|
|
691
|
-
try {
|
|
692
|
-
const result = await handler(message.payload, {
|
|
693
|
-
messageId: message.messageId,
|
|
694
|
-
deliveryCount: message.deliveryCount,
|
|
695
|
-
createdAt: message.createdAt,
|
|
696
|
-
topicName: this.topicName,
|
|
697
|
-
consumerGroup: this.consumerGroupName
|
|
698
|
-
});
|
|
699
|
-
await stopExtension();
|
|
700
|
-
if (result && "timeoutSeconds" in result) {
|
|
701
|
-
await this.client.changeVisibility({
|
|
702
|
-
queueName: this.topicName,
|
|
703
|
-
consumerGroup: this.consumerGroupName,
|
|
704
|
-
messageId: message.messageId,
|
|
705
|
-
ticket: message.ticket,
|
|
706
|
-
visibilityTimeoutSeconds: result.timeoutSeconds
|
|
707
|
-
});
|
|
708
|
-
} else {
|
|
709
|
-
await this.client.deleteMessage({
|
|
710
|
-
queueName: this.topicName,
|
|
711
|
-
consumerGroup: this.consumerGroupName,
|
|
712
|
-
messageId: message.messageId,
|
|
713
|
-
ticket: message.ticket
|
|
714
|
-
});
|
|
715
|
-
}
|
|
716
|
-
} catch (error) {
|
|
717
|
-
await stopExtension();
|
|
718
|
-
if (this.transport.finalize && message.payload !== void 0 && message.payload !== null) {
|
|
719
|
-
try {
|
|
720
|
-
await this.transport.finalize(message.payload);
|
|
721
|
-
} catch (finalizeError) {
|
|
722
|
-
console.warn("Failed to finalize message payload:", finalizeError);
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
throw error;
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
async consume(handler, options) {
|
|
729
|
-
if (options?.messageId) {
|
|
730
|
-
if (options.skipPayload) {
|
|
731
|
-
const response = await this.client.receiveMessageById(
|
|
732
|
-
{
|
|
733
|
-
queueName: this.topicName,
|
|
734
|
-
consumerGroup: this.consumerGroupName,
|
|
735
|
-
messageId: options.messageId,
|
|
736
|
-
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
737
|
-
skipPayload: true
|
|
738
|
-
},
|
|
739
|
-
this.transport
|
|
740
|
-
);
|
|
741
|
-
await this.processMessage(
|
|
742
|
-
response.message,
|
|
743
|
-
handler
|
|
744
|
-
);
|
|
745
|
-
} else {
|
|
746
|
-
const response = await this.client.receiveMessageById(
|
|
747
|
-
{
|
|
748
|
-
queueName: this.topicName,
|
|
749
|
-
consumerGroup: this.consumerGroupName,
|
|
750
|
-
messageId: options.messageId,
|
|
751
|
-
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
752
|
-
},
|
|
753
|
-
this.transport
|
|
754
|
-
);
|
|
755
|
-
await this.processMessage(
|
|
756
|
-
response.message,
|
|
757
|
-
handler
|
|
758
|
-
);
|
|
759
|
-
}
|
|
760
|
-
} else {
|
|
761
|
-
let messageFound = false;
|
|
762
|
-
for await (const message of this.client.receiveMessages(
|
|
763
|
-
{
|
|
764
|
-
queueName: this.topicName,
|
|
765
|
-
consumerGroup: this.consumerGroupName,
|
|
766
|
-
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
767
|
-
limit: 1
|
|
768
|
-
},
|
|
769
|
-
this.transport
|
|
770
|
-
)) {
|
|
771
|
-
messageFound = true;
|
|
772
|
-
await this.processMessage(message, handler);
|
|
773
|
-
break;
|
|
774
|
-
}
|
|
775
|
-
if (!messageFound) {
|
|
776
|
-
throw new Error("No messages available");
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
/**
|
|
781
|
-
* Get the consumer group name
|
|
782
|
-
*/
|
|
783
|
-
get name() {
|
|
784
|
-
return this.consumerGroupName;
|
|
785
|
-
}
|
|
786
|
-
/**
|
|
787
|
-
* Get the topic name this consumer group is subscribed to
|
|
788
|
-
*/
|
|
789
|
-
get topic() {
|
|
790
|
-
return this.topicName;
|
|
791
|
-
}
|
|
792
|
-
};
|
|
793
|
-
|
|
794
593
|
// src/callback.ts
|
|
795
594
|
function validateWildcardPattern(pattern) {
|
|
796
595
|
const firstIndex = pattern.indexOf("*");
|
|
@@ -1039,6 +838,17 @@ function createMockCloudEventRequest(topicName, consumerGroup, messageId) {
|
|
|
1039
838
|
});
|
|
1040
839
|
}
|
|
1041
840
|
var DEV_CALLBACK_DELAY = 1e3;
|
|
841
|
+
function scheduleDevTimeout(topicName, messageId, timeoutSeconds) {
|
|
842
|
+
console.log(
|
|
843
|
+
`[Dev Mode] Message ${messageId} timed out for ${timeoutSeconds}s, will re-trigger`
|
|
844
|
+
);
|
|
845
|
+
setTimeout(() => {
|
|
846
|
+
console.log(
|
|
847
|
+
`[Dev Mode] Re-triggering callback for timed-out message ${messageId}`
|
|
848
|
+
);
|
|
849
|
+
triggerDevCallbacks(topicName, messageId);
|
|
850
|
+
}, timeoutSeconds * 1e3);
|
|
851
|
+
}
|
|
1042
852
|
function triggerDevCallbacks(topicName, messageId) {
|
|
1043
853
|
const handlersMap = findRouteHandlersForTopic(topicName);
|
|
1044
854
|
if (handlersMap.size === 0) {
|
|
@@ -1108,6 +918,214 @@ if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
|
1108
918
|
globalThis.__clearDevHandlers = clearDevHandlers;
|
|
1109
919
|
}
|
|
1110
920
|
|
|
921
|
+
// src/consumer-group.ts
|
|
922
|
+
var ConsumerGroup = class {
|
|
923
|
+
client;
|
|
924
|
+
topicName;
|
|
925
|
+
consumerGroupName;
|
|
926
|
+
visibilityTimeout;
|
|
927
|
+
refreshInterval;
|
|
928
|
+
transport;
|
|
929
|
+
/**
|
|
930
|
+
* Create a new ConsumerGroup instance
|
|
931
|
+
* @param client QueueClient instance to use for API calls
|
|
932
|
+
* @param topicName Name of the topic to consume from
|
|
933
|
+
* @param consumerGroupName Name of the consumer group
|
|
934
|
+
* @param options Optional configuration
|
|
935
|
+
*/
|
|
936
|
+
constructor(client, topicName, consumerGroupName, options = {}) {
|
|
937
|
+
this.client = client;
|
|
938
|
+
this.topicName = topicName;
|
|
939
|
+
this.consumerGroupName = consumerGroupName;
|
|
940
|
+
this.visibilityTimeout = options.visibilityTimeoutSeconds || 30;
|
|
941
|
+
this.refreshInterval = options.refreshInterval || 10;
|
|
942
|
+
this.transport = options.transport || new JsonTransport();
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Starts a background loop that periodically extends the visibility timeout for a message.
|
|
946
|
+
* This prevents the message from becoming visible to other consumers while it's being processed.
|
|
947
|
+
*
|
|
948
|
+
* The extension loop runs every `refreshInterval` seconds and updates the message's
|
|
949
|
+
* visibility timeout to `visibilityTimeout` seconds from the current time.
|
|
950
|
+
*
|
|
951
|
+
* @param messageId - The unique identifier of the message to extend visibility for
|
|
952
|
+
* @param ticket - The receipt ticket that proves ownership of the message
|
|
953
|
+
* @returns A function that when called will stop the extension loop
|
|
954
|
+
*
|
|
955
|
+
* @remarks
|
|
956
|
+
* - The first extension attempt occurs after `refreshInterval` seconds, not immediately
|
|
957
|
+
* - If an extension fails, the loop terminates with an error logged to console
|
|
958
|
+
* - The returned stop function is idempotent - calling it multiple times is safe
|
|
959
|
+
* - By default, the stop function returns immediately without waiting for in-flight
|
|
960
|
+
* - Pass `true` to the stop function to wait for any in-flight extension to complete
|
|
961
|
+
*/
|
|
962
|
+
startVisibilityExtension(messageId, ticket) {
|
|
963
|
+
let isRunning = true;
|
|
964
|
+
let resolveLifecycle;
|
|
965
|
+
let timeoutId = null;
|
|
966
|
+
const lifecyclePromise = new Promise((resolve) => {
|
|
967
|
+
resolveLifecycle = resolve;
|
|
968
|
+
});
|
|
969
|
+
const extend = async () => {
|
|
970
|
+
if (!isRunning) {
|
|
971
|
+
resolveLifecycle();
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
try {
|
|
975
|
+
await this.client.changeVisibility({
|
|
976
|
+
queueName: this.topicName,
|
|
977
|
+
consumerGroup: this.consumerGroupName,
|
|
978
|
+
messageId,
|
|
979
|
+
ticket,
|
|
980
|
+
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
981
|
+
});
|
|
982
|
+
if (isRunning) {
|
|
983
|
+
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
984
|
+
} else {
|
|
985
|
+
resolveLifecycle();
|
|
986
|
+
}
|
|
987
|
+
} catch (error) {
|
|
988
|
+
console.error(
|
|
989
|
+
`Failed to extend visibility for message ${messageId}:`,
|
|
990
|
+
error
|
|
991
|
+
);
|
|
992
|
+
resolveLifecycle();
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
timeoutId = setTimeout(() => extend(), this.refreshInterval * 1e3);
|
|
996
|
+
return async (waitForCompletion = false) => {
|
|
997
|
+
isRunning = false;
|
|
998
|
+
if (timeoutId) {
|
|
999
|
+
clearTimeout(timeoutId);
|
|
1000
|
+
timeoutId = null;
|
|
1001
|
+
}
|
|
1002
|
+
if (waitForCompletion) {
|
|
1003
|
+
await lifecyclePromise;
|
|
1004
|
+
} else {
|
|
1005
|
+
resolveLifecycle();
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Process a single message with the given handler
|
|
1011
|
+
* @param message The message to process
|
|
1012
|
+
* @param handler Function to process the message
|
|
1013
|
+
*/
|
|
1014
|
+
async processMessage(message, handler) {
|
|
1015
|
+
const stopExtension = this.startVisibilityExtension(
|
|
1016
|
+
message.messageId,
|
|
1017
|
+
message.ticket
|
|
1018
|
+
);
|
|
1019
|
+
try {
|
|
1020
|
+
const result = await handler(message.payload, {
|
|
1021
|
+
messageId: message.messageId,
|
|
1022
|
+
deliveryCount: message.deliveryCount,
|
|
1023
|
+
createdAt: message.createdAt,
|
|
1024
|
+
topicName: this.topicName,
|
|
1025
|
+
consumerGroup: this.consumerGroupName
|
|
1026
|
+
});
|
|
1027
|
+
await stopExtension();
|
|
1028
|
+
if (result && "timeoutSeconds" in result) {
|
|
1029
|
+
await this.client.changeVisibility({
|
|
1030
|
+
queueName: this.topicName,
|
|
1031
|
+
consumerGroup: this.consumerGroupName,
|
|
1032
|
+
messageId: message.messageId,
|
|
1033
|
+
ticket: message.ticket,
|
|
1034
|
+
visibilityTimeoutSeconds: result.timeoutSeconds
|
|
1035
|
+
});
|
|
1036
|
+
if (isDevMode()) {
|
|
1037
|
+
scheduleDevTimeout(
|
|
1038
|
+
this.topicName,
|
|
1039
|
+
message.messageId,
|
|
1040
|
+
result.timeoutSeconds
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
} else {
|
|
1044
|
+
await this.client.deleteMessage({
|
|
1045
|
+
queueName: this.topicName,
|
|
1046
|
+
consumerGroup: this.consumerGroupName,
|
|
1047
|
+
messageId: message.messageId,
|
|
1048
|
+
ticket: message.ticket
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
} catch (error) {
|
|
1052
|
+
await stopExtension();
|
|
1053
|
+
if (this.transport.finalize && message.payload !== void 0 && message.payload !== null) {
|
|
1054
|
+
try {
|
|
1055
|
+
await this.transport.finalize(message.payload);
|
|
1056
|
+
} catch (finalizeError) {
|
|
1057
|
+
console.warn("Failed to finalize message payload:", finalizeError);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
throw error;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
async consume(handler, options) {
|
|
1064
|
+
if (options?.messageId) {
|
|
1065
|
+
if (options.skipPayload) {
|
|
1066
|
+
const response = await this.client.receiveMessageById(
|
|
1067
|
+
{
|
|
1068
|
+
queueName: this.topicName,
|
|
1069
|
+
consumerGroup: this.consumerGroupName,
|
|
1070
|
+
messageId: options.messageId,
|
|
1071
|
+
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
1072
|
+
skipPayload: true
|
|
1073
|
+
},
|
|
1074
|
+
this.transport
|
|
1075
|
+
);
|
|
1076
|
+
await this.processMessage(
|
|
1077
|
+
response.message,
|
|
1078
|
+
handler
|
|
1079
|
+
);
|
|
1080
|
+
} else {
|
|
1081
|
+
const response = await this.client.receiveMessageById(
|
|
1082
|
+
{
|
|
1083
|
+
queueName: this.topicName,
|
|
1084
|
+
consumerGroup: this.consumerGroupName,
|
|
1085
|
+
messageId: options.messageId,
|
|
1086
|
+
visibilityTimeoutSeconds: this.visibilityTimeout
|
|
1087
|
+
},
|
|
1088
|
+
this.transport
|
|
1089
|
+
);
|
|
1090
|
+
await this.processMessage(
|
|
1091
|
+
response.message,
|
|
1092
|
+
handler
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
} else {
|
|
1096
|
+
let messageFound = false;
|
|
1097
|
+
for await (const message of this.client.receiveMessages(
|
|
1098
|
+
{
|
|
1099
|
+
queueName: this.topicName,
|
|
1100
|
+
consumerGroup: this.consumerGroupName,
|
|
1101
|
+
visibilityTimeoutSeconds: this.visibilityTimeout,
|
|
1102
|
+
limit: 1
|
|
1103
|
+
},
|
|
1104
|
+
this.transport
|
|
1105
|
+
)) {
|
|
1106
|
+
messageFound = true;
|
|
1107
|
+
await this.processMessage(message, handler);
|
|
1108
|
+
break;
|
|
1109
|
+
}
|
|
1110
|
+
if (!messageFound) {
|
|
1111
|
+
throw new Error("No messages available");
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Get the consumer group name
|
|
1117
|
+
*/
|
|
1118
|
+
get name() {
|
|
1119
|
+
return this.consumerGroupName;
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Get the topic name this consumer group is subscribed to
|
|
1123
|
+
*/
|
|
1124
|
+
get topic() {
|
|
1125
|
+
return this.topicName;
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1111
1129
|
// src/topic.ts
|
|
1112
1130
|
var Topic = class {
|
|
1113
1131
|
client;
|