@coframe/sdk

中文文档
1.1.56发布于 2026/11/04TypeScriptESM + CJS近 30 天下载 2287

Coframe optimizes your website using generative AI to deliver maximum impact to your audience and allow your site to improve itself as you sleep. Learn more at coframe.com.

原始使用文档
文档目录21 节
  1. Install
  2. Quick start
  3. Browser
  4. Runtime endpoint overrides
  5. URL controls
  6. React
  7. Server prefetch
  8. Server-side assignment
  9. Feature flags and remote config
  10. Common React usage
  11. Read variants in components
  12. Track conversions
  13. Analytics and log callbacks
  14. Next.js / Remix / SSR notes
  15. Package layout
  16. TypeScript compatibility
  17. API overview
  18. @coframe/sdk
  19. @coframe/sdk/feature-flags
  20. @coframe/sdk/server
  21. @coframe/sdk/react

@coframe/sdk

Bring your UX to life with AI-powered optimization and personalization.

npm

Coframe helps you personalize and optimize your site with experiments, targeting, anti-flicker rendering, analytics callbacks, and conversion tracking.

This package exposes three entry points:

  • @coframe/sdk for the browser runtime
  • @coframe/sdk/server for SSR and edge prefetching
  • @coframe/sdk/react for React apps

Install

npm install @coframe/sdk

If you use the React entry point, install React as well:

npm install react@">=17"

Quick start

Browser

import { init } from '@coframe/sdk'

await init({
  config: {
    projectId: 'your-project-id',
  },
})

init() accepts config and optional projectData, so npm consumers do not need to manually populate window.CFQ first.

Runtime endpoint overrides

Private-CDN integrations can override the browser SDK's project and ingest endpoints:

await init({
  config: {
    projectId: 'your-project-id',
    projectUrl: 'https://cdn.example.com/coframe/project.json',
    ingestUrl: 'https://www.example.com/ingest/v2',
    replayScriptUrl: 'https://www.example.com/replay/latest.js',
    enableDebug: false,
  },
})

projectUrl must return a raw, complete Project JSON object. ingestUrl replaces the ingest base URL (https://ingest.app.coframe.com/ingest/v2); the SDK still appends the category and project path, so beacons go to <ingestUrl>/batched_events/<projectId>/, <ingestUrl>/batched_monitoring/<projectId>/, <ingestUrl>/session_result/, and <ingestUrl>/identify/<projectId>/. A proxy only has to forward <ingestUrl>/* to https://ingest.app.coframe.com/ingest/v2/* with the path unchanged. Both overrides are optional and retain the compiled Coframe endpoints when omitted.

replayScriptUrl replaces the session replay recorder script URL. A proxy must forward it to <edge>/replay/latest.js unchanged.

Configuring either projectUrl or ingestUrl makes CMAB experiments use deterministic local assignment without calling the Edge assignment API. Configure all three overrides when the browser must avoid the SDK-compiled Coframe project, ingest and replay script endpoints entirely. Debug tools remain controlled by enableDebug.

URL controls

The npm entry uses the same browser URL controls as the script-tag and Edge installations:

  • cf_disable skips initialization and reveals any antiflicker state.
  • coframe_variant_id previews one or more comma-separated variant IDs. Add rollouts to the list to apply them on top of the rolled-out winners a production visitor would get.
  • coframe_should_send_events=true allows events during preview for testing.

Debug tools are allowed by default. A preview link (coframe_variant_id with cf_preview=true) keeps the selected variants active for the current tab as you navigate; turn off Keep across navigation in the Preview Session tool to stop. Set enableDebug: false to prevent URL or saved session state from loading the Launcher (a preview link then applies to its own page only).

For SSR, pass the complete request URL, including its query string, as currentUrl. This is required for query-sensitive targeting and allows getProjectData() to short-circuit before any request when cf_disable is present.

React

import { CoframeProvider } from '@coframe/sdk/react'
import type { CoframeConfig } from '@coframe/sdk'

const projectConfig: CoframeConfig = {
  projectId: 'your-project-id',
}

export default function App({ children }) {
  return (
    <CoframeProvider projectConfig={projectConfig}>
      {children}
    </CoframeProvider>
  )
}

Mount the provider once at the root of your app. It initializes the SDK on mount and keeps treatment state available to child components. Keep it mounted across client-side route changes; runtime commands and the SDK's navigation observer handle subsequent page changes without remounting it.

Server prefetch

import { getProjectData } from '@coframe/sdk/server'

const serverData = await getProjectData({
  projectId: 'your-project-id',
  currentUrl: request.url,
  headers: request.headers,
})

const projectData = serverData?.project
  ? {
      project: serverData.project,
      personalizationHeaders: serverData.personalizationHeaders,
    }
  : undefined

Pass projectData into <CoframeProvider> to avoid the extra client-side project fetch and personalize on first paint.

Server-side assignment

Use getExperimentAssignment() when the server must know one experiment's variant before rendering or calling an upstream service. Edge performs the full assignment; the package never falls back to local bucketing.

import {
  generateUserToken,
  getExperimentAssignment,
} from '@coframe/sdk/server'

// Next.js `request.cookies.get()` returns `{ name, value }`; other runtimes
// return the string. Either way `userToken` must be the string value.
const existingToken = request.cookies.get('coframe.user.token')?.value
const userToken = existingToken ?? generateUserToken()

if (!existingToken) {
  // Do not set `httpOnly`: the browser SDK reads this cookie to reuse the
  // same identity. An HttpOnly token makes the browser generate a new one and
  // bucket differently, with no error.
  response.cookies.set('coframe.user.token', userToken, {
    path: '/',
    secure: true,
    sameSite: 'lax',
    maxAge: 400 * 24 * 60 * 60,
  })
}

const assignment = await getExperimentAssignment({
  projectId: 'your-project-id',
  experimentId: 'your-experiment-id',
  userToken,
  currentUrl: request.url,
  headers: request.headers,
})

const upstreamVersion =
  assignment?.variant?.slug === 'variant-2' ? 'v2' : 'v1'

The package derives visitor context from an allowlist of request headers. Explicit userAgent, languages, browserPlatform, country, region, city, device, botScore, verifiedBotCategory, clientIp, userVisitCount, and userContext options override the derived values. Only the lane cookie is forwarded to Edge; authorization and application cookies are not forwarded. clientIp is used only for existing blocklist targeting.

Geography, device, bot score, and client IP are read from cf-* request headers. Cloudflare adds cf-ipcountry and cf-connecting-ip to every request that reaches your origin; cf-region-code and cf-ipcity need the "Add visitor location headers" Managed Transform, cf-device-type needs "cache by device type", and cf-bot-score needs Bot Management plus a Transform Rule. If your server is not behind Cloudflare, or a header is not enabled, that value is missing: the country becomes unknown, country audiences never match, and the device falls back to the user agent. Pass country, region, city, and device explicitly in that case, from whatever geo source your stack already has. Values are forwarded verbatim and audiences compare them exactly, so use Cloudflare's spelling: country is the uppercase ISO 3166-1 alpha-2 code (US, not us), region the ISO 3166-2 subdivision code (CA), and device one of desktop, mobile, tablet.

When a result is available, pass its project snapshot to the provider. The browser reads the same standard token cookie and runs its existing assignment flow without fetching project data again.

<CoframeProvider
  projectConfig={{ projectId: 'your-project-id' }}
  projectData={assignment?.projectData}
>
  {children}
</CoframeProvider>

assignment is undefined when the call failed (see below), so read projectData with optional chaining and decide there whether the browser may initialize on its own.

generateUserToken() uses Web Crypto when available and falls back to a non-cryptographic UUID generator in server runtimes without Web Crypto. The token is assignment identity, not an authentication credential. It remains stable until its cookie is removed, expires, or is replaced. The separate visit count uses the SDK's existing two-hour visit boundary. The package only reads the visit cookies the browser SDK writes (coframe.cvc, coframe.lastVisitTime) and never sets them, so without the browser SDK running after consent the count stays at 1 and returning audiences never match.

The result contains either one variant or variant: null with a structured reason (experiment_not_found; not_assigned, which also covers a visitor held out of this experiment or of the whole project by a holdout and renders the default experience in the browser too; unsupported_experiment_type for CMAB targets; preview_active for URL previews; a newer Edge may add reasons, so treat any reason you do not know as "no variant"). Both forms include the exact projectData snapshot used by Edge, so the browser can initialize from that snapshot. The snapshot also carries the normalized language, platform, and visit-count values used by Edge. The browser uses those values only for its initial assignment and returns to its normal browser-derived context for later SPA navigations. In every case without a variant, use the default upstream experience.

The function returns undefined for transport, timeout (timeoutMs, default 1500 ms), Edge, or response validation failures, and when currentUrl carries cf_disable. Handle that state explicitly. Invalid options are a programming error and throw instead: an empty projectId, experimentId or userToken, a currentUrl that is not an absolute URL (Express and Fastify req.url is a path, build the full URL first), out-of-range languages, device, botScore, timeoutMs and userVisitCount values, any string option longer than its limit (4096 characters for URLs, 2048 for the user agent, 200 for projectId, 512 for experimentId and userToken), or a userContext that serializes to more than 4096 bytes (userContext is too large). Values derived from request headers are clamped instead. To preserve server/browser consistency for the response, do not let the browser independently fetch project data and assign an experiment after the server has fallen back to its default. Applications that accept that possible mismatch may instead initialize the browser normally as their fail-open policy.

What Edge cannot evaluate

Server assignment runs the same deterministic logic as the browser, with the same token, the same project snapshot, and the same visitor context. The browser then reaches the same bucket because every input is the same. Five kinds of input only exist in the page, so Edge cannot evaluate them:

  • JavaScript targeting. A js_function condition (custom JavaScript that reads a page value, a cookie, localStorage, or the dataLayer) runs only in the browser, whether it sits on an audience or on a frame's activation condition. Edge treats it as not matched.
    • If the target experiment itself uses a JavaScript audience, the result is variant: null with reason not_assigned. Do not use JavaScript audiences on experiments you resolve on the server.
    • Experiments in the same frame are evaluated in order and the first match wins. If a JavaScript-targeted experiment sits ahead of the target in the same frame, Edge skips it and assigns the target, while the browser can match it and skip the target. Keep JavaScript-targeted experiments out of the target experiment's frame, or place them after the target.
    • The same applies to a JavaScript-targeted experiment that shares a lane with the target, wherever it sits: lane winners are drawn from the experiments the visitor can reach, and Edge cannot tell whether that one is reachable. Keep JavaScript-targeted experiments out of the target's lane.
  • CMAB experiments. A target CMAB experiment returns variant: null with reason unsupported_experiment_type.
  • URL fragments. A server never sees the #fragment of the page URL, and the browser re-evaluates the snapshot with its full location.href. Frames that use base_url_matches ignore query and fragment and are safe; an exact url condition compares both, and a url_regex runs against the full location.href in the browser, so pass the same URL the browser will have, without a fragment, and avoid regexes anchored on the end of the URL.
  • Previews. Previews are browser-only. A coframe_variant_id URL parameter reaches Edge, but the published preview payloads do not name the experiment a variant belongs to, so the server answers variant: null with reason preview_active and the browser renders the preview from the returned snapshot. A preview kept across navigation by the Preview Session tool lives in the browser's sessionStorage and never reaches Edge; the browser discards the snapshot and fetches its own project. Both affect preview users only; render the default upstream experience.
  • Lane state. Edge reads lane claims from the coframe.lanes cookie only. Before cookie consent the browser keeps claims in localStorage, which the server cannot see, and the server does not persist the claims Edge commits. Laned experiments therefore agree once the browser has written the cookie, not for a visitor without cookie consent.

Audience conditions based on URL (base_url_matches), country, device, language, platform, visit type, search, time, allocation, or userContext evaluate identically on both sides, as does bot cloaking. For platform, Edge derives the same navigator.platform family the browser reports (a Linux … string on Android, for example Linux armv8l), or takes an explicit browserPlatform option.

If the application uses a custom identity cookie, call setUserToken() before browser initialization with waitForUserToken: true. That integration must also keep visit and lane state aligned between the server and browser.

Feature flags and remote config

Browser Feature Flags use the existing public projectId and receive only flags marked Public. selector is optional; use names for individual flag keys and groups to evaluate every flag assigned to a page or business area.

import { setUserToken } from '@coframe/sdk'
import {
  featureFlags,
  getFeatureFlag,
  getFeatureFlagPayload,
  isFeatureEnabled,
  onFeatureFlags,
} from '@coframe/sdk/feature-flags'

setUserToken('customer-123')

await featureFlags.init({
  projectId: 'your-project-id',
  // Optional: replaces the complete default ingest base, including /ingest/v2.
  ingestUrl: 'https://events.example.com/coframe',
  selector: {
    names: ['checkout-redesign'],
    groups: ['homepage_campaigns'],
  },
})

onFeatureFlags(() => {
  if (isFeatureEnabled('checkout-redesign')) {
    showNewCheckout()
  }

  const content = getFeatureFlagPayload('homepage-content')
  const checkoutVariant = getFeatureFlag('checkout-layout')
})

Feature Flags are a separate browser entry point and do not increase or initialize the main SDK bundle. When both clients are used, Feature Flags reuse the latest setUserToken() command from Coframe's shared command queue and follow later identity changes automatically.

Loading does not delay Coframe page modification. Synchronous getters return undefined until a cached or fresh evaluation is available. The browser client always keeps the latest successful response in memory. Before consent, no Feature Flag result or evaluation context is persisted. After consent, the client also caches the response in localStorage for five minutes and keys it with an opaque SHA-256 fingerprint derived from the user token and evaluation context. It can fall back to that persistent cache if Edge is temporarily unavailable. Call reloadFeatureFlags() to request fresh evaluations explicitly.

Every single-flag getter automatically records a feature_flag_called event after a result has loaded. This applies to boolean, multivariate, and Remote Config flags. The event includes the key, evaluated value, payload, version, reason, and evaluation request metadata. The project, key, and version identify the corresponding flag definition without changing the Edge response. Every getter or hook access records its own event, including repeated reads of the same result. To read without recording an event, pass { sendEvent: false }:

const variant = getFeatureFlag('checkout-layout', { sendEvent: false })

Tracking uses an independent Feature Flags queue and does not initialize the main SDK. It follows the shared consent state: while consent is explicitly disabled, events remain in memory and are sent after consent is enabled. The optional Feature Flags ingestUrl has the same base-replacement behavior as the main SDK; the client appends /batched_events/<projectId>/.

Payloads support any JSON value up to 32 KB. A payload is returned only when its flag is enabled. Remote Config uses the same transport and payload API and returns the same payload to all users while active:

import { getRemoteConfig } from '@coframe/sdk/feature-flags'

const navigation = getRemoteConfig('navigation-config')

Multivariate flags return a stable variant key from getFeatureFlag(). Their weights divide users into deterministic buckets, and each variant can carry an independent optional payload. isFeatureEnabled() remains a boolean convenience and returns true whenever a variant is assigned:

import {
  getFeatureFlag,
  getFeatureFlagPayload,
  isFeatureEnabled,
} from '@coframe/sdk/feature-flags'

const variant = getFeatureFlag('checkout-layout')
const config = getFeatureFlagPayload('checkout-layout')
const enabled = isFeatureEnabled('checkout-layout')

React apps can read evaluated values from the existing provider:

import {
  useFeatureFlag,
  useFeatureFlagEnabled,
  useFeatureFlagPayload,
  useFeatureFlagVariantKey,
} from '@coframe/sdk/react'

function Checkout() {
  const enabled = useFeatureFlag('checkout-redesign')
  const payload = useFeatureFlagPayload('checkout-redesign')

  if (enabled === undefined) return <CheckoutSkeleton />
  return enabled ? <NewCheckout config={payload} /> : <OldCheckout />
}

function CheckoutLayout() {
  const variant = useFeatureFlagVariantKey('checkout-layout')
  const enabled = useFeatureFlagEnabled('checkout-layout')
  const payload = useFeatureFlagPayload('checkout-layout')
  return <Checkout variant={variant} enabled={enabled} config={payload} />
}

For server and edge runtimes, use a secret Feature Flags Server API key. It is project-scoped, can read Private flags, and must never be included in a browser bundle or a NEXT_PUBLIC_* environment variable. evaluateFlags() records every flag returned by its selector, while each single-key helper records only that key. Repeated evaluations record new events even when the result is unchanged. Both paths enqueue tracking without delaying their result and accept sendEvent: false to evaluate without recording an event. Single-key helpers send a key-filtered request and fail closed unless a defaultValue is supplied. Browser and server helpers both call /api/v1/flags; server helpers include the API key as a Bearer credential so the response may include both Public and Private flags:

import {
  evaluateFlags,
  flushFeatureFlagEvents,
  getFeatureFlag,
  getRemoteConfig,
  isFeatureEnabled,
} from '@coframe/sdk/server'

const options = {
  projectId: 'your-project-id',
  apiKey: process.env.COFRAME_FEATURE_FLAGS_SERVER_KEY!,
  userToken: user.id,
  currentUrl: request.url,
  userContext: { plan: user.plan },
}

const checkoutFlags = await evaluateFlags({
  ...options,
  selector: { groups: ['cart_checkout'] },
  ingestUrl: 'https://events.example.com/coframe',
})

// evaluateFlags tracks every returned flag without another Edge request.
// Single-key helpers track their requested key automatically.
const checkoutVariant = await getFeatureFlag('checkout-layout', {
  ...options,
  ingestUrl: 'https://events.example.com/coframe',
})

// Useful before a short-lived process exits. Tracking failures are swallowed.
await flushFeatureFlagEvents()

const enabled = await isFeatureEnabled('checkout-redesign', {
  ...options,
  defaultValue: false,
})
const variant = await getFeatureFlag('checkout-layout', options)
const content = await getRemoteConfig('homepage-content', options)

Common React usage

Read variants in components

'use client'

import { useCoframeVariant } from '@coframe/sdk/react'

export function CheckoutPage() {
  const variant = useCoframeVariant('checkout_flow_v2')

  if (variant === undefined) return <CheckoutSkeleton />
  if (variant === 'treatment') return <NewCheckout />
  return <OldCheckout />
}

Other hooks:

  • useCoframeTreatments() returns all treatments or null while init is in flight
  • useCoframeTreatment(experimentId) returns one treatment payload

Track conversions

'use client'

import { trackConversion } from '@coframe/sdk'

export function CheckoutButton() {
  return (
    <button
      onClick={() =>
        trackConversion({
          metricName: 'checkout_complete',
          value: 99.95,
        })
      }
    >
      Complete checkout
    </button>
  )
}

Analytics and log callbacks

import { CoframeProvider } from '@coframe/sdk/react'

<CoframeProvider
  projectConfig={projectConfig}
  onTreatmentAppliedBulkV2={(data) => {
    for (const treatment of data) {
      analytics.track('Experiment Viewed', treatment)
    }
  }}
  onLog={(payload) => {
    console.error('[coframe]', payload.monitoring_type, payload)
  }}
>
  {children}
</CoframeProvider>

If your privacy policy permits treatment analytics before cookie consent, use onTreatmentAppliedBulkV2WithoutConsent instead. It has the same callback arguments and does not fire again when consent is later granted.

Outside React, you can also register callbacks from the main entry:

import { onLog, onTreatmentApplied } from '@coframe/sdk'

Next.js / Remix / SSR notes

  • getProjectData() is intended for SSR and edge runtimes that can provide a full request URL and request headers.
  • In Pages Router or Remix, per-request server loaders are the most straightforward place to call it.
  • In App Router, keep the provider mounted in a persistent shell, but be careful about request URL handling in layout.tsx if you need query-sensitive SSR targeting.
  • The npm runtime reveals antiflicker styles but cannot insert them early enough to protect the initial paint. Render antiflickerCSS from getProjectData() in the server document head, or place equivalent static CSS and a timeout fallback there before application content.

Package layout

The published tarball contains only dist/pkg/:

dist/pkg/
├── cf.esm.mjs        cf.cjs.js        types/index.d.ts    ← @coframe/sdk
├── server.esm.mjs    server.cjs.js    types/server.d.ts   ← @coframe/sdk/server
└── react.esm.mjs     react.cjs.js     types/react.d.ts    ← @coframe/sdk/react

TypeScript compatibility

The package publishes typed subpath exports for:

  • @coframe/sdk
  • @coframe/sdk/server
  • @coframe/sdk/react

If your app uses an older TypeScript module resolution mode, prefer moving to one of:

  • moduleResolution: "bundler"
  • moduleResolution: "node16"
  • moduleResolution: "nodenext"

Modern resolution modes handle package subpath exports more reliably.

API overview

@coframe/sdk

  • init(options)
  • trackConversion(payload)
  • setConsent(enabled)
  • setUserToken(token)
  • setFeatureFlag(enabled)
  • setPageHydrated()
  • onTreatmentApplied(...)
  • onLog(...)

@coframe/sdk/feature-flags

  • featureFlags.init(options)
  • featureFlags.setUserToken(userToken) / featureFlags.reset()
  • featureFlags.getUserToken()
  • getFeatureFlag(key) / isFeatureEnabled(key)
  • getFeatureFlagPayload(key) / getRemoteConfig(key)
  • onFeatureFlags(callback) / reloadFeatureFlags()

@coframe/sdk/server

  • getProjectData(options)
  • getExperimentAssignment(options)
  • generateUserToken()
  • evaluateFlags(options)
  • getFeatureFlag(key, options) / isFeatureEnabled(key, options)
  • getFeatureFlagPayload(key, options) / getRemoteConfig(key, options)
  • flushFeatureFlagEvents()

Returns a payload that may include:

  • project
  • personalizationHeaders
  • antiflickerCSS

@coframe/sdk/react

  • CoframeProvider
  • useCoframeTreatments()
  • useCoframeTreatment(experimentId)
  • useCoframeVariant(experimentId)
  • useFeatureFlag(key) / useFeatureFlagVariantKey(key)
  • useFeatureFlagEnabled(key)
  • useFeatureFlagResult(key) / useFeatureFlagPayload(key)

版本与发布记录

@coframe/sdk 共发布 51 个版本,首次发布于 2026/06/12;过去 12 个月发布了 51 个版本。

版本发布日期距今
1.1.56latest2026/11/045 天前
1.1.552026/10/2714 天前
1.1.542026/10/2318 天前
1.1.522026/10/2120 天前
1.1.532026/10/2120 天前
1.1.512026/10/2120 天前

关于 @coframe/sdk 的常见问题

@coframe/sdk 是什么?

Coframe optimizes your website using generative AI to deliver maximum impact to your audience and allow your site to improve itself as you sleep. Learn more at coframe.com.

如何安装 @coframe/sdk?

在项目目录运行 npm install @coframe/sdk,或使用 pnpm add / yarn add / bun add。

@coframe/sdk 支持 TypeScript 吗?

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

@coframe/sdk 支持 ESM 还是 CommonJS?

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

@coframe/sdk 可以商用吗?

@coframe/sdk 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@coframe/sdk 的下载量有多少?

根据 npm 官方下载统计,@coframe/sdk 在 2026/10/10 – 2026/11/08 这 30 天内被下载 2,287 次,日均约 76 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@coframe/sdk 还在维护吗?

最新版本 1.1.56 发布于 2026/11/04(5 天前);至今共发布 51 个版本,其中过去 12 个月发布了 51 个;首次发布于 2026/06/12。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。