mcpField
N
nuxt

nuxt

github.com/vercel-labs开发2.0k78 次安装2 天前更新
代码仓库

关于 nuxt

nuxt 是将 API 模拟器嵌入 Nuxt 应用、通过同源路由提供服务的适配器,适合预览部署和集成测试。它支持多个模拟器、可选状态持久化,并重写响应中的路由前缀。

主要功能

  1. 01在 Nuxt 服务路由中挂载模拟器
  2. 02通过同源路径提供多个服务
  3. 03支持可选状态持久化
  4. 04重写响应中的服务路径前缀
  5. 05兼容自定义模拟器定义

适用场景

  1. 01在预览部署中测试 OAuth 回调
  2. 02在 Nuxt 应用中运行 API 模拟器
  3. 03跨重启保留模拟器状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/vercel-labs/emulate --skill nuxt

安装后在 Claude Code 中输入/nuxt确认已加载。

详细介绍

Nuxt 集成

@emulators/adapter-nuxt 包可将模拟器直接嵌入 Nuxt 应用,并在同一源上运行。这对于预览部署很有用,因为 OAuth 回调 URL 会随每次部署而变化。

安装

npm install @emulators/adapter-nuxt @emulators/github @emulators/google

只安装所需的模拟器。每个 @emulators/* 包都独立发布,以保持服务器 bundle 精简。

服务端路由

创建一个具名的全匹配路由,用于处理模拟器流量:

// server/routes/emulate/[...path].ts
import { createEmulateHandler } from '@emulators/adapter-nuxt'
import * as github from '@emulators/github'
import * as google from '@emulators/google'

export default defineEventHandler(createEmulateHandler({
  services: {
    github: {
      emulator: github,
      seed: {
        users: [{ login: 'octocat', name: 'The Octocat' }],
        repos: [{ owner: 'octocat', name: 'hello-world', auto_init: true }],
      },
    },
    google: {
      emulator: google,
      seed: {
        users: [{ email: '[email protected]', name: 'Test User' }],
      },
    },
  },
}))

这会创建以下路由:

  • /emulate/github/** 提供 GitHub 模拟器
  • /emulate/google/** 提供 Google 模拟器

GitHub 安装令牌元数据可在服务端通过 /emulate/github/_emulate/installation-tokens 获取。

Nuxt 配置

模拟器 UI 页面使用打包的字体。请包装 Nuxt 配置,以便 Nitro 将核心包资源追踪到生产构建中:

// nuxt.config.ts
import { withEmulate } from '@emulators/adapter-nuxt'

export default defineNuxtConfig(withEmulate({
  // your normal Nuxt config
}))

OAuth 配置

将 OAuth 提供方指向同一源上的模拟器路径:

const baseUrl = process.env.NUXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'

export const githubOAuth = {
  clientId: 'any-value',
  clientSecret: 'any-value',
  authorizationUrl: `${baseUrl}/emulate/github/login/oauth/authorize`,
  tokenUrl: `${baseUrl}/emulate/github/login/oauth/access_token`,
  userInfoUrl: `${baseUrl}/emulate/github/user`,
}

无需预先植入 oauth_apps。未配置任何应用时,模拟器会跳过对 client_id、client_secret 和 redirect_uri 的验证。

持久化

默认情况下,模拟器状态存储在内存中,并在每次冷启动时重置。若要在重启后保留状态,请传入 persistence 适配器。

Nitro 存储
import { createEmulateHandler } from '@emulators/adapter-nuxt'
import * as github from '@emulators/github'

const storageAdapter = {
  async load() { return await useStorage('emulate').getItem<string>('state') },
  async save(data: string) { await useStorage('emulate').setItem('state', data) },
}

export default defineEventHandler(createEmulateHandler({
  services: { github: { emulator: github } },
  persistence: storageAdapter,
}))
文件持久化

对于本地开发,@emulators/core 提供了一个基于文件的适配器:

import { filePersistence } from '@emulators/core'

persistence: filePersistence('.emulate/state.json'),

GitHub App 种子数据可以省略 private_key。保留处理程序并调用仅限服务端使用的 generatedSecrets();显式密钥不会纳入其中。请妥善保管持久化快照,并以原子方式实现 initialize。

持久化的工作方式
  • 冷启动:适配器从持久化适配器加载状态。如果找到状态,则恢复完整的 Store 和令牌映射;否则,根据配置植入种子数据并保存初始状态。
  • 变更请求之后(POST、PUT、PATCH、DELETE):保存状态。保存操作通过内部队列串行执行,以防止竞争条件。
  • 未配置持久化:回退到纯内存模式。种子数据会在每次冷启动时重新初始化。

工作原理

  1. 接收请求:/emulate/github/login/oauth/authorize?client_id=...
  2. 解析:service = github,rest = /login/oauth/authorize
  3. 移除前缀:创建一个路径已移除前缀的新 Request,并将其转发给 GitHub 服务应用
  4. 重写响应:为 HTML 的 action 和 href 属性、CSS 的 url() 字体引用以及 Location 标头添加服务前缀
  5. 持久化:变更请求之后,通过持久化适配器保存状态

限制

  • 需要兼容 Node 的 Nuxt 服务器运行时,因为模拟器使用 Node API
  • 并发变更采用最后写入者胜出语义。生成的身份要求 initialize 在多次冷启动之间以原子方式选择初始快照。

配置参考

createEmulateHandler(config, options?)
字段类型说明
servicesRecord<string, EmulatorEntry>服务名称到模拟器配置的映射
persistence?PersistenceAdapter可选的持久化适配器,用于在冷启动之间保留状态

每个 EmulatorEntry:

字段类型说明
emulatorEmulatorModule模拟器包,例如 import * as github from '@emulators/github'
seed?Record<string, unknown>符合服务配置架构的种子数据

选项:

选项类型默认值说明
paramstring"path"具名的全匹配路由参数
routePrefixstring检测所得全匹配路由挂载的路径前缀
withEmulate(nuxtConfig)

包装 Nuxt 配置,将 @emulators/core 资源纳入 Nitro 的生产追踪。在 nuxt.config.ts 的 defineNuxtConfig 中调用。

PersistenceAdapter
interface PersistenceAdapter {
  load(): Promise<string | null>
  save(data: string): Promise<void>
  initialize?(data: string): Promise<string>
}

initialize 必须以原子方式创建初始值,或返回另一个实例先创建的值。请使用具有比较并交换语义的方式实现,例如 Redis SET NX。@emulators/core 内置的 filePersistence(path) 可在本地开发中提供此行为。

自定义模拟器

该适配器接受由 emulate 中的 defineEmulator 创建的定义,并通过 services[name].emulator 配置。可在 CLI/测试中复用该定义;自定义检查器为可选项。相对于根目录的重定向位置仍位于服务挂载路径下,而自定义 HTML 正文则原样传递。请在测试清理时等待返回的处理程序的 close() 完成。部署定义时,请将 emulate 安装为运行时依赖项。有关种子、快照、持久化和生命周期规则,请参阅 https://emulate.dev/docs/custom-emulators。

评论

支持 Markdown