Skip to content

feat(vercel): support queues in local dev#4264

Merged
pi0 merged 6 commits into
mainfrom
feat/vercel-queues-local-dev
May 22, 2026
Merged

feat(vercel): support queues in local dev#4264
pi0 merged 6 commits into
mainfrom
feat/vercel-queues-local-dev

Conversation

@RihanArfan
Copy link
Copy Markdown
Member

@RihanArfan RihanArfan commented May 13, 2026

🔗 Linked issue

#4127

Blocked by unjs/env-runner#16

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme, or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Support Vercel Queues during local development. Calling send() from @vercel/queue will call the consumers registered within nitro.config.ts.

Also introduces usage of Vercel env-runner preset during dev when Vercel preset is set.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@RihanArfan RihanArfan requested a review from pi0 as a code owner May 13, 2026 19:22
@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nitro.build Ready Ready Preview, Comment May 22, 2026 2:07pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Adds local development for Vercel Queues: new vercelDev preset and types, a dev module that injects a runtime plugin, a runtime plugin that forwards local Vercel Queue messages to the vercel:queue hook, documentation, and a small dependency bump.

Changes

Vercel Queues Local Development

Layer / File(s) Summary
Preset type definitions and vercelDev preset
src/presets/_types.gen.ts, src/presets/vercel/preset.ts
PresetName and PresetNameInput updated to include vercel-dev (kebab/camel/snake); vercelDev preset added, imports vercelDevModule, sets devServer.runner: "vercel", modules: [vercelDevModule], dev: true, and is added to default export.
Dev module setup and validation
src/presets/vercel/dev.ts
vercelDevModule checks dev mode and configured triggers, enforces devServer.runner is "vercel", mirrors triggers to runtimeConfig.vercel.queues.triggers, and injects the dev runtime plugin into Nitro plugins.
Queue consumer plugin implementation
src/presets/vercel/runtime/queue.dev.ts
queueDevPlugin registers a local consumer per configured trigger, forwards consumed messages to the vercel:queue hook with send and MessageMetadata, logs/captures and rethrows hook errors to enable @vercel/queue local retries, and unregisters consumers on Nitro shutdown.
Local development documentation
docs/2.deploy/20.providers/vercel.md
New "Local development" section documents queue behavior in nitro dev, delivery to the vercel:queue hook, using vercel link and vercel env pull for SDK auth, and retry behavior when the hook throws.
Dependency bump
package.json
env-runner dependency updated from ^0.1.8 to ^0.1.9.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • nitrojs/nitro#4127: Extends the existing Vercel Queues integration by adding a local-dev preset/module and a queue consumer plugin that forwards messages to the vercel:queue Nitro hook.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title follows conventional commit format with the 'feat' type and 'vercel' scope, and accurately summarizes the main change of adding queue support for local development.
Description check ✅ Passed The pull request description provides relevant context about the changes, including the objective of supporting Vercel Queues during local development and references to linked issues.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/vercel-queues-local-dev

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/presets/vercel/runtime/queue.dev.ts (1)

50-53: ⚡ Quick win

Avoid silent teardown failures in close hook

The empty catch hides unregister failures, making local queue teardown issues hard to diagnose. Log/capture the error instead of swallowing it.

Suggested diff
   nitroApp.hooks.hook("close", () => {
     for (const unregister of unregisters) {
       try {
         unregister();
-      } catch {}
+      } catch (error) {
+        nitroApp.captureError?.(error as Error, {
+          tags: ["vercel:queue", "close"],
+        });
+      }
     }
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/presets/vercel/runtime/queue.dev.ts` around lines 50 - 53, The close hook
currently swallows errors from unregister() with an empty catch; update the
catch to capture the exception and log it (e.g., catch (err) {
console.error('Failed to unregister local queue in close hook:', err) } or use
an existing logger like processLogger.error) so failures in unregister() are
visible; keep the call to unregister() and ensure the log includes context and
the error object for debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/presets/vercel/runtime/queue.dev.ts`:
- Line 36: Replace the direct console.error call in queue.dev.ts with the
preset/dev logger: use nitroApp.logger.error(...) if a nitroApp instance is in
scope, otherwise import and use consola.error(...); specifically change the
console.error("[vercel:queue]", error) invocation to call the appropriate
logger, and add the required import or ensure nitroApp is available in the
surrounding function where the error is handled.
- Line 5: The import path and symbol are wrong: replace the import of
registerVercelQueueConsumer from "env-runner/runners/vercel/queue-dev" with the
correct export from "env-runner/runners/vercel" (verify the exact exported
function name in that package and update the import to match), update any
references to registerVercelQueueConsumer if the exported name differs, change
the console.error call at the place currently using console.error to
consola.error, and inside the teardown catch block (the empty catch around
teardown logic) add explicit error handling/logging (e.g., log the error via
consola.error with context) so errors are not silently swallowed.

---

Nitpick comments:
In `@src/presets/vercel/runtime/queue.dev.ts`:
- Around line 50-53: The close hook currently swallows errors from unregister()
with an empty catch; update the catch to capture the exception and log it (e.g.,
catch (err) { console.error('Failed to unregister local queue in close hook:',
err) } or use an existing logger like processLogger.error) so failures in
unregister() are visible; keep the call to unregister() and ensure the log
includes context and the error object for debugging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9ccb08c1-7a29-4496-bf7c-d75f658bd36e

📥 Commits

Reviewing files that changed from the base of the PR and between 0498ce7 and 3a82754.

📒 Files selected for processing (5)
  • docs/2.deploy/20.providers/vercel.md
  • src/presets/_types.gen.ts
  • src/presets/vercel/dev.ts
  • src/presets/vercel/preset.ts
  • src/presets/vercel/runtime/queue.dev.ts

Comment thread src/presets/vercel/runtime/queue.dev.ts
Comment thread src/presets/vercel/runtime/queue.dev.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/presets/vercel/runtime/queue.dev.ts (2)

36-36: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use Nitro/consola logger instead of console.error.

Line [36] and Line [51] should use nitroApp.logger (or consola) for preset/dev logging consistency.

Suggested diff
-          console.error("[vercel:queue]", error);
+          nitroApp.logger.error("[vercel:queue]", error);
...
-    console.error("[vercel:queue] failed to register dev consumer:", error);
+    nitroApp.logger.error("[vercel:queue] failed to register dev consumer:", error);

As per coding guidelines src/{build,dev,cli,presets}/**/*.{ts,js,mjs}: Use consola for logging in build/dev code, or nitro.logger when available.

Also applies to: 50-52

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/presets/vercel/runtime/queue.dev.ts` at line 36, Replace direct
console.error calls in the dev preset with the project logger: use
nitroApp.logger.error (if nitroApp is in scope) or import and use consola.error
to keep logging consistent; update the error calls in the file (the
console.error at the error-handling site and the similar call around lines
50–52) to call the logger instead, ensuring the same error object/message is
passed through and adding an appropriate prefix like "[vercel:queue]" if needed.

54-59: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Don’t silently swallow unregister failures on shutdown.

Line [58] has an empty catch {}; this hides teardown problems and makes retry/debug behavior opaque.

Suggested diff
   nitroApp.hooks.hook("close", () => {
     for (const unregister of unregisters) {
       try {
         unregister();
-      } catch {}
+      } catch (error) {
+        nitroApp.logger.error("[vercel:queue] failed to unregister dev consumer:", error);
+      }
     }
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/presets/vercel/runtime/queue.dev.ts` around lines 54 - 59, The shutdown
hook registered via nitroApp.hooks.hook("close", ...) currently swallows
exceptions from each unregister() call (the empty catch {}); modify the handler
to catch errors from each unregister(), log the error (including the unregister
identifier/context if available) via the existing logger or console.error, and
optionally continue to attempt remaining unregisters; ensure the catch block at
least records the exception (e.g., with processLogger.error or console.error)
rather than being empty so teardown failures are visible and debuggable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/presets/vercel/runtime/queue.dev.ts`:
- Around line 44-46: The Promise.then callback currently uses an
implicitly-typed parameter `unregister`; update its signature to the explicit
type matching the `unregisters` array element type declared earlier (the same
type used at its declaration on line 22) so TypeScript strict mode compiles;
also replace any console.error calls in this file (including the ones at the
sites around the .then and at line 51) with the proyecto-wide logger
`consola.error` (ensure `consola` is imported), and remove the empty catch block
at the cleanup section by logging the caught error with `consola.error` (include
contextual message) so cleanup failures are not swallowed.

---

Duplicate comments:
In `@src/presets/vercel/runtime/queue.dev.ts`:
- Line 36: Replace direct console.error calls in the dev preset with the project
logger: use nitroApp.logger.error (if nitroApp is in scope) or import and use
consola.error to keep logging consistent; update the error calls in the file
(the console.error at the error-handling site and the similar call around lines
50–52) to call the logger instead, ensuring the same error object/message is
passed through and adding an appropriate prefix like "[vercel:queue]" if needed.
- Around line 54-59: The shutdown hook registered via
nitroApp.hooks.hook("close", ...) currently swallows exceptions from each
unregister() call (the empty catch {}); modify the handler to catch errors from
each unregister(), log the error (including the unregister identifier/context if
available) via the existing logger or console.error, and optionally continue to
attempt remaining unregisters; ensure the catch block at least records the
exception (e.g., with processLogger.error or console.error) rather than being
empty so teardown failures are visible and debuggable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5d2bba4f-a559-479d-9408-abb3b8075007

📥 Commits

Reviewing files that changed from the base of the PR and between 3a82754 and 000ec3c.

📒 Files selected for processing (1)
  • src/presets/vercel/runtime/queue.dev.ts

Comment thread src/presets/vercel/runtime/queue.dev.ts Outdated
@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 22, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedgeist@​1.7.01001001009190
Addedmotion-v@​2.2.110010010091100

View full report

@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 22, 2026

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm ioredis is 96.0% likely obfuscated

Confidence: 0.96

Location: Package overview

From: docs/pnpm-lock.yamlnpm/ioredis@5.10.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/ioredis@5.10.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (4)
src/presets/vercel/runtime/queue.dev.ts (4)

38-38: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Replace console.error with consola or nitroApp.logger.

Both console.error calls violate the coding guideline for this file path. As per coding guidelines: src/{build,dev,cli,presets}/**/*.{ts,js,mjs}: Use consola for logging in build/dev code, or nitro.logger when available.

♻️ Proposed fix

Import consola at the top of the file:

+import consola from "consola";
 import { send } from "`@vercel/queue`";

Then replace both console.error calls:

-          console.error("[vercel:queue]", error);
+          consola.error("[vercel:queue]", error);
-  console.error("[vercel:queue] failed to register dev consumer:", error);
+  consola.error("[vercel:queue] failed to register dev consumer:", error);

Alternatively, use nitroApp.logger.error() since nitroApp is already in scope.

Also applies to: 51-51

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/presets/vercel/runtime/queue.dev.ts` at line 38, Replace the direct
console.error calls in queue.dev.ts (the calls that log "[vercel:queue]" errors)
with the project logger: either import and use consola and call
consola.error(...) or call nitroApp.logger.error(...) since nitroApp is in
scope; update both occurrences (around the "[vercel:queue]" error logs) and
remove the console.error usage to comply with src/{build,dev,cli,presets}
logging guidelines.

46-47: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add explicit type annotation to fix strict TypeScript error.

The unregister parameter has an implicit any type, which fails strict TypeScript compilation. Add an explicit type annotation to match the unregisters array type.

🔧 Proposed fix
-    }).then((unregister) => {
+    }).then((unregister: () => void) => {
       unregisters.push(unregister);
     })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/presets/vercel/runtime/queue.dev.ts` around lines 46 - 47, The
then-callback parameter "unregister" has an implicit any; annotate it explicitly
to match the element type of the existing "unregisters" array: change the
callback signature in the .then(...) on the Promise to use (unregister: typeof
unregisters[number]) => { ... } so the parameter type aligns with the
unregisters array element type (ensuring strict TypeScript compiles) while
keeping the body that pushes the value into unregisters unchanged.

56-60: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Log cleanup errors instead of silently swallowing them.

The empty catch block hides unregistration failures, making debugging difficult. Add error logging to track cleanup issues.

🛡️ Proposed fix
     for (const unregister of unregisters) {
       try {
         unregister();
-      } catch {}
+      } catch (error) {
+        consola.error("[vercel:queue] failed to unregister consumer:", error);
+      }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/presets/vercel/runtime/queue.dev.ts` around lines 56 - 60, The current
cleanup loop silently swallows exceptions when calling each unregister function
(for (const unregister of unregisters) { try { unregister(); } catch {} }),
which hides failures; update the catch to log the error instead of leaving it
empty—use the existing logger (or processLogger) to call error with a
descriptive message and include the caught error and some context (e.g., which
unregister or index) so unregistration failures are visible during teardown;
ensure the catch handles both Error objects and unknown throwables when
referencing unregister/unregisters for context.

3-3: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Verify the import path and function availability.

The import env-runner/runners/vercel/queue-dev fails with a module-not-found error. Since this PR is blocked by env-runner PR #16, either that change hasn't been published in version 0.1.9, or the import path is incorrect.

Verify the correct import path and function export:

#!/bin/bash
# Description: Verify env-runner exports and version

echo "=== Check installed env-runner version ==="
npm list env-runner 2>/dev/null || echo "Not found in package-lock"

echo -e "\n=== Check env-runner package.json for exports ==="
cat node_modules/env-runner/package.json 2>/dev/null | jq '.version, .exports' || echo "node_modules not available"

echo -e "\n=== Search for queue-dev or registerVercelQueueConsumer in env-runner ==="
fd -e ts -e mts -e js -e mjs . node_modules/env-runner 2>/dev/null | xargs rg -l "registerVercelQueueConsumer|queue-dev" || echo "Not found"

echo -e "\n=== Check what's actually exported from vercel runner ==="
cat node_modules/env-runner/dist/runners/vercel/index.d.ts 2>/dev/null || cat node_modules/env-runner/runners/vercel/index.d.ts 2>/dev/null || echo "Type definitions not found"

Additionally, search for the latest env-runner documentation:

env-runner package version 0.1.9 registerVercelQueueConsumer queue-dev export
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/presets/vercel/runtime/queue.dev.ts` at line 3, The import of
registerVercelQueueConsumer from env-runner/runners/vercel/queue-dev is failing;
inspect the installed env-runner package version and its exports, search
node_modules for symbols registerVercelQueueConsumer and queue-dev (or similar
names) to find the correct module path or exported name, then update the import
in src/presets/vercel/runtime/queue.dev.ts to match the actual exported
symbol/path (or switch to the correct named export such as whatever is found
under the vercel runner index), and if the API is not yet published pin or
update the dependency to the version that contains the export or add a guarded
fallback to avoid the module-not-found error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/presets/vercel/runtime/queue.dev.ts`:
- Line 38: Replace the direct console.error calls in queue.dev.ts (the calls
that log "[vercel:queue]" errors) with the project logger: either import and use
consola and call consola.error(...) or call nitroApp.logger.error(...) since
nitroApp is in scope; update both occurrences (around the "[vercel:queue]" error
logs) and remove the console.error usage to comply with
src/{build,dev,cli,presets} logging guidelines.
- Around line 46-47: The then-callback parameter "unregister" has an implicit
any; annotate it explicitly to match the element type of the existing
"unregisters" array: change the callback signature in the .then(...) on the
Promise to use (unregister: typeof unregisters[number]) => { ... } so the
parameter type aligns with the unregisters array element type (ensuring strict
TypeScript compiles) while keeping the body that pushes the value into
unregisters unchanged.
- Around line 56-60: The current cleanup loop silently swallows exceptions when
calling each unregister function (for (const unregister of unregisters) { try {
unregister(); } catch {} }), which hides failures; update the catch to log the
error instead of leaving it empty—use the existing logger (or processLogger) to
call error with a descriptive message and include the caught error and some
context (e.g., which unregister or index) so unregistration failures are visible
during teardown; ensure the catch handles both Error objects and unknown
throwables when referencing unregister/unregisters for context.
- Line 3: The import of registerVercelQueueConsumer from
env-runner/runners/vercel/queue-dev is failing; inspect the installed env-runner
package version and its exports, search node_modules for symbols
registerVercelQueueConsumer and queue-dev (or similar names) to find the correct
module path or exported name, then update the import in
src/presets/vercel/runtime/queue.dev.ts to match the actual exported symbol/path
(or switch to the correct named export such as whatever is found under the
vercel runner index), and if the API is not yet published pin or update the
dependency to the version that contains the export or add a guarded fallback to
avoid the module-not-found error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 591fa55a-0c9a-4356-a661-a4a4ce02eeec

📥 Commits

Reviewing files that changed from the base of the PR and between c6e33ea and 176046e.

📒 Files selected for processing (1)
  • src/presets/vercel/runtime/queue.dev.ts

@pi0 pi0 merged commit 99c41f8 into main May 22, 2026
13 checks passed
@pi0 pi0 deleted the feat/vercel-queues-local-dev branch May 22, 2026 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants