@vercel/flags-core 1.5.1 → 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.
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;var __defProp = Object.defineProperty;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5;var __defProp = Object.defineProperty;
2
2
  var __export = (target, all) => {
3
3
  for (var name in all)
4
4
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -50,6 +50,22 @@ var MAX_REGEX_INPUT_LENGTH = 1e4;
50
50
  var UINT32_MAX = 4294967295;
51
51
  var SCALED_WEIGHTS = /* @__PURE__ */ Symbol("@vercel/flags-core:scaledWeights");
52
52
  var COMPILED_REGEX = /* @__PURE__ */ Symbol("@vercel/flags-core:compiledRegex");
53
+ var EVALUATION_VARIANT_INDEX = /* @__PURE__ */ Symbol(
54
+ "@vercel/flags-core:evaluationVariantIndex"
55
+ );
56
+ function withVariantIndex(value, outcomeType, variantIndex) {
57
+ return Object.defineProperty(
58
+ {
59
+ value,
60
+ outcomeType
61
+ },
62
+ EVALUATION_VARIANT_INDEX,
63
+ { value: variantIndex }
64
+ );
65
+ }
66
+ function getEvaluationVariantIndex(result) {
67
+ return result[EVALUATION_VARIANT_INDEX];
68
+ }
53
69
  function getScaledWeights(outcome) {
54
70
  const cached = outcome[SCALED_WEIGHTS];
55
71
  if (cached) return cached;
@@ -313,10 +329,11 @@ function getVariant(variants, index) {
313
329
  }
314
330
  function handleOutcome(params, outcome) {
315
331
  if (typeof outcome === "number") {
316
- return {
317
- value: getVariant(params.definition.variants, outcome),
318
- outcomeType: "value" /* VALUE */
319
- };
332
+ return withVariantIndex(
333
+ getVariant(params.definition.variants, outcome),
334
+ "value" /* VALUE */,
335
+ outcome
336
+ );
320
337
  }
321
338
  switch (outcome.type) {
322
339
  case "split": {
@@ -326,15 +343,21 @@ function handleOutcome(params, outcome) {
326
343
  outcome.defaultVariant
327
344
  );
328
345
  if (typeof lhs !== "string") {
329
- return { value: defaultOutcome, outcomeType: "split" /* SPLIT */ };
346
+ return withVariantIndex(
347
+ defaultOutcome,
348
+ "split" /* SPLIT */,
349
+ outcome.defaultVariant
350
+ );
330
351
  }
331
352
  const value = _jsxxhash.xxHash32.call(void 0, lhs, params.definition.seed);
332
353
  const scaledWeights = getScaledWeights(outcome);
333
354
  const variantIndex = findWeightedIndex(scaledWeights, value, UINT32_MAX);
334
- return {
335
- value: variantIndex === -1 ? defaultOutcome : getVariant(params.definition.variants, variantIndex),
336
- outcomeType: "split" /* SPLIT */
337
- };
355
+ const selectedVariantIndex = variantIndex === -1 ? outcome.defaultVariant : variantIndex;
356
+ return withVariantIndex(
357
+ selectedVariantIndex === outcome.defaultVariant ? defaultOutcome : getVariant(params.definition.variants, selectedVariantIndex),
358
+ "split" /* SPLIT */,
359
+ selectedVariantIndex
360
+ );
338
361
  }
339
362
  case "rollout": {
340
363
  const lhs = access(outcome.base, params);
@@ -343,18 +366,20 @@ function handleOutcome(params, outcome) {
343
366
  outcome.defaultVariant
344
367
  );
345
368
  if (typeof lhs !== "string") {
346
- return { value: defaultOutcome, outcomeType: "rollout" /* ROLLOUT */ };
369
+ return withVariantIndex(
370
+ defaultOutcome,
371
+ "rollout" /* ROLLOUT */,
372
+ outcome.defaultVariant
373
+ );
347
374
  }
348
375
  const now = Date.now();
349
376
  const elapsed = now - outcome.startTimestamp;
350
377
  if (elapsed < 0 || outcome.slots.length === 0) {
351
- return {
352
- value: getVariant(
353
- params.definition.variants,
354
- outcome.rollFromVariant
355
- ),
356
- outcomeType: "rollout" /* ROLLOUT */
357
- };
378
+ return withVariantIndex(
379
+ getVariant(params.definition.variants, outcome.rollFromVariant),
380
+ "rollout" /* ROLLOUT */,
381
+ outcome.rollFromVariant
382
+ );
358
383
  }
359
384
  let cumulativeDuration = 0;
360
385
  let currentPromille = 0;
@@ -369,32 +394,27 @@ function handleOutcome(params, outcome) {
369
394
  }
370
395
  if (exhausted) currentPromille = 1e5;
371
396
  if (currentPromille <= 0) {
372
- return {
373
- value: getVariant(
374
- params.definition.variants,
375
- outcome.rollFromVariant
376
- ),
377
- outcomeType: "rollout" /* ROLLOUT */
378
- };
397
+ return withVariantIndex(
398
+ getVariant(params.definition.variants, outcome.rollFromVariant),
399
+ "rollout" /* ROLLOUT */,
400
+ outcome.rollFromVariant
401
+ );
379
402
  }
380
403
  if (currentPromille >= 1e5) {
381
- return {
382
- value: getVariant(
383
- params.definition.variants,
384
- outcome.rollToVariant
385
- ),
386
- outcomeType: "rollout" /* ROLLOUT */
387
- };
404
+ return withVariantIndex(
405
+ getVariant(params.definition.variants, outcome.rollToVariant),
406
+ "rollout" /* ROLLOUT */,
407
+ outcome.rollToVariant
408
+ );
388
409
  }
389
410
  const value = _jsxxhash.xxHash32.call(void 0, lhs, params.definition.seed);
390
411
  const threshold = currentPromille / 1e5 * UINT32_MAX;
391
- return {
392
- value: value < threshold ? getVariant(params.definition.variants, outcome.rollToVariant) : getVariant(
393
- params.definition.variants,
394
- outcome.rollFromVariant
395
- ),
396
- outcomeType: "rollout" /* ROLLOUT */
397
- };
412
+ const selectedVariantIndex = value < threshold ? outcome.rollToVariant : outcome.rollFromVariant;
413
+ return withVariantIndex(
414
+ getVariant(params.definition.variants, selectedVariantIndex),
415
+ "rollout" /* ROLLOUT */,
416
+ selectedVariantIndex
417
+ );
398
418
  }
399
419
  default: {
400
420
  const { type } = outcome;
@@ -482,7 +502,7 @@ function findWeightedIndex(weights, value, maxValue) {
482
502
  }
483
503
 
484
504
  // package.json
485
- var version = "1.5.1";
505
+ var version = "1.6.0-7d29945-20260616152249";
486
506
 
487
507
  // src/lib/report-value.ts
488
508
  function internalReportValue(key, value, data) {
@@ -521,6 +541,14 @@ function getFallbackDatafile(id) {
521
541
  if (ds.getFallbackDatafile) return ds.getFallbackDatafile();
522
542
  throw new Error("flags: This data source does not support fallbacks");
523
543
  }
544
+ function getVariantIdentifier(definition, result) {
545
+ const variantIndex = getEvaluationVariantIndex(result);
546
+ if (variantIndex === void 0) return "unknown";
547
+ return _nullishCoalesce(_optionalChain([definition, 'access', _20 => _20.variantIds, 'optionalAccess', _21 => _21[variantIndex]]), () => ( String(variantIndex)));
548
+ }
549
+ function trackEvaluation(controller, options) {
550
+ _optionalChain([controller, 'access', _22 => _22.trackEvaluation, 'optionalCall', _23 => _23(options)]);
551
+ }
524
552
  async function evaluate2(id, flagKey, defaultValue, entities) {
525
553
  const controller = getInstance(id).controller;
526
554
  let datafile;
@@ -528,11 +556,17 @@ async function evaluate2(id, flagKey, defaultValue, entities) {
528
556
  datafile = await controller.read();
529
557
  } catch (error) {
530
558
  if (defaultValue !== void 0) {
531
- return {
559
+ const result2 = {
532
560
  value: defaultValue,
533
561
  reason: "error" /* ERROR */,
534
562
  errorMessage: error instanceof Error ? error.message : "Failed to read datafile"
535
563
  };
564
+ trackEvaluation(controller, {
565
+ flagKey,
566
+ variant: "unknown",
567
+ reason: result2.reason
568
+ });
569
+ return result2;
536
570
  }
537
571
  throw error;
538
572
  }
@@ -545,7 +579,7 @@ async function evaluate2(id, flagKey, defaultValue, entities) {
545
579
  reason: "error" /* ERROR */
546
580
  });
547
581
  }
548
- return {
582
+ const result2 = {
549
583
  value: defaultValue,
550
584
  reason: "error" /* ERROR */,
551
585
  errorCode: "FLAG_NOT_FOUND" /* FLAG_NOT_FOUND */,
@@ -559,6 +593,12 @@ async function evaluate2(id, flagKey, defaultValue, entities) {
559
593
  mode: datafile.metrics.mode
560
594
  }
561
595
  };
596
+ trackEvaluation(controller, {
597
+ flagKey,
598
+ variant: "unknown",
599
+ reason: result2.reason
600
+ });
601
+ return result2;
562
602
  }
563
603
  const evalStartTime = Date.now();
564
604
  const result = evaluate({
@@ -577,6 +617,14 @@ async function evaluate2(id, flagKey, defaultValue, entities) {
577
617
  outcomeType: result.reason !== "error" /* ERROR */ ? result.outcomeType : void 0
578
618
  });
579
619
  }
620
+ trackEvaluation(controller, {
621
+ flagKey,
622
+ variant: getVariantIdentifier(
623
+ flagDefinition,
624
+ result
625
+ ),
626
+ reason: result.reason
627
+ });
580
628
  return Object.assign(result, {
581
629
  metrics: {
582
630
  evaluationMs: evaluationDurationMs,
@@ -602,6 +650,11 @@ async function bulkEvaluate2(id, flags, entities) {
602
650
  reason: "error" /* ERROR */,
603
651
  errorMessage
604
652
  };
653
+ trackEvaluation(controller, {
654
+ flagKey: flag.key,
655
+ variant: "unknown",
656
+ reason: "error" /* ERROR */
657
+ });
605
658
  }
606
659
  return results2;
607
660
  }
@@ -633,6 +686,11 @@ async function bulkEvaluate2(id, flags, entities) {
633
686
  errorMessage: `@vercel/flags-core: Definition not found for flag "${key}"`,
634
687
  metrics: { evaluationMs: 0, ...baseMetrics }
635
688
  };
689
+ trackEvaluation(controller, {
690
+ flagKey: key,
691
+ variant: "unknown",
692
+ reason: "error" /* ERROR */
693
+ });
636
694
  continue;
637
695
  }
638
696
  toEvaluate[key] = { definition: flagDefinition, defaultValue };
@@ -654,6 +712,14 @@ async function bulkEvaluate2(id, flags, entities) {
654
712
  outcomeType: result.reason !== "error" /* ERROR */ ? result.outcomeType : void 0
655
713
  });
656
714
  }
715
+ trackEvaluation(controller, {
716
+ flagKey: key,
717
+ variant: getVariantIdentifier(
718
+ toEvaluate[key].definition,
719
+ result
720
+ ),
721
+ reason: result.reason
722
+ });
657
723
  results[key] = Object.assign(result, {
658
724
  metrics: { evaluationMs: evaluationDurationMs, ...baseMetrics }
659
725
  });
@@ -706,7 +772,7 @@ function createCreateRawClient(fns) {
706
772
  },
707
773
  getDatafile: async () => {
708
774
  const instance = controllerInstanceMap.get(id);
709
- if (_optionalChain([instance, 'optionalAccess', _20 => _20.initPromise])) {
775
+ if (_optionalChain([instance, 'optionalAccess', _24 => _24.initPromise])) {
710
776
  try {
711
777
  await instance.initPromise;
712
778
  } catch (e) {
@@ -719,7 +785,7 @@ function createCreateRawClient(fns) {
719
785
  },
720
786
  evaluate: async (flagKey, defaultValue, entities) => {
721
787
  const instance = controllerInstanceMap.get(id);
722
- if (!_optionalChain([instance, 'optionalAccess', _21 => _21.initialized])) {
788
+ if (!_optionalChain([instance, 'optionalAccess', _25 => _25.initialized])) {
723
789
  try {
724
790
  await api.initialize();
725
791
  } catch (e2) {
@@ -729,7 +795,7 @@ function createCreateRawClient(fns) {
729
795
  },
730
796
  bulkEvaluate: async (flags, entities) => {
731
797
  const instance = controllerInstanceMap.get(id);
732
- if (!_optionalChain([instance, 'optionalAccess', _22 => _22.initialized])) {
798
+ if (!_optionalChain([instance, 'optionalAccess', _26 => _26.initialized])) {
733
799
  try {
734
800
  await api.initialize();
735
801
  } catch (e3) {
@@ -798,8 +864,8 @@ async function readBundledDefinitions(auth) {
798
864
  return { definitions: null, state: "missing-entry" };
799
865
  }
800
866
 
801
- // src/utils/usage-tracker.ts
802
- var _functions = require('@vercel/functions');
867
+ // src/utils/ingest.ts
868
+ var _oidc = require('@vercel/oidc');
803
869
 
804
870
  // src/utils/backoff.ts
805
871
  var DEFAULT_BASE_MS = 250;
@@ -810,64 +876,304 @@ function getRetryDelayMs(attempt, options = {}) {
810
876
  const ceiling = Math.min(capMs, baseMs * 2 ** Math.max(0, attempt - 1));
811
877
  return Math.floor(Math.random() * ceiling);
812
878
  }
813
- function getJitteredWaitMs(baseMs, ratio) {
814
- const min = baseMs * (1 - ratio);
815
- const span = baseMs * 2 * ratio;
816
- return Math.floor(min + Math.random() * span);
817
- }
818
879
 
819
- // src/utils/usage-tracker.ts
820
- var RESOLVED_VOID = Promise.resolve();
821
- var isDebugMode = _optionalChain([process, 'access', _23 => _23.env, 'access', _24 => _24.DEBUG, 'optionalAccess', _25 => _25.includes, 'call', _26 => _26("@vercel/flags-core")]);
880
+ // src/utils/ingest.ts
881
+ var MAX_RETRIES = 3;
882
+ var EVALUATING_OIDC_TOKEN_HEADER = "X-Vercel-Flags-Evaluating-OIDC-Token";
883
+ var isDebugMode = _optionalChain([process, 'access', _27 => _27.env, 'access', _28 => _28.DEBUG, 'optionalAccess', _29 => _29.includes, 'call', _30 => _30("@vercel/flags-core")]);
822
884
  var debugLog = (...args) => {
823
885
  if (!isDebugMode) return;
824
886
  console.log(...args);
825
887
  };
826
- var MAX_RETRIES = 3;
827
- var MAX_BATCH_SIZE = 50;
828
- var MAX_BATCH_WAIT_MS = 5e3;
829
- var BATCH_WAIT_JITTER_RATIO = 0.2;
888
+ async function getEvaluatingOidcToken(auth) {
889
+ if (!auth.sdkKey) return void 0;
890
+ try {
891
+ return await _oidc.getVercelOidcToken.call(void 0, );
892
+ } catch (e4) {
893
+ return void 0;
894
+ }
895
+ }
896
+ async function getIngestHeaders(options) {
897
+ const token = await options.auth.resolveToken();
898
+ const evaluatingOidcToken = await getEvaluatingOidcToken(options.auth);
899
+ return {
900
+ "Content-Type": "application/json",
901
+ Authorization: `Bearer ${token}`,
902
+ "User-Agent": `VercelFlagsCore/${version}`,
903
+ ...process.env.VERCEL_ENV ? { "X-Vercel-Env": process.env.VERCEL_ENV } : null,
904
+ ...evaluatingOidcToken ? { [EVALUATING_OIDC_TOKEN_HEADER]: evaluatingOidcToken } : null,
905
+ ...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
906
+ };
907
+ }
908
+ async function sendIngestEvents(options, events, flushId) {
909
+ const eventsToSend = events.map((event) => event.ingestEvent());
910
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
911
+ try {
912
+ const response = await options.fetch(`${options.host}/v1/ingest`, {
913
+ method: "POST",
914
+ headers: await getIngestHeaders(options),
915
+ body: JSON.stringify(eventsToSend)
916
+ });
917
+ debugLog(
918
+ `@vercel/flags-core: Ingest response ${response.status} for ${eventsToSend.length} events on ${response.headers.get("x-vercel-id")}`
919
+ );
920
+ if (response.ok) {
921
+ break;
922
+ }
923
+ throw new Error(
924
+ `Ingest endpoint responded with status ${response.status} for ${eventsToSend.length} events on request ${response.headers.get("x-vercel-id")}.
925
+ Response body: ${await response.text().catch(() => null)}`
926
+ );
927
+ } catch (error) {
928
+ console.error(
929
+ `@vercel/flags-core: Error sending events (attempt=${attempt}/${MAX_RETRIES} flushId=${flushId}):`,
930
+ error
931
+ );
932
+ if (attempt < MAX_RETRIES) {
933
+ const delayMs = getRetryDelayMs(attempt);
934
+ await new Promise((res) => setTimeout(res, delayMs));
935
+ } else {
936
+ console.error(
937
+ `@vercel/flags-core: Dropped ${eventsToSend.length} events after ${MAX_RETRIES} attempts (flushId=${flushId})`
938
+ );
939
+ }
940
+ }
941
+ }
942
+ }
943
+
944
+ // src/utils/request-context.ts
830
945
  var SYMBOL_FOR_REQ_CONTEXT = /* @__PURE__ */ Symbol.for("@vercel/request-context");
831
946
  var fromSymbol = globalThis;
832
947
  function getRequestContext() {
833
948
  try {
834
- const ctx = _optionalChain([fromSymbol, 'access', _27 => _27[SYMBOL_FOR_REQ_CONTEXT], 'optionalAccess', _28 => _28.get, 'optionalCall', _29 => _29()]);
949
+ const ctx = _optionalChain([fromSymbol, 'access', _31 => _31[SYMBOL_FOR_REQ_CONTEXT], 'optionalAccess', _32 => _32.get, 'optionalCall', _33 => _33()]);
835
950
  if (ctx && Object.hasOwn(ctx, "headers")) {
836
951
  return {
837
952
  ctx,
838
953
  headers: ctx.headers
839
954
  };
840
955
  }
841
- return { ctx, headers: void 0 };
842
- } catch (e4) {
956
+ return { ctx: void 0, headers: void 0 };
957
+ } catch (e5) {
843
958
  return { ctx: void 0, headers: void 0 };
844
959
  }
845
960
  }
846
- var UsageTracker = (_class = class {
847
- __init() {this.flushCounter = 0}
961
+
962
+ // src/utils/scheduler.ts
963
+ var _functions = require('@vercel/functions');
964
+ var MAX_COUNT = 50;
965
+ var IDLE_FLUSH_WAIT_MS = 5e3;
966
+ var MAX_FLUSH_WAIT_MS = 6e4;
967
+ var Scheduler = (_class = class {
968
+ constructor(onFlush) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);
969
+ this.onFlush = onFlush;
970
+ }
971
+ __init() {this.count = 0}
972
+ __init2() {this.resolveWait = null}
973
+ __init3() {this.pending = null}
974
+ __init4() {this.idleTimeout = null}
975
+ __init5() {this.maxTimeout = null}
976
+ increment() {
977
+ this.count += 1;
978
+ if (this.count >= MAX_COUNT) {
979
+ this.resolveScheduledFlush();
980
+ }
981
+ }
982
+ scheduleFlush() {
983
+ if (!this.pending) {
984
+ this.pending = (async () => {
985
+ await new Promise((res) => {
986
+ this.resolveWait = res;
987
+ });
988
+ this.reset();
989
+ await this.onFlush();
990
+ })();
991
+ try {
992
+ _functions.waitUntil.call(void 0, this.pending);
993
+ } catch (e6) {
994
+ }
995
+ this.maxTimeout = setTimeout(
996
+ () => this.resolveScheduledFlush(),
997
+ MAX_FLUSH_WAIT_MS
998
+ );
999
+ }
1000
+ this.resetIdleTimeout();
1001
+ }
1002
+ /**
1003
+ * Resolves any in-flight scheduled flush and waits for its ingest to finish.
1004
+ */
1005
+ async shutdown() {
1006
+ this.clearTimeouts();
1007
+ const pending = this.pending;
1008
+ _optionalChain([this, 'access', _34 => _34.resolveWait, 'optionalCall', _35 => _35()]);
1009
+ if (pending) await pending;
1010
+ }
1011
+ resetIdleTimeout() {
1012
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
1013
+ this.idleTimeout = setTimeout(
1014
+ () => this.resolveScheduledFlush(),
1015
+ IDLE_FLUSH_WAIT_MS
1016
+ );
1017
+ }
1018
+ resolveScheduledFlush() {
1019
+ this.clearTimeouts();
1020
+ _optionalChain([this, 'access', _36 => _36.resolveWait, 'optionalCall', _37 => _37()]);
1021
+ }
1022
+ reset() {
1023
+ this.pending = null;
1024
+ this.resolveWait = null;
1025
+ this.count = 0;
1026
+ this.clearTimeouts();
1027
+ }
1028
+ clearTimeouts() {
1029
+ if (this.idleTimeout) {
1030
+ clearTimeout(this.idleTimeout);
1031
+ this.idleTimeout = null;
1032
+ }
1033
+ if (this.maxTimeout) {
1034
+ clearTimeout(this.maxTimeout);
1035
+ this.maxTimeout = null;
1036
+ }
1037
+ }
1038
+ }, _class);
1039
+
1040
+ // src/utils/usage/flags-config-read.ts
1041
+ var FlagsConfigReadEvent = class {
848
1042
 
849
- __init2() {this.trackedRequests = /* @__PURE__ */ new WeakSet()}
850
- __init3() {this.batcher = {
851
- events: [],
852
- resolveWait: null,
853
- pending: null
854
- }}
855
- constructor(options) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);
1043
+ constructor(headers, options) {
1044
+ this.payload = {
1045
+ deploymentId: process.env.VERCEL_DEPLOYMENT_ID,
1046
+ region: process.env.VERCEL_REGION
1047
+ };
1048
+ if (headers) {
1049
+ this.payload.vercelRequestId = _nullishCoalesce(headers["x-vercel-id"], () => ( void 0));
1050
+ this.payload.invocationHost = _nullishCoalesce(headers.host, () => ( void 0));
1051
+ }
1052
+ if (options) {
1053
+ this.payload.configOrigin = options.configOrigin;
1054
+ if (options.cacheStatus !== void 0) {
1055
+ this.payload.cacheStatus = options.cacheStatus;
1056
+ }
1057
+ if (options.cacheAction !== void 0) {
1058
+ this.payload.cacheAction = options.cacheAction;
1059
+ }
1060
+ if (options.cacheIsFirstRead !== void 0) {
1061
+ this.payload.cacheIsFirstRead = options.cacheIsFirstRead;
1062
+ }
1063
+ if (options.cacheIsBlocking !== void 0) {
1064
+ this.payload.cacheIsBlocking = options.cacheIsBlocking;
1065
+ }
1066
+ if (options.duration !== void 0) {
1067
+ this.payload.duration = options.duration;
1068
+ }
1069
+ if (options.configUpdatedAt !== void 0) {
1070
+ this.payload.configUpdatedAt = options.configUpdatedAt;
1071
+ }
1072
+ if (options.mode !== void 0) {
1073
+ this.payload.mode = options.mode;
1074
+ }
1075
+ if (options.revision !== void 0) {
1076
+ this.payload.revision = String(options.revision);
1077
+ }
1078
+ }
1079
+ const environment = process.env.VERCEL_ENV || process.env.NODE_ENV || void 0;
1080
+ if (environment) {
1081
+ this.payload.environment = environment;
1082
+ }
1083
+ }
1084
+ ingestEvent() {
1085
+ return {
1086
+ type: "FLAGS_CONFIG_READ",
1087
+ ts: Date.now(),
1088
+ payload: this.payload
1089
+ };
1090
+ }
1091
+ };
1092
+
1093
+ // src/utils/usage/flags-evaluation.ts
1094
+ function evaluationBatchKey(options) {
1095
+ return JSON.stringify([
1096
+ options.flagKey,
1097
+ options.variant,
1098
+ options.reason,
1099
+ _nullishCoalesce(options.clientName, () => ( null))
1100
+ ]);
1101
+ }
1102
+ var FlagsEvaluationEvent = class {
1103
+
1104
+ constructor(eventOptions) {
1105
+ this.payload = {
1106
+ flagKey: eventOptions.flagKey,
1107
+ variant: eventOptions.variant,
1108
+ reason: eventOptions.reason,
1109
+ count: 1
1110
+ };
1111
+ if (eventOptions.clientName) {
1112
+ this.payload.clientName = eventOptions.clientName;
1113
+ }
1114
+ Object.defineProperty(this, "batchKey", {
1115
+ value: JSON.stringify([
1116
+ this.payload.flagKey,
1117
+ this.payload.variant,
1118
+ this.payload.reason,
1119
+ _nullishCoalesce(this.payload.clientName, () => ( null))
1120
+ ]),
1121
+ enumerable: false
1122
+ });
1123
+ }
1124
+ increment() {
1125
+ this.payload.count += 1;
1126
+ }
1127
+ ingestEvent() {
1128
+ return {
1129
+ type: "FLAGS_EVALUATION",
1130
+ ts: Date.now(),
1131
+ payload: this.payload
1132
+ };
1133
+ }
1134
+ };
1135
+
1136
+ // src/utils/weak-map.ts
1137
+ var BetterWeakMap = (_class2 = class {constructor() { _class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this); }
1138
+ __init6() {this._values = []}
1139
+ __init7() {this._keys = []}
1140
+ set(key, value) {
1141
+ this._keys.push(key);
1142
+ this._values.push(value);
1143
+ }
1144
+ values() {
1145
+ return this._values;
1146
+ }
1147
+ has(key) {
1148
+ return this._keys.includes(key);
1149
+ }
1150
+ keys() {
1151
+ return this._keys;
1152
+ }
1153
+ clear() {
1154
+ this._values = [];
1155
+ this._keys = [];
1156
+ }
1157
+ }, _class2);
1158
+
1159
+ // src/utils/usage-tracker.ts
1160
+ var UsageTracker = (_class3 = class {
1161
+ __init8() {this.flushCount = 0}
1162
+
1163
+
1164
+ __init9() {this.readEvents = new BetterWeakMap()}
1165
+ __init10() {this.evaluationEvents = /* @__PURE__ */ new Map()}
1166
+ constructor(options) {;_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);
856
1167
  this.options = options;
1168
+ this.scheduler = new Scheduler(() => this.flushEvents());
857
1169
  }
858
1170
  /**
859
1171
  * Triggers an immediate flush of any pending events.
860
1172
  * Returns a promise that resolves when the flush completes.
861
1173
  */
862
- flush() {
863
- if (this.batcher.pending) {
864
- _optionalChain([this, 'access', _30 => _30.batcher, 'access', _31 => _31.resolveWait, 'optionalCall', _32 => _32()]);
865
- return this.batcher.pending;
866
- }
867
- if (this.batcher.events.length > 0) {
868
- return this.flushEvents();
869
- }
870
- return RESOLVED_VOID;
1174
+ async shutdown() {
1175
+ await this.scheduler.shutdown();
1176
+ await this.flushEvents();
871
1177
  }
872
1178
  /**
873
1179
  * Tracks a config read event. Deduplicates by request context.
@@ -876,132 +1182,53 @@ var UsageTracker = (_class = class {
876
1182
  try {
877
1183
  const { ctx, headers } = getRequestContext();
878
1184
  if (!ctx) return;
879
- if (this.trackedRequests.has(ctx)) return;
880
- this.trackedRequests.add(ctx);
881
- const event = {
882
- type: "FLAGS_CONFIG_READ",
883
- ts: Date.now(),
884
- payload: {
885
- deploymentId: process.env.VERCEL_DEPLOYMENT_ID,
886
- region: process.env.VERCEL_REGION
887
- }
888
- };
889
- if (headers) {
890
- event.payload.vercelRequestId = _nullishCoalesce(headers["x-vercel-id"], () => ( void 0));
891
- event.payload.invocationHost = _nullishCoalesce(headers.host, () => ( void 0));
892
- }
893
- if (options) {
894
- event.payload.configOrigin = options.configOrigin;
895
- if (options.cacheStatus !== void 0) {
896
- event.payload.cacheStatus = options.cacheStatus;
897
- }
898
- if (options.cacheAction !== void 0) {
899
- event.payload.cacheAction = options.cacheAction;
900
- }
901
- if (options.cacheIsFirstRead !== void 0) {
902
- event.payload.cacheIsFirstRead = options.cacheIsFirstRead;
903
- }
904
- if (options.cacheIsBlocking !== void 0) {
905
- event.payload.cacheIsBlocking = options.cacheIsBlocking;
906
- }
907
- if (options.duration !== void 0) {
908
- event.payload.duration = options.duration;
909
- }
910
- if (options.configUpdatedAt !== void 0) {
911
- event.payload.configUpdatedAt = options.configUpdatedAt;
912
- }
913
- if (options.mode !== void 0) {
914
- event.payload.mode = options.mode;
915
- }
916
- if (options.revision !== void 0) {
917
- event.payload.revision = String(options.revision);
918
- }
919
- }
920
- const environment = process.env.VERCEL_ENV || process.env.NODE_ENV || void 0;
921
- if (environment) {
922
- event.payload.environment = environment;
1185
+ if (this.readEvents.has(ctx)) {
1186
+ return;
923
1187
  }
924
- this.batcher.events.push(event);
925
- this.scheduleFlush();
1188
+ this.readEvents.set(ctx, new FlagsConfigReadEvent(headers, options));
1189
+ this.scheduler.scheduleFlush();
1190
+ this.scheduler.increment();
926
1191
  } catch (error) {
927
1192
  console.error("@vercel/flags-core: Failed to record event:", error);
928
1193
  }
929
1194
  }
930
- scheduleFlush() {
931
- if (!this.batcher.pending) {
932
- let timeout = null;
933
- const pending = (async () => {
934
- await new Promise((res) => {
935
- this.batcher.resolveWait = res;
936
- timeout = setTimeout(
937
- res,
938
- getJitteredWaitMs(MAX_BATCH_WAIT_MS, BATCH_WAIT_JITTER_RATIO)
939
- );
940
- });
941
- this.batcher.pending = null;
942
- this.batcher.resolveWait = null;
943
- if (timeout) clearTimeout(timeout);
944
- await this.flushEvents();
945
- })();
946
- try {
947
- _functions.waitUntil.call(void 0, pending);
948
- } catch (e5) {
1195
+ /**
1196
+ * Tracks a flag evaluation event.
1197
+ */
1198
+ trackEvaluation(options) {
1199
+ try {
1200
+ const batchKey = evaluationBatchKey(options);
1201
+ const existingEvent = this.evaluationEvents.get(batchKey);
1202
+ if (existingEvent) {
1203
+ existingEvent.increment();
1204
+ } else {
1205
+ this.evaluationEvents.set(batchKey, new FlagsEvaluationEvent(options));
1206
+ this.scheduler.increment();
949
1207
  }
950
- this.batcher.pending = pending;
951
- }
952
- if (this.batcher.events.length >= MAX_BATCH_SIZE) {
953
- _optionalChain([this, 'access', _33 => _33.batcher, 'access', _34 => _34.resolveWait, 'optionalCall', _35 => _35()]);
1208
+ this.scheduler.scheduleFlush();
1209
+ } catch (error) {
1210
+ console.error(
1211
+ "@vercel/flags-core: Failed to record evaluation event:",
1212
+ error
1213
+ );
954
1214
  }
955
1215
  }
1216
+ /**
1217
+ * Send all events to the ingest service
1218
+ */
956
1219
  async flushEvents() {
957
- if (this.batcher.events.length === 0) return;
958
- const eventsToSend = this.batcher.events;
959
- this.batcher.events = [];
960
- const flushId = ++this.flushCounter;
961
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
962
- try {
963
- const token = await this.options.auth.resolveToken();
964
- const response = await this.options.fetch(
965
- `${this.options.host}/v1/ingest`,
966
- {
967
- method: "POST",
968
- headers: {
969
- "Content-Type": "application/json",
970
- Authorization: `Bearer ${token}`,
971
- "User-Agent": `VercelFlagsCore/${version}`,
972
- ...process.env.VERCEL_ENV ? { "X-Vercel-Env": process.env.VERCEL_ENV } : null,
973
- ...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
974
- },
975
- body: JSON.stringify(eventsToSend)
976
- }
977
- );
978
- debugLog(
979
- `@vercel/flags-core: Ingest response ${response.status} for ${eventsToSend.length} events on ${response.headers.get("x-vercel-id")}`
980
- );
981
- if (response.ok) {
982
- break;
983
- }
984
- throw new Error(
985
- `Ingest endpoint responded with status ${response.status} for ${eventsToSend.length} events on request ${response.headers.get("x-vercel-id")}.
986
- Response body: ${await response.text().catch(() => null)}`
987
- );
988
- } catch (error) {
989
- console.error(
990
- `@vercel/flags-core: Error sending events (attempt=${attempt}/${MAX_RETRIES} flushId=${flushId}):`,
991
- error
992
- );
993
- if (attempt < MAX_RETRIES) {
994
- const delayMs = getRetryDelayMs(attempt);
995
- await new Promise((res) => setTimeout(res, delayMs));
996
- } else {
997
- console.error(
998
- `@vercel/flags-core: Dropped ${eventsToSend.length} events after ${MAX_RETRIES} attempts (flushId=${flushId})`
999
- );
1000
- }
1001
- }
1002
- }
1220
+ const events = [
1221
+ ...this.readEvents.values(),
1222
+ ...this.evaluationEvents.values()
1223
+ ];
1224
+ if (events.length === 0) return;
1225
+ this.flushCount += 1;
1226
+ const flushId = this.flushCount;
1227
+ this.readEvents.clear();
1228
+ this.evaluationEvents.clear();
1229
+ await sendIngestEvents(this.options, events, flushId);
1003
1230
  }
1004
- }, _class);
1231
+ }, _class3);
1005
1232
 
1006
1233
  // src/errors.ts
1007
1234
  var FallbackNotFoundError = class extends Error {
@@ -1060,7 +1287,7 @@ var BundledSource = class {
1060
1287
  */
1061
1288
  async tryLoad() {
1062
1289
  const result = await this.getResult();
1063
- if (_optionalChain([result, 'optionalAccess', _36 => _36.state]) === "ok" && result.definitions) {
1290
+ if (_optionalChain([result, 'optionalAccess', _38 => _38.state]) === "ok" && result.definitions) {
1064
1291
  return result.definitions;
1065
1292
  }
1066
1293
  return void 0;
@@ -1100,14 +1327,14 @@ async function fetchDatafile(options) {
1100
1327
  signal: controller.signal
1101
1328
  });
1102
1329
  clearTimeout(timeoutId);
1103
- _optionalChain([options, 'access', _37 => _37.signal, 'optionalAccess', _38 => _38.removeEventListener, 'call', _39 => _39("abort", onExternalAbort)]);
1330
+ _optionalChain([options, 'access', _39 => _39.signal, 'optionalAccess', _40 => _40.removeEventListener, 'call', _41 => _41("abort", onExternalAbort)]);
1104
1331
  if (!res.ok) {
1105
1332
  throw new Error(`Failed to fetch data: ${res.statusText}`);
1106
1333
  }
1107
1334
  return res.json();
1108
1335
  } catch (error) {
1109
1336
  clearTimeout(timeoutId);
1110
- _optionalChain([options, 'access', _40 => _40.signal, 'optionalAccess', _41 => _41.removeEventListener, 'call', _42 => _42("abort", onExternalAbort)]);
1337
+ _optionalChain([options, 'access', _42 => _42.signal, 'optionalAccess', _43 => _43.removeEventListener, 'call', _44 => _44("abort", onExternalAbort)]);
1111
1338
  throw error instanceof Error ? error : new Error("Unknown fetch error");
1112
1339
  }
1113
1340
  }
@@ -1156,13 +1383,14 @@ function normalizeOptions(options) {
1156
1383
  polling,
1157
1384
  buildStep,
1158
1385
  fetch: _nullishCoalesce(options.fetch, () => ( globalThis.fetch)),
1159
- host: "https://flags.vercel.com"
1386
+ host: "https://flags.vercel.com",
1387
+ clientName: options.clientName
1160
1388
  };
1161
1389
  }
1162
1390
 
1163
1391
  // src/controller/typed-emitter.ts
1164
- var TypedEmitter = (_class2 = class {constructor() { _class2.prototype.__init4.call(this); }
1165
- __init4() {this.handlers = /* @__PURE__ */ new Map()}
1392
+ var TypedEmitter = (_class4 = class {constructor() { _class4.prototype.__init11.call(this); }
1393
+ __init11() {this.handlers = /* @__PURE__ */ new Map()}
1166
1394
  on(event, handler) {
1167
1395
  let set = this.handlers.get(event);
1168
1396
  if (!set) {
@@ -1172,7 +1400,7 @@ var TypedEmitter = (_class2 = class {constructor() { _class2.prototype.__init4.c
1172
1400
  set.add(handler);
1173
1401
  }
1174
1402
  off(event, handler) {
1175
- _optionalChain([this, 'access', _43 => _43.handlers, 'access', _44 => _44.get, 'call', _45 => _45(event), 'optionalAccess', _46 => _46.delete, 'call', _47 => _47(handler)]);
1403
+ _optionalChain([this, 'access', _45 => _45.handlers, 'access', _46 => _46.get, 'call', _47 => _47(event), 'optionalAccess', _48 => _48.delete, 'call', _49 => _49(handler)]);
1176
1404
  }
1177
1405
  emit(event, ...args) {
1178
1406
  const set = this.handlers.get(event);
@@ -1182,7 +1410,7 @@ var TypedEmitter = (_class2 = class {constructor() { _class2.prototype.__init4.c
1182
1410
  }
1183
1411
  }
1184
1412
  }
1185
- }, _class2);
1413
+ }, _class4);
1186
1414
 
1187
1415
  // src/controller/polling-source.ts
1188
1416
  var PollingSource = class extends TypedEmitter {
@@ -1198,11 +1426,11 @@ var PollingSource = class extends TypedEmitter {
1198
1426
  * Emits 'data' on success, 'error' on failure.
1199
1427
  */
1200
1428
  async poll() {
1201
- if (_optionalChain([this, 'access', _48 => _48.abortController, 'optionalAccess', _49 => _49.signal, 'access', _50 => _50.aborted])) return;
1429
+ if (_optionalChain([this, 'access', _50 => _50.abortController, 'optionalAccess', _51 => _51.signal, 'access', _52 => _52.aborted])) return;
1202
1430
  try {
1203
1431
  const data = await fetchDatafile({
1204
1432
  ...this.config,
1205
- signal: _optionalChain([this, 'access', _51 => _51.abortController, 'optionalAccess', _52 => _52.signal])
1433
+ signal: _optionalChain([this, 'access', _53 => _53.abortController, 'optionalAccess', _54 => _54.signal])
1206
1434
  });
1207
1435
  this.emit("data", data);
1208
1436
  } catch (error) {
@@ -1231,7 +1459,7 @@ var PollingSource = class extends TypedEmitter {
1231
1459
  clearInterval(this.intervalId);
1232
1460
  this.intervalId = void 0;
1233
1461
  }
1234
- _optionalChain([this, 'access', _53 => _53.abortController, 'optionalAccess', _54 => _54.abort, 'call', _55 => _55()]);
1462
+ _optionalChain([this, 'access', _55 => _55.abortController, 'optionalAccess', _56 => _56.abort, 'call', _57 => _57()]);
1235
1463
  this.abortController = void 0;
1236
1464
  }
1237
1465
  };
@@ -1279,13 +1507,9 @@ async function connectStream(config, callbacks) {
1279
1507
  });
1280
1508
  void (async () => {
1281
1509
  let initialDataReceived = false;
1282
- let lastError;
1283
1510
  while (!abortController.signal.aborted) {
1284
1511
  if (retryCount > MAX_RETRY_COUNT) {
1285
- console.error(
1286
- "@vercel/flags-core: Max retry count exceeded",
1287
- _nullishCoalesce(lastError, () => ( "stream closed repeatedly without an error"))
1288
- );
1512
+ console.error("@vercel/flags-core: Max retry count exceeded");
1289
1513
  if (!initialDataReceived) {
1290
1514
  rejectInit(
1291
1515
  new Error("stream: max retry count exceeded before receiving data")
@@ -1305,7 +1529,7 @@ async function connectStream(config, callbacks) {
1305
1529
  if (pingTimeoutId !== void 0) clearTimeout(pingTimeoutId);
1306
1530
  if (!initialDataReceived) return;
1307
1531
  pingTimeoutId = setTimeout(() => {
1308
- _optionalChain([responseBody, 'optionalAccess', _56 => _56.cancel, 'call', _57 => _57(), 'access', _58 => _58.catch, 'call', _59 => _59(() => {
1532
+ _optionalChain([responseBody, 'optionalAccess', _58 => _58.cancel, 'call', _59 => _59(), 'access', _60 => _60.catch, 'call', _61 => _61(() => {
1309
1533
  })]);
1310
1534
  connectionAbort.abort();
1311
1535
  }, PING_TIMEOUT_MS);
@@ -1324,7 +1548,7 @@ async function connectStream(config, callbacks) {
1324
1548
  if (vercelEnv) {
1325
1549
  headers["X-Vercel-Env"] = vercelEnv;
1326
1550
  }
1327
- const revision = _optionalChain([config, 'access', _60 => _60.revision, 'optionalCall', _61 => _61()]);
1551
+ const revision = _optionalChain([config, 'access', _62 => _62.revision, 'optionalCall', _63 => _63()]);
1328
1552
  if (revision !== void 0) {
1329
1553
  headers["X-Revision"] = String(revision);
1330
1554
  }
@@ -1370,7 +1594,7 @@ async function connectStream(config, callbacks) {
1370
1594
  let message;
1371
1595
  try {
1372
1596
  message = JSON.parse(line);
1373
- } catch (e6) {
1597
+ } catch (e7) {
1374
1598
  console.warn(
1375
1599
  "@vercel/flags-core: Failed to parse stream message, skipping"
1376
1600
  );
@@ -1386,7 +1610,7 @@ async function connectStream(config, callbacks) {
1386
1610
  resetPingTimeout();
1387
1611
  }
1388
1612
  if (message.type === "primed") {
1389
- _optionalChain([onPrimed, 'optionalCall', _62 => _62(message)]);
1613
+ _optionalChain([onPrimed, 'optionalCall', _64 => _64(message)]);
1390
1614
  retryCount = 0;
1391
1615
  if (!initialDataReceived) {
1392
1616
  initialDataReceived = true;
@@ -1409,7 +1633,7 @@ async function connectStream(config, callbacks) {
1409
1633
  clearTimeout(pingTimeoutId);
1410
1634
  abortController.signal.removeEventListener("abort", onMainAbort);
1411
1635
  if (!abortController.signal.aborted) {
1412
- _optionalChain([onDisconnect, 'optionalCall', _63 => _63()]);
1636
+ _optionalChain([onDisconnect, 'optionalCall', _65 => _65()]);
1413
1637
  retryCount++;
1414
1638
  const elapsed = Date.now() - lastAttemptTime;
1415
1639
  const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
@@ -1428,9 +1652,9 @@ async function connectStream(config, callbacks) {
1428
1652
  break;
1429
1653
  }
1430
1654
  if (!connectionAbort.signal.aborted) {
1431
- lastError = error;
1655
+ console.error("@vercel/flags-core: Stream error", error);
1432
1656
  }
1433
- _optionalChain([onDisconnect, 'optionalCall', _64 => _64()]);
1657
+ _optionalChain([onDisconnect, 'optionalCall', _66 => _66()]);
1434
1658
  retryCount++;
1435
1659
  const elapsed = Date.now() - lastAttemptTime;
1436
1660
  const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
@@ -1509,7 +1733,7 @@ var StreamSource = class extends TypedEmitter {
1509
1733
  * Stop the stream connection.
1510
1734
  */
1511
1735
  stop() {
1512
- _optionalChain([this, 'access', _65 => _65.abortController, 'optionalAccess', _66 => _66.abort, 'call', _67 => _67()]);
1736
+ _optionalChain([this, 'access', _67 => _67.abortController, 'optionalAccess', _68 => _68.abort, 'call', _69 => _69()]);
1513
1737
  this.abortController = void 0;
1514
1738
  this.promise = void 0;
1515
1739
  }
@@ -1541,34 +1765,34 @@ function parseConfigUpdatedAt(value) {
1541
1765
  }
1542
1766
  return void 0;
1543
1767
  }
1544
- var Controller = (_class3 = class {
1768
+ var Controller = (_class5 = class {
1545
1769
 
1546
1770
  // State machine
1547
- __init5() {this.state = "idle"}
1771
+ __init12() {this.state = "idle"}
1548
1772
  // Data state — tagged with origin
1549
1773
 
1550
1774
  // Memoized data spread for read() / getDatafile().
1551
1775
  // Rebuilt only when `this.data` reference changes (e.g. on stream/poll update).
1552
1776
  // Holds the result of stripping `_origin`; metrics are appended per-call.
1553
- __init6() {this.dataViewSource = void 0}
1554
- __init7() {this.dataViewBase = void 0}
1777
+ __init13() {this.dataViewSource = void 0}
1778
+ __init14() {this.dataViewBase = void 0}
1555
1779
  // Sources (I/O delegates)
1556
1780
 
1557
1781
 
1558
1782
 
1559
1783
  // Usage tracking
1560
1784
 
1561
- __init8() {this.isFirstGetData = true}
1785
+ __init15() {this.isFirstGetData = true}
1562
1786
  // Build-step deduplication
1563
- __init9() {this.buildDataPromise = null}
1564
- __init10() {this.buildReadTracked = false}
1787
+ __init16() {this.buildDataPromise = null}
1788
+ __init17() {this.buildReadTracked = false}
1565
1789
  // Suppresses usage tracking when the SDK key is unauthorized
1566
- __init11() {this.unauthorized = false}
1567
- 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);
1790
+ __init18() {this.unauthorized = false}
1791
+ constructor(options) {;_class5.prototype.__init12.call(this);_class5.prototype.__init13.call(this);_class5.prototype.__init14.call(this);_class5.prototype.__init15.call(this);_class5.prototype.__init16.call(this);_class5.prototype.__init17.call(this);_class5.prototype.__init18.call(this);_class5.prototype.__init19.call(this);_class5.prototype.__init20.call(this);_class5.prototype.__init21.call(this);_class5.prototype.__init22.call(this);_class5.prototype.__init23.call(this);_class5.prototype.__init24.call(this);
1568
1792
  this.options = normalizeOptions(options);
1569
1793
  this.streamSource = new StreamSource(
1570
1794
  this.options,
1571
- () => _optionalChain([this, 'access', _68 => _68.data, 'optionalAccess', _69 => _69.revision])
1795
+ () => _optionalChain([this, 'access', _70 => _70.data, 'optionalAccess', _71 => _71.revision])
1572
1796
  );
1573
1797
  this.pollingSource = new PollingSource(this.options);
1574
1798
  this.bundledSource = new BundledSource({
@@ -1582,32 +1806,32 @@ var Controller = (_class3 = class {
1582
1806
  this.usageTracker = new UsageTracker(this.options);
1583
1807
  }
1584
1808
  // Source event handlers (stored for cleanup)
1585
- __init12() {this.onStreamData = (data) => {
1809
+ __init19() {this.onStreamData = (data) => {
1586
1810
  if (this.isNewerData(data)) {
1587
1811
  this.data = tagData(data, "stream");
1588
1812
  }
1589
1813
  }}
1590
- __init13() {this.onStreamPrimed = () => {
1814
+ __init20() {this.onStreamPrimed = () => {
1591
1815
  if (this.state === "degraded" || this.state === "initializing:stream") {
1592
1816
  this.transition("streaming");
1593
1817
  }
1594
1818
  }}
1595
- __init14() {this.onStreamConnected = () => {
1819
+ __init21() {this.onStreamConnected = () => {
1596
1820
  if (this.state === "degraded" || this.state === "initializing:stream") {
1597
1821
  this.transition("streaming");
1598
1822
  }
1599
1823
  }}
1600
- __init15() {this.onStreamDisconnected = () => {
1824
+ __init22() {this.onStreamDisconnected = () => {
1601
1825
  if (this.state === "streaming") {
1602
1826
  this.transition("degraded");
1603
1827
  }
1604
1828
  }}
1605
- __init16() {this.onPollData = (data) => {
1829
+ __init23() {this.onPollData = (data) => {
1606
1830
  if (this.isNewerData(data)) {
1607
1831
  this.data = tagData(data, "poll");
1608
1832
  }
1609
1833
  }}
1610
- __init17() {this.onPollError = (error) => {
1834
+ __init24() {this.onPollError = (error) => {
1611
1835
  console.error("@vercel/flags-core: Poll failed:", error);
1612
1836
  }}
1613
1837
  // ---------------------------------------------------------------------------
@@ -1676,7 +1900,7 @@ var Controller = (_class3 = class {
1676
1900
  if (bundled) {
1677
1901
  this.data = tagData(bundled, "bundled");
1678
1902
  }
1679
- } catch (e7) {
1903
+ } catch (e8) {
1680
1904
  }
1681
1905
  }
1682
1906
  if (this.data) {
@@ -1745,7 +1969,7 @@ var Controller = (_class3 = class {
1745
1969
  this.pollingSource.stop();
1746
1970
  this.data = this.options.datafile ? tagData(this.options.datafile, "provided") : void 0;
1747
1971
  this.transition("shutdown");
1748
- await this.usageTracker.flush();
1972
+ await this.usageTracker.shutdown();
1749
1973
  }
1750
1974
  /**
1751
1975
  * Returns the datafile with metrics.
@@ -1778,7 +2002,7 @@ var Controller = (_class3 = class {
1778
2002
  this.data = tagData(fetched, "fetched");
1779
2003
  result = this.data;
1780
2004
  cacheStatus = "MISS";
1781
- } catch (e8) {
2005
+ } catch (e9) {
1782
2006
  throw new Error(
1783
2007
  "@vercel/flags-core: No flag definitions available. Initialize the client or provide a datafile."
1784
2008
  );
@@ -1863,7 +2087,7 @@ var Controller = (_class3 = class {
1863
2087
  clearTimeout(timeoutId);
1864
2088
  if (result === "timeout") {
1865
2089
  console.warn(
1866
- "@vercel/flags-core: Stream initialization timeout, falling back while continuing to connect in the background"
2090
+ "@vercel/flags-core: Stream initialization timeout, falling back"
1867
2091
  );
1868
2092
  void this.streamSource.start().catch(() => {
1869
2093
  });
@@ -1897,7 +2121,7 @@ var Controller = (_class3 = class {
1897
2121
  return true;
1898
2122
  }
1899
2123
  return false;
1900
- } catch (e9) {
2124
+ } catch (e10) {
1901
2125
  return false;
1902
2126
  }
1903
2127
  }
@@ -1913,7 +2137,7 @@ var Controller = (_class3 = class {
1913
2137
  clearTimeout(timeoutId);
1914
2138
  if (result === "timeout") {
1915
2139
  console.warn(
1916
- "@vercel/flags-core: Polling initialization timeout, falling back while continuing to poll in the background"
2140
+ "@vercel/flags-core: Polling initialization timeout, falling back"
1917
2141
  );
1918
2142
  return false;
1919
2143
  }
@@ -1922,7 +2146,7 @@ var Controller = (_class3 = class {
1922
2146
  return true;
1923
2147
  }
1924
2148
  return false;
1925
- } catch (e10) {
2149
+ } catch (e11) {
1926
2150
  clearTimeout(timeoutId);
1927
2151
  return false;
1928
2152
  }
@@ -1972,7 +2196,7 @@ var Controller = (_class3 = class {
1972
2196
  fetch: this.options.fetch
1973
2197
  });
1974
2198
  return tagData(fetched, "fetched");
1975
- } catch (e11) {
2199
+ } catch (e12) {
1976
2200
  }
1977
2201
  throw new Error(
1978
2202
  "@vercel/flags-core: No flag definitions available during build. Provide a datafile or bundled definitions."
@@ -2006,7 +2230,7 @@ var Controller = (_class3 = class {
2006
2230
  this.data = tagData(fetched, "fetched");
2007
2231
  this.transition("degraded");
2008
2232
  return;
2009
- } catch (e12) {
2233
+ } catch (e13) {
2010
2234
  }
2011
2235
  }
2012
2236
  throw new Error(
@@ -2058,7 +2282,7 @@ var Controller = (_class3 = class {
2058
2282
  this.data = tagData(fetched, "fetched");
2059
2283
  this.transition("degraded");
2060
2284
  return [this.data, "MISS"];
2061
- } catch (e13) {
2285
+ } catch (e14) {
2062
2286
  }
2063
2287
  }
2064
2288
  throw new Error(
@@ -2110,11 +2334,11 @@ var Controller = (_class3 = class {
2110
2334
  duration: Date.now() - startTime,
2111
2335
  mode: mode === "streaming" ? "stream" : mode === "polling" ? "poll" : mode
2112
2336
  };
2113
- const configUpdatedAt = _optionalChain([this, 'access', _70 => _70.data, 'optionalAccess', _71 => _71.configUpdatedAt]);
2337
+ const configUpdatedAt = _optionalChain([this, 'access', _72 => _72.data, 'optionalAccess', _73 => _73.configUpdatedAt]);
2114
2338
  if (typeof configUpdatedAt === "number") {
2115
2339
  trackOptions.configUpdatedAt = configUpdatedAt;
2116
2340
  }
2117
- const revision = _optionalChain([this, 'access', _72 => _72.data, 'optionalAccess', _73 => _73.revision]);
2341
+ const revision = _optionalChain([this, 'access', _74 => _74.data, 'optionalAccess', _75 => _75.revision]);
2118
2342
  if (typeof revision === "number") {
2119
2343
  trackOptions.revision = revision;
2120
2344
  }
@@ -2123,10 +2347,20 @@ var Controller = (_class3 = class {
2123
2347
  }
2124
2348
  this.usageTracker.trackRead(trackOptions);
2125
2349
  }
2126
- }, _class3);
2350
+ /**
2351
+ * Tracks a flag evaluation for usage analytics.
2352
+ */
2353
+ trackEvaluation(options) {
2354
+ if (this.unauthorized) return;
2355
+ this.usageTracker.trackEvaluation({
2356
+ ...options,
2357
+ clientName: _nullishCoalesce(options.clientName, () => ( this.options.clientName))
2358
+ });
2359
+ }
2360
+ }, _class5);
2127
2361
 
2128
2362
  // src/controller/auth.ts
2129
- var _oidc = require('@vercel/oidc');
2363
+
2130
2364
 
2131
2365
  // src/utils/sdk-keys.ts
2132
2366
  var SDK_KEY_REGEX = /^vf_(?:server|client)_/;
@@ -2137,7 +2371,7 @@ function parseSdkKeyFromFlagsConnectionString(text) {
2137
2371
  const params = new URLSearchParams(text.slice(6));
2138
2372
  const sdkKey = params.get("sdkKey");
2139
2373
  if (sdkKey && SDK_KEY_REGEX.test(sdkKey)) return sdkKey;
2140
- } catch (e14) {
2374
+ } catch (e15) {
2141
2375
  }
2142
2376
  return null;
2143
2377
  }
@@ -2146,7 +2380,7 @@ function parseSdkKeyFromFlagsConnectionString(text) {
2146
2380
  async function getOidcToken() {
2147
2381
  try {
2148
2382
  return await _oidc.getVercelOidcToken.call(void 0, );
2149
- } catch (e15) {
2383
+ } catch (e16) {
2150
2384
  throw new Error(
2151
2385
  [
2152
2386
  "@vercel/flags-core: Failed to get OIDC token.",
@@ -2265,4 +2499,4 @@ var {
2265
2499
 
2266
2500
 
2267
2501
  exports.ResolutionReason = ResolutionReason; exports.evaluate = evaluate; exports.FallbackNotFoundError = FallbackNotFoundError; exports.FallbackEntryNotFoundError = FallbackEntryNotFoundError; exports.Controller = Controller; exports.flagsClient = flagsClient; exports.resetDefaultFlagsClient = resetDefaultFlagsClient; exports.createClient = createClient;
2268
- //# sourceMappingURL=chunk-AXRUQULC.cjs.map
2502
+ //# sourceMappingURL=chunk-NHWFZTK2.cjs.map