@vercel/queue 0.1.1 → 0.1.3

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
@@ -78,6 +78,8 @@ import { parseMultipartStream } from "mixpart";
78
78
  import * as fs from "fs";
79
79
  import * as net from "net";
80
80
  import * as path from "path";
81
+ import { minimatch } from "minimatch";
82
+ import pc from "picocolors";
81
83
 
82
84
  // src/types.ts
83
85
  var MessageNotFoundError = class extends Error {
@@ -340,11 +342,12 @@ var ConsumerGroup = class {
340
342
  message.receiptHandle,
341
343
  options
342
344
  );
345
+ const DEFAULT_RETENTION_MS = 864e5;
343
346
  const metadata = {
344
347
  messageId: message.messageId,
345
348
  deliveryCount: message.deliveryCount,
346
349
  createdAt: message.createdAt,
347
- expiresAt: message.expiresAt,
350
+ expiresAt: message.expiresAt ?? new Date(message.createdAt.getTime() + DEFAULT_RETENTION_MS),
348
351
  topicName: this.topicName,
349
352
  consumerGroup: this.consumerGroupName,
350
353
  region: this.client.getRegion()
@@ -498,7 +501,9 @@ var Topic = class {
498
501
  invokeDevHandlers(
499
502
  this.topicName,
500
503
  result.messageId,
501
- this.client.getRegion()
504
+ this.client.getRegion(),
505
+ options?.delaySeconds,
506
+ options?.retentionSeconds
502
507
  );
503
508
  }
504
509
  return { messageId: result.messageId };
@@ -704,12 +709,30 @@ async function handleCallback(handler, request, options) {
704
709
  }
705
710
 
706
711
  // src/dev.ts
712
+ var PREFIX = pc.cyan("[queue]");
713
+ var OK = pc.green("\u2713");
714
+ var FAIL = pc.red("\u2717");
715
+ var RETRY = pc.yellow("\u21BB");
707
716
  function isDevMode() {
708
717
  return process.env.NODE_ENV === "development";
709
718
  }
710
719
  var ROUTE_MAPPINGS_KEY = Symbol.for("@vercel/queue.devRouteMappings");
711
720
  function filePathToConsumerGroup(filePath) {
712
- return filePath.replace(/_/g, "__").replace(/\//g, "_S").replace(/\./g, "_D");
721
+ let result = "";
722
+ for (const char of filePath) {
723
+ if (char === "_") {
724
+ result += "__";
725
+ } else if (char === "/") {
726
+ result += "_S";
727
+ } else if (char === ".") {
728
+ result += "_D";
729
+ } else if (/[A-Za-z0-9-]/.test(char)) {
730
+ result += char;
731
+ } else {
732
+ result += "_" + char.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0");
733
+ }
734
+ }
735
+ return result;
713
736
  }
714
737
  function getDevRouteMappings() {
715
738
  const g = globalThis;
@@ -734,21 +757,22 @@ function getDevRouteMappings() {
734
757
  if (!trigger.type?.startsWith("queue/") || !trigger.topic) continue;
735
758
  if (trigger.type !== "queue/v2beta") {
736
759
  console.warn(
737
- `[Dev Mode] Unsupported trigger type "${trigger.type}" for topic "${trigger.topic}" in ${filePath}. Use "queue/v2beta" instead.`
760
+ `${PREFIX} Unsupported trigger type "${trigger.type}" for topic "${trigger.topic}" in ${filePath}. Use "queue/v2beta" instead.`
738
761
  );
739
762
  continue;
740
763
  }
741
764
  mappings.push({
742
765
  filePath,
743
766
  topic: trigger.topic,
744
- consumer: filePathToConsumerGroup(filePath)
767
+ consumer: filePathToConsumerGroup(filePath),
768
+ retryAfterSeconds: trigger.retryAfterSeconds
745
769
  });
746
770
  }
747
771
  }
748
772
  g[ROUTE_MAPPINGS_KEY] = mappings.length > 0 ? mappings : null;
749
773
  return g[ROUTE_MAPPINGS_KEY];
750
774
  } catch (error) {
751
- console.warn("[Dev Mode] Failed to read vercel.json:", error);
775
+ console.warn(`${PREFIX} Failed to read vercel.json:`, error);
752
776
  g[ROUTE_MAPPINGS_KEY] = null;
753
777
  return null;
754
778
  }
@@ -763,6 +787,20 @@ function findMatchingRoutes(topicName) {
763
787
  return mapping.topic === topicName;
764
788
  });
765
789
  }
790
+ function findRetryAfterSeconds(topicName, consumerGroup) {
791
+ const routes = findMatchingRoutes(topicName);
792
+ const route = routes.find((r) => r.consumer === consumerGroup);
793
+ return route?.retryAfterSeconds;
794
+ }
795
+ function stripSrcPrefix(filePath) {
796
+ if (/^src\/(app|pages|server)\//.test(filePath)) {
797
+ return filePath.slice(4);
798
+ }
799
+ return null;
800
+ }
801
+ function matchesFunctionsPattern(sourceFile, pattern) {
802
+ return sourceFile === pattern || minimatch(sourceFile, pattern);
803
+ }
766
804
  function findMappingsForFile(absolutePath) {
767
805
  const mappings = getDevRouteMappings();
768
806
  if (!mappings) return [];
@@ -774,7 +812,10 @@ function findMappingsForFile(absolutePath) {
774
812
  return [];
775
813
  }
776
814
  const normalized = relative2.replace(/\\/g, "/");
777
- return mappings.filter((m) => m.filePath === normalized);
815
+ const stripped = stripSrcPrefix(normalized);
816
+ return mappings.filter(
817
+ (m) => matchesFunctionsPattern(normalized, m.filePath) || stripped !== null && matchesFunctionsPattern(stripped, m.filePath)
818
+ );
778
819
  }
779
820
  function parseFrameFilePath(line) {
780
821
  let match = line.match(/\((.+?):\d+:\d+\)/);
@@ -868,7 +909,7 @@ function registerDevHandler(handler, client, options, _testCallerPath) {
868
909
  const callerPath = _testCallerPath ?? extractCallerFilePath();
869
910
  if (!callerPath) {
870
911
  console.warn(
871
- "[Dev Mode] Could not determine caller file path for handler registration."
912
+ `${PREFIX} Could not determine caller file path for handler registration.`
872
913
  );
873
914
  return;
874
915
  }
@@ -880,6 +921,9 @@ function registerDevHandler(handler, client, options, _testCallerPath) {
880
921
  );
881
922
  if (!registered) {
882
923
  const allMappings = getDevRouteMappings();
924
+ if (allMappings && allMappings.length > 0) {
925
+ return;
926
+ }
883
927
  const cwd = process.cwd();
884
928
  let relative2;
885
929
  try {
@@ -887,19 +931,8 @@ function registerDevHandler(handler, client, options, _testCallerPath) {
887
931
  } catch {
888
932
  relative2 = callerPath;
889
933
  }
890
- if (allMappings && allMappings.length > 0) {
891
- const configuredFiles = Array.from(
892
- new Set(allMappings.map((m) => m.filePath))
893
- );
894
- console.warn(
895
- `[Dev Mode] handleCallback() in ${relative2} does not match any queue route in vercel.json. This handler won't receive messages.
896
- Configured queue routes: [${configuredFiles.join(", ")}]
897
- If this path is a bundled chunk, keep handleCallback()/handleNodeCallback() at module scope and let dev-mode route priming load the mapped file.`
898
- );
899
- return;
900
- }
901
934
  console.warn(
902
- `[Dev Mode] handleCallback() in ${relative2} has no matching experimentalTriggers in vercel.json. This handler won't receive messages.
935
+ `${PREFIX} handleCallback() in ${relative2} has no matching experimentalTriggers in vercel.json. This handler won't receive messages.
903
936
 
904
937
  Add a trigger to vercel.json:
905
938
  "${relative2}": {
@@ -1023,7 +1056,7 @@ async function invokeWithRetry(handler, request, options) {
1023
1056
  }
1024
1057
  }
1025
1058
  function filePathToUrlPath(filePath) {
1026
- let urlPath = filePath.replace(/^app\//, "/").replace(/^pages\//, "/").replace(/^server\//, "/").replace(/^src\/routes\//, "/").replace(/\/route\.(ts|mts|js|mjs|tsx|jsx)$/, "").replace(/\/\+server\.(ts|mts|js|mjs|tsx|jsx)$/, "").replace(/\.(ts|mts|js|mjs|tsx|jsx)$/, "");
1059
+ let urlPath = filePath.replace(/^src\/app\//, "/").replace(/^src\/pages\//, "/").replace(/^src\/server\//, "/").replace(/^src\/routes\//, "/").replace(/^app\//, "/").replace(/^pages\//, "/").replace(/^server\//, "/").replace(/\/route\.(ts|mts|js|mjs|tsx|jsx)$/, "").replace(/\/\+server\.(ts|mts|js|mjs|tsx|jsx)$/, "").replace(/\.(ts|mts|js|mjs|tsx|jsx)$/, "");
1027
1060
  if (!urlPath.startsWith("/")) {
1028
1061
  urlPath = "/" + urlPath;
1029
1062
  }
@@ -1116,7 +1149,7 @@ function buildNoHandlerWarning(topicName, routes, diagnostics) {
1116
1149
  Import failures: ` + diagnostics.importFailures.slice(0, 2).map((f) => `${f.filePath} (${f.reason})`).join("; ") : "";
1117
1150
  const primeSummary = diagnostics.primeFailures.length > 0 ? `
1118
1151
  Prime failures: ` + diagnostics.primeFailures.slice(0, 3).map((f) => `${f.url} (${f.reason})`).join("; ") : "";
1119
- return `[Dev Mode] No registered handler for topic "${topicName}". vercel.json maps this topic to [${files.join(", ")}] but auto-loading failed.
1152
+ return `${PREFIX} No registered handler for topic "${topicName}". vercel.json maps this topic to [${files.join(", ")}] but auto-loading failed.
1120
1153
  ${portSummary}${importSummary}${primeSummary}
1121
1154
  Ensure your dev server is running, set PORT if needed, and confirm mapped route files call handleCallback()/handleNodeCallback() at module scope.
1122
1155
  ` + (suggestedUrls.length > 0 ? `Try opening: ${suggestedUrls.join(" or ")}` : "Set PORT (or NEXT_PORT/NUXT_PORT/VITE_PORT) and try sending again.");
@@ -1126,18 +1159,106 @@ function isHandlerRegistered(topicName, consumerGroup) {
1126
1159
  (h) => h.consumerGroup === consumerGroup
1127
1160
  );
1128
1161
  }
1129
- function invokeDevHandlers(topicName, messageId, region, delaySeconds) {
1162
+ var DEV_REDELIVERY_MAX_DELAY_S = 10;
1163
+ var DEV_REDELIVERY_DEFAULT_DELAY_S = 2;
1164
+ var DEV_REDELIVERY_MAX_ATTEMPTS = 10;
1165
+ var DEFAULT_RETENTION_S = 86400;
1166
+ function scheduleDevRedelivery(ctx, delayS) {
1167
+ const cappedDelay = Math.min(Math.max(delayS, 0), DEV_REDELIVERY_MAX_DELAY_S);
1168
+ console.log(
1169
+ `${PREFIX} ${RETRY} Scheduling re-delivery in ${cappedDelay}s: topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}"`
1170
+ );
1171
+ setTimeout(async () => {
1172
+ const nextDeliveryCount = ctx.deliveryCount + 1;
1173
+ const expiresAt = new Date(
1174
+ ctx.createdAt.getTime() + ctx.retentionSeconds * 1e3
1175
+ );
1176
+ if (Date.now() >= expiresAt.getTime()) {
1177
+ console.log(
1178
+ `${PREFIX} Message expired, stopping retries: topic="${ctx.topicName}" messageId="${ctx.messageId}"`
1179
+ );
1180
+ return;
1181
+ }
1182
+ if (nextDeliveryCount > DEV_REDELIVERY_MAX_ATTEMPTS) {
1183
+ console.log(
1184
+ `${PREFIX} Max re-deliveries (${DEV_REDELIVERY_MAX_ATTEMPTS}) reached: topic="${ctx.topicName}" messageId="${ctx.messageId}"`
1185
+ );
1186
+ return;
1187
+ }
1188
+ const metadata = {
1189
+ messageId: ctx.messageId,
1190
+ deliveryCount: nextDeliveryCount,
1191
+ createdAt: ctx.createdAt,
1192
+ expiresAt,
1193
+ topicName: ctx.topicName,
1194
+ consumerGroup: ctx.consumerGroup,
1195
+ region: ctx.region
1196
+ };
1197
+ console.log(
1198
+ `${PREFIX} Re-delivering: topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}" deliveryCount=${nextDeliveryCount}`
1199
+ );
1200
+ let succeeded = true;
1201
+ let nextRetryAfterS = null;
1202
+ let nextAcknowledged = false;
1203
+ try {
1204
+ await ctx.handler(ctx.payload, metadata);
1205
+ } catch (error) {
1206
+ succeeded = false;
1207
+ if (ctx.retry) {
1208
+ let directive;
1209
+ try {
1210
+ directive = ctx.retry(error, metadata);
1211
+ } catch (retryErr) {
1212
+ console.warn(`${PREFIX} retry handler threw:`, retryErr);
1213
+ }
1214
+ if (directive && "afterSeconds" in directive) {
1215
+ nextRetryAfterS = directive.afterSeconds;
1216
+ } else if (directive && "acknowledge" in directive) {
1217
+ nextAcknowledged = true;
1218
+ }
1219
+ }
1220
+ if (!nextAcknowledged) {
1221
+ console.error(
1222
+ `${PREFIX} ${FAIL} Handler error on re-delivery: topic="${ctx.topicName}" messageId="${ctx.messageId}"`,
1223
+ error
1224
+ );
1225
+ }
1226
+ }
1227
+ if (succeeded) {
1228
+ console.log(
1229
+ `${PREFIX} ${OK} Message processed on re-delivery: topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}"`
1230
+ );
1231
+ } else if (nextAcknowledged) {
1232
+ console.log(
1233
+ `${PREFIX} ${OK} Message acknowledged (will not retry): topic="${ctx.topicName}" consumer="${ctx.consumerGroup}" messageId="${ctx.messageId}"`
1234
+ );
1235
+ } else {
1236
+ const nextDelay = nextRetryAfterS ?? ctx.defaultRetryDelayS;
1237
+ scheduleDevRedelivery(
1238
+ { ...ctx, deliveryCount: nextDeliveryCount },
1239
+ nextDelay
1240
+ );
1241
+ }
1242
+ }, cappedDelay * 1e3);
1243
+ }
1244
+ function invokeDevHandlers(topicName, messageId, region, delaySeconds, retentionSeconds) {
1130
1245
  if (delaySeconds && delaySeconds > 0) {
1131
1246
  console.log(
1132
- `[Dev Mode] Message sent with delay: topic="${topicName}" messageId="${messageId}" delay=${delaySeconds}s`
1247
+ `${PREFIX} Message sent with delay: topic="${topicName}" messageId="${messageId}" delay=${delaySeconds}s`
1133
1248
  );
1134
1249
  setTimeout(() => {
1135
- invokeDevHandlers(topicName, messageId, region);
1250
+ invokeDevHandlers(
1251
+ topicName,
1252
+ messageId,
1253
+ region,
1254
+ void 0,
1255
+ retentionSeconds
1256
+ );
1136
1257
  }, delaySeconds * 1e3);
1137
1258
  return;
1138
1259
  }
1139
1260
  console.log(
1140
- `[Dev Mode] Message sent: topic="${topicName}" messageId="${messageId}"`
1261
+ `${PREFIX} Message sent: topic="${topicName}" messageId="${messageId}"`
1141
1262
  );
1142
1263
  (async () => {
1143
1264
  let handlers = lookupHandlers(topicName);
@@ -1164,7 +1285,7 @@ function invokeDevHandlers(topicName, messageId, region, delaySeconds) {
1164
1285
  );
1165
1286
  } else {
1166
1287
  console.warn(
1167
- `[Dev Mode] No registered handler for topic "${topicName}".
1288
+ `${PREFIX} No registered handler for topic "${topicName}".
1168
1289
  Ensure vercel.json has a matching experimentalTriggers entry and the route file calls handleCallback().`
1169
1290
  );
1170
1291
  }
@@ -1172,9 +1293,36 @@ Ensure vercel.json has a matching experimentalTriggers entry and the route file
1172
1293
  }
1173
1294
  const consumerGroups = handlers.map((h) => h.consumerGroup);
1174
1295
  console.log(
1175
- `[Dev Mode] Invoking handlers for topic="${topicName}" messageId="${messageId}" \u2192 consumers: [${consumerGroups.join(", ")}]`
1296
+ `${PREFIX} Invoking handlers for topic="${topicName}" messageId="${messageId}" \u2192 consumers: [${consumerGroups.join(", ")}]`
1176
1297
  );
1298
+ const effectiveRetention = retentionSeconds ?? DEFAULT_RETENTION_S;
1177
1299
  for (const entry of handlers) {
1300
+ let capturedPayload;
1301
+ let capturedCreatedAt = /* @__PURE__ */ new Date();
1302
+ let capturedDeliveryCount = 1;
1303
+ let handlerSucceeded = true;
1304
+ let retryAfterS = null;
1305
+ let retryAcknowledged = false;
1306
+ const wrappedHandler = async (message, metadata) => {
1307
+ capturedPayload = message;
1308
+ capturedCreatedAt = metadata.createdAt;
1309
+ capturedDeliveryCount = metadata.deliveryCount;
1310
+ try {
1311
+ await entry.handler(message, metadata);
1312
+ } catch (error) {
1313
+ handlerSucceeded = false;
1314
+ throw error;
1315
+ }
1316
+ };
1317
+ const wrappedRetry = entry.options?.retry ? (error, metadata) => {
1318
+ const directive = entry.options.retry(error, metadata);
1319
+ if (directive && "afterSeconds" in directive) {
1320
+ retryAfterS = directive.afterSeconds;
1321
+ } else if (directive && "acknowledge" in directive) {
1322
+ retryAcknowledged = true;
1323
+ }
1324
+ return directive;
1325
+ } : void 0;
1178
1326
  const request = {
1179
1327
  queueName: topicName,
1180
1328
  consumerGroup: entry.consumerGroup,
@@ -1184,18 +1332,47 @@ Ensure vercel.json has a matching experimentalTriggers entry and the route file
1184
1332
  const callbackOptions = {
1185
1333
  client: entry.client,
1186
1334
  visibilityTimeoutSeconds: entry.options?.visibilityTimeoutSeconds,
1187
- retry: entry.options?.retry
1335
+ retry: wrappedRetry
1188
1336
  };
1337
+ const consumerDefaultDelay = Math.min(
1338
+ findRetryAfterSeconds(topicName, entry.consumerGroup) ?? DEV_REDELIVERY_DEFAULT_DELAY_S,
1339
+ DEV_REDELIVERY_MAX_DELAY_S
1340
+ );
1341
+ const buildRedeliveryCtx = () => ({
1342
+ handler: entry.handler,
1343
+ retry: entry.options?.retry,
1344
+ payload: capturedPayload,
1345
+ topicName,
1346
+ consumerGroup: entry.consumerGroup,
1347
+ messageId,
1348
+ region,
1349
+ createdAt: capturedCreatedAt,
1350
+ retentionSeconds: effectiveRetention,
1351
+ deliveryCount: capturedDeliveryCount,
1352
+ defaultRetryDelayS: consumerDefaultDelay
1353
+ });
1189
1354
  try {
1190
- await invokeWithRetry(entry.handler, request, callbackOptions);
1191
- console.log(
1192
- `[Dev Mode] \u2713 Message processed: topic="${topicName}" consumer="${entry.consumerGroup}" messageId="${messageId}"`
1193
- );
1355
+ await invokeWithRetry(wrappedHandler, request, callbackOptions);
1356
+ if (handlerSucceeded) {
1357
+ console.log(
1358
+ `${PREFIX} ${OK} Message processed: topic="${topicName}" consumer="${entry.consumerGroup}" messageId="${messageId}"`
1359
+ );
1360
+ } else if (retryAcknowledged) {
1361
+ console.log(
1362
+ `${PREFIX} ${OK} Message acknowledged (will not retry): topic="${topicName}" consumer="${entry.consumerGroup}" messageId="${messageId}"`
1363
+ );
1364
+ } else if (retryAfterS !== null) {
1365
+ const devDelay = Math.min(retryAfterS, DEV_REDELIVERY_MAX_DELAY_S);
1366
+ scheduleDevRedelivery(buildRedeliveryCtx(), devDelay);
1367
+ }
1194
1368
  } catch (error) {
1195
1369
  console.error(
1196
- `[Dev Mode] \u2717 Handler failed: topic="${topicName}" consumer="${entry.consumerGroup}" messageId="${messageId}"`,
1370
+ `${PREFIX} ${FAIL} Handler failed: topic="${topicName}" consumer="${entry.consumerGroup}" messageId="${messageId}"`,
1197
1371
  error
1198
1372
  );
1373
+ if (!handlerSucceeded) {
1374
+ scheduleDevRedelivery(buildRedeliveryCtx(), consumerDefaultDelay);
1375
+ }
1199
1376
  }
1200
1377
  }
1201
1378
  })();
@@ -1207,6 +1384,10 @@ function clearDevState() {
1207
1384
  }
1208
1385
  if (process.env.NODE_ENV === "test" || process.env.VITEST) {
1209
1386
  globalThis.__clearDevState = clearDevState;
1387
+ globalThis.__filePathToConsumerGroup = filePathToConsumerGroup;
1388
+ globalThis.__filePathToUrlPath = filePathToUrlPath;
1389
+ globalThis.__matchesFunctionsPattern = matchesFunctionsPattern;
1390
+ globalThis.__stripSrcPrefix = stripSrcPrefix;
1210
1391
  }
1211
1392
 
1212
1393
  // src/oidc.ts
@@ -1250,6 +1431,7 @@ function parseQueueHeaders(headers) {
1250
1431
  const timestamp = headers.get("Vqs-Timestamp");
1251
1432
  const contentType = headers.get("Content-Type") || "application/octet-stream";
1252
1433
  const receiptHandle = headers.get("Vqs-Receipt-Handle");
1434
+ const expiresAtStr = headers.get("Vqs-Expires-At");
1253
1435
  if (!messageId || !timestamp || !receiptHandle) {
1254
1436
  return null;
1255
1437
  }
@@ -1261,6 +1443,7 @@ function parseQueueHeaders(headers) {
1261
1443
  messageId,
1262
1444
  deliveryCount,
1263
1445
  createdAt: new Date(timestamp),
1446
+ expiresAt: expiresAtStr ? new Date(expiresAtStr) : void 0,
1264
1447
  contentType,
1265
1448
  receiptHandle
1266
1449
  };
@@ -1344,13 +1527,25 @@ var ApiClient = class _ApiClient {
1344
1527
  if (this.providedToken) {
1345
1528
  return this.providedToken;
1346
1529
  }
1347
- const token = await getVercelOidcToken();
1348
- if (!token) {
1530
+ try {
1531
+ return await getVercelOidcToken();
1532
+ } catch (err) {
1533
+ const cause = err instanceof Error ? err.message : String(err);
1349
1534
  throw new Error(
1350
- "Failed to get OIDC token from Vercel Functions. Make sure you are running in a Vercel Function environment, or provide a token explicitly.\n\nTo set up your environment:\n1. Link your project: 'vercel link'\n2. Pull environment variables: 'vercel env pull'\n3. Run with environment: 'dotenv -e .env.local -- your-command'"
1535
+ isDevMode() ? `Failed to get OIDC token for local development.
1536
+
1537
+ To fix this, pull your environment variables with Vercel CLI:
1538
+ \`vercel env pull\`
1539
+
1540
+ Cause: ${cause}` : `Failed to get OIDC token. This usually means the function is running outside of a Vercel Function environment.
1541
+
1542
+ To fix this, either:
1543
+ - Deploy to Vercel (OIDC tokens are provisioned automatically)
1544
+ - Provide a token explicitly: \`new QueueClient({ token: '...' })\`
1545
+
1546
+ Cause: ${cause}`
1351
1547
  );
1352
1548
  }
1353
- return token;
1354
1549
  }
1355
1550
  buildUrl(queueName, ...pathSegments) {
1356
1551
  const encodedQueue = encodeURIComponent(queueName);
@@ -1381,7 +1576,7 @@ var ApiClient = class _ApiClient {
1381
1576
  }
1382
1577
  console.debug("[VQS Debug] Request:", JSON.stringify(logData, null, 2));
1383
1578
  }
1384
- init.headers.set("User-Agent", `@vercel/queue/${"0.1.1"}`);
1579
+ init.headers.set("User-Agent", `@vercel/queue/${"0.1.3"}`);
1385
1580
  init.headers.set("Vqs-Client-Ts", (/* @__PURE__ */ new Date()).toISOString());
1386
1581
  const response = await fetch(url, init);
1387
1582
  if (isDebugEnabled()) {
@@ -1756,9 +1951,11 @@ function resolveRegion(region) {
1756
1951
  if (region) return region;
1757
1952
  const fromEnv = process.env.VERCEL_REGION;
1758
1953
  if (fromEnv) return fromEnv;
1759
- console.warn(
1760
- `[QueueClient] Region not detected \u2014 defaulting to "${DEFAULT_REGION}". On Vercel this is set automatically via VERCEL_REGION. To silence this warning, pass region explicitly: new QueueClient({ region: "iad1" })`
1761
- );
1954
+ if (!isDevMode()) {
1955
+ console.warn(
1956
+ `[QueueClient] Region not detected \u2014 defaulting to "${DEFAULT_REGION}". On Vercel this is set automatically via VERCEL_REGION. To silence this warning, pass region explicitly: new QueueClient({ region: "iad1" })`
1957
+ );
1958
+ }
1762
1959
  return DEFAULT_REGION;
1763
1960
  }
1764
1961
  var QueueClient = class {
@@ -1796,7 +1993,8 @@ var QueueClient = class {
1796
1993
  topicName,
1797
1994
  result.messageId,
1798
1995
  api.getRegion(),
1799
- options?.delaySeconds
1996
+ options?.delaySeconds,
1997
+ options?.retentionSeconds
1800
1998
  );
1801
1999
  }
1802
2000
  return { messageId: result.messageId };