@vercel/flags-core 1.4.0 → 1.5.0-2225248-20260525123053
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 +39 -0
- package/dist/{chunk-WKSYRFUY.cjs → chunk-5TVRNXPQ.cjs} +213 -60
- package/dist/chunk-5TVRNXPQ.cjs.map +1 -0
- package/dist/{chunk-2F6JQ5SL.js → chunk-HSEYV5NA.js} +175 -18
- package/dist/chunk-HSEYV5NA.js.map +1 -0
- package/dist/{chunk-3Z4P7Q6O.cjs → chunk-JGALOZHN.cjs} +218 -61
- package/dist/chunk-JGALOZHN.cjs.map +1 -0
- package/dist/{chunk-OABKRAPP.js → chunk-W3LGQYQT.js} +171 -18
- package/dist/chunk-W3LGQYQT.js.map +1 -0
- package/dist/index.default.cjs +2 -2
- package/dist/index.default.d.cts +4 -2
- package/dist/index.default.d.ts +4 -2
- package/dist/index.default.js +1 -1
- package/dist/index.next-js.cjs +2 -2
- package/dist/index.next-js.d.cts +4 -2
- package/dist/index.next-js.d.ts +4 -2
- 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-FKiSedQ9.d.cts → types-C70GeY3G.d.cts} +20 -0
- package/dist/{types-FKiSedQ9.d.ts → types-C70GeY3G.d.ts} +20 -0
- package/package.json +14 -4
- package/dist/chunk-2F6JQ5SL.js.map +0 -1
- package/dist/chunk-3Z4P7Q6O.cjs.map +0 -1
- package/dist/chunk-OABKRAPP.js.map +0 -1
- package/dist/chunk-WKSYRFUY.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @vercel/flags-core
|
|
2
2
|
|
|
3
|
+
## 1.5.0-2225248-20260525123053
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#385](https://github.com/vercel/flags/pull/385) [`5b2dc37`](https://github.com/vercel/flags/commit/5b2dc37041e24811f735beede41fc8c29f508cb7) Thanks [@dferber90](https://github.com/dferber90)! - Add `bulkEvaluate` method to `FlagsClient` for resolving multiple flags against shared entities in a single call.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const results = await client.bulkEvaluate(
|
|
11
|
+
[
|
|
12
|
+
{ key: "a", defaultValue: false },
|
|
13
|
+
{ key: "b", defaultValue: "off" },
|
|
14
|
+
],
|
|
15
|
+
entities
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
results.a; // EvaluationResult<boolean>
|
|
19
|
+
results.b; // EvaluationResult<string>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Avoids the per-flag overhead of separate `evaluate()` calls — the datafile is read once, entities are resolved once, and all flags share the same environment/segments lookup. Each entry in the returned record is a full `EvaluationResult` with `value`, `reason`, `outcomeType`, and `metrics`.
|
|
23
|
+
|
|
24
|
+
- [#371](https://github.com/vercel/flags/pull/371) [`bd4d01a`](https://github.com/vercel/flags/commit/bd4d01a9b2b5d70bf7ae62cda645d8cd7292ad83) Thanks [@vincent-derks](https://github.com/vincent-derks)! - Add jitter to ingest retries and the batch-flush window.
|
|
25
|
+
|
|
26
|
+
The usage tracker now uses AWS-style "Full Jitter" exponential backoff between
|
|
27
|
+
retry attempts (replacing the previous deterministic 100/200ms schedule) and
|
|
28
|
+
randomizes the 5s batch-flush window by ±20% to desynchronize concurrent
|
|
29
|
+
processes. When all retry attempts are exhausted the SDK now logs a structured
|
|
30
|
+
warning so consumers can alert on dropped batches.
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- [#382](https://github.com/vercel/flags/pull/382) [`4d90e91`](https://github.com/vercel/flags/commit/4d90e912a4d7c9d4ef986d5e8dc609c30b203242) Thanks [@dferber90](https://github.com/dferber90)! - Speed up flag evaluation on the hot path.
|
|
35
|
+
|
|
36
|
+
- `handleOutcome` no longer recomputes `scaledWeights` on every split-outcome evaluation; the per-outcome scaled weights are cached on first call.
|
|
37
|
+
- `matchConditions` no longer recompiles `RegExp` on every REGEX / NOT_REGEX condition; the compiled regex is cached on first call.
|
|
38
|
+
- `Controller.read()` and `getDatafile()` no longer re-destructure and re-spread the in-memory datafile on every call; the result is cached and rebuilt only when stream/poll replaces the underlying data.
|
|
39
|
+
|
|
40
|
+
In micro-benchmarks the pure `evaluate()` path is ~22% faster for split outcomes and ~32% faster for regex conditions. The full `client.evaluate()` path is 14–22% faster across all scenarios.
|
|
41
|
+
|
|
3
42
|
## 1.4.0
|
|
4
43
|
|
|
5
44
|
### Minor Changes
|
|
@@ -7,6 +7,7 @@ var __export = (target, all) => {
|
|
|
7
7
|
// src/controller-fns.ts
|
|
8
8
|
var controller_fns_exports = {};
|
|
9
9
|
__export(controller_fns_exports, {
|
|
10
|
+
bulkEvaluate: () => bulkEvaluate2,
|
|
10
11
|
controllerInstanceMap: () => controllerInstanceMap,
|
|
11
12
|
evaluate: () => evaluate2,
|
|
12
13
|
getDatafile: () => getDatafile,
|
|
@@ -46,6 +47,24 @@ var Packed;
|
|
|
46
47
|
|
|
47
48
|
// src/evaluate.ts
|
|
48
49
|
var MAX_REGEX_INPUT_LENGTH = 1e4;
|
|
50
|
+
var UINT32_MAX = 4294967295;
|
|
51
|
+
var SCALED_WEIGHTS = /* @__PURE__ */ Symbol("@vercel/flags-core:scaledWeights");
|
|
52
|
+
var COMPILED_REGEX = /* @__PURE__ */ Symbol("@vercel/flags-core:compiledRegex");
|
|
53
|
+
function getScaledWeights(outcome) {
|
|
54
|
+
const cached = outcome[SCALED_WEIGHTS];
|
|
55
|
+
if (cached) return cached;
|
|
56
|
+
const total = sum(outcome.weights);
|
|
57
|
+
const scaled = outcome.weights.map((w) => w / total * UINT32_MAX);
|
|
58
|
+
outcome[SCALED_WEIGHTS] = scaled;
|
|
59
|
+
return scaled;
|
|
60
|
+
}
|
|
61
|
+
function getCompiledRegex(rhs) {
|
|
62
|
+
const cached = rhs[COMPILED_REGEX];
|
|
63
|
+
if (cached) return cached;
|
|
64
|
+
const compiled = new RegExp(rhs.pattern, rhs.flags);
|
|
65
|
+
rhs[COMPILED_REGEX] = compiled;
|
|
66
|
+
return compiled;
|
|
67
|
+
}
|
|
49
68
|
function exhaustivenessCheck(_) {
|
|
50
69
|
throw new Error("Exhaustiveness check failed");
|
|
51
70
|
}
|
|
@@ -232,12 +251,12 @@ function matchConditions(conditions, params) {
|
|
|
232
251
|
return (isNumber(rhs) || isString(rhs)) && lhs <= rhs;
|
|
233
252
|
case "regex" /* REGEX */:
|
|
234
253
|
if (isString(lhs) && lhs.length <= MAX_REGEX_INPUT_LENGTH && typeof rhs === "object" && !Array.isArray(rhs) && _optionalChain([rhs, 'optionalAccess', _12 => _12.type]) === "regex") {
|
|
235
|
-
return
|
|
254
|
+
return getCompiledRegex(rhs).test(lhs);
|
|
236
255
|
}
|
|
237
256
|
return false;
|
|
238
257
|
case "!regex" /* NOT_REGEX */:
|
|
239
258
|
if (isString(lhs) && lhs.length <= MAX_REGEX_INPUT_LENGTH && typeof rhs === "object" && !Array.isArray(rhs) && _optionalChain([rhs, 'optionalAccess', _13 => _13.type]) === "regex") {
|
|
240
|
-
return !
|
|
259
|
+
return !getCompiledRegex(rhs).test(lhs);
|
|
241
260
|
}
|
|
242
261
|
return false;
|
|
243
262
|
case "before" /* BEFORE */: {
|
|
@@ -309,13 +328,9 @@ function handleOutcome(params, outcome) {
|
|
|
309
328
|
if (typeof lhs !== "string") {
|
|
310
329
|
return { value: defaultOutcome, outcomeType: "split" /* SPLIT */ };
|
|
311
330
|
}
|
|
312
|
-
const maxValue = 4294967295;
|
|
313
331
|
const value = _jsxxhash.xxHash32.call(void 0, lhs, params.definition.seed);
|
|
314
|
-
const
|
|
315
|
-
const
|
|
316
|
-
(weight) => weight / sumOfWeights * maxValue
|
|
317
|
-
);
|
|
318
|
-
const variantIndex = findWeightedIndex(scaledWeights, value, maxValue);
|
|
332
|
+
const scaledWeights = getScaledWeights(outcome);
|
|
333
|
+
const variantIndex = findWeightedIndex(scaledWeights, value, UINT32_MAX);
|
|
319
334
|
return {
|
|
320
335
|
value: variantIndex === -1 ? defaultOutcome : getVariant(params.definition.variants, variantIndex),
|
|
321
336
|
outcomeType: "split" /* SPLIT */
|
|
@@ -371,9 +386,8 @@ function handleOutcome(params, outcome) {
|
|
|
371
386
|
outcomeType: "rollout" /* ROLLOUT */
|
|
372
387
|
};
|
|
373
388
|
}
|
|
374
|
-
const maxValue = 4294967295;
|
|
375
389
|
const value = _jsxxhash.xxHash32.call(void 0, lhs, params.definition.seed);
|
|
376
|
-
const threshold = currentPromille / 1e5 *
|
|
390
|
+
const threshold = currentPromille / 1e5 * UINT32_MAX;
|
|
377
391
|
return {
|
|
378
392
|
value: value < threshold ? getVariant(params.definition.variants, outcome.rollToVariant) : getVariant(
|
|
379
393
|
params.definition.variants,
|
|
@@ -440,6 +454,23 @@ function evaluate(params, _visited) {
|
|
|
440
454
|
reason: "fallthrough" /* FALLTHROUGH */
|
|
441
455
|
});
|
|
442
456
|
}
|
|
457
|
+
function bulkEvaluate(flags, shared) {
|
|
458
|
+
const params = {
|
|
459
|
+
entities: shared.entities,
|
|
460
|
+
environment: shared.environment,
|
|
461
|
+
segments: shared.segments,
|
|
462
|
+
definition: void 0,
|
|
463
|
+
defaultValue: void 0
|
|
464
|
+
};
|
|
465
|
+
const results = {};
|
|
466
|
+
for (const key in flags) {
|
|
467
|
+
const flag = flags[key];
|
|
468
|
+
params.definition = flag.definition;
|
|
469
|
+
params.defaultValue = flag.defaultValue;
|
|
470
|
+
results[key] = evaluate(params);
|
|
471
|
+
}
|
|
472
|
+
return results;
|
|
473
|
+
}
|
|
443
474
|
function findWeightedIndex(weights, value, maxValue) {
|
|
444
475
|
if (value < 0 || value >= maxValue) return -1;
|
|
445
476
|
let sum2 = 0;
|
|
@@ -451,7 +482,7 @@ function findWeightedIndex(weights, value, maxValue) {
|
|
|
451
482
|
}
|
|
452
483
|
|
|
453
484
|
// package.json
|
|
454
|
-
var version = "1.
|
|
485
|
+
var version = "1.5.0-2225248-20260525123053";
|
|
455
486
|
|
|
456
487
|
// src/lib/report-value.ts
|
|
457
488
|
function internalReportValue(key, value, data) {
|
|
@@ -557,6 +588,78 @@ async function evaluate2(id, flagKey, defaultValue, entities) {
|
|
|
557
588
|
}
|
|
558
589
|
});
|
|
559
590
|
}
|
|
591
|
+
async function bulkEvaluate2(id, flags, entities) {
|
|
592
|
+
const controller = getInstance(id).controller;
|
|
593
|
+
let datafile;
|
|
594
|
+
try {
|
|
595
|
+
datafile = await controller.read();
|
|
596
|
+
} catch (error) {
|
|
597
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to read datafile";
|
|
598
|
+
const results2 = {};
|
|
599
|
+
for (const flag of flags) {
|
|
600
|
+
results2[flag.key] = {
|
|
601
|
+
value: flag.defaultValue,
|
|
602
|
+
reason: "error" /* ERROR */,
|
|
603
|
+
errorMessage
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
return results2;
|
|
607
|
+
}
|
|
608
|
+
const baseMetrics = {
|
|
609
|
+
readMs: datafile.metrics.readMs,
|
|
610
|
+
source: datafile.metrics.source,
|
|
611
|
+
cacheStatus: datafile.metrics.cacheStatus,
|
|
612
|
+
connectionState: datafile.metrics.connectionState,
|
|
613
|
+
mode: datafile.metrics.mode
|
|
614
|
+
};
|
|
615
|
+
const projectId = datafile.projectId;
|
|
616
|
+
const results = {};
|
|
617
|
+
const toEvaluate = {};
|
|
618
|
+
for (const flag of flags) {
|
|
619
|
+
const { key, defaultValue } = flag;
|
|
620
|
+
const flagDefinition = datafile.definitions[key];
|
|
621
|
+
if (flagDefinition === void 0) {
|
|
622
|
+
if (projectId) {
|
|
623
|
+
internalReportValue(key, defaultValue, {
|
|
624
|
+
originProjectId: projectId,
|
|
625
|
+
originProvider: "vercel",
|
|
626
|
+
reason: "error" /* ERROR */
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
results[key] = {
|
|
630
|
+
value: defaultValue,
|
|
631
|
+
reason: "error" /* ERROR */,
|
|
632
|
+
errorCode: "FLAG_NOT_FOUND" /* FLAG_NOT_FOUND */,
|
|
633
|
+
errorMessage: `@vercel/flags-core: Definition not found for flag "${key}"`,
|
|
634
|
+
metrics: { evaluationMs: 0, ...baseMetrics }
|
|
635
|
+
};
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
toEvaluate[key] = { definition: flagDefinition, defaultValue };
|
|
639
|
+
}
|
|
640
|
+
const evalStartTime = Date.now();
|
|
641
|
+
const evaluated = bulkEvaluate(toEvaluate, {
|
|
642
|
+
entities: _nullishCoalesce(entities, () => ( {})),
|
|
643
|
+
environment: datafile.environment,
|
|
644
|
+
segments: datafile.segments
|
|
645
|
+
});
|
|
646
|
+
const evaluationDurationMs = Date.now() - evalStartTime;
|
|
647
|
+
for (const key in toEvaluate) {
|
|
648
|
+
const result = evaluated[key];
|
|
649
|
+
if (projectId) {
|
|
650
|
+
internalReportValue(key, result.value, {
|
|
651
|
+
originProjectId: projectId,
|
|
652
|
+
originProvider: "vercel",
|
|
653
|
+
reason: result.reason,
|
|
654
|
+
outcomeType: result.reason !== "error" /* ERROR */ ? result.outcomeType : void 0
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
results[key] = Object.assign(result, {
|
|
658
|
+
metrics: { evaluationMs: evaluationDurationMs, ...baseMetrics }
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
return results;
|
|
662
|
+
}
|
|
560
663
|
|
|
561
664
|
// src/create-raw-client.ts
|
|
562
665
|
var idCount = 0;
|
|
@@ -623,6 +726,16 @@ function createCreateRawClient(fns) {
|
|
|
623
726
|
}
|
|
624
727
|
}
|
|
625
728
|
return fns.evaluate(id, flagKey, defaultValue, entities);
|
|
729
|
+
},
|
|
730
|
+
bulkEvaluate: async (flags, entities) => {
|
|
731
|
+
const instance = controllerInstanceMap.get(id);
|
|
732
|
+
if (!_optionalChain([instance, 'optionalAccess', _22 => _22.initialized])) {
|
|
733
|
+
try {
|
|
734
|
+
await api.initialize();
|
|
735
|
+
} catch (e3) {
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return fns.bulkEvaluate(id, flags, entities);
|
|
626
739
|
}
|
|
627
740
|
};
|
|
628
741
|
return api;
|
|
@@ -677,8 +790,25 @@ async function readBundledDefinitions(sdkKey) {
|
|
|
677
790
|
|
|
678
791
|
// src/utils/usage-tracker.ts
|
|
679
792
|
var _functions = require('@vercel/functions');
|
|
793
|
+
|
|
794
|
+
// src/utils/backoff.ts
|
|
795
|
+
var DEFAULT_BASE_MS = 250;
|
|
796
|
+
var DEFAULT_CAP_MS = 5e3;
|
|
797
|
+
function getRetryDelayMs(attempt, options = {}) {
|
|
798
|
+
const baseMs = _nullishCoalesce(options.baseMs, () => ( DEFAULT_BASE_MS));
|
|
799
|
+
const capMs = _nullishCoalesce(options.capMs, () => ( DEFAULT_CAP_MS));
|
|
800
|
+
const ceiling = Math.min(capMs, baseMs * 2 ** Math.max(0, attempt - 1));
|
|
801
|
+
return Math.floor(Math.random() * ceiling);
|
|
802
|
+
}
|
|
803
|
+
function getJitteredWaitMs(baseMs, ratio) {
|
|
804
|
+
const min = baseMs * (1 - ratio);
|
|
805
|
+
const span = baseMs * 2 * ratio;
|
|
806
|
+
return Math.floor(min + Math.random() * span);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// src/utils/usage-tracker.ts
|
|
680
810
|
var RESOLVED_VOID = Promise.resolve();
|
|
681
|
-
var isDebugMode = _optionalChain([process, 'access',
|
|
811
|
+
var isDebugMode = _optionalChain([process, 'access', _23 => _23.env, 'access', _24 => _24.DEBUG, 'optionalAccess', _25 => _25.includes, 'call', _26 => _26("@vercel/flags-core")]);
|
|
682
812
|
var debugLog = (...args) => {
|
|
683
813
|
if (!isDebugMode) return;
|
|
684
814
|
console.log(...args);
|
|
@@ -686,11 +816,12 @@ var debugLog = (...args) => {
|
|
|
686
816
|
var MAX_RETRIES = 3;
|
|
687
817
|
var MAX_BATCH_SIZE = 50;
|
|
688
818
|
var MAX_BATCH_WAIT_MS = 5e3;
|
|
819
|
+
var BATCH_WAIT_JITTER_RATIO = 0.2;
|
|
689
820
|
var SYMBOL_FOR_REQ_CONTEXT = /* @__PURE__ */ Symbol.for("@vercel/request-context");
|
|
690
821
|
var fromSymbol = globalThis;
|
|
691
822
|
function getRequestContext() {
|
|
692
823
|
try {
|
|
693
|
-
const ctx = _optionalChain([fromSymbol, 'access',
|
|
824
|
+
const ctx = _optionalChain([fromSymbol, 'access', _27 => _27[SYMBOL_FOR_REQ_CONTEXT], 'optionalAccess', _28 => _28.get, 'optionalCall', _29 => _29()]);
|
|
694
825
|
if (ctx && Object.hasOwn(ctx, "headers")) {
|
|
695
826
|
return {
|
|
696
827
|
ctx,
|
|
@@ -698,7 +829,7 @@ function getRequestContext() {
|
|
|
698
829
|
};
|
|
699
830
|
}
|
|
700
831
|
return { ctx, headers: void 0 };
|
|
701
|
-
} catch (
|
|
832
|
+
} catch (e4) {
|
|
702
833
|
return { ctx: void 0, headers: void 0 };
|
|
703
834
|
}
|
|
704
835
|
}
|
|
@@ -720,7 +851,7 @@ var UsageTracker = (_class = class {
|
|
|
720
851
|
*/
|
|
721
852
|
flush() {
|
|
722
853
|
if (this.batcher.pending) {
|
|
723
|
-
_optionalChain([this, 'access',
|
|
854
|
+
_optionalChain([this, 'access', _30 => _30.batcher, 'access', _31 => _31.resolveWait, 'optionalCall', _32 => _32()]);
|
|
724
855
|
return this.batcher.pending;
|
|
725
856
|
}
|
|
726
857
|
if (this.batcher.events.length > 0) {
|
|
@@ -792,7 +923,10 @@ var UsageTracker = (_class = class {
|
|
|
792
923
|
const pending = (async () => {
|
|
793
924
|
await new Promise((res) => {
|
|
794
925
|
this.batcher.resolveWait = res;
|
|
795
|
-
timeout = setTimeout(
|
|
926
|
+
timeout = setTimeout(
|
|
927
|
+
res,
|
|
928
|
+
getJitteredWaitMs(MAX_BATCH_WAIT_MS, BATCH_WAIT_JITTER_RATIO)
|
|
929
|
+
);
|
|
796
930
|
});
|
|
797
931
|
this.batcher.pending = null;
|
|
798
932
|
this.batcher.resolveWait = null;
|
|
@@ -801,12 +935,12 @@ var UsageTracker = (_class = class {
|
|
|
801
935
|
})();
|
|
802
936
|
try {
|
|
803
937
|
_functions.waitUntil.call(void 0, pending);
|
|
804
|
-
} catch (
|
|
938
|
+
} catch (e5) {
|
|
805
939
|
}
|
|
806
940
|
this.batcher.pending = pending;
|
|
807
941
|
}
|
|
808
942
|
if (this.batcher.events.length >= MAX_BATCH_SIZE) {
|
|
809
|
-
_optionalChain([this, 'access',
|
|
943
|
+
_optionalChain([this, 'access', _33 => _33.batcher, 'access', _34 => _34.resolveWait, 'optionalCall', _35 => _35()]);
|
|
810
944
|
}
|
|
811
945
|
}
|
|
812
946
|
async flushEvents() {
|
|
@@ -846,7 +980,12 @@ Response body: ${await response.text().catch(() => null)}`
|
|
|
846
980
|
error
|
|
847
981
|
);
|
|
848
982
|
if (attempt < MAX_RETRIES) {
|
|
849
|
-
|
|
983
|
+
const delayMs = getRetryDelayMs(attempt);
|
|
984
|
+
await new Promise((res) => setTimeout(res, delayMs));
|
|
985
|
+
} else {
|
|
986
|
+
console.error(
|
|
987
|
+
`@vercel/flags-core: Dropped ${eventsToSend.length} events after ${MAX_RETRIES} attempts (flushId=${flushId})`
|
|
988
|
+
);
|
|
850
989
|
}
|
|
851
990
|
}
|
|
852
991
|
}
|
|
@@ -911,7 +1050,7 @@ var BundledSource = class {
|
|
|
911
1050
|
*/
|
|
912
1051
|
async tryLoad() {
|
|
913
1052
|
const result = await this.getResult();
|
|
914
|
-
if (_optionalChain([result, 'optionalAccess',
|
|
1053
|
+
if (_optionalChain([result, 'optionalAccess', _36 => _36.state]) === "ok" && result.definitions) {
|
|
915
1054
|
return result.definitions;
|
|
916
1055
|
}
|
|
917
1056
|
return void 0;
|
|
@@ -950,14 +1089,14 @@ async function fetchDatafile(options) {
|
|
|
950
1089
|
signal: controller.signal
|
|
951
1090
|
});
|
|
952
1091
|
clearTimeout(timeoutId);
|
|
953
|
-
_optionalChain([options, 'access',
|
|
1092
|
+
_optionalChain([options, 'access', _37 => _37.signal, 'optionalAccess', _38 => _38.removeEventListener, 'call', _39 => _39("abort", onExternalAbort)]);
|
|
954
1093
|
if (!res.ok) {
|
|
955
1094
|
throw new Error(`Failed to fetch data: ${res.statusText}`);
|
|
956
1095
|
}
|
|
957
1096
|
return res.json();
|
|
958
1097
|
} catch (error) {
|
|
959
1098
|
clearTimeout(timeoutId);
|
|
960
|
-
_optionalChain([options, 'access',
|
|
1099
|
+
_optionalChain([options, 'access', _40 => _40.signal, 'optionalAccess', _41 => _41.removeEventListener, 'call', _42 => _42("abort", onExternalAbort)]);
|
|
961
1100
|
throw error instanceof Error ? error : new Error("Unknown fetch error");
|
|
962
1101
|
}
|
|
963
1102
|
}
|
|
@@ -1022,7 +1161,7 @@ var TypedEmitter = (_class2 = class {constructor() { _class2.prototype.__init4.c
|
|
|
1022
1161
|
set.add(handler);
|
|
1023
1162
|
}
|
|
1024
1163
|
off(event, handler) {
|
|
1025
|
-
_optionalChain([this, 'access',
|
|
1164
|
+
_optionalChain([this, 'access', _43 => _43.handlers, 'access', _44 => _44.get, 'call', _45 => _45(event), 'optionalAccess', _46 => _46.delete, 'call', _47 => _47(handler)]);
|
|
1026
1165
|
}
|
|
1027
1166
|
emit(event, ...args) {
|
|
1028
1167
|
const set = this.handlers.get(event);
|
|
@@ -1048,11 +1187,11 @@ var PollingSource = class extends TypedEmitter {
|
|
|
1048
1187
|
* Emits 'data' on success, 'error' on failure.
|
|
1049
1188
|
*/
|
|
1050
1189
|
async poll() {
|
|
1051
|
-
if (_optionalChain([this, 'access',
|
|
1190
|
+
if (_optionalChain([this, 'access', _48 => _48.abortController, 'optionalAccess', _49 => _49.signal, 'access', _50 => _50.aborted])) return;
|
|
1052
1191
|
try {
|
|
1053
1192
|
const data = await fetchDatafile({
|
|
1054
1193
|
...this.config,
|
|
1055
|
-
signal: _optionalChain([this, 'access',
|
|
1194
|
+
signal: _optionalChain([this, 'access', _51 => _51.abortController, 'optionalAccess', _52 => _52.signal])
|
|
1056
1195
|
});
|
|
1057
1196
|
this.emit("data", data);
|
|
1058
1197
|
} catch (error) {
|
|
@@ -1081,7 +1220,7 @@ var PollingSource = class extends TypedEmitter {
|
|
|
1081
1220
|
clearInterval(this.intervalId);
|
|
1082
1221
|
this.intervalId = void 0;
|
|
1083
1222
|
}
|
|
1084
|
-
_optionalChain([this, 'access',
|
|
1223
|
+
_optionalChain([this, 'access', _53 => _53.abortController, 'optionalAccess', _54 => _54.abort, 'call', _55 => _55()]);
|
|
1085
1224
|
this.abortController = void 0;
|
|
1086
1225
|
}
|
|
1087
1226
|
};
|
|
@@ -1150,7 +1289,7 @@ async function connectStream(config, callbacks) {
|
|
|
1150
1289
|
if (pingTimeoutId !== void 0) clearTimeout(pingTimeoutId);
|
|
1151
1290
|
if (!initialDataReceived) return;
|
|
1152
1291
|
pingTimeoutId = setTimeout(() => {
|
|
1153
|
-
_optionalChain([responseBody, 'optionalAccess',
|
|
1292
|
+
_optionalChain([responseBody, 'optionalAccess', _56 => _56.cancel, 'call', _57 => _57(), 'access', _58 => _58.catch, 'call', _59 => _59(() => {
|
|
1154
1293
|
})]);
|
|
1155
1294
|
connectionAbort.abort();
|
|
1156
1295
|
}, PING_TIMEOUT_MS);
|
|
@@ -1166,7 +1305,7 @@ async function connectStream(config, callbacks) {
|
|
|
1166
1305
|
if (vercelEnv) {
|
|
1167
1306
|
headers["X-Vercel-Env"] = vercelEnv;
|
|
1168
1307
|
}
|
|
1169
|
-
const revision = _optionalChain([config, 'access',
|
|
1308
|
+
const revision = _optionalChain([config, 'access', _60 => _60.revision, 'optionalCall', _61 => _61()]);
|
|
1170
1309
|
if (revision !== void 0) {
|
|
1171
1310
|
headers["X-Revision"] = String(revision);
|
|
1172
1311
|
}
|
|
@@ -1212,7 +1351,7 @@ async function connectStream(config, callbacks) {
|
|
|
1212
1351
|
let message;
|
|
1213
1352
|
try {
|
|
1214
1353
|
message = JSON.parse(line);
|
|
1215
|
-
} catch (
|
|
1354
|
+
} catch (e6) {
|
|
1216
1355
|
console.warn(
|
|
1217
1356
|
"@vercel/flags-core: Failed to parse stream message, skipping"
|
|
1218
1357
|
);
|
|
@@ -1228,7 +1367,7 @@ async function connectStream(config, callbacks) {
|
|
|
1228
1367
|
resetPingTimeout();
|
|
1229
1368
|
}
|
|
1230
1369
|
if (message.type === "primed") {
|
|
1231
|
-
_optionalChain([onPrimed, 'optionalCall',
|
|
1370
|
+
_optionalChain([onPrimed, 'optionalCall', _62 => _62(message)]);
|
|
1232
1371
|
retryCount = 0;
|
|
1233
1372
|
if (!initialDataReceived) {
|
|
1234
1373
|
initialDataReceived = true;
|
|
@@ -1251,7 +1390,7 @@ async function connectStream(config, callbacks) {
|
|
|
1251
1390
|
clearTimeout(pingTimeoutId);
|
|
1252
1391
|
abortController.signal.removeEventListener("abort", onMainAbort);
|
|
1253
1392
|
if (!abortController.signal.aborted) {
|
|
1254
|
-
_optionalChain([onDisconnect, 'optionalCall',
|
|
1393
|
+
_optionalChain([onDisconnect, 'optionalCall', _63 => _63()]);
|
|
1255
1394
|
retryCount++;
|
|
1256
1395
|
const elapsed = Date.now() - lastAttemptTime;
|
|
1257
1396
|
const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
|
|
@@ -1267,7 +1406,7 @@ async function connectStream(config, callbacks) {
|
|
|
1267
1406
|
if (!connectionAbort.signal.aborted) {
|
|
1268
1407
|
console.error("@vercel/flags-core: Stream error", error);
|
|
1269
1408
|
}
|
|
1270
|
-
_optionalChain([onDisconnect, 'optionalCall',
|
|
1409
|
+
_optionalChain([onDisconnect, 'optionalCall', _64 => _64()]);
|
|
1271
1410
|
retryCount++;
|
|
1272
1411
|
const elapsed = Date.now() - lastAttemptTime;
|
|
1273
1412
|
const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
|
|
@@ -1346,7 +1485,7 @@ var StreamSource = class extends TypedEmitter {
|
|
|
1346
1485
|
* Stop the stream connection.
|
|
1347
1486
|
*/
|
|
1348
1487
|
stop() {
|
|
1349
|
-
_optionalChain([this, 'access',
|
|
1488
|
+
_optionalChain([this, 'access', _65 => _65.abortController, 'optionalAccess', _66 => _66.abort, 'call', _67 => _67()]);
|
|
1350
1489
|
this.abortController = void 0;
|
|
1351
1490
|
this.promise = void 0;
|
|
1352
1491
|
}
|
|
@@ -1384,19 +1523,24 @@ var Controller = (_class3 = class {
|
|
|
1384
1523
|
__init5() {this.state = "idle"}
|
|
1385
1524
|
// Data state — tagged with origin
|
|
1386
1525
|
|
|
1526
|
+
// Memoized data spread for read() / getDatafile().
|
|
1527
|
+
// Rebuilt only when `this.data` reference changes (e.g. on stream/poll update).
|
|
1528
|
+
// Holds the result of stripping `_origin`; metrics are appended per-call.
|
|
1529
|
+
__init6() {this.dataViewSource = void 0}
|
|
1530
|
+
__init7() {this.dataViewBase = void 0}
|
|
1387
1531
|
// Sources (I/O delegates)
|
|
1388
1532
|
|
|
1389
1533
|
|
|
1390
1534
|
|
|
1391
1535
|
// Usage tracking
|
|
1392
1536
|
|
|
1393
|
-
|
|
1537
|
+
__init8() {this.isFirstGetData = true}
|
|
1394
1538
|
// Build-step deduplication
|
|
1395
|
-
|
|
1396
|
-
|
|
1539
|
+
__init9() {this.buildDataPromise = null}
|
|
1540
|
+
__init10() {this.buildReadTracked = false}
|
|
1397
1541
|
// Suppresses usage tracking when the SDK key is unauthorized
|
|
1398
|
-
|
|
1399
|
-
constructor(options) {;_class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);
|
|
1542
|
+
__init11() {this.unauthorized = false}
|
|
1543
|
+
constructor(options) {;_class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);
|
|
1400
1544
|
if (!options.sdkKey || typeof options.sdkKey !== "string" || !options.sdkKey.startsWith("vf_")) {
|
|
1401
1545
|
throw new Error(
|
|
1402
1546
|
'@vercel/flags-core: SDK key must be a string starting with "vf_"'
|
|
@@ -1405,7 +1549,7 @@ var Controller = (_class3 = class {
|
|
|
1405
1549
|
this.options = normalizeOptions(options);
|
|
1406
1550
|
this.streamSource = new StreamSource(
|
|
1407
1551
|
this.options,
|
|
1408
|
-
() => _optionalChain([this, 'access',
|
|
1552
|
+
() => _optionalChain([this, 'access', _68 => _68.data, 'optionalAccess', _69 => _69.revision])
|
|
1409
1553
|
);
|
|
1410
1554
|
this.pollingSource = new PollingSource(this.options);
|
|
1411
1555
|
this.bundledSource = new BundledSource({
|
|
@@ -1419,32 +1563,32 @@ var Controller = (_class3 = class {
|
|
|
1419
1563
|
this.usageTracker = new UsageTracker(this.options);
|
|
1420
1564
|
}
|
|
1421
1565
|
// Source event handlers (stored for cleanup)
|
|
1422
|
-
|
|
1566
|
+
__init12() {this.onStreamData = (data) => {
|
|
1423
1567
|
if (this.isNewerData(data)) {
|
|
1424
1568
|
this.data = tagData(data, "stream");
|
|
1425
1569
|
}
|
|
1426
1570
|
}}
|
|
1427
|
-
|
|
1571
|
+
__init13() {this.onStreamPrimed = () => {
|
|
1428
1572
|
if (this.state === "degraded" || this.state === "initializing:stream") {
|
|
1429
1573
|
this.transition("streaming");
|
|
1430
1574
|
}
|
|
1431
1575
|
}}
|
|
1432
|
-
|
|
1576
|
+
__init14() {this.onStreamConnected = () => {
|
|
1433
1577
|
if (this.state === "degraded" || this.state === "initializing:stream") {
|
|
1434
1578
|
this.transition("streaming");
|
|
1435
1579
|
}
|
|
1436
1580
|
}}
|
|
1437
|
-
|
|
1581
|
+
__init15() {this.onStreamDisconnected = () => {
|
|
1438
1582
|
if (this.state === "streaming") {
|
|
1439
1583
|
this.transition("degraded");
|
|
1440
1584
|
}
|
|
1441
1585
|
}}
|
|
1442
|
-
|
|
1586
|
+
__init16() {this.onPollData = (data) => {
|
|
1443
1587
|
if (this.isNewerData(data)) {
|
|
1444
1588
|
this.data = tagData(data, "poll");
|
|
1445
1589
|
}
|
|
1446
1590
|
}}
|
|
1447
|
-
|
|
1591
|
+
__init17() {this.onPollError = (error) => {
|
|
1448
1592
|
console.error("@vercel/flags-core: Poll failed:", error);
|
|
1449
1593
|
}}
|
|
1450
1594
|
// ---------------------------------------------------------------------------
|
|
@@ -1513,7 +1657,7 @@ var Controller = (_class3 = class {
|
|
|
1513
1657
|
if (bundled) {
|
|
1514
1658
|
this.data = tagData(bundled, "bundled");
|
|
1515
1659
|
}
|
|
1516
|
-
} catch (
|
|
1660
|
+
} catch (e7) {
|
|
1517
1661
|
}
|
|
1518
1662
|
}
|
|
1519
1663
|
if (this.data) {
|
|
@@ -1555,11 +1699,15 @@ var Controller = (_class3 = class {
|
|
|
1555
1699
|
this.isFirstGetData = false;
|
|
1556
1700
|
const [result, cacheStatus] = await this.resolveData();
|
|
1557
1701
|
const readMs = Date.now() - startTime;
|
|
1558
|
-
const
|
|
1559
|
-
const source = originToMetricsSource(_origin);
|
|
1702
|
+
const source = originToMetricsSource(result._origin);
|
|
1560
1703
|
this.trackRead(startTime, cacheHadDefinitions, isFirstRead, source);
|
|
1704
|
+
if (this.dataViewSource !== result) {
|
|
1705
|
+
const { _origin, ...rest } = result;
|
|
1706
|
+
this.dataViewBase = rest;
|
|
1707
|
+
this.dataViewSource = result;
|
|
1708
|
+
}
|
|
1561
1709
|
return {
|
|
1562
|
-
...
|
|
1710
|
+
...this.dataViewBase,
|
|
1563
1711
|
metrics: {
|
|
1564
1712
|
readMs,
|
|
1565
1713
|
source,
|
|
@@ -1611,7 +1759,7 @@ var Controller = (_class3 = class {
|
|
|
1611
1759
|
this.data = tagData(fetched, "fetched");
|
|
1612
1760
|
result = this.data;
|
|
1613
1761
|
cacheStatus = "MISS";
|
|
1614
|
-
} catch (
|
|
1762
|
+
} catch (e8) {
|
|
1615
1763
|
throw new Error(
|
|
1616
1764
|
"@vercel/flags-core: No flag definitions available. Initialize the client or provide a datafile."
|
|
1617
1765
|
);
|
|
@@ -1619,8 +1767,13 @@ var Controller = (_class3 = class {
|
|
|
1619
1767
|
}
|
|
1620
1768
|
}
|
|
1621
1769
|
const source = originToMetricsSource(result._origin);
|
|
1770
|
+
if (this.dataViewSource !== result) {
|
|
1771
|
+
const { _origin, ...rest } = result;
|
|
1772
|
+
this.dataViewBase = rest;
|
|
1773
|
+
this.dataViewSource = result;
|
|
1774
|
+
}
|
|
1622
1775
|
return {
|
|
1623
|
-
...
|
|
1776
|
+
...this.dataViewBase,
|
|
1624
1777
|
metrics: {
|
|
1625
1778
|
readMs: Date.now() - startTime,
|
|
1626
1779
|
source,
|
|
@@ -1725,7 +1878,7 @@ var Controller = (_class3 = class {
|
|
|
1725
1878
|
return true;
|
|
1726
1879
|
}
|
|
1727
1880
|
return false;
|
|
1728
|
-
} catch (
|
|
1881
|
+
} catch (e9) {
|
|
1729
1882
|
return false;
|
|
1730
1883
|
}
|
|
1731
1884
|
}
|
|
@@ -1750,7 +1903,7 @@ var Controller = (_class3 = class {
|
|
|
1750
1903
|
return true;
|
|
1751
1904
|
}
|
|
1752
1905
|
return false;
|
|
1753
|
-
} catch (
|
|
1906
|
+
} catch (e10) {
|
|
1754
1907
|
clearTimeout(timeoutId);
|
|
1755
1908
|
return false;
|
|
1756
1909
|
}
|
|
@@ -1800,7 +1953,7 @@ var Controller = (_class3 = class {
|
|
|
1800
1953
|
fetch: this.options.fetch
|
|
1801
1954
|
});
|
|
1802
1955
|
return tagData(fetched, "fetched");
|
|
1803
|
-
} catch (
|
|
1956
|
+
} catch (e11) {
|
|
1804
1957
|
}
|
|
1805
1958
|
throw new Error(
|
|
1806
1959
|
"@vercel/flags-core: No flag definitions available during build. Provide a datafile or bundled definitions."
|
|
@@ -1834,7 +1987,7 @@ var Controller = (_class3 = class {
|
|
|
1834
1987
|
this.data = tagData(fetched, "fetched");
|
|
1835
1988
|
this.transition("degraded");
|
|
1836
1989
|
return;
|
|
1837
|
-
} catch (
|
|
1990
|
+
} catch (e12) {
|
|
1838
1991
|
}
|
|
1839
1992
|
}
|
|
1840
1993
|
throw new Error(
|
|
@@ -1886,7 +2039,7 @@ var Controller = (_class3 = class {
|
|
|
1886
2039
|
this.data = tagData(fetched, "fetched");
|
|
1887
2040
|
this.transition("degraded");
|
|
1888
2041
|
return [this.data, "MISS"];
|
|
1889
|
-
} catch (
|
|
2042
|
+
} catch (e13) {
|
|
1890
2043
|
}
|
|
1891
2044
|
}
|
|
1892
2045
|
throw new Error(
|
|
@@ -1938,11 +2091,11 @@ var Controller = (_class3 = class {
|
|
|
1938
2091
|
duration: Date.now() - startTime,
|
|
1939
2092
|
mode: mode === "streaming" ? "stream" : mode === "polling" ? "poll" : mode
|
|
1940
2093
|
};
|
|
1941
|
-
const configUpdatedAt = _optionalChain([this, 'access',
|
|
2094
|
+
const configUpdatedAt = _optionalChain([this, 'access', _70 => _70.data, 'optionalAccess', _71 => _71.configUpdatedAt]);
|
|
1942
2095
|
if (typeof configUpdatedAt === "number") {
|
|
1943
2096
|
trackOptions.configUpdatedAt = configUpdatedAt;
|
|
1944
2097
|
}
|
|
1945
|
-
const revision = _optionalChain([this, 'access',
|
|
2098
|
+
const revision = _optionalChain([this, 'access', _72 => _72.data, 'optionalAccess', _73 => _73.revision]);
|
|
1946
2099
|
if (typeof revision === "number") {
|
|
1947
2100
|
trackOptions.revision = revision;
|
|
1948
2101
|
}
|
|
@@ -1962,7 +2115,7 @@ function parseSdkKeyFromFlagsConnectionString(text) {
|
|
|
1962
2115
|
const params = new URLSearchParams(text.slice(6));
|
|
1963
2116
|
const sdkKey = params.get("sdkKey");
|
|
1964
2117
|
if (sdkKey && SDK_KEY_REGEX.test(sdkKey)) return sdkKey;
|
|
1965
|
-
} catch (
|
|
2118
|
+
} catch (e14) {
|
|
1966
2119
|
}
|
|
1967
2120
|
return null;
|
|
1968
2121
|
}
|
|
@@ -2045,4 +2198,4 @@ var {
|
|
|
2045
2198
|
|
|
2046
2199
|
|
|
2047
2200
|
exports.ResolutionReason = ResolutionReason; exports.evaluate = evaluate; exports.FallbackNotFoundError = FallbackNotFoundError; exports.FallbackEntryNotFoundError = FallbackEntryNotFoundError; exports.Controller = Controller; exports.flagsClient = flagsClient; exports.resetDefaultFlagsClient = resetDefaultFlagsClient; exports.createClient = createClient;
|
|
2048
|
-
//# sourceMappingURL=chunk-
|
|
2201
|
+
//# sourceMappingURL=chunk-5TVRNXPQ.cjs.map
|