@vercel/flags-core 1.5.0 → 1.6.0-7d29945-20260616152249
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 +6 -0
- package/dist/{chunk-CSSU7O2G.cjs → chunk-LPIUDLYE.cjs} +484 -246
- package/dist/chunk-LPIUDLYE.cjs.map +1 -0
- package/dist/{chunk-K3VOC24J.cjs → chunk-NHWFZTK2.cjs} +483 -245
- package/dist/chunk-NHWFZTK2.cjs.map +1 -0
- package/dist/{chunk-WTGXBSWR.js → chunk-PSYHUXJC.js} +429 -191
- package/dist/chunk-PSYHUXJC.js.map +1 -0
- package/dist/{chunk-D3AZORK2.js → chunk-S62BO7JU.js} +429 -191
- package/dist/chunk-S62BO7JU.js.map +1 -0
- package/dist/index.default.cjs +2 -2
- package/dist/index.default.d.cts +18 -3
- package/dist/index.default.d.ts +18 -3
- package/dist/index.default.js +1 -1
- package/dist/index.next-js.cjs +2 -2
- package/dist/index.next-js.d.cts +18 -3
- package/dist/index.next-js.d.ts +18 -3
- package/dist/index.next-js.js +1 -1
- package/dist/openfeature.default.cjs +2 -2
- package/dist/openfeature.default.js +1 -1
- package/dist/openfeature.next-js.cjs +2 -2
- package/dist/openfeature.next-js.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-CSSU7O2G.cjs.map +0 -1
- package/dist/chunk-D3AZORK2.js.map +0 -1
- package/dist/chunk-K3VOC24J.cjs.map +0 -1
- package/dist/chunk-WTGXBSWR.js.map +0 -1
|
@@ -35,6 +35,22 @@ var MAX_REGEX_INPUT_LENGTH = 1e4;
|
|
|
35
35
|
var UINT32_MAX = 4294967295;
|
|
36
36
|
var SCALED_WEIGHTS = /* @__PURE__ */ Symbol("@vercel/flags-core:scaledWeights");
|
|
37
37
|
var COMPILED_REGEX = /* @__PURE__ */ Symbol("@vercel/flags-core:compiledRegex");
|
|
38
|
+
var EVALUATION_VARIANT_INDEX = /* @__PURE__ */ Symbol(
|
|
39
|
+
"@vercel/flags-core:evaluationVariantIndex"
|
|
40
|
+
);
|
|
41
|
+
function withVariantIndex(value, outcomeType, variantIndex) {
|
|
42
|
+
return Object.defineProperty(
|
|
43
|
+
{
|
|
44
|
+
value,
|
|
45
|
+
outcomeType
|
|
46
|
+
},
|
|
47
|
+
EVALUATION_VARIANT_INDEX,
|
|
48
|
+
{ value: variantIndex }
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
function getEvaluationVariantIndex(result) {
|
|
52
|
+
return result[EVALUATION_VARIANT_INDEX];
|
|
53
|
+
}
|
|
38
54
|
function getScaledWeights(outcome) {
|
|
39
55
|
const cached = outcome[SCALED_WEIGHTS];
|
|
40
56
|
if (cached) return cached;
|
|
@@ -298,10 +314,11 @@ function getVariant(variants, index) {
|
|
|
298
314
|
}
|
|
299
315
|
function handleOutcome(params, outcome) {
|
|
300
316
|
if (typeof outcome === "number") {
|
|
301
|
-
return
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
317
|
+
return withVariantIndex(
|
|
318
|
+
getVariant(params.definition.variants, outcome),
|
|
319
|
+
"value" /* VALUE */,
|
|
320
|
+
outcome
|
|
321
|
+
);
|
|
305
322
|
}
|
|
306
323
|
switch (outcome.type) {
|
|
307
324
|
case "split": {
|
|
@@ -311,15 +328,21 @@ function handleOutcome(params, outcome) {
|
|
|
311
328
|
outcome.defaultVariant
|
|
312
329
|
);
|
|
313
330
|
if (typeof lhs !== "string") {
|
|
314
|
-
return
|
|
331
|
+
return withVariantIndex(
|
|
332
|
+
defaultOutcome,
|
|
333
|
+
"split" /* SPLIT */,
|
|
334
|
+
outcome.defaultVariant
|
|
335
|
+
);
|
|
315
336
|
}
|
|
316
337
|
const value = hashInput(lhs, params.definition.seed);
|
|
317
338
|
const scaledWeights = getScaledWeights(outcome);
|
|
318
339
|
const variantIndex = findWeightedIndex(scaledWeights, value, UINT32_MAX);
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
340
|
+
const selectedVariantIndex = variantIndex === -1 ? outcome.defaultVariant : variantIndex;
|
|
341
|
+
return withVariantIndex(
|
|
342
|
+
selectedVariantIndex === outcome.defaultVariant ? defaultOutcome : getVariant(params.definition.variants, selectedVariantIndex),
|
|
343
|
+
"split" /* SPLIT */,
|
|
344
|
+
selectedVariantIndex
|
|
345
|
+
);
|
|
323
346
|
}
|
|
324
347
|
case "rollout": {
|
|
325
348
|
const lhs = access(outcome.base, params);
|
|
@@ -328,18 +351,20 @@ function handleOutcome(params, outcome) {
|
|
|
328
351
|
outcome.defaultVariant
|
|
329
352
|
);
|
|
330
353
|
if (typeof lhs !== "string") {
|
|
331
|
-
return
|
|
354
|
+
return withVariantIndex(
|
|
355
|
+
defaultOutcome,
|
|
356
|
+
"rollout" /* ROLLOUT */,
|
|
357
|
+
outcome.defaultVariant
|
|
358
|
+
);
|
|
332
359
|
}
|
|
333
360
|
const now = Date.now();
|
|
334
361
|
const elapsed = now - outcome.startTimestamp;
|
|
335
362
|
if (elapsed < 0 || outcome.slots.length === 0) {
|
|
336
|
-
return
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
outcomeType: "rollout" /* ROLLOUT */
|
|
342
|
-
};
|
|
363
|
+
return withVariantIndex(
|
|
364
|
+
getVariant(params.definition.variants, outcome.rollFromVariant),
|
|
365
|
+
"rollout" /* ROLLOUT */,
|
|
366
|
+
outcome.rollFromVariant
|
|
367
|
+
);
|
|
343
368
|
}
|
|
344
369
|
let cumulativeDuration = 0;
|
|
345
370
|
let currentPromille = 0;
|
|
@@ -354,32 +379,27 @@ function handleOutcome(params, outcome) {
|
|
|
354
379
|
}
|
|
355
380
|
if (exhausted) currentPromille = 1e5;
|
|
356
381
|
if (currentPromille <= 0) {
|
|
357
|
-
return
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
outcomeType: "rollout" /* ROLLOUT */
|
|
363
|
-
};
|
|
382
|
+
return withVariantIndex(
|
|
383
|
+
getVariant(params.definition.variants, outcome.rollFromVariant),
|
|
384
|
+
"rollout" /* ROLLOUT */,
|
|
385
|
+
outcome.rollFromVariant
|
|
386
|
+
);
|
|
364
387
|
}
|
|
365
388
|
if (currentPromille >= 1e5) {
|
|
366
|
-
return
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
outcomeType: "rollout" /* ROLLOUT */
|
|
372
|
-
};
|
|
389
|
+
return withVariantIndex(
|
|
390
|
+
getVariant(params.definition.variants, outcome.rollToVariant),
|
|
391
|
+
"rollout" /* ROLLOUT */,
|
|
392
|
+
outcome.rollToVariant
|
|
393
|
+
);
|
|
373
394
|
}
|
|
374
395
|
const value = hashInput(lhs, params.definition.seed);
|
|
375
396
|
const threshold = currentPromille / 1e5 * UINT32_MAX;
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
};
|
|
397
|
+
const selectedVariantIndex = value < threshold ? outcome.rollToVariant : outcome.rollFromVariant;
|
|
398
|
+
return withVariantIndex(
|
|
399
|
+
getVariant(params.definition.variants, selectedVariantIndex),
|
|
400
|
+
"rollout" /* ROLLOUT */,
|
|
401
|
+
selectedVariantIndex
|
|
402
|
+
);
|
|
383
403
|
}
|
|
384
404
|
default: {
|
|
385
405
|
const { type } = outcome;
|
|
@@ -467,7 +487,7 @@ function findWeightedIndex(weights, value, maxValue) {
|
|
|
467
487
|
}
|
|
468
488
|
|
|
469
489
|
// package.json
|
|
470
|
-
var version = "1.
|
|
490
|
+
var version = "1.6.0-7d29945-20260616152249";
|
|
471
491
|
|
|
472
492
|
// src/lib/report-value.ts
|
|
473
493
|
function internalReportValue(key, value, data) {
|
|
@@ -506,6 +526,14 @@ function getFallbackDatafile(id) {
|
|
|
506
526
|
if (ds.getFallbackDatafile) return ds.getFallbackDatafile();
|
|
507
527
|
throw new Error("flags: This data source does not support fallbacks");
|
|
508
528
|
}
|
|
529
|
+
function getVariantIdentifier(definition, result) {
|
|
530
|
+
const variantIndex = getEvaluationVariantIndex(result);
|
|
531
|
+
if (variantIndex === void 0) return "unknown";
|
|
532
|
+
return definition.variantIds?.[variantIndex] ?? String(variantIndex);
|
|
533
|
+
}
|
|
534
|
+
function trackEvaluation(controller, options) {
|
|
535
|
+
controller.trackEvaluation?.(options);
|
|
536
|
+
}
|
|
509
537
|
async function evaluate2(id, flagKey, defaultValue, entities) {
|
|
510
538
|
const controller = getInstance(id).controller;
|
|
511
539
|
let datafile;
|
|
@@ -513,11 +541,17 @@ async function evaluate2(id, flagKey, defaultValue, entities) {
|
|
|
513
541
|
datafile = await controller.read();
|
|
514
542
|
} catch (error) {
|
|
515
543
|
if (defaultValue !== void 0) {
|
|
516
|
-
|
|
544
|
+
const result2 = {
|
|
517
545
|
value: defaultValue,
|
|
518
546
|
reason: "error" /* ERROR */,
|
|
519
547
|
errorMessage: error instanceof Error ? error.message : "Failed to read datafile"
|
|
520
548
|
};
|
|
549
|
+
trackEvaluation(controller, {
|
|
550
|
+
flagKey,
|
|
551
|
+
variant: "unknown",
|
|
552
|
+
reason: result2.reason
|
|
553
|
+
});
|
|
554
|
+
return result2;
|
|
521
555
|
}
|
|
522
556
|
throw error;
|
|
523
557
|
}
|
|
@@ -530,7 +564,7 @@ async function evaluate2(id, flagKey, defaultValue, entities) {
|
|
|
530
564
|
reason: "error" /* ERROR */
|
|
531
565
|
});
|
|
532
566
|
}
|
|
533
|
-
|
|
567
|
+
const result2 = {
|
|
534
568
|
value: defaultValue,
|
|
535
569
|
reason: "error" /* ERROR */,
|
|
536
570
|
errorCode: "FLAG_NOT_FOUND" /* FLAG_NOT_FOUND */,
|
|
@@ -544,6 +578,12 @@ async function evaluate2(id, flagKey, defaultValue, entities) {
|
|
|
544
578
|
mode: datafile.metrics.mode
|
|
545
579
|
}
|
|
546
580
|
};
|
|
581
|
+
trackEvaluation(controller, {
|
|
582
|
+
flagKey,
|
|
583
|
+
variant: "unknown",
|
|
584
|
+
reason: result2.reason
|
|
585
|
+
});
|
|
586
|
+
return result2;
|
|
547
587
|
}
|
|
548
588
|
const evalStartTime = Date.now();
|
|
549
589
|
const result = evaluate({
|
|
@@ -562,6 +602,14 @@ async function evaluate2(id, flagKey, defaultValue, entities) {
|
|
|
562
602
|
outcomeType: result.reason !== "error" /* ERROR */ ? result.outcomeType : void 0
|
|
563
603
|
});
|
|
564
604
|
}
|
|
605
|
+
trackEvaluation(controller, {
|
|
606
|
+
flagKey,
|
|
607
|
+
variant: getVariantIdentifier(
|
|
608
|
+
flagDefinition,
|
|
609
|
+
result
|
|
610
|
+
),
|
|
611
|
+
reason: result.reason
|
|
612
|
+
});
|
|
565
613
|
return Object.assign(result, {
|
|
566
614
|
metrics: {
|
|
567
615
|
evaluationMs: evaluationDurationMs,
|
|
@@ -587,6 +635,11 @@ async function bulkEvaluate2(id, flags, entities) {
|
|
|
587
635
|
reason: "error" /* ERROR */,
|
|
588
636
|
errorMessage
|
|
589
637
|
};
|
|
638
|
+
trackEvaluation(controller, {
|
|
639
|
+
flagKey: flag.key,
|
|
640
|
+
variant: "unknown",
|
|
641
|
+
reason: "error" /* ERROR */
|
|
642
|
+
});
|
|
590
643
|
}
|
|
591
644
|
return results2;
|
|
592
645
|
}
|
|
@@ -618,6 +671,11 @@ async function bulkEvaluate2(id, flags, entities) {
|
|
|
618
671
|
errorMessage: `@vercel/flags-core: Definition not found for flag "${key}"`,
|
|
619
672
|
metrics: { evaluationMs: 0, ...baseMetrics }
|
|
620
673
|
};
|
|
674
|
+
trackEvaluation(controller, {
|
|
675
|
+
flagKey: key,
|
|
676
|
+
variant: "unknown",
|
|
677
|
+
reason: "error" /* ERROR */
|
|
678
|
+
});
|
|
621
679
|
continue;
|
|
622
680
|
}
|
|
623
681
|
toEvaluate[key] = { definition: flagDefinition, defaultValue };
|
|
@@ -639,6 +697,14 @@ async function bulkEvaluate2(id, flags, entities) {
|
|
|
639
697
|
outcomeType: result.reason !== "error" /* ERROR */ ? result.outcomeType : void 0
|
|
640
698
|
});
|
|
641
699
|
}
|
|
700
|
+
trackEvaluation(controller, {
|
|
701
|
+
flagKey: key,
|
|
702
|
+
variant: getVariantIdentifier(
|
|
703
|
+
toEvaluate[key].definition,
|
|
704
|
+
result
|
|
705
|
+
),
|
|
706
|
+
reason: result.reason
|
|
707
|
+
});
|
|
642
708
|
results[key] = Object.assign(result, {
|
|
643
709
|
metrics: { evaluationMs: evaluationDurationMs, ...baseMetrics }
|
|
644
710
|
});
|
|
@@ -783,8 +849,8 @@ async function readBundledDefinitions(auth) {
|
|
|
783
849
|
return { definitions: null, state: "missing-entry" };
|
|
784
850
|
}
|
|
785
851
|
|
|
786
|
-
// src/utils/
|
|
787
|
-
import {
|
|
852
|
+
// src/utils/ingest.ts
|
|
853
|
+
import { getVercelOidcToken } from "@vercel/oidc";
|
|
788
854
|
|
|
789
855
|
// src/utils/backoff.ts
|
|
790
856
|
var DEFAULT_BASE_MS = 250;
|
|
@@ -795,23 +861,72 @@ function getRetryDelayMs(attempt, options = {}) {
|
|
|
795
861
|
const ceiling = Math.min(capMs, baseMs * 2 ** Math.max(0, attempt - 1));
|
|
796
862
|
return Math.floor(Math.random() * ceiling);
|
|
797
863
|
}
|
|
798
|
-
function getJitteredWaitMs(baseMs, ratio) {
|
|
799
|
-
const min = baseMs * (1 - ratio);
|
|
800
|
-
const span = baseMs * 2 * ratio;
|
|
801
|
-
return Math.floor(min + Math.random() * span);
|
|
802
|
-
}
|
|
803
864
|
|
|
804
|
-
// src/utils/
|
|
805
|
-
var
|
|
865
|
+
// src/utils/ingest.ts
|
|
866
|
+
var MAX_RETRIES = 3;
|
|
867
|
+
var EVALUATING_OIDC_TOKEN_HEADER = "X-Vercel-Flags-Evaluating-OIDC-Token";
|
|
806
868
|
var isDebugMode = process.env.DEBUG?.includes("@vercel/flags-core");
|
|
807
869
|
var debugLog = (...args) => {
|
|
808
870
|
if (!isDebugMode) return;
|
|
809
871
|
console.log(...args);
|
|
810
872
|
};
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
873
|
+
async function getEvaluatingOidcToken(auth) {
|
|
874
|
+
if (!auth.sdkKey) return void 0;
|
|
875
|
+
try {
|
|
876
|
+
return await getVercelOidcToken();
|
|
877
|
+
} catch {
|
|
878
|
+
return void 0;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
async function getIngestHeaders(options) {
|
|
882
|
+
const token = await options.auth.resolveToken();
|
|
883
|
+
const evaluatingOidcToken = await getEvaluatingOidcToken(options.auth);
|
|
884
|
+
return {
|
|
885
|
+
"Content-Type": "application/json",
|
|
886
|
+
Authorization: `Bearer ${token}`,
|
|
887
|
+
"User-Agent": `VercelFlagsCore/${version}`,
|
|
888
|
+
...process.env.VERCEL_ENV ? { "X-Vercel-Env": process.env.VERCEL_ENV } : null,
|
|
889
|
+
...evaluatingOidcToken ? { [EVALUATING_OIDC_TOKEN_HEADER]: evaluatingOidcToken } : null,
|
|
890
|
+
...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
async function sendIngestEvents(options, events, flushId) {
|
|
894
|
+
const eventsToSend = events.map((event) => event.ingestEvent());
|
|
895
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
896
|
+
try {
|
|
897
|
+
const response = await options.fetch(`${options.host}/v1/ingest`, {
|
|
898
|
+
method: "POST",
|
|
899
|
+
headers: await getIngestHeaders(options),
|
|
900
|
+
body: JSON.stringify(eventsToSend)
|
|
901
|
+
});
|
|
902
|
+
debugLog(
|
|
903
|
+
`@vercel/flags-core: Ingest response ${response.status} for ${eventsToSend.length} events on ${response.headers.get("x-vercel-id")}`
|
|
904
|
+
);
|
|
905
|
+
if (response.ok) {
|
|
906
|
+
break;
|
|
907
|
+
}
|
|
908
|
+
throw new Error(
|
|
909
|
+
`Ingest endpoint responded with status ${response.status} for ${eventsToSend.length} events on request ${response.headers.get("x-vercel-id")}.
|
|
910
|
+
Response body: ${await response.text().catch(() => null)}`
|
|
911
|
+
);
|
|
912
|
+
} catch (error) {
|
|
913
|
+
console.error(
|
|
914
|
+
`@vercel/flags-core: Error sending events (attempt=${attempt}/${MAX_RETRIES} flushId=${flushId}):`,
|
|
915
|
+
error
|
|
916
|
+
);
|
|
917
|
+
if (attempt < MAX_RETRIES) {
|
|
918
|
+
const delayMs = getRetryDelayMs(attempt);
|
|
919
|
+
await new Promise((res) => setTimeout(res, delayMs));
|
|
920
|
+
} else {
|
|
921
|
+
console.error(
|
|
922
|
+
`@vercel/flags-core: Dropped ${eventsToSend.length} events after ${MAX_RETRIES} attempts (flushId=${flushId})`
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// src/utils/request-context.ts
|
|
815
930
|
var SYMBOL_FOR_REQ_CONTEXT = /* @__PURE__ */ Symbol.for("@vercel/request-context");
|
|
816
931
|
var fromSymbol = globalThis;
|
|
817
932
|
function getRequestContext() {
|
|
@@ -823,36 +938,227 @@ function getRequestContext() {
|
|
|
823
938
|
headers: ctx.headers
|
|
824
939
|
};
|
|
825
940
|
}
|
|
826
|
-
return { ctx, headers: void 0 };
|
|
941
|
+
return { ctx: void 0, headers: void 0 };
|
|
827
942
|
} catch {
|
|
828
943
|
return { ctx: void 0, headers: void 0 };
|
|
829
944
|
}
|
|
830
945
|
}
|
|
946
|
+
|
|
947
|
+
// src/utils/scheduler.ts
|
|
948
|
+
import { waitUntil } from "@vercel/functions";
|
|
949
|
+
var MAX_COUNT = 50;
|
|
950
|
+
var IDLE_FLUSH_WAIT_MS = 5e3;
|
|
951
|
+
var MAX_FLUSH_WAIT_MS = 6e4;
|
|
952
|
+
var Scheduler = class {
|
|
953
|
+
constructor(onFlush) {
|
|
954
|
+
this.onFlush = onFlush;
|
|
955
|
+
}
|
|
956
|
+
count = 0;
|
|
957
|
+
resolveWait = null;
|
|
958
|
+
pending = null;
|
|
959
|
+
idleTimeout = null;
|
|
960
|
+
maxTimeout = null;
|
|
961
|
+
increment() {
|
|
962
|
+
this.count += 1;
|
|
963
|
+
if (this.count >= MAX_COUNT) {
|
|
964
|
+
this.resolveScheduledFlush();
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
scheduleFlush() {
|
|
968
|
+
if (!this.pending) {
|
|
969
|
+
this.pending = (async () => {
|
|
970
|
+
await new Promise((res) => {
|
|
971
|
+
this.resolveWait = res;
|
|
972
|
+
});
|
|
973
|
+
this.reset();
|
|
974
|
+
await this.onFlush();
|
|
975
|
+
})();
|
|
976
|
+
try {
|
|
977
|
+
waitUntil(this.pending);
|
|
978
|
+
} catch {
|
|
979
|
+
}
|
|
980
|
+
this.maxTimeout = setTimeout(
|
|
981
|
+
() => this.resolveScheduledFlush(),
|
|
982
|
+
MAX_FLUSH_WAIT_MS
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
this.resetIdleTimeout();
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Resolves any in-flight scheduled flush and waits for its ingest to finish.
|
|
989
|
+
*/
|
|
990
|
+
async shutdown() {
|
|
991
|
+
this.clearTimeouts();
|
|
992
|
+
const pending = this.pending;
|
|
993
|
+
this.resolveWait?.();
|
|
994
|
+
if (pending) await pending;
|
|
995
|
+
}
|
|
996
|
+
resetIdleTimeout() {
|
|
997
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
998
|
+
this.idleTimeout = setTimeout(
|
|
999
|
+
() => this.resolveScheduledFlush(),
|
|
1000
|
+
IDLE_FLUSH_WAIT_MS
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
resolveScheduledFlush() {
|
|
1004
|
+
this.clearTimeouts();
|
|
1005
|
+
this.resolveWait?.();
|
|
1006
|
+
}
|
|
1007
|
+
reset() {
|
|
1008
|
+
this.pending = null;
|
|
1009
|
+
this.resolveWait = null;
|
|
1010
|
+
this.count = 0;
|
|
1011
|
+
this.clearTimeouts();
|
|
1012
|
+
}
|
|
1013
|
+
clearTimeouts() {
|
|
1014
|
+
if (this.idleTimeout) {
|
|
1015
|
+
clearTimeout(this.idleTimeout);
|
|
1016
|
+
this.idleTimeout = null;
|
|
1017
|
+
}
|
|
1018
|
+
if (this.maxTimeout) {
|
|
1019
|
+
clearTimeout(this.maxTimeout);
|
|
1020
|
+
this.maxTimeout = null;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
|
|
1025
|
+
// src/utils/usage/flags-config-read.ts
|
|
1026
|
+
var FlagsConfigReadEvent = class {
|
|
1027
|
+
payload;
|
|
1028
|
+
constructor(headers, options) {
|
|
1029
|
+
this.payload = {
|
|
1030
|
+
deploymentId: process.env.VERCEL_DEPLOYMENT_ID,
|
|
1031
|
+
region: process.env.VERCEL_REGION
|
|
1032
|
+
};
|
|
1033
|
+
if (headers) {
|
|
1034
|
+
this.payload.vercelRequestId = headers["x-vercel-id"] ?? void 0;
|
|
1035
|
+
this.payload.invocationHost = headers.host ?? void 0;
|
|
1036
|
+
}
|
|
1037
|
+
if (options) {
|
|
1038
|
+
this.payload.configOrigin = options.configOrigin;
|
|
1039
|
+
if (options.cacheStatus !== void 0) {
|
|
1040
|
+
this.payload.cacheStatus = options.cacheStatus;
|
|
1041
|
+
}
|
|
1042
|
+
if (options.cacheAction !== void 0) {
|
|
1043
|
+
this.payload.cacheAction = options.cacheAction;
|
|
1044
|
+
}
|
|
1045
|
+
if (options.cacheIsFirstRead !== void 0) {
|
|
1046
|
+
this.payload.cacheIsFirstRead = options.cacheIsFirstRead;
|
|
1047
|
+
}
|
|
1048
|
+
if (options.cacheIsBlocking !== void 0) {
|
|
1049
|
+
this.payload.cacheIsBlocking = options.cacheIsBlocking;
|
|
1050
|
+
}
|
|
1051
|
+
if (options.duration !== void 0) {
|
|
1052
|
+
this.payload.duration = options.duration;
|
|
1053
|
+
}
|
|
1054
|
+
if (options.configUpdatedAt !== void 0) {
|
|
1055
|
+
this.payload.configUpdatedAt = options.configUpdatedAt;
|
|
1056
|
+
}
|
|
1057
|
+
if (options.mode !== void 0) {
|
|
1058
|
+
this.payload.mode = options.mode;
|
|
1059
|
+
}
|
|
1060
|
+
if (options.revision !== void 0) {
|
|
1061
|
+
this.payload.revision = String(options.revision);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
const environment = process.env.VERCEL_ENV || process.env.NODE_ENV || void 0;
|
|
1065
|
+
if (environment) {
|
|
1066
|
+
this.payload.environment = environment;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
ingestEvent() {
|
|
1070
|
+
return {
|
|
1071
|
+
type: "FLAGS_CONFIG_READ",
|
|
1072
|
+
ts: Date.now(),
|
|
1073
|
+
payload: this.payload
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1078
|
+
// src/utils/usage/flags-evaluation.ts
|
|
1079
|
+
function evaluationBatchKey(options) {
|
|
1080
|
+
return JSON.stringify([
|
|
1081
|
+
options.flagKey,
|
|
1082
|
+
options.variant,
|
|
1083
|
+
options.reason,
|
|
1084
|
+
options.clientName ?? null
|
|
1085
|
+
]);
|
|
1086
|
+
}
|
|
1087
|
+
var FlagsEvaluationEvent = class {
|
|
1088
|
+
payload;
|
|
1089
|
+
constructor(eventOptions) {
|
|
1090
|
+
this.payload = {
|
|
1091
|
+
flagKey: eventOptions.flagKey,
|
|
1092
|
+
variant: eventOptions.variant,
|
|
1093
|
+
reason: eventOptions.reason,
|
|
1094
|
+
count: 1
|
|
1095
|
+
};
|
|
1096
|
+
if (eventOptions.clientName) {
|
|
1097
|
+
this.payload.clientName = eventOptions.clientName;
|
|
1098
|
+
}
|
|
1099
|
+
Object.defineProperty(this, "batchKey", {
|
|
1100
|
+
value: JSON.stringify([
|
|
1101
|
+
this.payload.flagKey,
|
|
1102
|
+
this.payload.variant,
|
|
1103
|
+
this.payload.reason,
|
|
1104
|
+
this.payload.clientName ?? null
|
|
1105
|
+
]),
|
|
1106
|
+
enumerable: false
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
increment() {
|
|
1110
|
+
this.payload.count += 1;
|
|
1111
|
+
}
|
|
1112
|
+
ingestEvent() {
|
|
1113
|
+
return {
|
|
1114
|
+
type: "FLAGS_EVALUATION",
|
|
1115
|
+
ts: Date.now(),
|
|
1116
|
+
payload: this.payload
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
|
|
1121
|
+
// src/utils/weak-map.ts
|
|
1122
|
+
var BetterWeakMap = class {
|
|
1123
|
+
_values = [];
|
|
1124
|
+
_keys = [];
|
|
1125
|
+
set(key, value) {
|
|
1126
|
+
this._keys.push(key);
|
|
1127
|
+
this._values.push(value);
|
|
1128
|
+
}
|
|
1129
|
+
values() {
|
|
1130
|
+
return this._values;
|
|
1131
|
+
}
|
|
1132
|
+
has(key) {
|
|
1133
|
+
return this._keys.includes(key);
|
|
1134
|
+
}
|
|
1135
|
+
keys() {
|
|
1136
|
+
return this._keys;
|
|
1137
|
+
}
|
|
1138
|
+
clear() {
|
|
1139
|
+
this._values = [];
|
|
1140
|
+
this._keys = [];
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
|
|
1144
|
+
// src/utils/usage-tracker.ts
|
|
831
1145
|
var UsageTracker = class {
|
|
832
|
-
|
|
1146
|
+
flushCount = 0;
|
|
833
1147
|
options;
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
resolveWait: null,
|
|
838
|
-
pending: null
|
|
839
|
-
};
|
|
1148
|
+
scheduler;
|
|
1149
|
+
readEvents = new BetterWeakMap();
|
|
1150
|
+
evaluationEvents = /* @__PURE__ */ new Map();
|
|
840
1151
|
constructor(options) {
|
|
841
1152
|
this.options = options;
|
|
1153
|
+
this.scheduler = new Scheduler(() => this.flushEvents());
|
|
842
1154
|
}
|
|
843
1155
|
/**
|
|
844
1156
|
* Triggers an immediate flush of any pending events.
|
|
845
1157
|
* Returns a promise that resolves when the flush completes.
|
|
846
1158
|
*/
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
return this.batcher.pending;
|
|
851
|
-
}
|
|
852
|
-
if (this.batcher.events.length > 0) {
|
|
853
|
-
return this.flushEvents();
|
|
854
|
-
}
|
|
855
|
-
return RESOLVED_VOID;
|
|
1159
|
+
async shutdown() {
|
|
1160
|
+
await this.scheduler.shutdown();
|
|
1161
|
+
await this.flushEvents();
|
|
856
1162
|
}
|
|
857
1163
|
/**
|
|
858
1164
|
* Tracks a config read event. Deduplicates by request context.
|
|
@@ -861,130 +1167,51 @@ var UsageTracker = class {
|
|
|
861
1167
|
try {
|
|
862
1168
|
const { ctx, headers } = getRequestContext();
|
|
863
1169
|
if (!ctx) return;
|
|
864
|
-
if (this.
|
|
865
|
-
|
|
866
|
-
const event = {
|
|
867
|
-
type: "FLAGS_CONFIG_READ",
|
|
868
|
-
ts: Date.now(),
|
|
869
|
-
payload: {
|
|
870
|
-
deploymentId: process.env.VERCEL_DEPLOYMENT_ID,
|
|
871
|
-
region: process.env.VERCEL_REGION
|
|
872
|
-
}
|
|
873
|
-
};
|
|
874
|
-
if (headers) {
|
|
875
|
-
event.payload.vercelRequestId = headers["x-vercel-id"] ?? void 0;
|
|
876
|
-
event.payload.invocationHost = headers.host ?? void 0;
|
|
877
|
-
}
|
|
878
|
-
if (options) {
|
|
879
|
-
event.payload.configOrigin = options.configOrigin;
|
|
880
|
-
if (options.cacheStatus !== void 0) {
|
|
881
|
-
event.payload.cacheStatus = options.cacheStatus;
|
|
882
|
-
}
|
|
883
|
-
if (options.cacheAction !== void 0) {
|
|
884
|
-
event.payload.cacheAction = options.cacheAction;
|
|
885
|
-
}
|
|
886
|
-
if (options.cacheIsFirstRead !== void 0) {
|
|
887
|
-
event.payload.cacheIsFirstRead = options.cacheIsFirstRead;
|
|
888
|
-
}
|
|
889
|
-
if (options.cacheIsBlocking !== void 0) {
|
|
890
|
-
event.payload.cacheIsBlocking = options.cacheIsBlocking;
|
|
891
|
-
}
|
|
892
|
-
if (options.duration !== void 0) {
|
|
893
|
-
event.payload.duration = options.duration;
|
|
894
|
-
}
|
|
895
|
-
if (options.configUpdatedAt !== void 0) {
|
|
896
|
-
event.payload.configUpdatedAt = options.configUpdatedAt;
|
|
897
|
-
}
|
|
898
|
-
if (options.mode !== void 0) {
|
|
899
|
-
event.payload.mode = options.mode;
|
|
900
|
-
}
|
|
901
|
-
if (options.revision !== void 0) {
|
|
902
|
-
event.payload.revision = String(options.revision);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
const environment = process.env.VERCEL_ENV || process.env.NODE_ENV || void 0;
|
|
906
|
-
if (environment) {
|
|
907
|
-
event.payload.environment = environment;
|
|
1170
|
+
if (this.readEvents.has(ctx)) {
|
|
1171
|
+
return;
|
|
908
1172
|
}
|
|
909
|
-
this.
|
|
910
|
-
this.scheduleFlush();
|
|
1173
|
+
this.readEvents.set(ctx, new FlagsConfigReadEvent(headers, options));
|
|
1174
|
+
this.scheduler.scheduleFlush();
|
|
1175
|
+
this.scheduler.increment();
|
|
911
1176
|
} catch (error) {
|
|
912
1177
|
console.error("@vercel/flags-core: Failed to record event:", error);
|
|
913
1178
|
}
|
|
914
1179
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
this.
|
|
927
|
-
this.batcher.resolveWait = null;
|
|
928
|
-
if (timeout) clearTimeout(timeout);
|
|
929
|
-
await this.flushEvents();
|
|
930
|
-
})();
|
|
931
|
-
try {
|
|
932
|
-
waitUntil(pending);
|
|
933
|
-
} catch {
|
|
1180
|
+
/**
|
|
1181
|
+
* Tracks a flag evaluation event.
|
|
1182
|
+
*/
|
|
1183
|
+
trackEvaluation(options) {
|
|
1184
|
+
try {
|
|
1185
|
+
const batchKey = evaluationBatchKey(options);
|
|
1186
|
+
const existingEvent = this.evaluationEvents.get(batchKey);
|
|
1187
|
+
if (existingEvent) {
|
|
1188
|
+
existingEvent.increment();
|
|
1189
|
+
} else {
|
|
1190
|
+
this.evaluationEvents.set(batchKey, new FlagsEvaluationEvent(options));
|
|
1191
|
+
this.scheduler.increment();
|
|
934
1192
|
}
|
|
935
|
-
this.
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
|
|
1193
|
+
this.scheduler.scheduleFlush();
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
console.error(
|
|
1196
|
+
"@vercel/flags-core: Failed to record evaluation event:",
|
|
1197
|
+
error
|
|
1198
|
+
);
|
|
939
1199
|
}
|
|
940
1200
|
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Send all events to the ingest service
|
|
1203
|
+
*/
|
|
941
1204
|
async flushEvents() {
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
method: "POST",
|
|
953
|
-
headers: {
|
|
954
|
-
"Content-Type": "application/json",
|
|
955
|
-
Authorization: `Bearer ${token}`,
|
|
956
|
-
"User-Agent": `VercelFlagsCore/${version}`,
|
|
957
|
-
...process.env.VERCEL_ENV ? { "X-Vercel-Env": process.env.VERCEL_ENV } : null,
|
|
958
|
-
...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
|
|
959
|
-
},
|
|
960
|
-
body: JSON.stringify(eventsToSend)
|
|
961
|
-
}
|
|
962
|
-
);
|
|
963
|
-
debugLog(
|
|
964
|
-
`@vercel/flags-core: Ingest response ${response.status} for ${eventsToSend.length} events on ${response.headers.get("x-vercel-id")}`
|
|
965
|
-
);
|
|
966
|
-
if (response.ok) {
|
|
967
|
-
break;
|
|
968
|
-
}
|
|
969
|
-
throw new Error(
|
|
970
|
-
`Ingest endpoint responded with status ${response.status} for ${eventsToSend.length} events on request ${response.headers.get("x-vercel-id")}.
|
|
971
|
-
Response body: ${await response.text().catch(() => null)}`
|
|
972
|
-
);
|
|
973
|
-
} catch (error) {
|
|
974
|
-
console.error(
|
|
975
|
-
`@vercel/flags-core: Error sending events (attempt=${attempt}/${MAX_RETRIES} flushId=${flushId}):`,
|
|
976
|
-
error
|
|
977
|
-
);
|
|
978
|
-
if (attempt < MAX_RETRIES) {
|
|
979
|
-
const delayMs = getRetryDelayMs(attempt);
|
|
980
|
-
await new Promise((res) => setTimeout(res, delayMs));
|
|
981
|
-
} else {
|
|
982
|
-
console.error(
|
|
983
|
-
`@vercel/flags-core: Dropped ${eventsToSend.length} events after ${MAX_RETRIES} attempts (flushId=${flushId})`
|
|
984
|
-
);
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
}
|
|
1205
|
+
const events = [
|
|
1206
|
+
...this.readEvents.values(),
|
|
1207
|
+
...this.evaluationEvents.values()
|
|
1208
|
+
];
|
|
1209
|
+
if (events.length === 0) return;
|
|
1210
|
+
this.flushCount += 1;
|
|
1211
|
+
const flushId = this.flushCount;
|
|
1212
|
+
this.readEvents.clear();
|
|
1213
|
+
this.evaluationEvents.clear();
|
|
1214
|
+
await sendIngestEvents(this.options, events, flushId);
|
|
988
1215
|
}
|
|
989
1216
|
};
|
|
990
1217
|
|
|
@@ -1141,7 +1368,8 @@ function normalizeOptions(options) {
|
|
|
1141
1368
|
polling,
|
|
1142
1369
|
buildStep,
|
|
1143
1370
|
fetch: options.fetch ?? globalThis.fetch,
|
|
1144
|
-
host: "https://flags.vercel.com"
|
|
1371
|
+
host: "https://flags.vercel.com",
|
|
1372
|
+
clientName: options.clientName
|
|
1145
1373
|
};
|
|
1146
1374
|
}
|
|
1147
1375
|
|
|
@@ -1726,7 +1954,7 @@ var Controller = class {
|
|
|
1726
1954
|
this.pollingSource.stop();
|
|
1727
1955
|
this.data = this.options.datafile ? tagData(this.options.datafile, "provided") : void 0;
|
|
1728
1956
|
this.transition("shutdown");
|
|
1729
|
-
await this.usageTracker.
|
|
1957
|
+
await this.usageTracker.shutdown();
|
|
1730
1958
|
}
|
|
1731
1959
|
/**
|
|
1732
1960
|
* Returns the datafile with metrics.
|
|
@@ -2104,10 +2332,20 @@ var Controller = class {
|
|
|
2104
2332
|
}
|
|
2105
2333
|
this.usageTracker.trackRead(trackOptions);
|
|
2106
2334
|
}
|
|
2335
|
+
/**
|
|
2336
|
+
* Tracks a flag evaluation for usage analytics.
|
|
2337
|
+
*/
|
|
2338
|
+
trackEvaluation(options) {
|
|
2339
|
+
if (this.unauthorized) return;
|
|
2340
|
+
this.usageTracker.trackEvaluation({
|
|
2341
|
+
...options,
|
|
2342
|
+
clientName: options.clientName ?? this.options.clientName
|
|
2343
|
+
});
|
|
2344
|
+
}
|
|
2107
2345
|
};
|
|
2108
2346
|
|
|
2109
2347
|
// src/controller/auth.ts
|
|
2110
|
-
import { getVercelOidcToken } from "@vercel/oidc";
|
|
2348
|
+
import { getVercelOidcToken as getVercelOidcToken2 } from "@vercel/oidc";
|
|
2111
2349
|
|
|
2112
2350
|
// src/utils/sdk-keys.ts
|
|
2113
2351
|
var SDK_KEY_REGEX = /^vf_(?:server|client)_/;
|
|
@@ -2126,7 +2364,7 @@ function parseSdkKeyFromFlagsConnectionString(text) {
|
|
|
2126
2364
|
// src/controller/auth.ts
|
|
2127
2365
|
async function getOidcToken() {
|
|
2128
2366
|
try {
|
|
2129
|
-
return await
|
|
2367
|
+
return await getVercelOidcToken2();
|
|
2130
2368
|
} catch {
|
|
2131
2369
|
throw new Error(
|
|
2132
2370
|
[
|
|
@@ -2271,4 +2509,4 @@ export {
|
|
|
2271
2509
|
resetDefaultFlagsClient,
|
|
2272
2510
|
createClient
|
|
2273
2511
|
};
|
|
2274
|
-
//# sourceMappingURL=chunk-
|
|
2512
|
+
//# sourceMappingURL=chunk-S62BO7JU.js.map
|