@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.
@@ -4,6 +4,9 @@ var __export = (target, all) => {
4
4
  __defProp(target, name, { get: all[name], enumerable: true });
5
5
  };
6
6
 
7
+ // src/index.default.ts
8
+ import { waitUntil as waitUntil2 } from "@vercel/functions";
9
+
7
10
  // src/controller-fns.ts
8
11
  var controller_fns_exports = {};
9
12
  __export(controller_fns_exports, {
@@ -573,7 +576,7 @@ function bulkEvaluate(flags, shared) {
573
576
  }
574
577
 
575
578
  // package.json
576
- var version = "1.8.1";
579
+ var version = "1.8.2";
577
580
 
578
581
  // src/lib/report-value.ts
579
582
  function internalReportValue(key, value, data) {
@@ -792,7 +795,7 @@ async function bulkEvaluate2(id, flags, entities) {
792
795
  }
793
796
 
794
797
  // src/create-raw-client.ts
795
- import { waitUntil } from "@vercel/functions";
798
+ import { waitUntil as defaultWaitUntil } from "@vercel/functions";
796
799
  import { dequal } from "dequal/lite";
797
800
  var idCount = 0;
798
801
  async function performInitialize(instance, initFn) {
@@ -808,9 +811,11 @@ function createCreateRawClient(fns) {
808
811
  return function createRawClient({
809
812
  controller,
810
813
  origin,
811
- experimental_reportExposures
814
+ experimental_reportExposures,
815
+ waitUntil: waitUntil3 = defaultWaitUntil
812
816
  }) {
813
817
  const id = idCount++;
818
+ const pendingExposureReports = /* @__PURE__ */ new Set();
814
819
  controllerInstanceMap.set(id, {
815
820
  controller,
816
821
  initialized: false,
@@ -828,8 +833,10 @@ function createCreateRawClient(fns) {
828
833
  );
829
834
  }
830
835
  })();
836
+ pendingExposureReports.add(pending);
837
+ void pending.finally(() => pendingExposureReports.delete(pending));
831
838
  try {
832
- waitUntil(pending);
839
+ waitUntil3(pending);
833
840
  } catch {
834
841
  }
835
842
  }
@@ -864,6 +871,9 @@ function createCreateRawClient(fns) {
864
871
  },
865
872
  shutdown: async () => {
866
873
  await fns.shutdown(id);
874
+ while (pendingExposureReports.size > 0) {
875
+ await Promise.all(pendingExposureReports);
876
+ }
867
877
  controllerInstanceMap.delete(id);
868
878
  },
869
879
  getDatafile: async () => {
@@ -1045,6 +1055,17 @@ function getJitteredWaitMs(baseMs, ratio) {
1045
1055
  return Math.floor(min + Math.random() * span);
1046
1056
  }
1047
1057
 
1058
+ // src/utils/runtime-ingest.ts
1059
+ var FLAGS_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("@vercel/flags-context");
1060
+ function getRuntimeIngest() {
1061
+ try {
1062
+ const context = globalThis[FLAGS_CONTEXT_SYMBOL];
1063
+ return typeof context?.ingest === "function" ? context.ingest : void 0;
1064
+ } catch {
1065
+ return void 0;
1066
+ }
1067
+ }
1068
+
1048
1069
  // src/utils/ingest.ts
1049
1070
  var MAX_RETRIES = 3;
1050
1071
  var MAX_EVENTS_PER_REQUEST = 2e3;
@@ -1078,8 +1099,28 @@ async function getIngestHeaders(options, flushReason) {
1078
1099
  ...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
1079
1100
  };
1080
1101
  }
1102
+ function getRuntimeIngestHeaders(options, flushReason) {
1103
+ return {
1104
+ "Content-Type": "application/json",
1105
+ ...options.auth.sdkKey ? { Authorization: `Bearer ${options.auth.sdkKey}` } : null,
1106
+ "User-Agent": `VercelFlagsCore/${version}`,
1107
+ [FLUSH_REASON_HEADER]: flushReason,
1108
+ ...options.metricEnvironment ?? process.env.VERCEL_ENV ? {
1109
+ "X-Vercel-Env": options.metricEnvironment ?? process.env.VERCEL_ENV
1110
+ } : null,
1111
+ ...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
1112
+ };
1113
+ }
1081
1114
  async function sendIngestEvents(options, events, flushId, flushReason) {
1082
- const eventsToSend = events.map((event) => event.ingestEvent());
1115
+ let eventsToSend = events.map((event) => event.ingestEvent());
1116
+ const runtimeIngest = getRuntimeIngest();
1117
+ if (runtimeIngest) {
1118
+ const headers = getRuntimeIngestHeaders(options, flushReason);
1119
+ eventsToSend = eventsToSend.filter(
1120
+ (event) => !runtimeIngest({ headers, body: [event] })
1121
+ );
1122
+ if (eventsToSend.length === 0) return;
1123
+ }
1083
1124
  for (let i = 0; i < eventsToSend.length; i += MAX_EVENTS_PER_REQUEST) {
1084
1125
  await sendIngestChunk(
1085
1126
  options,
@@ -1143,15 +1184,17 @@ function getRequestContext() {
1143
1184
  }
1144
1185
 
1145
1186
  // src/utils/scheduler.ts
1146
- import { waitUntil as waitUntil2 } from "@vercel/functions";
1187
+ import { waitUntil } from "@vercel/functions";
1147
1188
  var IDLE_FLUSH_WAIT_MS = 5e3;
1148
1189
  var IDLE_FLUSH_JITTER_RATIO = 0.2;
1149
1190
  var MAX_FLUSH_WAIT_MS = 6e4;
1150
1191
  var Scheduler = class {
1151
- constructor(onFlush) {
1192
+ constructor(onFlush, scheduleTask = waitUntil) {
1152
1193
  this.onFlush = onFlush;
1194
+ this.scheduleTask = scheduleTask;
1153
1195
  }
1154
1196
  onFlush;
1197
+ scheduleTask;
1155
1198
  resolveWait = null;
1156
1199
  pending = null;
1157
1200
  idleTimeout = null;
@@ -1166,7 +1209,7 @@ var Scheduler = class {
1166
1209
  await this.onFlush(reason);
1167
1210
  })();
1168
1211
  try {
1169
- waitUntil2(this.pending);
1212
+ this.scheduleTask(this.pending);
1170
1213
  } catch {
1171
1214
  }
1172
1215
  this.maxTimeout = setTimeout(
@@ -1313,12 +1356,18 @@ var UsageTracker = class {
1313
1356
  flushCount = 0;
1314
1357
  options;
1315
1358
  scheduler;
1359
+ waitUntil;
1360
+ inflightFlushes = /* @__PURE__ */ new Set();
1316
1361
  trackedRequests = /* @__PURE__ */ new WeakSet();
1317
1362
  readEvents = [];
1318
1363
  evaluationEvents = /* @__PURE__ */ new Map();
1319
1364
  constructor(options) {
1320
1365
  this.options = options;
1321
- this.scheduler = new Scheduler((reason) => this.flushEvents(reason));
1366
+ this.waitUntil = options.waitUntil;
1367
+ this.scheduler = new Scheduler(
1368
+ (reason) => this.flushEvents(reason),
1369
+ options.waitUntil
1370
+ );
1322
1371
  }
1323
1372
  /**
1324
1373
  * Triggers an immediate flush of any pending events.
@@ -1327,6 +1376,7 @@ var UsageTracker = class {
1327
1376
  async shutdown() {
1328
1377
  await this.scheduler.shutdown();
1329
1378
  await this.flushEvents("shutdown");
1379
+ await Promise.all([...this.inflightFlushes]);
1330
1380
  }
1331
1381
  /**
1332
1382
  * Tracks a config read event. Deduplicates by request context.
@@ -1338,7 +1388,7 @@ var UsageTracker = class {
1338
1388
  if (this.trackedRequests.has(ctx)) return;
1339
1389
  this.trackedRequests.add(ctx);
1340
1390
  this.readEvents.push(new FlagsConfigReadEvent(headers, options));
1341
- this.scheduler.scheduleFlush();
1391
+ this.requestFlush();
1342
1392
  } catch (error) {
1343
1393
  console.error("@vercel/flags-core: Failed to record event:", error);
1344
1394
  }
@@ -1362,7 +1412,7 @@ var UsageTracker = class {
1362
1412
  new FlagsEvaluationEvent(bucketedOptions)
1363
1413
  );
1364
1414
  }
1365
- this.scheduler.scheduleFlush();
1415
+ this.requestFlush();
1366
1416
  } catch (error) {
1367
1417
  console.error(
1368
1418
  "@vercel/flags-core: Failed to record evaluation event:",
@@ -1370,6 +1420,25 @@ var UsageTracker = class {
1370
1420
  );
1371
1421
  }
1372
1422
  }
1423
+ /**
1424
+ * Flushes immediately when the runtime provides an ingest transport,
1425
+ * otherwise falls back to the time-based scheduler.
1426
+ */
1427
+ requestFlush() {
1428
+ if (getRuntimeIngest()) {
1429
+ const flush = this.flushEvents("immediate").catch((error) => {
1430
+ console.error("@vercel/flags-core: Failed to flush events:", error);
1431
+ });
1432
+ this.inflightFlushes.add(flush);
1433
+ void flush.finally(() => this.inflightFlushes.delete(flush));
1434
+ try {
1435
+ this.waitUntil?.(flush);
1436
+ } catch {
1437
+ }
1438
+ } else {
1439
+ this.scheduler.scheduleFlush();
1440
+ }
1441
+ }
1373
1442
  /**
1374
1443
  * Send all events to the ingest service
1375
1444
  */
@@ -1495,6 +1564,7 @@ async function fetchDatafile(options) {
1495
1564
  }
1496
1565
 
1497
1566
  // src/controller/normalized-options.ts
1567
+ import { waitUntil as defaultWaitUntil2 } from "@vercel/functions";
1498
1568
  var DEFAULT_STREAM_INIT_TIMEOUT_MS = 3e3;
1499
1569
  var DEFAULT_POLLING_INTERVAL_MS = 3e4;
1500
1570
  var MIN_POLLING_INTERVAL_MS = 3e4;
@@ -1538,6 +1608,7 @@ function normalizeOptions(options) {
1538
1608
  polling,
1539
1609
  buildStep,
1540
1610
  fetch: options.fetch ?? globalThis.fetch,
1611
+ waitUntil: options.waitUntil ?? defaultWaitUntil2,
1541
1612
  host: "https://flags.vercel.com",
1542
1613
  metricEnvironment: options.metricEnvironment,
1543
1614
  clientName: options.clientName,
@@ -2611,7 +2682,7 @@ var Authentication = class {
2611
2682
  };
2612
2683
 
2613
2684
  // src/index.make.ts
2614
- function make(createRawClient) {
2685
+ function make(createRawClient, defaults) {
2615
2686
  let _defaultFlagsClient = null;
2616
2687
  function createClient2(sdkKeyOrConnectionStringOrOptions, options) {
2617
2688
  const optionsOnly = typeof sdkKeyOrConnectionStringOrOptions === "object" && sdkKeyOrConnectionStringOrOptions !== null;
@@ -2619,10 +2690,16 @@ function make(createRawClient) {
2619
2690
  const createClientOptions = optionsOnly ? sdkKeyOrConnectionStringOrOptions : options;
2620
2691
  const { experimental_reportExposures, ...controllerOptions } = createClientOptions ?? {};
2621
2692
  const auth = new Authentication(sdkKeyOrConnectionString);
2622
- const controller = new Controller({ auth, ...controllerOptions });
2693
+ const waitUntil3 = controllerOptions.waitUntil ?? defaults.waitUntil;
2694
+ const controller = new Controller({
2695
+ auth,
2696
+ ...controllerOptions,
2697
+ waitUntil: waitUntil3
2698
+ });
2623
2699
  return createRawClient({
2624
2700
  controller,
2625
2701
  origin: { provider: "vercel", sdkKey: auth.sdkKey },
2702
+ waitUntil: waitUntil3,
2626
2703
  ...experimental_reportExposures ? { experimental_reportExposures } : {}
2627
2704
  });
2628
2705
  }
@@ -2661,7 +2738,7 @@ var {
2661
2738
  * Create a flags client using an SDK key, connection string, or Vercel OIDC.
2662
2739
  */
2663
2740
  createClient
2664
- } = make(createCreateRawClient(controller_fns_exports));
2741
+ } = make(createCreateRawClient(controller_fns_exports), { waitUntil: waitUntil2 });
2665
2742
 
2666
2743
  export {
2667
2744
  ResolutionReason,
@@ -2673,4 +2750,4 @@ export {
2673
2750
  resetDefaultFlagsClient,
2674
2751
  createClient
2675
2752
  };
2676
- //# sourceMappingURL=chunk-5BWCBI7I.js.map
2753
+ //# sourceMappingURL=chunk-AMDRNXFQ.js.map