@vercel/flags-core 1.8.1 → 1.8.2

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,5 +1,6 @@
1
1
  // src/index.next-js.ts
2
2
  import { cacheLife } from "next/cache";
3
+ import { after } from "next/server";
3
4
 
4
5
  // src/evaluate.ts
5
6
  import { xxHash32 as hashInput } from "js-xxhash";
@@ -558,7 +559,7 @@ function bulkEvaluate(flags, shared) {
558
559
  }
559
560
 
560
561
  // package.json
561
- var version = "1.8.1";
562
+ var version = "1.8.2";
562
563
 
563
564
  // src/lib/report-value.ts
564
565
  function internalReportValue(key, value, data) {
@@ -777,7 +778,7 @@ async function bulkEvaluate2(id, flags, entities) {
777
778
  }
778
779
 
779
780
  // src/create-raw-client.ts
780
- import { waitUntil } from "@vercel/functions";
781
+ import { waitUntil as defaultWaitUntil } from "@vercel/functions";
781
782
  import { dequal } from "dequal/lite";
782
783
  var idCount = 0;
783
784
  async function performInitialize(instance, initFn) {
@@ -793,9 +794,11 @@ function createCreateRawClient(fns) {
793
794
  return function createRawClient({
794
795
  controller,
795
796
  origin,
796
- experimental_reportExposures
797
+ experimental_reportExposures,
798
+ waitUntil: waitUntil2 = defaultWaitUntil
797
799
  }) {
798
800
  const id = idCount++;
801
+ const pendingExposureReports = /* @__PURE__ */ new Set();
799
802
  controllerInstanceMap.set(id, {
800
803
  controller,
801
804
  initialized: false,
@@ -813,8 +816,10 @@ function createCreateRawClient(fns) {
813
816
  );
814
817
  }
815
818
  })();
819
+ pendingExposureReports.add(pending);
820
+ void pending.finally(() => pendingExposureReports.delete(pending));
816
821
  try {
817
- waitUntil(pending);
822
+ waitUntil2(pending);
818
823
  } catch {
819
824
  }
820
825
  }
@@ -849,6 +854,9 @@ function createCreateRawClient(fns) {
849
854
  },
850
855
  shutdown: async () => {
851
856
  await fns.shutdown(id);
857
+ while (pendingExposureReports.size > 0) {
858
+ await Promise.all(pendingExposureReports);
859
+ }
852
860
  controllerInstanceMap.delete(id);
853
861
  },
854
862
  getDatafile: async () => {
@@ -1030,6 +1038,17 @@ function getJitteredWaitMs(baseMs, ratio) {
1030
1038
  return Math.floor(min + Math.random() * span);
1031
1039
  }
1032
1040
 
1041
+ // src/utils/runtime-ingest.ts
1042
+ var FLAGS_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("@vercel/flags-context");
1043
+ function getRuntimeIngest() {
1044
+ try {
1045
+ const context = globalThis[FLAGS_CONTEXT_SYMBOL];
1046
+ return typeof context?.ingest === "function" ? context.ingest : void 0;
1047
+ } catch {
1048
+ return void 0;
1049
+ }
1050
+ }
1051
+
1033
1052
  // src/utils/ingest.ts
1034
1053
  var MAX_RETRIES = 3;
1035
1054
  var MAX_EVENTS_PER_REQUEST = 2e3;
@@ -1063,8 +1082,28 @@ async function getIngestHeaders(options, flushReason) {
1063
1082
  ...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
1064
1083
  };
1065
1084
  }
1085
+ function getRuntimeIngestHeaders(options, flushReason) {
1086
+ return {
1087
+ "Content-Type": "application/json",
1088
+ ...options.auth.sdkKey ? { Authorization: `Bearer ${options.auth.sdkKey}` } : null,
1089
+ "User-Agent": `VercelFlagsCore/${version}`,
1090
+ [FLUSH_REASON_HEADER]: flushReason,
1091
+ ...options.metricEnvironment ?? process.env.VERCEL_ENV ? {
1092
+ "X-Vercel-Env": options.metricEnvironment ?? process.env.VERCEL_ENV
1093
+ } : null,
1094
+ ...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
1095
+ };
1096
+ }
1066
1097
  async function sendIngestEvents(options, events, flushId, flushReason) {
1067
- const eventsToSend = events.map((event) => event.ingestEvent());
1098
+ let eventsToSend = events.map((event) => event.ingestEvent());
1099
+ const runtimeIngest = getRuntimeIngest();
1100
+ if (runtimeIngest) {
1101
+ const headers = getRuntimeIngestHeaders(options, flushReason);
1102
+ eventsToSend = eventsToSend.filter(
1103
+ (event) => !runtimeIngest({ headers, body: [event] })
1104
+ );
1105
+ if (eventsToSend.length === 0) return;
1106
+ }
1068
1107
  for (let i = 0; i < eventsToSend.length; i += MAX_EVENTS_PER_REQUEST) {
1069
1108
  await sendIngestChunk(
1070
1109
  options,
@@ -1128,15 +1167,17 @@ function getRequestContext() {
1128
1167
  }
1129
1168
 
1130
1169
  // src/utils/scheduler.ts
1131
- import { waitUntil as waitUntil2 } from "@vercel/functions";
1170
+ import { waitUntil } from "@vercel/functions";
1132
1171
  var IDLE_FLUSH_WAIT_MS = 5e3;
1133
1172
  var IDLE_FLUSH_JITTER_RATIO = 0.2;
1134
1173
  var MAX_FLUSH_WAIT_MS = 6e4;
1135
1174
  var Scheduler = class {
1136
- constructor(onFlush) {
1175
+ constructor(onFlush, scheduleTask = waitUntil) {
1137
1176
  this.onFlush = onFlush;
1177
+ this.scheduleTask = scheduleTask;
1138
1178
  }
1139
1179
  onFlush;
1180
+ scheduleTask;
1140
1181
  resolveWait = null;
1141
1182
  pending = null;
1142
1183
  idleTimeout = null;
@@ -1151,7 +1192,7 @@ var Scheduler = class {
1151
1192
  await this.onFlush(reason);
1152
1193
  })();
1153
1194
  try {
1154
- waitUntil2(this.pending);
1195
+ this.scheduleTask(this.pending);
1155
1196
  } catch {
1156
1197
  }
1157
1198
  this.maxTimeout = setTimeout(
@@ -1298,12 +1339,18 @@ var UsageTracker = class {
1298
1339
  flushCount = 0;
1299
1340
  options;
1300
1341
  scheduler;
1342
+ waitUntil;
1343
+ inflightFlushes = /* @__PURE__ */ new Set();
1301
1344
  trackedRequests = /* @__PURE__ */ new WeakSet();
1302
1345
  readEvents = [];
1303
1346
  evaluationEvents = /* @__PURE__ */ new Map();
1304
1347
  constructor(options) {
1305
1348
  this.options = options;
1306
- this.scheduler = new Scheduler((reason) => this.flushEvents(reason));
1349
+ this.waitUntil = options.waitUntil;
1350
+ this.scheduler = new Scheduler(
1351
+ (reason) => this.flushEvents(reason),
1352
+ options.waitUntil
1353
+ );
1307
1354
  }
1308
1355
  /**
1309
1356
  * Triggers an immediate flush of any pending events.
@@ -1312,6 +1359,7 @@ var UsageTracker = class {
1312
1359
  async shutdown() {
1313
1360
  await this.scheduler.shutdown();
1314
1361
  await this.flushEvents("shutdown");
1362
+ await Promise.all([...this.inflightFlushes]);
1315
1363
  }
1316
1364
  /**
1317
1365
  * Tracks a config read event. Deduplicates by request context.
@@ -1323,7 +1371,7 @@ var UsageTracker = class {
1323
1371
  if (this.trackedRequests.has(ctx)) return;
1324
1372
  this.trackedRequests.add(ctx);
1325
1373
  this.readEvents.push(new FlagsConfigReadEvent(headers, options));
1326
- this.scheduler.scheduleFlush();
1374
+ this.requestFlush();
1327
1375
  } catch (error) {
1328
1376
  console.error("@vercel/flags-core: Failed to record event:", error);
1329
1377
  }
@@ -1347,7 +1395,7 @@ var UsageTracker = class {
1347
1395
  new FlagsEvaluationEvent(bucketedOptions)
1348
1396
  );
1349
1397
  }
1350
- this.scheduler.scheduleFlush();
1398
+ this.requestFlush();
1351
1399
  } catch (error) {
1352
1400
  console.error(
1353
1401
  "@vercel/flags-core: Failed to record evaluation event:",
@@ -1355,6 +1403,25 @@ var UsageTracker = class {
1355
1403
  );
1356
1404
  }
1357
1405
  }
1406
+ /**
1407
+ * Flushes immediately when the runtime provides an ingest transport,
1408
+ * otherwise falls back to the time-based scheduler.
1409
+ */
1410
+ requestFlush() {
1411
+ if (getRuntimeIngest()) {
1412
+ const flush = this.flushEvents("immediate").catch((error) => {
1413
+ console.error("@vercel/flags-core: Failed to flush events:", error);
1414
+ });
1415
+ this.inflightFlushes.add(flush);
1416
+ void flush.finally(() => this.inflightFlushes.delete(flush));
1417
+ try {
1418
+ this.waitUntil?.(flush);
1419
+ } catch {
1420
+ }
1421
+ } else {
1422
+ this.scheduler.scheduleFlush();
1423
+ }
1424
+ }
1358
1425
  /**
1359
1426
  * Send all events to the ingest service
1360
1427
  */
@@ -1480,6 +1547,7 @@ async function fetchDatafile(options) {
1480
1547
  }
1481
1548
 
1482
1549
  // src/controller/normalized-options.ts
1550
+ import { waitUntil as defaultWaitUntil2 } from "@vercel/functions";
1483
1551
  var DEFAULT_STREAM_INIT_TIMEOUT_MS = 3e3;
1484
1552
  var DEFAULT_POLLING_INTERVAL_MS = 3e4;
1485
1553
  var MIN_POLLING_INTERVAL_MS = 3e4;
@@ -1523,6 +1591,7 @@ function normalizeOptions(options) {
1523
1591
  polling,
1524
1592
  buildStep,
1525
1593
  fetch: options.fetch ?? globalThis.fetch,
1594
+ waitUntil: options.waitUntil ?? defaultWaitUntil2,
1526
1595
  host: "https://flags.vercel.com",
1527
1596
  metricEnvironment: options.metricEnvironment,
1528
1597
  clientName: options.clientName,
@@ -2596,7 +2665,7 @@ var Authentication = class {
2596
2665
  };
2597
2666
 
2598
2667
  // src/index.make.ts
2599
- function make(createRawClient) {
2668
+ function make(createRawClient, defaults) {
2600
2669
  let _defaultFlagsClient = null;
2601
2670
  function createClient2(sdkKeyOrConnectionStringOrOptions, options) {
2602
2671
  const optionsOnly = typeof sdkKeyOrConnectionStringOrOptions === "object" && sdkKeyOrConnectionStringOrOptions !== null;
@@ -2604,10 +2673,16 @@ function make(createRawClient) {
2604
2673
  const createClientOptions = optionsOnly ? sdkKeyOrConnectionStringOrOptions : options;
2605
2674
  const { experimental_reportExposures, ...controllerOptions } = createClientOptions ?? {};
2606
2675
  const auth = new Authentication(sdkKeyOrConnectionString);
2607
- const controller = new Controller({ auth, ...controllerOptions });
2676
+ const waitUntil2 = controllerOptions.waitUntil ?? defaults.waitUntil;
2677
+ const controller = new Controller({
2678
+ auth,
2679
+ ...controllerOptions,
2680
+ waitUntil: waitUntil2
2681
+ });
2608
2682
  return createRawClient({
2609
2683
  controller,
2610
2684
  origin: { provider: "vercel", sdkKey: auth.sdkKey },
2685
+ waitUntil: waitUntil2,
2611
2686
  ...experimental_reportExposures ? { experimental_reportExposures } : {}
2612
2687
  });
2613
2688
  }
@@ -2670,7 +2745,8 @@ var cachedFns = {
2670
2745
  }
2671
2746
  };
2672
2747
  var { flagsClient, resetDefaultFlagsClient, createClient } = make(
2673
- createCreateRawClient(cachedFns)
2748
+ createCreateRawClient(cachedFns),
2749
+ { waitUntil: after }
2674
2750
  );
2675
2751
 
2676
2752
  export {
@@ -2683,4 +2759,4 @@ export {
2683
2759
  resetDefaultFlagsClient,
2684
2760
  createClient
2685
2761
  };
2686
- //# sourceMappingURL=chunk-DYSF37WV.js.map
2762
+ //# sourceMappingURL=chunk-KK44WKSW.js.map