You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix: a failed poll tick is no longer an unhandled promise rejection. During an outage with no
last-known-good entry, every tick rejected with no handler: Node exits on the first one, and
browsers report each to error trackers. The loop now keeps the current config, warns once per
outage, and keeps polling.
Fix: one malformed evaluation no longer hangs init() or stops polling. A bad entry (a json
value sent as a string, or a null value) made the whole payload fail inside the loader, which then
never settled. Each key is now decoded on its own: the bad key returns undefined and getDetails reports reason: "ERROR", errorCode: "TYPE_MISMATCH" (the same path as a malformed
duration), and the other keys load. A load also always settles if installing a result throws.
Fix: the per-leg fetch timeout covers the response body. It was cleared when headers
arrived, so a server that sent headers and then stalled hung the load with no deadline.
Fix: a load replaced by updateContext() can no longer install the previous context's values
(qfg-goi1.2.7). When updateContext() raced an in-flight refresh or poll tick, a leg of the
older load that still completed was installed under the old context, so the client could serve
user A's flags while contexts said user B. The aborted older load also counted as a primary
failure and fired the secondary leg, and its hedge timer kept running. A replaced load now does
nothing: no install, no secondary leg, no last-known-good, and its promise settles with the load
that replaced it instead of rejecting with the abort.
Fix: updateContext(ctx, true) followed by poll() installs the new context's values
(qfg-goi1.2.48).skipLoad changed the context but not the one the loader fetched for, and poll() did not sync it, so the first poll fetch was for the previous context and was installed,
and written to the last-known-good cache, as the new one's. At the same generation the later,
correct fetches were dropped as "not newer", so user B saw user A's flags until the next publish,
and an offline reload for B served A's values as stale. poll() and updateContext(ctx, true)
now point the loader at the current context, and every leg of a load (and its last-known-good
fallback) is for the context the load started with.
Fix: close() or stopPolling() during the first poll() fetch no longer restarts polling.
A second poll() call also no longer leaves two loops running.
Fix: config keys that match Object.prototype names (for example constructor) are no longer
found when absent; getDetails reports FLAG_NOT_FOUND.
Fix: base64Encode (used for the context in the fetch URL) is UTF-8 in every runtime. Without TextEncoder (React Native) it produced Latin-1 for accented text and threw above U+00FF; without window (Web/Service Workers, Cloudflare Workers) it threw. Output on modern browsers and Node is
unchanged.
Fix: importing the SDK no longer requires crypto.getRandomValues. The telemetry instance id
is created on first use and falls back to a non-cryptographic random id.
Fix: the last-known-good cache keeps one entry per SDK key. It stored one localStorage entry
per distinct context and never removed any, which could fill the origin's quota (breaking the
app's own localStorage writes) and kept the raw context in the key. The entry is now quonfig.lkg.v2:<sdkKey> with a hash of the context, served only for the same context. Old quonfig.lkg.v1:<sdkKey>:* entries are removed on the first write.
Change: shouldLog({loggerPath}) no longer adds quonfig-sdk-logging to the client context.
It changed every later fetch's context, which let an older config generation replace a newer one
and changed the inputs to every flag. The browser evaluates server-side once per fetched context,
so the logger path never selected per-logger rules. Logger paths from the browser no longer appear
in example contexts.
Packaging: removed the module field (it named dist/index.mjs, which was never built;
bundlers fall back to main) and the lint script (eslint is not installed).