@vercel/flags-core 1.9.0 → 1.10.0-6128616-20261001141857
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +156 -0
- package/dist/{chunk-TDZBUVBG.js → chunk-IFD4U7MP.js} +974 -322
- package/dist/chunk-IFD4U7MP.js.map +1 -0
- package/dist/{chunk-PGKH4XQ7.cjs → chunk-KXMY3PY3.cjs} +1014 -362
- package/dist/chunk-KXMY3PY3.cjs.map +1 -0
- package/dist/{chunk-W742GGZU.cjs → chunk-NZKIYKBW.cjs} +1014 -362
- package/dist/chunk-NZKIYKBW.cjs.map +1 -0
- package/dist/{chunk-SLNLPFKU.js → chunk-RP5DGEIE.js} +974 -322
- package/dist/chunk-RP5DGEIE.js.map +1 -0
- package/dist/index.default.cjs +2 -2
- package/dist/index.default.d.cts +68 -33
- package/dist/index.default.d.ts +68 -33
- package/dist/index.default.js +1 -1
- package/dist/index.next-js.cjs +2 -2
- package/dist/index.next-js.d.cts +68 -33
- package/dist/index.next-js.d.ts +68 -33
- package/dist/index.next-js.js +1 -1
- package/dist/openfeature.default.cjs +2 -2
- package/dist/openfeature.default.d.cts +1 -1
- package/dist/openfeature.default.d.ts +1 -1
- package/dist/openfeature.default.js +1 -1
- package/dist/openfeature.next-js.cjs +2 -2
- package/dist/openfeature.next-js.d.cts +1 -1
- package/dist/openfeature.next-js.d.ts +1 -1
- package/dist/openfeature.next-js.js +1 -1
- package/dist/{types-1OK52NNi.d.cts → types-DpVlT3fI.d.cts} +7 -1
- package/dist/{types-1OK52NNi.d.ts → types-DpVlT3fI.d.ts} +7 -1
- package/package.json +1 -1
- package/dist/chunk-PGKH4XQ7.cjs.map +0 -1
- package/dist/chunk-SLNLPFKU.js.map +0 -1
- package/dist/chunk-TDZBUVBG.js.map +0 -1
- package/dist/chunk-W742GGZU.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @vercel/flags-core
|
|
2
2
|
|
|
3
|
+
## 1.10.0-6128616-20261001141857
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#511](https://github.com/vercel/flags/pull/511) [`88c323c`](https://github.com/vercel/flags/commit/88c323c9539661fef385f1381ff7e3e89776fdc7) Thanks [@luismeyer](https://github.com/luismeyer)! - Add a header-driven `vercel` client mode, enabled by default when `VERCEL=1`. Initialization loads provided/bundled definitions; request versions trigger refreshes and an empty cache fetches on its first read. Explicit offline/build behavior is preserved.
|
|
8
|
+
|
|
9
|
+
Evaluations and `getDatafile()` require a valid positive version header for their own project. Missing, empty, malformed, or unrelated project entries permanently start streaming when enabled, otherwise polling; multiple clients select their sources independently. Concurrent reads share startup and pending HTTP refreshes. Accepted stream updates and valid confirmations cancel superseded refreshes; waiting reads use the confirmed cache, and late responses cannot change cache or authorization state. `getDatafile()` shares lazy initialization and the evaluation resolution path, including header assessment, SWR, blocking refresh, stale-if-error, and source fallback. Initialization alone does not trigger this switch. A cold shared fetch discovers project identity before accepting header evidence; failed discovery starts source fallback.
|
|
10
|
+
|
|
11
|
+
The controller supplies a source freshness-status callback to the cache and configures one shared fetch callback. HeaderSource keeps version observations; the cache handles serving, background/blocking refreshes, shared work, cancellation, and stale-if-error. The cache owns age and resets it on accepted updates or confirmations, without rewriting `fetchedAt`. Streaming becomes stale after 60 seconds and expires after 90 seconds. Polling becomes stale after its interval plus the 10-second fetch deadline and expires after two intervals plus that deadline (40/70 seconds by default). Stale reads refresh in the background; expired reads block on the shared refresh. Stream pings reset age and clear failures. Polling initialization honors its configured timeout while preserving the pending poll and interval; cached fallback does not renew cache age or stale-if-error.
|
|
12
|
+
|
|
13
|
+
Ping timeouts reconnect the stream internally, allowing suspended runtimes to resume without starting polling. Reconnection alone does not renew cache freshness. Connection errors, server closure, and exhausted retries trigger an immediate background poll followed by interval polling. Reads and polling share pending HTTP refreshes; stream recovery stops polling.
|
|
14
|
+
|
|
15
|
+
Use `staleWhileRevalidate` (default 10) and `staleIfError` (default Infinity) in **seconds**, including fractions. Setting either to `0` disables its stale allowance. `staleWhileRevalidate` controls header-driven refreshes; stream/poll freshness follows their update schedules. Datafiles preserve optional `fetchedAt` epoch-millisecond timestamps across serialization and bundled/provided reuse.
|
|
16
|
+
|
|
17
|
+
- [#540](https://github.com/vercel/flags/pull/540) [`1a38dee`](https://github.com/vercel/flags/commit/1a38dee43b41030496bd63290816e06e5d5890ae) Thanks [@vincent-derks](https://github.com/vincent-derks)! - Remove `projectId` from connection strings.
|
|
18
|
+
|
|
19
|
+
- [#510](https://github.com/vercel/flags/pull/510) [`3ef9a2e`](https://github.com/vercel/flags/commit/3ef9a2e8c99905142b1d349fa2d68ffa06e04445) Thanks [@luismeyer](https://github.com/luismeyer)! - Add `staleIfError` in seconds to bound cached runtime reads after the first consecutive stream/poll failure or stream disconnect. The default `Infinity` preserves unlimited fallback; finite nonnegative durations (including fractional seconds) use existing evaluation defaults and errors after expiry, and `getDatafile()` follows the same allowance. Accepted updates, valid equal-version responses, or matching stream primed revisions reset the allowance. Storing fallback data does not confirm freshness or renew the failure clock. Build/offline behavior and source scheduling remain unchanged.
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- [#518](https://github.com/vercel/flags/pull/518) [`7c434bf`](https://github.com/vercel/flags/commit/7c434bf866cf58b860345b717021af382b481862) Thanks [@luismeyer](https://github.com/luismeyer)! - Add detailed client lifecycle, cache freshness, and network diagnostics using the existing `DEBUG=@vercel/flags-core` environment variable. A shared global logger emits events without passing logger instances through the client. Every client diagnostic includes the configured `clientName`, including cache, source, and background network activity. Logs omit credentials, definitions, and raw errors.
|
|
24
|
+
|
|
25
|
+
State changes include their cause; refresh logs distinguish response receipt, cache application, shared work, stream confirmation, and shutdown cancellation. Startup and source logs expose timeout and freshness thresholds, and recovery is logged only when a failure clears.
|
|
26
|
+
|
|
27
|
+
- [#511](https://github.com/vercel/flags/pull/511) [`1971366`](https://github.com/vercel/flags/commit/1971366423fa61bf5c99023a0a946d79d5bf057e) Thanks [@luismeyer](https://github.com/luismeyer)! - Record `fetchedAt` when a datafile fetch completes and preserve it in generated flag definitions. Loading the bundle retains the original timestamp so the Flags SDK can determine its age.
|
|
28
|
+
|
|
29
|
+
Expose optional `fetchedAt` metadata on datafiles. Record it for accepted live updates and preserve valid timestamps when loading provided or bundled definitions, without mutating the input.
|
|
30
|
+
|
|
3
31
|
## 1.9.0
|
|
4
32
|
|
|
5
33
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -33,6 +33,101 @@ export default app;
|
|
|
33
33
|
|
|
34
34
|
Outside Vercel, pass an SDK key explicitly: `createClient(process.env.FLAGS)`.
|
|
35
35
|
|
|
36
|
+
## Header-driven reads on Vercel
|
|
37
|
+
|
|
38
|
+
When `VERCEL=1`, the client defaults to `vercel: true`. Initialization loads provided
|
|
39
|
+
or bundled definitions without starting a stream or polling. Request version headers
|
|
40
|
+
indicate when cached definitions need refreshing. Header mode requires a valid positive
|
|
41
|
+
version for the client’s own `projectId`. Missing, empty, malformed, or unrelated entries
|
|
42
|
+
permanently switch that client to streaming when enabled, otherwise polling. Clients
|
|
43
|
+
also switch when cached definitions have no valid positive config version to compare.
|
|
44
|
+
Clients with different projects select their sources independently within the same request.
|
|
45
|
+
Concurrent reads share that startup and later headers do
|
|
46
|
+
not switch the client back. Pending HTTP refreshes remain shared until the stream
|
|
47
|
+
delivers current data or confirms the cached version. That confirmation cancels the
|
|
48
|
+
superseded refresh, and waiting reads use the confirmed cache; late responses cannot
|
|
49
|
+
change cache or authorization state. With an empty cache, the first read uses a shared
|
|
50
|
+
fetch to load definitions and discover the client’s project. The next read assesses
|
|
51
|
+
that project’s header entry and starts the stream/poll fallback if it is unavailable.
|
|
52
|
+
If the cold fetch fails, the read rejects without switching sources. Fetch failures
|
|
53
|
+
use cached data only while stale-if-error permits it; source assessment errors start fallback.
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const client = createClient(process.env.FLAGS!, {
|
|
57
|
+
vercel: true,
|
|
58
|
+
staleWhileRevalidate: 10, // Seconds of background-refresh grace.
|
|
59
|
+
staleIfError: 60, // Seconds of cached fallback after a refresh failure.
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`staleWhileRevalidate` defaults to 10 seconds and accepts finite, nonnegative values,
|
|
64
|
+
including fractions. `0` makes header-driven refreshes block. The window starts at the latest
|
|
65
|
+
accepted fetch or valid confirmation, including an equal-version fetch response.
|
|
66
|
+
The cache tracks this age independently of `fetchedAt`. Bundled/provided definitions
|
|
67
|
+
preserve their original `fetchedAt`; unknown or expired cache age requires a blocking
|
|
68
|
+
refresh when a newer request version arrives. Refresh failures use `staleIfError`.
|
|
69
|
+
A newer-header read attempts blocking recovery after that failure allowance expires.
|
|
70
|
+
|
|
71
|
+
`getDatafile()` uses the same lazy initialization and resolution path as evaluations,
|
|
72
|
+
including header checks, background revalidation, blocking refresh, stale-if-error, and
|
|
73
|
+
source fallback. Concurrent calls share HTTP refreshes across both APIs.
|
|
74
|
+
Use `vercel: false` to select the stream/poll behavior. Disabling both
|
|
75
|
+
stream and polling still selects offline mode, and builds retain their existing loading.
|
|
76
|
+
|
|
77
|
+
## Cached reads after errors
|
|
78
|
+
|
|
79
|
+
`staleIfError` controls how many seconds evaluations and `getDatafile()` may use
|
|
80
|
+
cached flag definitions after a stream/poll/header-refresh failure or stream disconnect:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
const client = createClient(process.env.FLAGS!, {
|
|
84
|
+
staleIfError: 60,
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The default is `Infinity`, preserving unlimited cached fallback. Use a finite
|
|
89
|
+
nonnegative number of seconds to bound fallback. Fractional seconds are supported
|
|
90
|
+
(for example, `0.5` allows 500 milliseconds). A positive window includes its exact
|
|
91
|
+
deadline; `0` disables cached fallback immediately after failure.
|
|
92
|
+
Negative values, `NaN`, and negative infinity throw when creating the client.
|
|
93
|
+
|
|
94
|
+
The allowance starts at the first consecutive failure. Repeated errors,
|
|
95
|
+
disconnects, and provided or bundled fallback data do not renew it. An accepted
|
|
96
|
+
source update, or a finite equal version for the same project and environment,
|
|
97
|
+
clears the outage. A stream `primed` message also clears it when its finite numeric
|
|
98
|
+
revision and identity match the cached entry. Pings clear failures too: the server
|
|
99
|
+
sends `primed` or a datafile before pings on each connection. Opening a connection
|
|
100
|
+
alone does not clear a failure. A later failure starts a new allowance.
|
|
101
|
+
Responses are observed in completion order, with existing version acceptance.
|
|
102
|
+
|
|
103
|
+
After expiry, `evaluate()` returns the caller's default with reason `error`, or
|
|
104
|
+
throws the first failure when no default is supplied. `bulkEvaluate()` returns
|
|
105
|
+
an error result for each requested flag, with its default value when provided.
|
|
106
|
+
`getDatafile()` follows the same allowance and throws after expiry. The entry is
|
|
107
|
+
retained for recovery, including its revision for stream reconnection. A clean
|
|
108
|
+
stream close records `stream: disconnected` if no earlier failure exists. Ping timeouts
|
|
109
|
+
reconnect quietly without recording a failure or starting polling, including after runtime
|
|
110
|
+
suspension. Genuine disconnections start an immediate poll, sharing pending read
|
|
111
|
+
refreshes, then continue at the configured interval. Every transition to polling
|
|
112
|
+
waits for the first poll or `polling.initTimeoutMs`, including reads with cached data.
|
|
113
|
+
On timeout, reads follow `staleIfError` while polling continues. A zero initialization
|
|
114
|
+
timeout waits for the poll, which still has a ten-second fetch deadline. Stream
|
|
115
|
+
recovery stops polling. `getFallbackDatafile()` remains an independent bundled-data export.
|
|
116
|
+
|
|
117
|
+
Streaming data becomes stale after 60 seconds and expires after 90 seconds, allowing
|
|
118
|
+
one missed 30-second ping before revalidation and matching the stream's ping
|
|
119
|
+
timeout. Polling data becomes stale after its interval plus the 10-second fetch
|
|
120
|
+
deadline, and expires after two intervals plus that deadline (40 and 70 seconds with
|
|
121
|
+
the default 30-second interval). These windows are independent of `staleWhileRevalidate`.
|
|
122
|
+
Stale evaluations and `getDatafile()` calls refresh in the background; expired reads
|
|
123
|
+
wait for the shared refresh. Refresh failures still follow `staleIfError`.
|
|
124
|
+
|
|
125
|
+
Accepted updates and valid confirmations reset cache age without rewriting `fetchedAt`.
|
|
126
|
+
Stream pings also reset age and clear any failure. Poll errors feed the shared failure
|
|
127
|
+
handler without logging each failed poll. An initialization timeout alone does not
|
|
128
|
+
start the failure allowance or reset cache age. It permits cached fallback while
|
|
129
|
+
the pending update continues; polling intervals remain active after startup timeout.
|
|
130
|
+
|
|
36
131
|
## Evaluation Metrics
|
|
37
132
|
|
|
38
133
|
To associate evaluation metrics with an environment, pass the
|
|
@@ -64,3 +159,64 @@ const client = OpenFeature.getClient();
|
|
|
64
159
|
- [Core Library Docs](https://vercel.com/docs/flags/vercel-flags/sdks/core)
|
|
65
160
|
- [OpenFeature Provider Docs](https://vercel.com/docs/flags/vercel-flags/sdks/openfeature)
|
|
66
161
|
- [Vercel Flags](https://vercel.com/docs/flags/vercel-flags)
|
|
162
|
+
|
|
163
|
+
### Client debug logging
|
|
164
|
+
|
|
165
|
+
Set `DEBUG=@vercel/flags-core` to enable detailed diagnostics. This reuses the
|
|
166
|
+
existing ingest debug switch:
|
|
167
|
+
|
|
168
|
+
```sh
|
|
169
|
+
DEBUG=@vercel/flags-core pnpm dev
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Client diagnostics use `console.debug` with the `@vercel/flags-core` prefix and
|
|
173
|
+
an object containing an `event`, the configured `clientName`, and its diagnostic
|
|
174
|
+
details. Set `clientName` when creating each client to distinguish their logs:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
const client = createClient(undefined, { clientName: 'checkout' });
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The controller, cache, and network sources all use the same global logging
|
|
181
|
+
function, including the client name on background work and reconnects. For example:
|
|
182
|
+
|
|
183
|
+
```text
|
|
184
|
+
@vercel/flags-core { event: 'client.state', clientName: 'checkout', from: 'idle', to: 'vercel', reason: 'header-mode-enabled', ... }
|
|
185
|
+
@vercel/flags-core { event: 'cache.freshness', clientName: 'checkout', status: 'expired', revision: 42, ageMs: 15000, ... }
|
|
186
|
+
@vercel/flags-core { event: 'cache.refresh.blocking', clientName: 'checkout', reason: 'expired', ... }
|
|
187
|
+
@vercel/flags-core { event: 'datafile.fetch.attempt', clientName: 'checkout', attempt: 1, maxAttempts: 3 }
|
|
188
|
+
@vercel/flags-core { event: 'datafile.fetch.response', clientName: 'checkout', attempt: 1, status: 200 }
|
|
189
|
+
@vercel/flags-core { event: 'cache.update.accepted', clientName: 'checkout', revision: 43, ... }
|
|
190
|
+
@vercel/flags-core { event: 'cache.fetch.applied', clientName: 'checkout', revision: 43, ... }
|
|
191
|
+
@vercel/flags-core { event: 'client.read', clientName: 'checkout', state: 'vercel', cacheStatus: 'MISS', ... }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Follow `client.state` for the previous state, next state, and transition reason.
|
|
195
|
+
`state` describes the internal lifecycle; `mode` is the public operating mode.
|
|
196
|
+
Read and cache events include the project, revision, and cache age when available.
|
|
197
|
+
|
|
198
|
+
| Event | What it explains |
|
|
199
|
+
| --- | --- |
|
|
200
|
+
| `cache.freshness` | The source's age/version assessment and whether failure policy permits serving the entry. |
|
|
201
|
+
| `cache.source-error` / `header.fallback` | The source cannot assess this project or its cached version; the controller starts fallback without changing the fetch-failure deadline. |
|
|
202
|
+
| `cache.refresh.background` / `.blocking` | Why a read returns stale data immediately or waits for refresh. |
|
|
203
|
+
| `cache.fetch.shared` | A caller joins an existing HTTP refresh instead of starting another. |
|
|
204
|
+
| `datafile.fetch.complete` / `cache.fetch.applied` | The response has been parsed / processed by the cache version guard. Only the latter finishes cache refresh work; an older response may be ignored. |
|
|
205
|
+
| `cache.fetch.cancel` / `cache.refresh.superseded` | A stream update or confirmation supersedes HTTP work / releases a waiting read using the confirmed cache. |
|
|
206
|
+
| `cache.fetch.aborted` | `source-confirmed` distinguishes superseded work from `cache-cleared` during shutdown. |
|
|
207
|
+
| `cache.stale-if-error.expired` / `cache.recovered` | The failure allowance prevents serving data / an actual failure has cleared. Ordinary confirmations do not log recovery. |
|
|
208
|
+
| `stream.initialize.timeout` / `poll.initialize.timeout` | Startup reached its deadline; `hasData` and `startupFallback` show whether cached fallback is available. |
|
|
209
|
+
|
|
210
|
+
Events cover initialization and shutdown, selected modes and state transitions,
|
|
211
|
+
evaluation/getDatafile results, cache versions and age, version acceptance/confirmation,
|
|
212
|
+
header timestamps, background/blocking/shared refreshes, stale-if-error expiry,
|
|
213
|
+
HTTP attempts and retry delays, polling, stream pings, timeouts, and reconnect reasons.
|
|
214
|
+
Stream/poll startup events include their stale and expired age thresholds.
|
|
215
|
+
Cache ages and delays are in milliseconds; `Infinity` denotes unknown age or an
|
|
216
|
+
unlimited stale-if-error window. The new client diagnostics omit credentials,
|
|
217
|
+
raw headers, flag definitions, evaluation entities, and raw error messages.
|
|
218
|
+
|
|
219
|
+
Logging is off by default. The logger checks `DEBUG` on each call; removing the
|
|
220
|
+
namespace disables client diagnostics, including for existing clients. Diagnostics
|
|
221
|
+
are verbose, including an event for each read, and event names/fields are internal
|
|
222
|
+
rather than a stable API. Configure your log collector to include `console.debug` output.
|