-
-
Notifications
You must be signed in to change notification settings - Fork 851
presets(vercel): support local queue delivery during dev #4340
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
RihanArfan
wants to merge
1
commit into
backport-vercel-queues
Choose a base branch
from
feat/vercel-queues-local-dev-backport
base: backport-vercel-queues
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+210
β11
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| import { fileURLToPath } from "node:url"; | ||
| import { resolveModulePath } from "exsolve"; | ||
| import type { Nitro } from "nitropack/types"; | ||
|
|
||
| /** | ||
| * Configure local development emulation for the Vercel preset. | ||
| * | ||
| * When `vercel.queues.triggers` is configured, propagates the trigger list to | ||
| * runtime config and injects a runtime plugin that binds each topic to the | ||
| * `vercel:queue` hook through `@vercel/queue`'s local dev consumer registry. | ||
| */ | ||
| export async function vercelDev(nitro: Nitro) { | ||
| if (!nitro.options.dev) { | ||
| return; // Production doesn't need this | ||
| } | ||
|
|
||
| const triggers = nitro.options.vercel?.queues?.triggers; | ||
| if (!triggers?.length) { | ||
| return; | ||
| } | ||
|
|
||
| // `@vercel/queue` is an optional peer dependency. Without it, local queue | ||
| // delivery is simply disabled (dev startup is never blocked). | ||
| const resolved = resolveModulePath("@vercel/queue", { | ||
| from: nitro.options.nodeModulesDirs, | ||
| try: true, | ||
| }); | ||
| if (!resolved) { | ||
| nitro.logger.warn( | ||
| "`@vercel/queue` is not installed. Local queue delivery is disabled. Install it with `npx nypm i @vercel/queue` to enable it." | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| // Propagate triggers to the runtime plugin via runtimeConfig. | ||
| nitro.options.runtimeConfig.vercel = { | ||
| ...nitro.options.runtimeConfig.vercel, | ||
| queues: { | ||
| triggers: triggers.map((t) => ({ ...t })), | ||
| }, | ||
| }; | ||
|
|
||
| // Make sure the runtime plugin is transpiled. | ||
| nitro.options.externals.inline = nitro.options.externals.inline || []; | ||
| nitro.options.externals.inline.push( | ||
| fileURLToPath(new URL("runtime/", import.meta.url)) | ||
| ); | ||
|
|
||
| // Inject the dev consumer plugin. | ||
| nitro.options.plugins = nitro.options.plugins || []; | ||
| nitro.options.plugins.unshift( | ||
| fileURLToPath(new URL("runtime/queue.dev", import.meta.url)) | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| import { defineNitroPlugin, useRuntimeConfig } from "nitropack/runtime"; | ||
|
|
||
| import type { MessageHandler, QueueClient } from "@vercel/queue"; | ||
|
|
||
| // `@vercel/queue` gates local `send()` routing behind `isDevMode()`, which | ||
| // checks `process.env.NODE_ENV === "development"`. This plugin is only ever | ||
| // loaded by the dev-only `vercel-dev` preset, so defaulting it here is safe | ||
| // and never affects production builds. Bracket access avoids the dev | ||
| // bundler's static `process.env.NODE_ENV` replacement (which would otherwise | ||
| // rewrite this into an assignment to a string literal). | ||
| process.env["NODE_ENV"] ??= "development"; | ||
|
|
||
| const CONSUMER_GROUP = "nitro-vercel-dev"; | ||
|
|
||
| interface DevTrigger { | ||
| topic: string; | ||
| retryAfterSeconds?: number; | ||
| } | ||
|
|
||
| type VercelQueueSdk = typeof import("@vercel/queue"); | ||
|
|
||
| let sdkPromise: Promise<VercelQueueSdk | null> | undefined; | ||
| let client: QueueClient | undefined; | ||
|
|
||
| /** | ||
| * Lazily load `@vercel/queue` and construct a shared `QueueClient`. | ||
| * | ||
| * Resolves to `null` (and logs a one-time warning) when the package is not | ||
| * installed or is too old to expose `registerDevConsumer` (added in | ||
| * `@vercel/queue@^0.2.0`), so registrations become no-ops. | ||
| */ | ||
| function ensureSdk(): Promise<VercelQueueSdk | null> { | ||
| if (sdkPromise) { | ||
| return sdkPromise; | ||
| } | ||
| sdkPromise = (async () => { | ||
| let mod: VercelQueueSdk; | ||
| try { | ||
| mod = await import("@vercel/queue"); | ||
| } catch { | ||
| console.warn( | ||
| "[vercel:queue] `@vercel/queue` is not installed. Local queue delivery is disabled." | ||
| ); | ||
| return null; | ||
| } | ||
| if (typeof mod.registerDevConsumer !== "function") { | ||
| console.warn( | ||
| "[vercel:queue] Installed `@vercel/queue` does not export `registerDevConsumer`. Upgrade to `@vercel/queue@^0.2.0` to enable local queue delivery." | ||
| ); | ||
| return null; | ||
| } | ||
| client = new mod.QueueClient(); | ||
| return mod; | ||
| })(); | ||
| return sdkPromise; | ||
| } | ||
|
|
||
| export default defineNitroPlugin((nitroApp) => { | ||
| const triggers = | ||
| ( | ||
| useRuntimeConfig() as { | ||
| vercel?: { queues?: { triggers?: DevTrigger[] } }; | ||
| } | ||
| ).vercel?.queues?.triggers || []; | ||
|
|
||
| if (triggers.length === 0) { | ||
| return; | ||
| } | ||
|
|
||
| const unregisters: Array<() => void> = []; | ||
|
|
||
| const ready = (async () => { | ||
| const sdk = await ensureSdk(); | ||
| if (!sdk || !client) { | ||
| return; | ||
| } | ||
| for (const trigger of triggers) { | ||
| const handler: MessageHandler = async (message, metadata) => { | ||
| try { | ||
| await nitroApp.hooks.callHook("vercel:queue", { | ||
| message, | ||
| metadata, | ||
| send: sdk.send, | ||
| }); | ||
| } catch (error) { | ||
| console.error("[vercel:queue]", error); | ||
| nitroApp.captureError?.(error as Error, { | ||
| tags: ["vercel:queue"], | ||
| }); | ||
| // Rethrow so @vercel/queue schedules a local retry. | ||
| throw error; | ||
| } | ||
| }; | ||
|
|
||
| // Compare against `undefined` so a configured `0` is honored. | ||
| const { retryAfterSeconds } = trigger; | ||
| unregisters.push( | ||
| sdk.registerDevConsumer({ | ||
| topic: trigger.topic, | ||
| client, | ||
| consumerGroup: CONSUMER_GROUP, | ||
| retry: | ||
| retryAfterSeconds === undefined | ||
| ? undefined | ||
| : () => ({ afterSeconds: retryAfterSeconds }), | ||
| handler, | ||
| }) | ||
| ); | ||
| } | ||
| })().catch((error) => { | ||
| console.error("[vercel:queue] failed to register dev consumer:", error); | ||
| }); | ||
|
|
||
| nitroApp.hooks.hook("close", async () => { | ||
| await ready; | ||
| for (const unregister of unregisters) { | ||
| try { | ||
| unregister(); | ||
| } catch { | ||
| // ignore unregister failures during shutdown | ||
| } | ||
| } | ||
| }); | ||
| }); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use an explicit extension in the dynamic import.
Line 156 uses
import("./dev"). Please switch to an explicit extension (.ts/.mjs) for consistency with the repository ESM import rule.Suggested patch
As per coding guidelines,
src/**/*.{ts,js}imports should use explicit.ts/.mjsextensions.π€ Prompt for AI Agents
Source: Coding guidelines