# flagnetic 中文文档

规范地址：https://npmcn.com/package/flagnetic/

版本：0\.1\.2
npm 元数据更新时间：2026-11-03T16:17:31.769Z
中文译文更新时间：2026-11-03T18:22:28.566Z
软件包许可：Apache-2\.0

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/flagnetic
原文对照：https://npmcn.com/package/flagnetic/?lang=original
项目主页：https://github.com/azverinus/flagnetic/tree/main/sdks/js#readme
源码仓库：https://github.com/azverinus/flagnetic

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：3.9 MB，43 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=22
- 运行依赖：0 个
- peer 依赖：react \>=18，@openfeature/web-sdk ^1\.10\.0，@openfeature/server-sdk ^1\.23\.0
- 首次发布：2026-10-27T08:14:18.114Z
- 最新版本发布：2026-11-03T16:17:31.230Z
- 已发布版本数：4（近 12 个月 4 个）

## 中文简介

Flag磁性SDK用于JavaScript和TypeScript: 功能标志和实验，自托管。

## 安装

```sh
npm install flagnetic
```

也可以使用：`pnpm add flagnetic`、`yarn add flagnetic`、`bun add flagnetic`

## README

# 用于JavaScript的磁性SDK

`flagnetic` 是Flagnetic的JavaScript和TypeScript SDK。一个npm包，带子路径exports、ESM和CommonJS，不依赖于运行时：

| 进口 | 为 |
| --- | --- |
| `flagnetic/server` | 节点22及更高版本，使用服务器密钥: 下载规则集，跟踪流中的更改并在本地评估标志，就像服务器一样 |
| `flagnetic/client` | 浏览器，与客户端密钥: 远程评估，从来没有评估 |
| `flagnetic/react` | 通过浏览器客户端进行React 18及更高版本的绑定 |
| `flagnetic/openfeature` | 服务器和web SDK的OpenFeature提供程序 |

`react`， `@openfeature/server-sdk` 和 `@openfeature/web-sdk` 是使用它们的入口点的可选对等依赖；所有其他入口点都没有它们。

```bash
npm install flagnetic
```

支持: 节点22及更高版本 (releases节点仍然支持; 在次要版本中删除已达到生命周期的版本)，运行ES2022的浏览器，React 18及更高版本， `@openfeature/server-sdk` 1.23以后， `@openfeature/web-sdk` 1.10及更新版本。该包使用SDK API `/sdk/v1` 并适用于从0.1.0开始的每个flagmagnetic服务器([兼容性](https://npmcn.com/docs/sdks/compatibility.md)); [docs/sdks/javascript.md](https://npmcn.com/docs/sdks/javascript.md) 如何选择和部署每个入口点。

## 服务器模式

```ts
import { createServer } from "flagnetic/server";

const flagnetic = createServer(process.env.FLAGNETIC_SDK_KEY!, "https://flags.example.com");

// Wait at most two seconds for the first ruleset; it never rejects and
// tells the stage reached: "fresh", "last_good" (from the store) or "built_in".
await flagnetic.ready(2000);

const context = { units: { user: "u-1001" }, attributes: { country: "DE" } };
if (flagnetic.getBool(context, "new_checkout", false)) {
  // ...
}

// Why a value was served.
const detail = flagnetic.getDetail(context, "banner_text", "Welcome");
// detail.value, .variation, .reason, .errorCode, .rulesetVersion, .stage, .experiment

flagnetic.exposure(context, "banner_text"); // an experiment's exposure is explicit
flagnetic.alias({ fromUnitType: "anonymous", fromUnitId: "d-8f1c", toUnitType: "user", toUnitId: "u-1001" });

await flagnetic.close(); // flushes what is queued, within 5 seconds
```

`createServer` 拒绝客户端密钥(`fn_cli_…`)，它属于 `flagnetic/client`；网络麻烦永远不会让它抛出。getter返回您的内置默认值，而不是每当标志未知，具有另一种类型或无法评估时抛出。该规则集是通过以下方式下载的 `If-None-Match`，默认情况下，流被保留，并且在p99的2秒内到达客户端；流下降时，客户端每30秒轮询一次，流上升时每300秒轮询一次。一个 `store` 选项命名一个文件，该文件在重新启动时保留最后一个好的规则集。

选项: `pollInterval`， `pollIntervalWithoutStream`， `stream`， `requestTimeout`， `store`， `logger`， `allowOverrides`， `sendSummaries`， `sendExposures`， `flushInterval`， `summaryInterval`， `platform`， `appVersion` 和 `offline`． 请求携带 `User-Agent: flagnetic-js/<version> (<platform>)`。

`onChange(listener)` 和 `onStage(listener)` 返回一个删除侦听器的函数。 `stage` 和 `status()` 报告回退链的位置。

### 当地的覆盖

```ts
const flagnetic = createServer(key, url, { allowOverrides: true });
flagnetic.overrides.set("new_checkout", true); // reads as reason "local_override"
```

`overrides.set` 抛出 `LocalOverridesNotAllowed` 没有 `allowOverrides` 和 `LocalOverrideRefused` 而规则集属于受保护的环境。两个日志 `override_refused`。

### 在服务器上呈现

`flagnetic.values(context, { flags? })` 将上下文的标志计算到浏览器客户端作为它的文档中 `bootstrap`． 它被掩盖为客户端键的答案是：目标决定值的原因是什么 `targeting_match` 并且没有命名规则，因为文档被写入任何人都可以阅读的页面。

### 测试

```ts
import { testServer, testServerFromValues } from "flagnetic/server";

const flagnetic = testServerFromValues({ new_checkout: true, limit: 10 });
flagnetic.set("new_checkout", false); // rewrite a value; change listeners fire
flagnetic.reload();                   // back to what the client was built from
flagnetic.recorder.exposures();       // exposures and aliases that would have been sent
```

`testServer(document)` 从规则集文档构建相同的脱机客户机。两者都不发出网络请求。

### 传播

在flagile中进行的更改在流启动时在p99的2秒内到达服务器客户端，否则在轮询间隔内。

## 浏览器客户端

```ts
import { anonymousId, createClient } from "flagnetic/client";

const flagnetic = createClient("fn_cli_…", "https://flags.example.com", {
  context: { units: { anonymous: anonymousId() }, attributes: { country: "DE" } },
});

// Values serve at once: the bootstrap document, else the last good values
// this browser kept for the key and the context's units, else your defaults.
await flagnetic.ready(2000); // "fresh", "last_good" or "built_in"; never rejects

if (flagnetic.getBool("new_checkout", false)) {
  // ...
}

// After sign-in: resolves once the new context's values arrived, while the
// previous values keep serving, so there is no flash of defaults.
await flagnetic.setContext({ units: { anonymous: anonymousId(), user: "u-1001" } });
```

`createClient` 拒绝服务器密钥(`fn_srv_…`)，绝不能捆绑发货。客户端要求服务器评估其上下文，并且从不包含评估器，因此 `flagnetic/client` 保持在CI中检查的gzip大小预算内。它通过上下文将自己命名给服务器 `sdk`， `sdk_version` 和 `platform` 属性，因为浏览器不能设置 `User-Agent`。

最后一个好的值保存在 `localStorage` 在SDK密钥下和上下文的单元(在内存中 `localStorage` 不能到达的，不能到达的 `store: false`)，因此下一页加载在任何请求之前为它们提供服务，并且一个浏览器中的两个键永远不会看到彼此的值。一个 `bootstrap` 选项接受文档 `values(context)` 的 `flagnetic/server` 呈现到页面中，作为 `fresh`;一个 `offline` 选项接受一个值文档，并且根本不发出任何请求 `offline.set`， `offline.reload` 和 `offline.recorded()` 用于测试。

当选项卡变得可见或网络恢复时，客户端重新获取，最多每10秒一次，并且在选项卡可见时每300秒轮询一次。与 `stream: true` 它还保存更改流，读取 `fetch` 这样它就可以发送它的密钥和 `Last-Event-ID` 在报头和重新连接自己；默认情况下是关闭的，因为每个打开的选项卡都有一个连接。

`anonymousId(unitType = "anonymous")` 为每个单元类型的浏览器提供一个ID：在第一次调用时生成并保存在其中的随机uidv7 `localStorage` 对于每一个后来的访问的起源。它存在于密钥的存储之外，因此旋转或替换的客户端密钥将实验中的每个匿名访问者的ID桶保存到其中; 其中 `localStorage` 无法到达，则该页的ID保留。传递项目为匿名流量声明的单元类型，并在登录后将ID保留在上下文的单元中。

风险、别名和评估摘要在队列中 `localStorage` 下键和发送每5秒，而TAB是可见的； `flush()` 和 `close()` 在5秒内发送。具有相同键的一个源的选项卡共享队列，因此关闭的选项卡剩下的内容将由它们中的任何一个的下一批发送。当选项卡被隐藏或页面被关闭时，客户端将队列交给 `navigator.sendBeacon`，浏览器在页面消失后传递该密钥：客户端密钥作为 `sdk_key` 查询参数和信封为 `text/plain`，因此不需要CORS预飞行，并且不符合浏览器的64 KiB信标配额的内容将在下一次访问时保持排队。选项: `context`， `bootstrap`， `offline`， `store`， `stream`， `pollInterval`， `requestTimeout`， `flushInterval`， `sendExposures`， `sendSummaries`， `allowOverrides`， `logger`， `platform` 和 `appVersion`。吸气剂， `getDetail`， `stage`， `status()`， `onChange`， `onStage`， `overrides`， `exposure(key)` 和 `alias` 在服务器模式下工作，没有context参数。

### 传播

在fllagnetwork中所做的更改最迟在300秒的轮询后到达浏览器客户端，而在选项卡可见时，在选项卡再次可见或网络返回时立即到达浏览器客户端，并且在流打开的几秒钟内。

## 反应

```tsx
import { FlagneticProvider, useFlag, useFlagDetail, useStage, useFlagnetic } from "flagnetic/react";

function App({ bootstrap }: { bootstrap?: object }) {
  return (
    <FlagneticProvider sdkKey="fn_cli_…" baseUrl="https://flags.example.com"
                       options={{ context: { units: { user: "u-1001" } } }}
                       bootstrap={bootstrap}>
      <Checkout />
    </FlagneticProvider>
  );
}

function Checkout() {
  const enabled = useFlag("new_checkout", false);  // re-renders when this flag's value changes
  const detail = useFlagDetail("banner_text", "Welcome"); // value, reason, stage, experiment
  const stage = useStage();                         // "built_in", "last_good" or "fresh"
  const client = useFlagnetic();                    // setContext, exposure, alias; undefined until mounted
  // ...
}
```

提供程序采用构建应用程序的客户端 `createClient`,它永远不会关闭，或者 `sdkKey`， `baseUrl`， `options` 和 `bootstrap` 建造并拥有一个。当提供程序挂载时，这四个字只读取一次，因此JSX中的对象字面值不会重新构建客户端；给提供者一个 `key` 重新开始.钩子通过订阅 `useSyncExternalStore`更改会重新呈现读取已更改标志的组件，而不是其他组件，并且会抛出提供商外部的钩子。

要在服务器上呈现，请传递文档 `values(context)` 的 `flagnetic/server` 作为 `bootstrap`,并将相同的文档写入浏览器的页面。当页面在服务器上呈现并且水合时，钩子为它服务，因此浏览器中的第一个呈现与HTML匹配; 提供者在水合之后从同一文档构建其客户端，然后发生第一个远程评估。当页面在服务器上呈现时，不会获取任何内容。一个 `bootstrap` 的 `undefined`,什么 `values()` 在服务器模式具有规则集之前给出，在阶段呈现内置默认值 `built_in` 两边都是。

## OpenFeature

`flagnetic/openfeature` 每个OpenFeature SDK都有一个提供程序: `FlagneticServerProvider` 为 `@openfeature/server-sdk` 1.23及更高版本的Node `FlagneticWebProvider` 为 `@openfeature/web-sdk` 在浏览器中1.10和更高版本。安装你方的SDK; 入口点在运行时不加载。

```ts
import { OpenFeature } from "@openfeature/server-sdk";
import { FlagneticServerProvider } from "flagnetic/openfeature";

await OpenFeature.setProviderAndWait(new FlagneticServerProvider(process.env.FLAGNETIC_SDK_KEY!, "https://flags.example.com"));
const client = OpenFeature.getClient();

const context = { targetingKey: "u-1001", country: "DE", units: { anonymous: "d-8f1c" } };
const enabled = await client.getBooleanValue("new_checkout", false, context);

client.track("exposure", context, { flag: "pricing" }); // an experiment's exposure is explicit
client.track("alias", context, { fromUnitType: "anonymous", fromUnitId: "d-8f1c", toUnitType: "user", toUnitId: "u-1001" });
```

```ts
import { OpenFeature } from "@openfeature/web-sdk";
import { FlagneticWebProvider } from "flagnetic/openfeature";

await OpenFeature.setProviderAndWait(new FlagneticWebProvider("fn_cli_…", "https://flags.example.com"), { targetingKey: "u-1001" });
const enabled = OpenFeature.getClient().getBooleanValue("new_checkout", false);
await OpenFeature.setContext({ targetingKey: "u-2002" }); // evaluated for the new context
```

每个提供者构建并拥有其模式的客户端， `provider.client`,并采用该模式的选项加上两个自己的选项: `unitType`，目标键是（）的单位ID。`user` 默认情况下)，以及 `readyTimeout`多长时间？ `initialize` 等待第一个规则集或值 (服务器上的10秒，浏览器中的5秒)。 `initialize` 从不失败：在舞台上 `built_in` 每一个吸气剂服务你的默认。上下文属性 `units` 按单位类型添加单位id；所有其他属性都是一个属性，a `Date` 作为RFC 3339字符串发送。web提供程序评估OpenFeature的上下文，因此它不需要 `context` 选择。

原因映射为 `reasonOf` 确实，每个fl磁性提供商都一样: `rule`， `prerequisite_failed`， `override` 和 `targeting_match` 是 `TARGETING_MATCH`， `rollout` 和 `experiment` `SPLIT`， `fallthrough` `DEFAULT`， `off` `DISABLED`， `local_override` `STATIC`，以及新服务器添加的一个原因 `UNKNOWN`． 而提供的内置默认值是 `ERROR` 与 `FLAG_NOT_FOUND`， `TYPE_MISMATCH` 或 `GENERAL`,flor磁性错误代码作为消息。旗标元数据承载着飘扬的原因 `REASON_METADATA_KEY` （`flagnetic_reason`), 与 `stage`， `ruleset_version`， `variation`， `overridden` 还有一个实验 `experiment_key`， `experiment_variation`， `experiment_unit_type`， `experiment_unit_id`， `experiment_inclusion_bucket`， `experiment_variation_bucket` 和 `experiment_override`。

`track("exposure")` 报告了由 `flag`，用于上下文（浏览器中的当前上下文），为 `exposure` 做; `track("alias")` 记录别名。任何其他事件名称，或者缺少所需内容的事件名称，都不起任何作用。

事件: `PROVIDER_CONFIGURATION_CHANGED` 用可能已经改变的标志的键； `PROVIDER_STALE` 当客户端移动到 `last_good`； `PROVIDER_READY` 当它再次新鲜时； `PROVIDER_ERROR` 当SDK密钥被撤销时，一直保留到密钥再次被接受为止。OpenFeature报告 `READY` 一次 `initialize` 或上下文更改解决，无论达到什么; 在它之后，提供者发送 `ERROR` 或 `STALE` 当客户站在那里的时候。

## 评估者

```ts
import { decodeRuleset } from "flagnetic/server";

const ruleset = decodeRuleset(document); // a ruleset document, as JSON text, bytes or a parsed value
const result = ruleset.evaluate("new_checkout", { units: { user: "u-1001" }, attributes: { country: "DE" } });
```

的正则表达式 `matches` 条件在包的线性时间RE2引擎上运行，因此模式意味着它在服务器上的含义，属性值不会使进程停滞。来自比包更新的服务器的规则集可能包含计算器不知道的操作符、条件类型或服务类型； `ruleset.unsupportedRules()` 报告他们的标志，并评估这样一个标志的答案 `error` 与 `inconsistent_ruleset`。

## 发展

```bash
pnpm install
pnpm lint && pnpm typecheck
pnpm test            # the shared conformance suite and unit tests, in Node
pnpm test:browser    # the same suite in Chromium (pnpm exec playwright install chromium once)
pnpm test:browsers   # the browser client cross-origin and the React bindings in Chromium, Firefox and WebKit (FLAGNETIC_BROWSERS=webkit for one)
pnpm test:p99        # 1,000 flags evaluate under 1 ms at p99
pnpm build && pnpm check:package && pnpm check:size
pnpm test:e2e        # the smokes in Node and Chromium against two real replicas (below)
```

烟需要端到端的控制 [`e2e/`](https://npmcn.com/e2e/README.md) 运行: 开始 `flagnetic-e2e up` 与 `FLAGNETIC_E2E_SEED` 命名一个文件，然后运行 `pnpm test:e2e` 用相同的 `FLAGNETIC_E2E_SEED` 而且，除非 `flagnetic-e2e` 在……上 `PATH`， `FLAGNETIC_E2E_CMD` 命名它。它们以撤销种子的密钥而结束，因此在第二次运行之前再次提起线束。

正则表达式引擎的Unicode类表是从Go的类表生成的 `regexp/syntax`;在Go升级运行之后 `pnpm gen:unicode`,并且当它们陈旧时CI失败。

## 常见问题

### flagnetic 是什么？

Flag磁性SDK用于JavaScript和TypeScript: 功能标志和实验，自托管。

### 如何安装 flagnetic？

在项目目录运行 npm install flagnetic，或使用 pnpm add / yarn add / bun add。

### flagnetic 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### flagnetic 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### flagnetic 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### flagnetic 可以商用吗？

flagnetic 使用 Apache-2\.0 许可。宽松许可：允许商业使用、修改和再分发，并附带明确的专利授权；分发时需保留许可与 NOTICE 文件，修改过的文件要注明改动。以上为概括，不构成法律意见，请以许可原文为准。

### flagnetic 还在维护吗？

最新版本 0\.1\.2 发布于 2026/11/04（今天）；至今共发布 4 个版本，其中过去 12 个月发布了 4 个；首次发布于 2026/10/27。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

