Skip to content

feat(flags-core): read client config (disableMetrics) from datafile#425

Draft
luismeyer wants to merge 1 commit into
mainfrom
client-config-from-datafile
Draft

feat(flags-core): read client config (disableMetrics) from datafile#425
luismeyer wants to merge 1 commit into
mainfrom
client-config-from-datafile

Conversation

@luismeyer

Copy link
Copy Markdown
Member

Summary

Teaches @vercel/flags-core to read client configuration delivered by the flags server via the datafile payload, and uses it to control client behavior. The first config option is disableMetrics: when the server sends it, the client never ingests any usage metrics.

This is the client half of a two-part feature; the server half (vercel/proxy) sends the field. Agreed protocol:

{ "config": { "disableMetrics": true } }
  • config is an optional top-level datafile field; absent means all defaults (metrics enabled)
  • config.disableMetrics is an optional boolean, default false
  • The ClientConfig shape is extensible for future options

Design

ParsingClientConfig is typed on DatafileInput (src/types.ts). Since all sources (stream, polling, bundled definitions, provided datafile, one-time fetch) emit raw DatafileInput parsed from the same JSON payload, the config flows through every source with no per-source changes. ClientConfig is exported from the public entry point.

Gating — the Controller owns the latest tagged data, so it exposes a private metricsDisabledByConfig getter reading this.data?.config?.disableMetrics. Because it always reads the latest known data, a config flip arriving at runtime (e.g. a stream update) takes effect for subsequent tracking immediately.

Metrics are suppressed at two layers:

  1. Controller.trackRead / Controller.trackEvaluation return early while disabled — no events accumulate
  2. UsageTracker takes an isDisabled predicate consulted before recording and before flushing: while disabled, pending events are dropped instead of sent, so flushes never hit the network (covers events queued before a runtime flip)

Controller.shutdown() now flushes the tracker before resetting this.data, so the final flush still sees the latest config.

The existing local disableMetrics constructor option (client-side, evaluation metrics only) is unchanged; the server-driven config gates all metrics.

Tests

New black-box tests (through createClient, fetchMock for network assertions):

  • provided datafile with config.disableMetrics: true → zero ingest requests (evaluation still works)
  • datafile with config present but no disableMetrics → ingest body byte-identical to current behavior
  • stream update flips disableMetrics on → subsequent tracking stops, pending events dropped, no ingest request ever
  • stream update flips disableMetrics off → tracking resumes; ingest contains only post-flip events
  • bundled definitions with config.disableMetrics: true (build step) → zero ingest requests

pnpm test (472 passed), pnpm type-check, pnpm check (biome), and pnpm build all pass in packages/vercel-flags-core.

🤖 Generated with Claude Code

The flags server can now deliver an optional top-level `config` field in
the datafile payload:

    { "config": { "disableMetrics": true } }

Since all sources (stream, poll, bundled, provided datafile, one-time
fetch) emit raw DatafileInput, the config flows through every source
automatically. The Controller reads it from the latest known data, so
config updates arriving at runtime take effect for subsequent tracking.

When the latest datafile sets config.disableMetrics: true, no usage
metrics are ingested: trackRead/trackEvaluation are gated in the
Controller, and the UsageTracker additionally consults an isDisabled
predicate before recording and before flushing (pending events are
dropped instead of sent, so flushes never hit the network).

Absent config means all defaults — behavior unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
flags-playground Ready Ready Preview, Comment, Open in v0 Jul 8, 2026 12:19pm
flags-sdk-dev Ready Ready Preview, Comment, Open in v0 Jul 8, 2026 12:19pm
flags-sdk-snippets Ready Ready Preview, Comment, Open in v0 Jul 8, 2026 12:19pm
flags-sdk-sveltekit-snippets Ready Ready Preview, Comment, Open in v0 Jul 8, 2026 12:19pm
shirt-shop Ready Ready Preview, Comment, Open in v0 Jul 8, 2026 12:19pm
shirt-shop-api Ready Ready Preview, Comment, Open in v0 Jul 8, 2026 12:19pm

* runtime (e.g. a stream update) takes effect for subsequent tracking.
*/
private get metricsDisabledByConfig(): boolean {
return this.data?.config?.disableMetrics === true;

@dferber90 dferber90 Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small fly-by nitpick: It's usually more future-proof to use an enum, just in case there are other modes in the future.

metrics: false can more easily turn into metrics: "config-reads-only" or any other modes we might want

so I'd keep "disable" out of the config name

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants