mcpField
F
flags-sdk

flags-sdk

github.com/vercel开发6262.6k 次安装今天更新
代码仓库

关于 flags-sdk

flags-sdk 是面向 Next.js 和 SvelteKit 的功能标志工具包及配套技能,允许将标志声明为可调用函数,并通过适配器连接不同提供方。其服务端评估与预计算模式用于避免布局偏移并保留静态页面。

主要功能

  1. 01将功能标志声明为函数
  2. 02通过适配器连接标志提供方
  3. 03在服务端评估标志
  4. 04使用预计算提供静态变体
  5. 05支持 Next.js 和 SvelteKit

适用场景

  1. 01在 Next.js 应用中添加功能标志
  2. 02在 SvelteKit 项目中评估标志
  3. 03使用不同提供方配置标志适配器
  4. 04预计算页面变体以保留静态页面

安装

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

$npx skills add https://github.com/vercel/flags --skill flags-sdk

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

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api.md
nextjs.md
providers.md
sveltekit.md

详细介绍

设置和使用 Flags SDK

Flags SDK(flags npm 软件包)是面向 Next.js 和 SvelteKit 的功能标记工具包。它会将每个功能标记转换为可调用的函数,通过适配器支持任意标记提供方,并使用预计算模式保持页面静态。Vercel Flags 是官方提供方,其适配器为 @flags-sdk/vercel。

本技能涵盖代码操作。规划功能标记、在 Flags SDK、@vercel/flags-core 和 OpenFeature 之间进行选择,以及在控制面板或通过 vercel flags CLI 管理标记,都属于 vercel-flags 技能。归档标记、删除标记或移除变体时,必须遵循该技能中的检查步骤(先移除代码,确认 Skew Protection 窗口内没有评估记录,再归档,然后执行 rm);没有该技能时,不要运行这些命令。如果无法使用该技能,请建议运行 npx skills add vercel/flags@vercel-flags(不要自动安装),同时运行 vercel flags <cmd> --help。对于使用其他框架的项目,请使用 @vercel/flags-core 替代此 SDK(Core SDK)。

用户要求安装、配置或设置 SDK 时,请遵循设置 SDK(如果缺少 .env.local,还要执行 vercel env pull)。用户要求在代码中添加标记时,请遵循创建标记或添加 Vercel 上已有的标记。在应用仓库中遇到含糊的请求时,应按完整流程处理。不要把 CLI 操作留作用户的“后续步骤”;请自行执行。

核心概念

将标记作为代码

每个标记都声明为函数。在调用位置无需使用字符串键:

import { flag } from 'flags/next';

export const exampleFlag = flag({
  key: 'example-flag',
  decide() { return false; },
});

const value = await exampleFlag();
服务端评估

标记在服务端评估,以避免布局偏移、保持页面静态并确保机密性。将路由中间件与预计算模式结合,从 CDN 提供静态变体。

适配器模式

适配器会替换标记声明中的 decide 和 origin,将标记连接到提供方。Vercel Flags(@flags-sdk/vercel)是官方适配器。Statsig、LaunchDarkly、PostHog 等也提供第三方适配器。

import { flag } from 'flags/next';
import { vercelAdapter } from '@flags-sdk/vercel';

export const exampleFlag = flag({
  key: 'example-flag',
  adapter: vercelAdapter,
});

版本说明:SDK 以 flags 发布(从 @vercel/flags 更名而来;旧名称仍会出现在变更日志中)。flags 4.2.0+ 支持通过引用传入适配器工厂(adapter: vercelAdapter),并会在每个声明中解析一次。旧版本要求调用工厂(adapter: vercelAdapter())。新版本仍支持调用形式,因此除非目标版本为 flags < 4.2.0,否则优先使用简写形式。

设置 SDK

一次性项目设置。当尚未安装 Flags SDK,或缺少 Toolbar、Flags Explorer 或 .env.local 时,请执行此操作。已完成的步骤可跳过。

开始之前

检查项目状态,以调整命令并确定哪些步骤可以跳过:

  • 存在哪个锁文件(pnpm-lock.yaml、package-lock.json、yarn.lock、bun.lockb)?→ 相应调整所有软件包管理器命令(pnpm add、npm install、yarn add、bun add)。
  • package.json 中是否有 flags?→ 跳过安装(步骤 1)
  • 项目是否已关联?→ 跳过步骤 2 中的 vercel link
  • .env.local 是否包含 VERCEL_OIDC_TOKEN=(或 SDK 密钥 FLAGS=)?→ 环境变量已拉取,跳过步骤 3
  • package.json 中是否有 @vercel/toolbar?→ 跳过 Toolbar 设置(步骤 4)
  • 是否存在 flags.ts(或 lib/flags.ts、src/flags.ts)?→ 跳过创建(步骤 5)
  • 是否存在 app/.well-known/vercel/flags/route.ts?→ Flags Explorer 已设置,跳过步骤 6
步骤
  1. 安装软件包(如果尚未添加到 package.json):

    pnpm i flags @flags-sdk/vercel
    
  2. 将项目关联到 Vercel(vercel env pull 和本地 OIDC 令牌刷新所必需):

    前提条件:必须已安装并完成身份验证的 Vercel CLI。如果尚未安装,请运行 pnpm i -g vercel。如果遇到身份验证或关联问题,请使用已安装的 vercel-cli skill。如果尚未安装,请建议用户使用 npx skills add vercel/vercel@vercel-cli 安装(不要自动安装),同时暂时依靠 vercel <command> --help。

    使用 vercel project inspect --non-interactive 验证关联,并检查报告的所有者和项目名称;仅有 .vercel/ 目录并不能证明项目已关联。如果报告显示 link_required,请运行 vercel link。如果用户指定了项目或团队,而输出与之不同,请停止操作并询问用户,不要重新关联。

  3. 拉取环境变量:如果 .env.local 中缺少 VERCEL_OIDC_TOKEN=,请按照拉取环境变量中的说明操作。

  4. 设置 Vercel Toolbar(如果尚未配置):

  5. 确保存在 flags.ts:如果文件不存在,请创建 flags.ts(或与项目结构匹配的 lib/flags.ts / src/flags.ts),并写入 export {},以便 TypeScript 将其视为模块。Flags Explorer 会导入此文件 — 请在发现路由之前创建它。

  6. 设置 Flags Explorer(如果尚未配置):创建 app/.well-known/vercel/flags/route.ts — 参阅 Flags Explorer 设置。仅在 flags.ts 存在后执行此操作。将导入指向实际的 flags 文件路径(代码片段假定文件位于根目录 flags.ts)。

拉取环境变量

vercel env pull 会将 Development 凭据写入 .env.local:vercelAdapter 在本地使用的 Vercel OIDC 令牌(部署会自动获取该令牌,入门指南)以及供 Flags Explorer 和覆盖项使用的 Development FLAGS_SECRET。在以下情况下运行此命令:

  • .env.local 中缺少 VERCEL_OIDC_TOKEN=(或 SDK 密钥 FLAGS=)
  • 你创建了项目的首个 flag;启用 Vercel Flags 会为每个环境创建一个 FLAGS_SECRET
  • 本地评估因身份验证错误而失败;SDK 会通过已关联的项目刷新过期令牌,重新拉取是备用方案

SDK 密钥(FLAGS)仅适用于 Vercel 之外的应用、自定义环境或其他项目的 flags(SDK 密钥)。如果拉取后仍缺少 FLAGS_SECRET,请按照 FLAGS_SECRET 中的说明生成。

创建 flag

当用户要求创建或添加一个尚未在 Vercel 上创建的功能 flag 时,请按顺序执行以下步骤。如果该 flag 已在控制面板中创建(提示词中有说明,或 vercel flags create 报告该键已存在),请改为按照添加已在 Vercel 上存在的 flag中的说明操作。

开始之前
  • 如果缺少软件包、Vercel 关联、.env.local、Toolbar、flags.ts 或 Flags Explorer,请先完成设置 SDK。跳过已完成的步骤。
  • .env.local 中是否包含 VERCEL_OIDC_TOKEN=?→ 环境变量已拉取;如果本地评估因身份验证错误而失败,请参阅拉取环境变量。
  • 是否存在 flags.ts(或 lib/flags.ts、src/flags.ts)?→ 请在该文件中添加,而不是从头创建。
步骤
  1. 确保已设置 SDK:如有需要,请按照设置 SDK 操作,然后继续。

  2. 在 Vercel 中注册标志:针对已关联的项目运行 vercel flags create <flag-key> --kind boolean --description "<description>",或者传入 --project <name-or-id> 和 --scope <team>。选择与标志类型参数匹配的 kind;之后无法更改。

  3. 拉取环境变量:如果这是项目的第一个标志,请再次按照拉取环境变量操作;激活时已创建 FLAGS_SECRET。

  4. 在代码中声明标志:使用 vercelAdapter 将其添加到 flags.ts(如果文件不存在,则创建该文件):

    import { flag } from 'flags/next';
    import { vercelAdapter } from '@flags-sdk/vercel';
    
    export const myFlag = flag({
      key: 'my-flag',
      adapter: vercelAdapter,
    });
    
  5. 使用标志:在页面或组件中调用它,并根据结果有条件地渲染内容:

    import { myFlag } from '../flags';
    
    export default async function Page() {
      const enabled = await myFlag();
      return <div>{enabled ? 'Feature on' : 'Feature off'}</div>;
    }
    

添加 Vercel 中已存在的标志

如果标志是在控制面板中创建的,或由其他人创建,请使用此流程;例如,提示词说标志“已创建”,或要求你运行 vercel flags inspect 时。对于已存在的键,不要运行 vercel flags create。

  1. 确保已设置 SDK:如有需要,请按照设置 SDK操作。
  2. 读取定义:运行 vercel flags inspect <flag-key>。记下 kind、变体(值和标签)、描述,以及各环境提供的内容。
  3. 拉取环境变量:如果 .env.local 中没有 VERCEL_OIDC_TOKEN=,请按照拉取环境变量操作。
  4. 声明标志:使用 vercelAdapter 将其添加到 flags.ts。映射 inspect 的输出:
    • key:照原样使用打印出的标志键
    • kind → 类型参数:boolean → flag<boolean>,string → flag<string>,number → flag<number>,json → flag<YourType>
    • description:从 inspect 输出中复制
    • defaultValue:标志已归档或评估失败时提供的值(通常是生产环境当前提供的值)
    • options:可选;使用预计算时,或希望在 Flags Explorer 中列出变体时,使用与变体相同的配置
    • identify:如果标志有定向规则,则添加或复用一个,并使用控制面板中配置的实体属性(参见带评估上下文的标志)
    export const welcomeMessage = flag<string>({
      key: 'welcome-message',
      description: 'Copy shown on the landing page',
      defaultValue: 'control',
      adapter: vercelAdapter,
    });
    
  5. 使用标志:按照创建标志的第 5 步操作。

Vercel Flags 适配器

@flags-sdk/vercel 将 flag() 声明连接到 Vercel Flags。vercelAdapter 使用项目的 Vercel OIDC 令牌进行身份验证,并评估当前环境的配置;SDK 密钥(FLAGS)仅用于手动身份验证:适用于 Vercel 之外的应用、自定义环境或其他项目的标志(SDK 密钥)。激活 Vercel Flags 时,会为每个环境创建一个 FLAGS_SECRET,供 Flags Explorer 使用。

有关用户定向、属性类型、自定义适配器配置、共享 @vercel/flags-core 客户端以及 Flags Explorer 端点的信息,请参阅references/providers.md — Vercel。

更改标志提供的内容(启用、设置、拆分、逐步推出、规则、归档)应在控制面板中或使用 vercel flags 完成,而不是在代码中进行。vercel-flags 技能涵盖这些流程;vercel flags <cmd> --help 会列出可用选项。

声明标志

使用 Vercel Flags 时,请按照创建标志中的示例,使用 vercelAdapter 声明标志。其他提供方请参阅references/providers.md。以下是通用的 flag() 模式。

基本标志
import { flag } from 'flags/next'; // or 'flags/sveltekit'

export const showBanner = flag<boolean>({
  key: 'show-banner',
  description: 'Show promotional banner',
  defaultValue: false,
  options: [
    { value: false, label: 'Hide' },
    { value: true, label: 'Show' },
  ],
  decide() { return false; },
});
携带评估上下文的 Flag

使用 identify 确定请求的对象。返回的实体会传递给 decide:

import { dedupe, flag } from 'flags/next';
import type { ReadonlyRequestCookies } from 'flags';

interface Entities {
  user?: { id: string };
}

const identify = dedupe(
  ({ cookies }: { cookies: ReadonlyRequestCookies }): Entities => {
    const userId = cookies.get('user-id')?.value;
    return { user: userId ? { id: userId } : undefined };
  },
);

export const dashboardFlag = flag<boolean, Entities>({
  key: 'new-dashboard',
  identify,
  decide({ entities }) {
    if (!entities?.user) return false;
    return ['user1', 'user2'].includes(entities.user.id);
  },
});

使用 vercelAdapter 时,返回对象中的实体和属性名称(此处为 user.id)是仪表板规则和 vercel flags split|rollout|rules --by 的目标。它们必须与仪表板中配置的实体及属性类型相匹配。请参阅 references/providers.md — 用户定向 和 属性类型。

使用其他 Adapter 的 Flag

Adapter 将 Flag 连接到第三方提供商。每个 Adapter 都会替换 decide 和 origin:

import { flag } from 'flags/next';
import { statsigAdapter } from '@flags-sdk/statsig';

export const myGate = flag({
  key: 'my_gate',
  adapter: statsigAdapter.featureGate((gate) => gate.value),
  identify,
});

请参阅 references/providers.md,了解所有受支持的 Adapter。

关键参数
参数类型描述
keystring唯一的 Flag 标识符
decidefunction解析 Flag 的值
defaultValueany如果 decide 返回 undefined 或抛出异常,则使用此回退值
descriptionstring在 Flags Explorer 中显示
originstring用于在提供商仪表板中管理 Flag 的 URL
options{ label?: string, value: any }[]可能的值,用于预计算和 Flags Explorer
adapterAdapter实现 decide 和 origin 的提供商 Adapter
identifyfunction返回供 decide 使用的评估上下文(实体)

去重

将共享函数(尤其是 identify)包装在 dedupe 中,使其每个请求只运行一次:

import { dedupe } from 'flags/next';

const identify = dedupe(({ cookies }) => {
  return { user: { id: cookies.get('uid')?.value } };
});

注意:Pages Router 中无法使用 dedupe。

批量评估

若要一次评估多个 Flag,请调用 evaluate()(来自 flags/next),而不是逐个等待 Flag 或使用 Promise.all()。若要评估单个 Flag,直接调用即可:await myFlag()。

import { evaluate } from 'flags/next';
import { flagA, flagB } from '../flags';

// avoid: each await blocks the next, so the flags resolve sequentially
const a = await flagA();
const b = await flagB();

// avoid: parallel, but each flag is evaluated in isolation
const [a, b] = await Promise.all([flagA(), flagB()]);

// prefer: shares work across the batch
const [a, b] = await evaluate([flagA, flagB]);

evaluate() 比这两种方式都快。逐个等待 Flag 会让总延迟等于所有 Flag 的评估时间之和,而不是最慢的单个 Flag 的评估时间;Promise.all() 虽然会并行运行,但会分别评估每个 Flag。evaluate() 会为整个批次只预先读取一次 headers、cookies 和 overrides,并让 Adapter 在单次调用中解析一组 Flag,从而减少运行时需要管理的并行 Promise 数量,也降低异步工作被其他微任务中断的可能性。

它接受数组(按位置返回结果)或对象(按键返回结果):

const [a, b] = await evaluate([flagA, flagB]);
const { a, b } = await evaluate({ a: flagA, b: flagB });

在 App Router 之外(Pages Router 的 getServerSideProps/API 路由,或路由中间件),将请求作为第二个参数传入:await evaluate([flagA, flagB], request)。

evaluate() 始终在请求时评估 Flag。它不用于读取预计算(静态)值——读取这类值时,请使用 getPrecomputed(或使用代码调用 Flag:await myFlag(code, flagGroup))。

Adapter 可以选择通过实现可选的 bulkDecide 钩子来支持批处理。Vercel Adapter(@flags-sdk/vercel)实现了该钩子——解析数百个 Flag 时,评估时间大约可减少 10 倍。有关如何实现 bulkDecide,请参阅 references/providers.md — 自定义 Adapter;完整签名请参阅 references/api.md — evaluate。

Flags Explorer 设置

Next.js(App Router)
// app/.well-known/vercel/flags/route.ts
import { createFlagsDiscoveryEndpoint } from 'flags/next';
import { getProviderData } from '@flags-sdk/vercel';
import * as flags from '../../../../flags'; // adjust if flags live under lib/ or src/

export const GET = createFlagsDiscoveryEndpoint(async () => {
  return getProviderData(flags);
});
使用外部提供商的数据

将第三方提供商与 Vercel Flags 配合使用时,请使用 mergeProviderData 合并其数据。每个提供商适配器都会导出自己的 getProviderData——请参阅 references/providers.md 中针对各提供商的示例。

SvelteKit
// src/hooks.server.ts
import { createHandle } from 'flags/sveltekit';
import { FLAGS_SECRET } from '$env/static/private';
import * as flags from '$lib/flags';

export const handle = createHandle({ secret: FLAGS_SECRET, flags });

FLAGS_SECRET

预计算和 Flags Explorer 必需。Vercel Flags 激活后会为每个环境创建一个值。请保留现有值;在常规 SDK 设置期间不要轮换这些值。本地值缺失不代表远程值缺失:请先检查目标环境,然后按照 拉取环境变量 中的说明配置 Development。

仅在某个环境缺少密钥时才生成。使用 32 个经加密学安全随机生成的字节,并进行 base64 编码;每个环境使用不同的值。将 Preview 和 Production 的值标记为 Sensitive。请将生成的值直接发送到存储位置,例如通过 stdin 传递给 vercel env add;不要将其打印到终端输出、日志或聊天中,也不要将其嵌入命令参数。

预计算模式

使用预计算可在使用功能标志的同时保持页面为静态页面。中间件会评估标志,并通过重写将结果编码到 URL 中。页面读取预计算的值,而不重新评估。

高级流程:

  1. 声明标志并将其分组到数组中
  2. 在中间件中调用 precompute(flagGroup),获取 code 字符串
  3. 将请求重写为 /${code}/original-path
  4. 页面从 code 中读取标志值:await myFlag(code, flagGroup)

有关完整实现详情,请参阅特定框架的参考文档:

  • Next.js:请参阅 references/nextjs.md——涵盖代理中间件、预计算设置、ISR、generatePermutations 和多个分组
  • SvelteKit:请参阅 references/sveltekit.md——涵盖 reroute 钩子、中间件、预计算设置、ISR 和预渲染

自定义适配器

创建一个适配器工厂,返回包含 origin 和 decide 的对象。有关完整模式(包括默认适配器和单例客户端示例),请参阅 references/providers.md。

加密函数

若要在浏览器中保护标志数据的机密性(Flags Explorer 使用),可使用以下函数:

函数用途
encryptFlagValues加密已解析的标志值
decryptFlagValues解密标志值
encryptFlagDefinitions加密标志定义/元数据
decryptFlagDefinitions解密标志定义
encryptOverrides加密工具栏覆盖值
decryptOverrides解密工具栏覆盖值

默认情况下,所有函数都会使用 FLAGS_SECRET。示例:

import { encryptFlagValues } from 'flags';
import { FlagValues } from 'flags/react';

async function ConfidentialFlags({ values }) {
  const encrypted = await encryptFlagValues(values);
  return <FlagValues values={encrypted} />;
}

React 组件

import { FlagValues, FlagDefinitions } from 'flags/react';

// Renders script tag with flag values for Flags Explorer
<FlagValues values={{ myFlag: true }} />

// Renders script tag with flag definitions for Flags Explorer
<FlagDefinitions definitions={{ myFlag: { options: [...], description: '...' } }} />

参考文档

框架和提供商的详细指南分开存放,以精简上下文:

  • references/nextjs.md:Next.js 快速入门、工具栏、App Router、Pages Router、中间件/代理、预计算、去重、仪表板页面、营销页面、Suspense 回退
  • references/sveltekit.md:SvelteKit 快速入门、工具栏、钩子设置、通过 reroute + 中间件进行预计算、仪表板页面、营销页面
  • references/providers.md:所有提供商适配器——Vercel、Global Config、Statsig、LaunchDarkly、PostHog、GrowthBook、Flagsmith、Reflag、Split、Optimizely、OpenFeature 和自定义适配器
  • references/api.md:flags、flags/react、flags/next 和 flags/sveltekit 的完整 API 参考

评论

支持 Markdown