@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;// src/index.next-js.ts
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;// src/index.next-js.ts
2
2
  var _cache = require('next/cache');
3
3
 
4
4
  // src/evaluate.ts
@@ -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
- value: getVariant(params.definition.variants, outcome),
303
- outcomeType: "value" /* VALUE */
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 { value: defaultOutcome, outcomeType: "split" /* SPLIT */ };
331
+ return withVariantIndex(
332
+ defaultOutcome,
333
+ "split" /* SPLIT */,
334
+ outcome.defaultVariant
335
+ );
315
336
  }
316
337
  const value = _jsxxhash.xxHash32.call(void 0, lhs, params.definition.seed);
317
338
  const scaledWeights = getScaledWeights(outcome);
318
339
  const variantIndex = findWeightedIndex(scaledWeights, value, UINT32_MAX);
319
- return {
320
- value: variantIndex === -1 ? defaultOutcome : getVariant(params.definition.variants, variantIndex),
321
- outcomeType: "split" /* SPLIT */
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 { value: defaultOutcome, outcomeType: "rollout" /* ROLLOUT */ };
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
- value: getVariant(
338
- params.definition.variants,
339
- outcome.rollFromVariant
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
- value: getVariant(
359
- params.definition.variants,
360
- outcome.rollFromVariant
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
- value: getVariant(
368
- params.definition.variants,
369
- outcome.rollToVariant
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 = _jsxxhash.xxHash32.call(void 0, lhs, params.definition.seed);
375
396
  const threshold = currentPromille / 1e5 * UINT32_MAX;
376
- return {
377
- value: value < threshold ? getVariant(params.definition.variants, outcome.rollToVariant) : getVariant(
378
- params.definition.variants,
379
- outcome.rollFromVariant
380
- ),
381
- outcomeType: "rollout" /* ROLLOUT */
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.5.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 _nullishCoalesce(_optionalChain([definition, 'access', _20 => _20.variantIds, 'optionalAccess', _21 => _21[variantIndex]]), () => ( String(variantIndex)));
533
+ }
534
+ function trackEvaluation(controller, options) {
535
+ _optionalChain([controller, 'access', _22 => _22.trackEvaluation, 'optionalCall', _23 => _23(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
- return {
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
- return {
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
  });
@@ -691,7 +757,7 @@ function createCreateRawClient(fns) {
691
757
  },
692
758
  getDatafile: async () => {
693
759
  const instance = controllerInstanceMap.get(id);
694
- if (_optionalChain([instance, 'optionalAccess', _20 => _20.initPromise])) {
760
+ if (_optionalChain([instance, 'optionalAccess', _24 => _24.initPromise])) {
695
761
  try {
696
762
  await instance.initPromise;
697
763
  } catch (e) {
@@ -704,7 +770,7 @@ function createCreateRawClient(fns) {
704
770
  },
705
771
  evaluate: async (flagKey, defaultValue, entities) => {
706
772
  const instance = controllerInstanceMap.get(id);
707
- if (!_optionalChain([instance, 'optionalAccess', _21 => _21.initialized])) {
773
+ if (!_optionalChain([instance, 'optionalAccess', _25 => _25.initialized])) {
708
774
  try {
709
775
  await api.initialize();
710
776
  } catch (e2) {
@@ -714,7 +780,7 @@ function createCreateRawClient(fns) {
714
780
  },
715
781
  bulkEvaluate: async (flags, entities) => {
716
782
  const instance = controllerInstanceMap.get(id);
717
- if (!_optionalChain([instance, 'optionalAccess', _22 => _22.initialized])) {
783
+ if (!_optionalChain([instance, 'optionalAccess', _26 => _26.initialized])) {
718
784
  try {
719
785
  await api.initialize();
720
786
  } catch (e3) {
@@ -783,8 +849,8 @@ async function readBundledDefinitions(auth) {
783
849
  return { definitions: null, state: "missing-entry" };
784
850
  }
785
851
 
786
- // src/utils/usage-tracker.ts
787
- var _functions = require('@vercel/functions');
852
+ // src/utils/ingest.ts
853
+ var _oidc = require('@vercel/oidc');
788
854
 
789
855
  // src/utils/backoff.ts
790
856
  var DEFAULT_BASE_MS = 250;
@@ -795,64 +861,304 @@ 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/usage-tracker.ts
805
- var RESOLVED_VOID = Promise.resolve();
806
- var isDebugMode = _optionalChain([process, 'access', _23 => _23.env, 'access', _24 => _24.DEBUG, 'optionalAccess', _25 => _25.includes, 'call', _26 => _26("@vercel/flags-core")]);
865
+ // src/utils/ingest.ts
866
+ var MAX_RETRIES = 3;
867
+ var EVALUATING_OIDC_TOKEN_HEADER = "X-Vercel-Flags-Evaluating-OIDC-Token";
868
+ var isDebugMode = _optionalChain([process, 'access', _27 => _27.env, 'access', _28 => _28.DEBUG, 'optionalAccess', _29 => _29.includes, 'call', _30 => _30("@vercel/flags-core")]);
807
869
  var debugLog = (...args) => {
808
870
  if (!isDebugMode) return;
809
871
  console.log(...args);
810
872
  };
811
- var MAX_RETRIES = 3;
812
- var MAX_BATCH_SIZE = 50;
813
- var MAX_BATCH_WAIT_MS = 5e3;
814
- var BATCH_WAIT_JITTER_RATIO = 0.2;
873
+ async function getEvaluatingOidcToken(auth) {
874
+ if (!auth.sdkKey) return void 0;
875
+ try {
876
+ return await _oidc.getVercelOidcToken.call(void 0, );
877
+ } catch (e4) {
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() {
818
933
  try {
819
- const ctx = _optionalChain([fromSymbol, 'access', _27 => _27[SYMBOL_FOR_REQ_CONTEXT], 'optionalAccess', _28 => _28.get, 'optionalCall', _29 => _29()]);
934
+ const ctx = _optionalChain([fromSymbol, 'access', _31 => _31[SYMBOL_FOR_REQ_CONTEXT], 'optionalAccess', _32 => _32.get, 'optionalCall', _33 => _33()]);
820
935
  if (ctx && Object.hasOwn(ctx, "headers")) {
821
936
  return {
822
937
  ctx,
823
938
  headers: ctx.headers
824
939
  };
825
940
  }
826
- return { ctx, headers: void 0 };
827
- } catch (e4) {
941
+ return { ctx: void 0, headers: void 0 };
942
+ } catch (e5) {
828
943
  return { ctx: void 0, headers: void 0 };
829
944
  }
830
945
  }
831
- var UsageTracker = (_class = class {
832
- __init() {this.flushCounter = 0}
946
+
947
+ // src/utils/scheduler.ts
948
+ var _functions = require('@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 = class {
953
+ 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);
954
+ this.onFlush = onFlush;
955
+ }
956
+ __init() {this.count = 0}
957
+ __init2() {this.resolveWait = null}
958
+ __init3() {this.pending = null}
959
+ __init4() {this.idleTimeout = null}
960
+ __init5() {this.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
+ _functions.waitUntil.call(void 0, this.pending);
978
+ } catch (e6) {
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
+ _optionalChain([this, 'access', _34 => _34.resolveWait, 'optionalCall', _35 => _35()]);
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
+ _optionalChain([this, 'access', _36 => _36.resolveWait, 'optionalCall', _37 => _37()]);
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
+ }, _class);
1024
+
1025
+ // src/utils/usage/flags-config-read.ts
1026
+ var FlagsConfigReadEvent = class {
833
1027
 
834
- __init2() {this.trackedRequests = /* @__PURE__ */ new WeakSet()}
835
- __init3() {this.batcher = {
836
- events: [],
837
- resolveWait: null,
838
- pending: null
839
- }}
840
- constructor(options) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);
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 = _nullishCoalesce(headers["x-vercel-id"], () => ( void 0));
1035
+ this.payload.invocationHost = _nullishCoalesce(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
+ _nullishCoalesce(options.clientName, () => ( null))
1085
+ ]);
1086
+ }
1087
+ var FlagsEvaluationEvent = class {
1088
+
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
+ _nullishCoalesce(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 = (_class2 = class {constructor() { _class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this); }
1123
+ __init6() {this._values = []}
1124
+ __init7() {this._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
+ }, _class2);
1143
+
1144
+ // src/utils/usage-tracker.ts
1145
+ var UsageTracker = (_class3 = class {
1146
+ __init8() {this.flushCount = 0}
1147
+
1148
+
1149
+ __init9() {this.readEvents = new BetterWeakMap()}
1150
+ __init10() {this.evaluationEvents = /* @__PURE__ */ new Map()}
1151
+ constructor(options) {;_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);
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
- flush() {
848
- if (this.batcher.pending) {
849
- _optionalChain([this, 'access', _30 => _30.batcher, 'access', _31 => _31.resolveWait, 'optionalCall', _32 => _32()]);
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,132 +1167,53 @@ var UsageTracker = (_class = class {
861
1167
  try {
862
1168
  const { ctx, headers } = getRequestContext();
863
1169
  if (!ctx) return;
864
- if (this.trackedRequests.has(ctx)) return;
865
- this.trackedRequests.add(ctx);
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 = _nullishCoalesce(headers["x-vercel-id"], () => ( void 0));
876
- event.payload.invocationHost = _nullishCoalesce(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.batcher.events.push(event);
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
- scheduleFlush() {
916
- if (!this.batcher.pending) {
917
- let timeout = null;
918
- const pending = (async () => {
919
- await new Promise((res) => {
920
- this.batcher.resolveWait = res;
921
- timeout = setTimeout(
922
- res,
923
- getJitteredWaitMs(MAX_BATCH_WAIT_MS, BATCH_WAIT_JITTER_RATIO)
924
- );
925
- });
926
- this.batcher.pending = null;
927
- this.batcher.resolveWait = null;
928
- if (timeout) clearTimeout(timeout);
929
- await this.flushEvents();
930
- })();
931
- try {
932
- _functions.waitUntil.call(void 0, pending);
933
- } catch (e5) {
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.batcher.pending = pending;
936
- }
937
- if (this.batcher.events.length >= MAX_BATCH_SIZE) {
938
- _optionalChain([this, 'access', _33 => _33.batcher, 'access', _34 => _34.resolveWait, 'optionalCall', _35 => _35()]);
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
- if (this.batcher.events.length === 0) return;
943
- const eventsToSend = this.batcher.events;
944
- this.batcher.events = [];
945
- const flushId = ++this.flushCounter;
946
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
947
- try {
948
- const token = await this.options.auth.resolveToken();
949
- const response = await this.options.fetch(
950
- `${this.options.host}/v1/ingest`,
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
- }, _class);
1216
+ }, _class3);
990
1217
 
991
1218
  // src/errors.ts
992
1219
  var FallbackNotFoundError = class extends Error {
@@ -1045,7 +1272,7 @@ var BundledSource = class {
1045
1272
  */
1046
1273
  async tryLoad() {
1047
1274
  const result = await this.getResult();
1048
- if (_optionalChain([result, 'optionalAccess', _36 => _36.state]) === "ok" && result.definitions) {
1275
+ if (_optionalChain([result, 'optionalAccess', _38 => _38.state]) === "ok" && result.definitions) {
1049
1276
  return result.definitions;
1050
1277
  }
1051
1278
  return void 0;
@@ -1085,14 +1312,14 @@ async function fetchDatafile(options) {
1085
1312
  signal: controller.signal
1086
1313
  });
1087
1314
  clearTimeout(timeoutId);
1088
- _optionalChain([options, 'access', _37 => _37.signal, 'optionalAccess', _38 => _38.removeEventListener, 'call', _39 => _39("abort", onExternalAbort)]);
1315
+ _optionalChain([options, 'access', _39 => _39.signal, 'optionalAccess', _40 => _40.removeEventListener, 'call', _41 => _41("abort", onExternalAbort)]);
1089
1316
  if (!res.ok) {
1090
1317
  throw new Error(`Failed to fetch data: ${res.statusText}`);
1091
1318
  }
1092
1319
  return res.json();
1093
1320
  } catch (error) {
1094
1321
  clearTimeout(timeoutId);
1095
- _optionalChain([options, 'access', _40 => _40.signal, 'optionalAccess', _41 => _41.removeEventListener, 'call', _42 => _42("abort", onExternalAbort)]);
1322
+ _optionalChain([options, 'access', _42 => _42.signal, 'optionalAccess', _43 => _43.removeEventListener, 'call', _44 => _44("abort", onExternalAbort)]);
1096
1323
  throw error instanceof Error ? error : new Error("Unknown fetch error");
1097
1324
  }
1098
1325
  }
@@ -1141,13 +1368,14 @@ function normalizeOptions(options) {
1141
1368
  polling,
1142
1369
  buildStep,
1143
1370
  fetch: _nullishCoalesce(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
 
1148
1376
  // src/controller/typed-emitter.ts
1149
- var TypedEmitter = (_class2 = class {constructor() { _class2.prototype.__init4.call(this); }
1150
- __init4() {this.handlers = /* @__PURE__ */ new Map()}
1377
+ var TypedEmitter = (_class4 = class {constructor() { _class4.prototype.__init11.call(this); }
1378
+ __init11() {this.handlers = /* @__PURE__ */ new Map()}
1151
1379
  on(event, handler) {
1152
1380
  let set = this.handlers.get(event);
1153
1381
  if (!set) {
@@ -1157,7 +1385,7 @@ var TypedEmitter = (_class2 = class {constructor() { _class2.prototype.__init4.c
1157
1385
  set.add(handler);
1158
1386
  }
1159
1387
  off(event, handler) {
1160
- _optionalChain([this, 'access', _43 => _43.handlers, 'access', _44 => _44.get, 'call', _45 => _45(event), 'optionalAccess', _46 => _46.delete, 'call', _47 => _47(handler)]);
1388
+ _optionalChain([this, 'access', _45 => _45.handlers, 'access', _46 => _46.get, 'call', _47 => _47(event), 'optionalAccess', _48 => _48.delete, 'call', _49 => _49(handler)]);
1161
1389
  }
1162
1390
  emit(event, ...args) {
1163
1391
  const set = this.handlers.get(event);
@@ -1167,7 +1395,7 @@ var TypedEmitter = (_class2 = class {constructor() { _class2.prototype.__init4.c
1167
1395
  }
1168
1396
  }
1169
1397
  }
1170
- }, _class2);
1398
+ }, _class4);
1171
1399
 
1172
1400
  // src/controller/polling-source.ts
1173
1401
  var PollingSource = class extends TypedEmitter {
@@ -1183,11 +1411,11 @@ var PollingSource = class extends TypedEmitter {
1183
1411
  * Emits 'data' on success, 'error' on failure.
1184
1412
  */
1185
1413
  async poll() {
1186
- if (_optionalChain([this, 'access', _48 => _48.abortController, 'optionalAccess', _49 => _49.signal, 'access', _50 => _50.aborted])) return;
1414
+ if (_optionalChain([this, 'access', _50 => _50.abortController, 'optionalAccess', _51 => _51.signal, 'access', _52 => _52.aborted])) return;
1187
1415
  try {
1188
1416
  const data = await fetchDatafile({
1189
1417
  ...this.config,
1190
- signal: _optionalChain([this, 'access', _51 => _51.abortController, 'optionalAccess', _52 => _52.signal])
1418
+ signal: _optionalChain([this, 'access', _53 => _53.abortController, 'optionalAccess', _54 => _54.signal])
1191
1419
  });
1192
1420
  this.emit("data", data);
1193
1421
  } catch (error) {
@@ -1216,7 +1444,7 @@ var PollingSource = class extends TypedEmitter {
1216
1444
  clearInterval(this.intervalId);
1217
1445
  this.intervalId = void 0;
1218
1446
  }
1219
- _optionalChain([this, 'access', _53 => _53.abortController, 'optionalAccess', _54 => _54.abort, 'call', _55 => _55()]);
1447
+ _optionalChain([this, 'access', _55 => _55.abortController, 'optionalAccess', _56 => _56.abort, 'call', _57 => _57()]);
1220
1448
  this.abortController = void 0;
1221
1449
  }
1222
1450
  };
@@ -1264,13 +1492,9 @@ async function connectStream(config, callbacks) {
1264
1492
  });
1265
1493
  void (async () => {
1266
1494
  let initialDataReceived = false;
1267
- let lastError;
1268
1495
  while (!abortController.signal.aborted) {
1269
1496
  if (retryCount > MAX_RETRY_COUNT) {
1270
- console.error(
1271
- "@vercel/flags-core: Max retry count exceeded",
1272
- _nullishCoalesce(lastError, () => ( "stream closed repeatedly without an error"))
1273
- );
1497
+ console.error("@vercel/flags-core: Max retry count exceeded");
1274
1498
  if (!initialDataReceived) {
1275
1499
  rejectInit(
1276
1500
  new Error("stream: max retry count exceeded before receiving data")
@@ -1290,7 +1514,7 @@ async function connectStream(config, callbacks) {
1290
1514
  if (pingTimeoutId !== void 0) clearTimeout(pingTimeoutId);
1291
1515
  if (!initialDataReceived) return;
1292
1516
  pingTimeoutId = setTimeout(() => {
1293
- _optionalChain([responseBody, 'optionalAccess', _56 => _56.cancel, 'call', _57 => _57(), 'access', _58 => _58.catch, 'call', _59 => _59(() => {
1517
+ _optionalChain([responseBody, 'optionalAccess', _58 => _58.cancel, 'call', _59 => _59(), 'access', _60 => _60.catch, 'call', _61 => _61(() => {
1294
1518
  })]);
1295
1519
  connectionAbort.abort();
1296
1520
  }, PING_TIMEOUT_MS);
@@ -1309,7 +1533,7 @@ async function connectStream(config, callbacks) {
1309
1533
  if (vercelEnv) {
1310
1534
  headers["X-Vercel-Env"] = vercelEnv;
1311
1535
  }
1312
- const revision = _optionalChain([config, 'access', _60 => _60.revision, 'optionalCall', _61 => _61()]);
1536
+ const revision = _optionalChain([config, 'access', _62 => _62.revision, 'optionalCall', _63 => _63()]);
1313
1537
  if (revision !== void 0) {
1314
1538
  headers["X-Revision"] = String(revision);
1315
1539
  }
@@ -1355,7 +1579,7 @@ async function connectStream(config, callbacks) {
1355
1579
  let message;
1356
1580
  try {
1357
1581
  message = JSON.parse(line);
1358
- } catch (e6) {
1582
+ } catch (e7) {
1359
1583
  console.warn(
1360
1584
  "@vercel/flags-core: Failed to parse stream message, skipping"
1361
1585
  );
@@ -1371,7 +1595,7 @@ async function connectStream(config, callbacks) {
1371
1595
  resetPingTimeout();
1372
1596
  }
1373
1597
  if (message.type === "primed") {
1374
- _optionalChain([onPrimed, 'optionalCall', _62 => _62(message)]);
1598
+ _optionalChain([onPrimed, 'optionalCall', _64 => _64(message)]);
1375
1599
  retryCount = 0;
1376
1600
  if (!initialDataReceived) {
1377
1601
  initialDataReceived = true;
@@ -1394,7 +1618,7 @@ async function connectStream(config, callbacks) {
1394
1618
  clearTimeout(pingTimeoutId);
1395
1619
  abortController.signal.removeEventListener("abort", onMainAbort);
1396
1620
  if (!abortController.signal.aborted) {
1397
- _optionalChain([onDisconnect, 'optionalCall', _63 => _63()]);
1621
+ _optionalChain([onDisconnect, 'optionalCall', _65 => _65()]);
1398
1622
  retryCount++;
1399
1623
  const elapsed = Date.now() - lastAttemptTime;
1400
1624
  const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
@@ -1413,9 +1637,9 @@ async function connectStream(config, callbacks) {
1413
1637
  break;
1414
1638
  }
1415
1639
  if (!connectionAbort.signal.aborted) {
1416
- lastError = error;
1640
+ console.error("@vercel/flags-core: Stream error", error);
1417
1641
  }
1418
- _optionalChain([onDisconnect, 'optionalCall', _64 => _64()]);
1642
+ _optionalChain([onDisconnect, 'optionalCall', _66 => _66()]);
1419
1643
  retryCount++;
1420
1644
  const elapsed = Date.now() - lastAttemptTime;
1421
1645
  const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
@@ -1494,7 +1718,7 @@ var StreamSource = class extends TypedEmitter {
1494
1718
  * Stop the stream connection.
1495
1719
  */
1496
1720
  stop() {
1497
- _optionalChain([this, 'access', _65 => _65.abortController, 'optionalAccess', _66 => _66.abort, 'call', _67 => _67()]);
1721
+ _optionalChain([this, 'access', _67 => _67.abortController, 'optionalAccess', _68 => _68.abort, 'call', _69 => _69()]);
1498
1722
  this.abortController = void 0;
1499
1723
  this.promise = void 0;
1500
1724
  }
@@ -1526,34 +1750,34 @@ function parseConfigUpdatedAt(value) {
1526
1750
  }
1527
1751
  return void 0;
1528
1752
  }
1529
- var Controller = (_class3 = class {
1753
+ var Controller = (_class5 = class {
1530
1754
 
1531
1755
  // State machine
1532
- __init5() {this.state = "idle"}
1756
+ __init12() {this.state = "idle"}
1533
1757
  // Data state — tagged with origin
1534
1758
 
1535
1759
  // Memoized data spread for read() / getDatafile().
1536
1760
  // Rebuilt only when `this.data` reference changes (e.g. on stream/poll update).
1537
1761
  // Holds the result of stripping `_origin`; metrics are appended per-call.
1538
- __init6() {this.dataViewSource = void 0}
1539
- __init7() {this.dataViewBase = void 0}
1762
+ __init13() {this.dataViewSource = void 0}
1763
+ __init14() {this.dataViewBase = void 0}
1540
1764
  // Sources (I/O delegates)
1541
1765
 
1542
1766
 
1543
1767
 
1544
1768
  // Usage tracking
1545
1769
 
1546
- __init8() {this.isFirstGetData = true}
1770
+ __init15() {this.isFirstGetData = true}
1547
1771
  // Build-step deduplication
1548
- __init9() {this.buildDataPromise = null}
1549
- __init10() {this.buildReadTracked = false}
1772
+ __init16() {this.buildDataPromise = null}
1773
+ __init17() {this.buildReadTracked = false}
1550
1774
  // Suppresses usage tracking when the SDK key is unauthorized
1551
- __init11() {this.unauthorized = false}
1552
- 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);
1775
+ __init18() {this.unauthorized = false}
1776
+ 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);
1553
1777
  this.options = normalizeOptions(options);
1554
1778
  this.streamSource = new StreamSource(
1555
1779
  this.options,
1556
- () => _optionalChain([this, 'access', _68 => _68.data, 'optionalAccess', _69 => _69.revision])
1780
+ () => _optionalChain([this, 'access', _70 => _70.data, 'optionalAccess', _71 => _71.revision])
1557
1781
  );
1558
1782
  this.pollingSource = new PollingSource(this.options);
1559
1783
  this.bundledSource = new BundledSource({
@@ -1567,32 +1791,32 @@ var Controller = (_class3 = class {
1567
1791
  this.usageTracker = new UsageTracker(this.options);
1568
1792
  }
1569
1793
  // Source event handlers (stored for cleanup)
1570
- __init12() {this.onStreamData = (data) => {
1794
+ __init19() {this.onStreamData = (data) => {
1571
1795
  if (this.isNewerData(data)) {
1572
1796
  this.data = tagData(data, "stream");
1573
1797
  }
1574
1798
  }}
1575
- __init13() {this.onStreamPrimed = () => {
1799
+ __init20() {this.onStreamPrimed = () => {
1576
1800
  if (this.state === "degraded" || this.state === "initializing:stream") {
1577
1801
  this.transition("streaming");
1578
1802
  }
1579
1803
  }}
1580
- __init14() {this.onStreamConnected = () => {
1804
+ __init21() {this.onStreamConnected = () => {
1581
1805
  if (this.state === "degraded" || this.state === "initializing:stream") {
1582
1806
  this.transition("streaming");
1583
1807
  }
1584
1808
  }}
1585
- __init15() {this.onStreamDisconnected = () => {
1809
+ __init22() {this.onStreamDisconnected = () => {
1586
1810
  if (this.state === "streaming") {
1587
1811
  this.transition("degraded");
1588
1812
  }
1589
1813
  }}
1590
- __init16() {this.onPollData = (data) => {
1814
+ __init23() {this.onPollData = (data) => {
1591
1815
  if (this.isNewerData(data)) {
1592
1816
  this.data = tagData(data, "poll");
1593
1817
  }
1594
1818
  }}
1595
- __init17() {this.onPollError = (error) => {
1819
+ __init24() {this.onPollError = (error) => {
1596
1820
  console.error("@vercel/flags-core: Poll failed:", error);
1597
1821
  }}
1598
1822
  // ---------------------------------------------------------------------------
@@ -1661,7 +1885,7 @@ var Controller = (_class3 = class {
1661
1885
  if (bundled) {
1662
1886
  this.data = tagData(bundled, "bundled");
1663
1887
  }
1664
- } catch (e7) {
1888
+ } catch (e8) {
1665
1889
  }
1666
1890
  }
1667
1891
  if (this.data) {
@@ -1730,7 +1954,7 @@ var Controller = (_class3 = class {
1730
1954
  this.pollingSource.stop();
1731
1955
  this.data = this.options.datafile ? tagData(this.options.datafile, "provided") : void 0;
1732
1956
  this.transition("shutdown");
1733
- await this.usageTracker.flush();
1957
+ await this.usageTracker.shutdown();
1734
1958
  }
1735
1959
  /**
1736
1960
  * Returns the datafile with metrics.
@@ -1763,7 +1987,7 @@ var Controller = (_class3 = class {
1763
1987
  this.data = tagData(fetched, "fetched");
1764
1988
  result = this.data;
1765
1989
  cacheStatus = "MISS";
1766
- } catch (e8) {
1990
+ } catch (e9) {
1767
1991
  throw new Error(
1768
1992
  "@vercel/flags-core: No flag definitions available. Initialize the client or provide a datafile."
1769
1993
  );
@@ -1848,7 +2072,7 @@ var Controller = (_class3 = class {
1848
2072
  clearTimeout(timeoutId);
1849
2073
  if (result === "timeout") {
1850
2074
  console.warn(
1851
- "@vercel/flags-core: Stream initialization timeout, falling back while continuing to connect in the background"
2075
+ "@vercel/flags-core: Stream initialization timeout, falling back"
1852
2076
  );
1853
2077
  void this.streamSource.start().catch(() => {
1854
2078
  });
@@ -1882,7 +2106,7 @@ var Controller = (_class3 = class {
1882
2106
  return true;
1883
2107
  }
1884
2108
  return false;
1885
- } catch (e9) {
2109
+ } catch (e10) {
1886
2110
  return false;
1887
2111
  }
1888
2112
  }
@@ -1898,7 +2122,7 @@ var Controller = (_class3 = class {
1898
2122
  clearTimeout(timeoutId);
1899
2123
  if (result === "timeout") {
1900
2124
  console.warn(
1901
- "@vercel/flags-core: Polling initialization timeout, falling back while continuing to poll in the background"
2125
+ "@vercel/flags-core: Polling initialization timeout, falling back"
1902
2126
  );
1903
2127
  return false;
1904
2128
  }
@@ -1907,7 +2131,7 @@ var Controller = (_class3 = class {
1907
2131
  return true;
1908
2132
  }
1909
2133
  return false;
1910
- } catch (e10) {
2134
+ } catch (e11) {
1911
2135
  clearTimeout(timeoutId);
1912
2136
  return false;
1913
2137
  }
@@ -1957,7 +2181,7 @@ var Controller = (_class3 = class {
1957
2181
  fetch: this.options.fetch
1958
2182
  });
1959
2183
  return tagData(fetched, "fetched");
1960
- } catch (e11) {
2184
+ } catch (e12) {
1961
2185
  }
1962
2186
  throw new Error(
1963
2187
  "@vercel/flags-core: No flag definitions available during build. Provide a datafile or bundled definitions."
@@ -1991,7 +2215,7 @@ var Controller = (_class3 = class {
1991
2215
  this.data = tagData(fetched, "fetched");
1992
2216
  this.transition("degraded");
1993
2217
  return;
1994
- } catch (e12) {
2218
+ } catch (e13) {
1995
2219
  }
1996
2220
  }
1997
2221
  throw new Error(
@@ -2043,7 +2267,7 @@ var Controller = (_class3 = class {
2043
2267
  this.data = tagData(fetched, "fetched");
2044
2268
  this.transition("degraded");
2045
2269
  return [this.data, "MISS"];
2046
- } catch (e13) {
2270
+ } catch (e14) {
2047
2271
  }
2048
2272
  }
2049
2273
  throw new Error(
@@ -2095,11 +2319,11 @@ var Controller = (_class3 = class {
2095
2319
  duration: Date.now() - startTime,
2096
2320
  mode: mode === "streaming" ? "stream" : mode === "polling" ? "poll" : mode
2097
2321
  };
2098
- const configUpdatedAt = _optionalChain([this, 'access', _70 => _70.data, 'optionalAccess', _71 => _71.configUpdatedAt]);
2322
+ const configUpdatedAt = _optionalChain([this, 'access', _72 => _72.data, 'optionalAccess', _73 => _73.configUpdatedAt]);
2099
2323
  if (typeof configUpdatedAt === "number") {
2100
2324
  trackOptions.configUpdatedAt = configUpdatedAt;
2101
2325
  }
2102
- const revision = _optionalChain([this, 'access', _72 => _72.data, 'optionalAccess', _73 => _73.revision]);
2326
+ const revision = _optionalChain([this, 'access', _74 => _74.data, 'optionalAccess', _75 => _75.revision]);
2103
2327
  if (typeof revision === "number") {
2104
2328
  trackOptions.revision = revision;
2105
2329
  }
@@ -2108,10 +2332,20 @@ var Controller = (_class3 = class {
2108
2332
  }
2109
2333
  this.usageTracker.trackRead(trackOptions);
2110
2334
  }
2111
- }, _class3);
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: _nullishCoalesce(options.clientName, () => ( this.options.clientName))
2343
+ });
2344
+ }
2345
+ }, _class5);
2112
2346
 
2113
2347
  // src/controller/auth.ts
2114
- var _oidc = require('@vercel/oidc');
2348
+
2115
2349
 
2116
2350
  // src/utils/sdk-keys.ts
2117
2351
  var SDK_KEY_REGEX = /^vf_(?:server|client)_/;
@@ -2122,7 +2356,7 @@ function parseSdkKeyFromFlagsConnectionString(text) {
2122
2356
  const params = new URLSearchParams(text.slice(6));
2123
2357
  const sdkKey = params.get("sdkKey");
2124
2358
  if (sdkKey && SDK_KEY_REGEX.test(sdkKey)) return sdkKey;
2125
- } catch (e14) {
2359
+ } catch (e15) {
2126
2360
  }
2127
2361
  return null;
2128
2362
  }
@@ -2131,7 +2365,7 @@ function parseSdkKeyFromFlagsConnectionString(text) {
2131
2365
  async function getOidcToken() {
2132
2366
  try {
2133
2367
  return await _oidc.getVercelOidcToken.call(void 0, );
2134
- } catch (e15) {
2368
+ } catch (e16) {
2135
2369
  throw new Error(
2136
2370
  [
2137
2371
  "@vercel/flags-core: Failed to get OIDC token.",
@@ -2226,7 +2460,7 @@ function setCacheLife() {
2226
2460
  try {
2227
2461
  _cache.cacheLife.call(void 0, { revalidate: 0, expire: 0 });
2228
2462
  _cache.cacheLife.call(void 0, { stale: 60 });
2229
- } catch (e16) {
2463
+ } catch (e17) {
2230
2464
  }
2231
2465
  }
2232
2466
  var cachedFns = {
@@ -2275,4 +2509,4 @@ var { flagsClient, resetDefaultFlagsClient, createClient } = make(
2275
2509
 
2276
2510
 
2277
2511
  exports.ResolutionReason = ResolutionReason; exports.evaluate = evaluate; exports.FallbackNotFoundError = FallbackNotFoundError; exports.FallbackEntryNotFoundError = FallbackEntryNotFoundError; exports.Controller = Controller; exports.flagsClient = flagsClient; exports.resetDefaultFlagsClient = resetDefaultFlagsClient; exports.createClient = createClient;
2278
- //# sourceMappingURL=chunk-JTIP7QPD.cjs.map
2512
+ //# sourceMappingURL=chunk-LPIUDLYE.cjs.map