@vercel/flags-core 1.8.1 → 1.8.2-d31aae4-20260916165712

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @vercel/flags-core
2
2
 
3
+ ## 1.8.2-d31aae4-20260916165712
4
+
5
+ ### Patch Changes
6
+
7
+ - [#497](https://github.com/vercel/flags/pull/497) [`4848877`](https://github.com/vercel/flags/commit/4848877ca60c8595745b7759e351e936d7fe5889) Thanks [@feugy](https://github.com/feugy)! - Allow passing a custom `waitUntil` function to `createClient` for background
8
+ usage and exposure reporting. Pending exposure reports are drained by
9
+ `client.shutdown()`. The Next.js conditional export uses `after` from
10
+ `next/server` by default.
11
+
12
+ - [#490](https://github.com/vercel/flags/pull/490) [`559c1e7`](https://github.com/vercel/flags/commit/559c1e7bb14765acb234bf004c8e839fadfc2922) Thanks [@AndyBitz](https://github.com/AndyBitz)! - Use the runtime-provided ingest transport when available
13
+
3
14
  ## 1.8.1
4
15
 
5
16
  ### Patch Changes
package/README.md CHANGED
@@ -12,18 +12,27 @@ npm i @vercel/flags-core
12
12
 
13
13
  ## Usage
14
14
 
15
+ Create a shared client at module scope, but evaluate flags inside a request handler when using Vercel OIDC authentication. `evaluate()` and `bulkEvaluate()` initialize the client automatically on first use; you do not need to call `initialize()` first.
16
+
17
+ For example, in an Express app deployed to Vercel:
18
+
15
19
  ```ts
20
+ import express from 'express';
16
21
  import { createClient } from '@vercel/flags-core';
17
22
 
18
- const client = createClient(process.env.FLAGS!);
19
-
20
- await client.initialize();
23
+ const app = express();
24
+ const client = createClient(); // Uses Vercel OIDC; does not initialize yet.
21
25
 
22
- const result = await client.evaluate<boolean>('show-new-feature', false, {
23
- user: { id: 'user-123' },
26
+ app.get('/api/feature', async (_req, res) => {
27
+ const result = await client.evaluate<boolean>('show-new-feature', false);
28
+ res.json({ enabled: result.value });
24
29
  });
30
+
31
+ export default app;
25
32
  ```
26
33
 
34
+ Outside Vercel, pass an SDK key explicitly: `createClient(process.env.FLAGS)`.
35
+
27
36
  ## Evaluation Metrics
28
37
 
29
38
  To associate evaluation metrics with an environment, pass the
@@ -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
+ var _functions = require('@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-d31aae4-20260916165712";
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
- var _functions = require('@vercel/functions');
798
+
796
799
  var _lite = require('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 = _functions.waitUntil
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
- _functions.waitUntil.call(void 0, pending);
839
+ waitUntil3(pending);
833
840
  } catch (e) {
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,12 +1055,23 @@ 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 _optionalChain([context, 'optionalAccess', _31 => _31.ingest]) === "function" ? context.ingest : void 0;
1064
+ } catch (e5) {
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;
1051
1072
  var EVALUATING_OIDC_TOKEN_HEADER = "X-Vercel-Flags-OIDC-Token";
1052
1073
  var FLUSH_REASON_HEADER = "X-Vercel-Flags-Flush-Reason";
1053
- var isDebugMode = _optionalChain([process, 'access', _31 => _31.env, 'access', _32 => _32.DEBUG, 'optionalAccess', _33 => _33.includes, 'call', _34 => _34("@vercel/flags-core")]);
1074
+ var isDebugMode = _optionalChain([process, 'access', _32 => _32.env, 'access', _33 => _33.DEBUG, 'optionalAccess', _34 => _34.includes, 'call', _35 => _35("@vercel/flags-core")]);
1054
1075
  var debugLog = (...args) => {
1055
1076
  if (!isDebugMode) return;
1056
1077
  console.log(...args);
@@ -1059,7 +1080,7 @@ async function getEvaluatingOidcToken(auth) {
1059
1080
  if (!auth.sdkKey) return void 0;
1060
1081
  try {
1061
1082
  return await _oidc.getVercelOidcToken.call(void 0, );
1062
- } catch (e5) {
1083
+ } catch (e6) {
1063
1084
  return void 0;
1064
1085
  }
1065
1086
  }
@@ -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
+ ..._nullishCoalesce(options.metricEnvironment, () => ( process.env.VERCEL_ENV)) ? {
1109
+ "X-Vercel-Env": _nullishCoalesce(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,
@@ -1129,7 +1170,7 @@ var SYMBOL_FOR_REQ_CONTEXT = /* @__PURE__ */ Symbol.for("@vercel/request-context
1129
1170
  var fromSymbol = globalThis;
1130
1171
  function getRequestContext() {
1131
1172
  try {
1132
- const ctx = _optionalChain([fromSymbol, 'access', _35 => _35[SYMBOL_FOR_REQ_CONTEXT], 'optionalAccess', _36 => _36.get, 'optionalCall', _37 => _37()]);
1173
+ const ctx = _optionalChain([fromSymbol, 'access', _36 => _36[SYMBOL_FOR_REQ_CONTEXT], 'optionalAccess', _37 => _37.get, 'optionalCall', _38 => _38()]);
1133
1174
  if (ctx && Object.hasOwn(ctx, "headers")) {
1134
1175
  return {
1135
1176
  ctx,
@@ -1137,7 +1178,7 @@ function getRequestContext() {
1137
1178
  };
1138
1179
  }
1139
1180
  return { ctx, headers: void 0 };
1140
- } catch (e6) {
1181
+ } catch (e7) {
1141
1182
  return { ctx: void 0, headers: void 0 };
1142
1183
  }
1143
1184
  }
@@ -1148,10 +1189,12 @@ 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 = class {
1151
- constructor(onFlush) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
1192
+ constructor(onFlush, scheduleTask = _functions.waitUntil) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
1152
1193
  this.onFlush = onFlush;
1194
+ this.scheduleTask = scheduleTask;
1153
1195
  }
1154
1196
 
1197
+
1155
1198
  __init() {this.resolveWait = null}
1156
1199
  __init2() {this.pending = null}
1157
1200
  __init3() {this.idleTimeout = null}
@@ -1166,8 +1209,8 @@ var Scheduler = (_class = class {
1166
1209
  await this.onFlush(reason);
1167
1210
  })();
1168
1211
  try {
1169
- _functions.waitUntil.call(void 0, this.pending);
1170
- } catch (e7) {
1212
+ this.scheduleTask(this.pending);
1213
+ } catch (e8) {
1171
1214
  }
1172
1215
  this.maxTimeout = setTimeout(
1173
1216
  () => this.resolveScheduledFlush("max_timeout"),
@@ -1194,7 +1237,7 @@ var Scheduler = (_class = class {
1194
1237
  }
1195
1238
  resolveScheduledFlush(reason) {
1196
1239
  this.clearTimeouts();
1197
- _optionalChain([this, 'access', _38 => _38.resolveWait, 'optionalCall', _39 => _39(reason)]);
1240
+ _optionalChain([this, 'access', _39 => _39.resolveWait, 'optionalCall', _40 => _40(reason)]);
1198
1241
  }
1199
1242
  reset() {
1200
1243
  this.pending = null;
@@ -1313,12 +1356,18 @@ var UsageTracker = (_class4 = class {
1313
1356
  __init7() {this.flushCount = 0}
1314
1357
 
1315
1358
 
1316
- __init8() {this.trackedRequests = /* @__PURE__ */ new WeakSet()}
1317
- __init9() {this.readEvents = []}
1318
- __init10() {this.evaluationEvents = /* @__PURE__ */ new Map()}
1319
- constructor(options) {;_class4.prototype.__init7.call(this);_class4.prototype.__init8.call(this);_class4.prototype.__init9.call(this);_class4.prototype.__init10.call(this);
1359
+
1360
+ __init8() {this.inflightFlushes = /* @__PURE__ */ new Set()}
1361
+ __init9() {this.trackedRequests = /* @__PURE__ */ new WeakSet()}
1362
+ __init10() {this.readEvents = []}
1363
+ __init11() {this.evaluationEvents = /* @__PURE__ */ new Map()}
1364
+ constructor(options) {;_class4.prototype.__init7.call(this);_class4.prototype.__init8.call(this);_class4.prototype.__init9.call(this);_class4.prototype.__init10.call(this);_class4.prototype.__init11.call(this);
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 = (_class4 = 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 = (_class4 = 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 = (_class4 = 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 = (_class4 = 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
+ _optionalChain([this, 'access', _41 => _41.waitUntil, 'optionalCall', _42 => _42(flush)]);
1436
+ } catch (e9) {
1437
+ }
1438
+ } else {
1439
+ this.scheduler.scheduleFlush();
1440
+ }
1441
+ }
1373
1442
  /**
1374
1443
  * Send all events to the ingest service
1375
1444
  */
@@ -1442,7 +1511,7 @@ var BundledSource = class {
1442
1511
  */
1443
1512
  async tryLoad() {
1444
1513
  const result = await this.getResult();
1445
- if (_optionalChain([result, 'optionalAccess', _40 => _40.state]) === "ok" && result.definitions) {
1514
+ if (_optionalChain([result, 'optionalAccess', _43 => _43.state]) === "ok" && result.definitions) {
1446
1515
  return result.definitions;
1447
1516
  }
1448
1517
  return void 0;
@@ -1482,19 +1551,20 @@ async function fetchDatafile(options) {
1482
1551
  signal: controller.signal
1483
1552
  });
1484
1553
  clearTimeout(timeoutId);
1485
- _optionalChain([options, 'access', _41 => _41.signal, 'optionalAccess', _42 => _42.removeEventListener, 'call', _43 => _43("abort", onExternalAbort)]);
1554
+ _optionalChain([options, 'access', _44 => _44.signal, 'optionalAccess', _45 => _45.removeEventListener, 'call', _46 => _46("abort", onExternalAbort)]);
1486
1555
  if (!res.ok) {
1487
1556
  throw new Error(`Failed to fetch data: ${res.statusText}`);
1488
1557
  }
1489
1558
  return res.json();
1490
1559
  } catch (error) {
1491
1560
  clearTimeout(timeoutId);
1492
- _optionalChain([options, 'access', _44 => _44.signal, 'optionalAccess', _45 => _45.removeEventListener, 'call', _46 => _46("abort", onExternalAbort)]);
1561
+ _optionalChain([options, 'access', _47 => _47.signal, 'optionalAccess', _48 => _48.removeEventListener, 'call', _49 => _49("abort", onExternalAbort)]);
1493
1562
  throw error instanceof Error ? error : new Error("Unknown fetch error");
1494
1563
  }
1495
1564
  }
1496
1565
 
1497
1566
  // src/controller/normalized-options.ts
1567
+
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: _nullishCoalesce(options.fetch, () => ( globalThis.fetch)),
1611
+ waitUntil: _nullishCoalesce(options.waitUntil, () => ( _functions.waitUntil)),
1541
1612
  host: "https://flags.vercel.com",
1542
1613
  metricEnvironment: options.metricEnvironment,
1543
1614
  clientName: options.clientName,
@@ -1546,8 +1617,8 @@ function normalizeOptions(options) {
1546
1617
  }
1547
1618
 
1548
1619
  // src/controller/typed-emitter.ts
1549
- var TypedEmitter = (_class5 = class {constructor() { _class5.prototype.__init11.call(this); }
1550
- __init11() {this.handlers = /* @__PURE__ */ new Map()}
1620
+ var TypedEmitter = (_class5 = class {constructor() { _class5.prototype.__init12.call(this); }
1621
+ __init12() {this.handlers = /* @__PURE__ */ new Map()}
1551
1622
  on(event, handler) {
1552
1623
  let set = this.handlers.get(event);
1553
1624
  if (!set) {
@@ -1557,7 +1628,7 @@ var TypedEmitter = (_class5 = class {constructor() { _class5.prototype.__init11.
1557
1628
  set.add(handler);
1558
1629
  }
1559
1630
  off(event, handler) {
1560
- _optionalChain([this, 'access', _47 => _47.handlers, 'access', _48 => _48.get, 'call', _49 => _49(event), 'optionalAccess', _50 => _50.delete, 'call', _51 => _51(handler)]);
1631
+ _optionalChain([this, 'access', _50 => _50.handlers, 'access', _51 => _51.get, 'call', _52 => _52(event), 'optionalAccess', _53 => _53.delete, 'call', _54 => _54(handler)]);
1561
1632
  }
1562
1633
  emit(event, ...args) {
1563
1634
  const set = this.handlers.get(event);
@@ -1583,11 +1654,11 @@ var PollingSource = class extends TypedEmitter {
1583
1654
  * Emits 'data' on success, 'error' on failure.
1584
1655
  */
1585
1656
  async poll() {
1586
- if (_optionalChain([this, 'access', _52 => _52.abortController, 'optionalAccess', _53 => _53.signal, 'access', _54 => _54.aborted])) return;
1657
+ if (_optionalChain([this, 'access', _55 => _55.abortController, 'optionalAccess', _56 => _56.signal, 'access', _57 => _57.aborted])) return;
1587
1658
  try {
1588
1659
  const data = await fetchDatafile({
1589
1660
  ...this.config,
1590
- signal: _optionalChain([this, 'access', _55 => _55.abortController, 'optionalAccess', _56 => _56.signal])
1661
+ signal: _optionalChain([this, 'access', _58 => _58.abortController, 'optionalAccess', _59 => _59.signal])
1591
1662
  });
1592
1663
  this.emit("data", data);
1593
1664
  } catch (error) {
@@ -1616,7 +1687,7 @@ var PollingSource = class extends TypedEmitter {
1616
1687
  clearInterval(this.intervalId);
1617
1688
  this.intervalId = void 0;
1618
1689
  }
1619
- _optionalChain([this, 'access', _57 => _57.abortController, 'optionalAccess', _58 => _58.abort, 'call', _59 => _59()]);
1690
+ _optionalChain([this, 'access', _60 => _60.abortController, 'optionalAccess', _61 => _61.abort, 'call', _62 => _62()]);
1620
1691
  this.abortController = void 0;
1621
1692
  }
1622
1693
  };
@@ -1695,7 +1766,7 @@ async function connectStream(config, callbacks) {
1695
1766
  if (pingTimeoutId !== void 0) clearTimeout(pingTimeoutId);
1696
1767
  if (!initialDataReceived) return;
1697
1768
  pingTimeoutId = setTimeout(() => {
1698
- _optionalChain([responseBody, 'optionalAccess', _60 => _60.cancel, 'call', _61 => _61(), 'access', _62 => _62.catch, 'call', _63 => _63(() => {
1769
+ _optionalChain([responseBody, 'optionalAccess', _63 => _63.cancel, 'call', _64 => _64(), 'access', _65 => _65.catch, 'call', _66 => _66(() => {
1699
1770
  })]);
1700
1771
  connectionAbort.abort();
1701
1772
  }, PING_TIMEOUT_MS);
@@ -1717,7 +1788,7 @@ async function connectStream(config, callbacks) {
1717
1788
  if (vercelEnv) {
1718
1789
  headers["X-Vercel-Env"] = vercelEnv;
1719
1790
  }
1720
- const revision = _optionalChain([config, 'access', _64 => _64.revision, 'optionalCall', _65 => _65()]);
1791
+ const revision = _optionalChain([config, 'access', _67 => _67.revision, 'optionalCall', _68 => _68()]);
1721
1792
  if (revision !== void 0) {
1722
1793
  headers["X-Revision"] = String(revision);
1723
1794
  }
@@ -1763,7 +1834,7 @@ async function connectStream(config, callbacks) {
1763
1834
  let message;
1764
1835
  try {
1765
1836
  message = JSON.parse(line);
1766
- } catch (e8) {
1837
+ } catch (e10) {
1767
1838
  console.warn(
1768
1839
  "@vercel/flags-core: Failed to parse stream message, skipping"
1769
1840
  );
@@ -1779,7 +1850,7 @@ async function connectStream(config, callbacks) {
1779
1850
  resetPingTimeout();
1780
1851
  }
1781
1852
  if (message.type === "primed") {
1782
- _optionalChain([onPrimed, 'optionalCall', _66 => _66(message)]);
1853
+ _optionalChain([onPrimed, 'optionalCall', _69 => _69(message)]);
1783
1854
  retryCount = 0;
1784
1855
  if (!initialDataReceived) {
1785
1856
  initialDataReceived = true;
@@ -1802,7 +1873,7 @@ async function connectStream(config, callbacks) {
1802
1873
  clearTimeout(pingTimeoutId);
1803
1874
  abortController.signal.removeEventListener("abort", onMainAbort);
1804
1875
  if (!abortController.signal.aborted) {
1805
- _optionalChain([onDisconnect, 'optionalCall', _67 => _67()]);
1876
+ _optionalChain([onDisconnect, 'optionalCall', _70 => _70()]);
1806
1877
  retryCount++;
1807
1878
  const elapsed = Date.now() - lastAttemptTime;
1808
1879
  const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
@@ -1823,7 +1894,7 @@ async function connectStream(config, callbacks) {
1823
1894
  if (!connectionAbort.signal.aborted) {
1824
1895
  lastError = error;
1825
1896
  }
1826
- _optionalChain([onDisconnect, 'optionalCall', _68 => _68()]);
1897
+ _optionalChain([onDisconnect, 'optionalCall', _71 => _71()]);
1827
1898
  retryCount++;
1828
1899
  const elapsed = Date.now() - lastAttemptTime;
1829
1900
  const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
@@ -1902,7 +1973,7 @@ var StreamSource = class extends TypedEmitter {
1902
1973
  * Stop the stream connection.
1903
1974
  */
1904
1975
  stop() {
1905
- _optionalChain([this, 'access', _69 => _69.abortController, 'optionalAccess', _70 => _70.abort, 'call', _71 => _71()]);
1976
+ _optionalChain([this, 'access', _72 => _72.abortController, 'optionalAccess', _73 => _73.abort, 'call', _74 => _74()]);
1906
1977
  this.abortController = void 0;
1907
1978
  this.promise = void 0;
1908
1979
  }
@@ -1937,31 +2008,31 @@ function parseConfigUpdatedAt(value) {
1937
2008
  var Controller = (_class6 = class {
1938
2009
 
1939
2010
  // State machine
1940
- __init12() {this.state = "idle"}
2011
+ __init13() {this.state = "idle"}
1941
2012
  // Data state — tagged with origin
1942
2013
 
1943
2014
  // Memoized data spread for read() / getDatafile().
1944
2015
  // Rebuilt only when `this.data` reference changes (e.g. on stream/poll update).
1945
2016
  // Holds the result of stripping `_origin`; metrics are appended per-call.
1946
- __init13() {this.dataViewSource = void 0}
1947
- __init14() {this.dataViewBase = void 0}
2017
+ __init14() {this.dataViewSource = void 0}
2018
+ __init15() {this.dataViewBase = void 0}
1948
2019
  // Sources (I/O delegates)
1949
2020
 
1950
2021
 
1951
2022
 
1952
2023
  // Usage tracking
1953
2024
 
1954
- __init15() {this.isFirstGetData = true}
2025
+ __init16() {this.isFirstGetData = true}
1955
2026
  // Build-step deduplication
1956
- __init16() {this.buildDataPromise = null}
1957
- __init17() {this.buildReadTracked = false}
2027
+ __init17() {this.buildDataPromise = null}
2028
+ __init18() {this.buildReadTracked = false}
1958
2029
  // Suppresses usage tracking when the SDK key is unauthorized
1959
- __init18() {this.unauthorized = false}
1960
- constructor(options) {;_class6.prototype.__init12.call(this);_class6.prototype.__init13.call(this);_class6.prototype.__init14.call(this);_class6.prototype.__init15.call(this);_class6.prototype.__init16.call(this);_class6.prototype.__init17.call(this);_class6.prototype.__init18.call(this);_class6.prototype.__init19.call(this);_class6.prototype.__init20.call(this);_class6.prototype.__init21.call(this);_class6.prototype.__init22.call(this);_class6.prototype.__init23.call(this);_class6.prototype.__init24.call(this);
2030
+ __init19() {this.unauthorized = false}
2031
+ constructor(options) {;_class6.prototype.__init13.call(this);_class6.prototype.__init14.call(this);_class6.prototype.__init15.call(this);_class6.prototype.__init16.call(this);_class6.prototype.__init17.call(this);_class6.prototype.__init18.call(this);_class6.prototype.__init19.call(this);_class6.prototype.__init20.call(this);_class6.prototype.__init21.call(this);_class6.prototype.__init22.call(this);_class6.prototype.__init23.call(this);_class6.prototype.__init24.call(this);_class6.prototype.__init25.call(this);
1961
2032
  this.options = normalizeOptions(options);
1962
2033
  this.streamSource = new StreamSource(
1963
2034
  this.options,
1964
- () => _optionalChain([this, 'access', _72 => _72.data, 'optionalAccess', _73 => _73.revision])
2035
+ () => _optionalChain([this, 'access', _75 => _75.data, 'optionalAccess', _76 => _76.revision])
1965
2036
  );
1966
2037
  this.pollingSource = new PollingSource(this.options);
1967
2038
  this.bundledSource = new BundledSource({
@@ -1975,32 +2046,32 @@ var Controller = (_class6 = class {
1975
2046
  this.usageTracker = new UsageTracker(this.options);
1976
2047
  }
1977
2048
  // Source event handlers (stored for cleanup)
1978
- __init19() {this.onStreamData = (data) => {
2049
+ __init20() {this.onStreamData = (data) => {
1979
2050
  if (this.isNewerData(data)) {
1980
2051
  this.data = tagData(data, "stream");
1981
2052
  }
1982
2053
  }}
1983
- __init20() {this.onStreamPrimed = () => {
2054
+ __init21() {this.onStreamPrimed = () => {
1984
2055
  if (this.state === "degraded" || this.state === "initializing:stream") {
1985
2056
  this.transition("streaming");
1986
2057
  }
1987
2058
  }}
1988
- __init21() {this.onStreamConnected = () => {
2059
+ __init22() {this.onStreamConnected = () => {
1989
2060
  if (this.state === "degraded" || this.state === "initializing:stream") {
1990
2061
  this.transition("streaming");
1991
2062
  }
1992
2063
  }}
1993
- __init22() {this.onStreamDisconnected = () => {
2064
+ __init23() {this.onStreamDisconnected = () => {
1994
2065
  if (this.state === "streaming") {
1995
2066
  this.transition("degraded");
1996
2067
  }
1997
2068
  }}
1998
- __init23() {this.onPollData = (data) => {
2069
+ __init24() {this.onPollData = (data) => {
1999
2070
  if (this.isNewerData(data)) {
2000
2071
  this.data = tagData(data, "poll");
2001
2072
  }
2002
2073
  }}
2003
- __init24() {this.onPollError = (error) => {
2074
+ __init25() {this.onPollError = (error) => {
2004
2075
  console.error("@vercel/flags-core: Poll failed:", error);
2005
2076
  }}
2006
2077
  // ---------------------------------------------------------------------------
@@ -2069,7 +2140,7 @@ var Controller = (_class6 = class {
2069
2140
  if (bundled) {
2070
2141
  this.data = tagData(bundled, "bundled");
2071
2142
  }
2072
- } catch (e9) {
2143
+ } catch (e11) {
2073
2144
  }
2074
2145
  }
2075
2146
  if (this.data) {
@@ -2171,7 +2242,7 @@ var Controller = (_class6 = class {
2171
2242
  this.data = tagData(fetched, "fetched");
2172
2243
  result = this.data;
2173
2244
  cacheStatus = "MISS";
2174
- } catch (e10) {
2245
+ } catch (e12) {
2175
2246
  throw new Error(
2176
2247
  "@vercel/flags-core: No flag definitions available. Initialize the client or provide a datafile."
2177
2248
  );
@@ -2290,7 +2361,7 @@ var Controller = (_class6 = class {
2290
2361
  return true;
2291
2362
  }
2292
2363
  return false;
2293
- } catch (e11) {
2364
+ } catch (e13) {
2294
2365
  return false;
2295
2366
  }
2296
2367
  }
@@ -2315,7 +2386,7 @@ var Controller = (_class6 = class {
2315
2386
  return true;
2316
2387
  }
2317
2388
  return false;
2318
- } catch (e12) {
2389
+ } catch (e14) {
2319
2390
  clearTimeout(timeoutId);
2320
2391
  return false;
2321
2392
  }
@@ -2365,7 +2436,7 @@ var Controller = (_class6 = class {
2365
2436
  fetch: this.options.fetch
2366
2437
  });
2367
2438
  return tagData(fetched, "fetched");
2368
- } catch (e13) {
2439
+ } catch (e15) {
2369
2440
  }
2370
2441
  throw new Error(
2371
2442
  "@vercel/flags-core: No flag definitions available during build. Provide a datafile or bundled definitions."
@@ -2399,7 +2470,7 @@ var Controller = (_class6 = class {
2399
2470
  this.data = tagData(fetched, "fetched");
2400
2471
  this.transition("degraded");
2401
2472
  return;
2402
- } catch (e14) {
2473
+ } catch (e16) {
2403
2474
  }
2404
2475
  }
2405
2476
  throw new Error(
@@ -2451,7 +2522,7 @@ var Controller = (_class6 = class {
2451
2522
  this.data = tagData(fetched, "fetched");
2452
2523
  this.transition("degraded");
2453
2524
  return [this.data, "MISS"];
2454
- } catch (e15) {
2525
+ } catch (e17) {
2455
2526
  }
2456
2527
  }
2457
2528
  throw new Error(
@@ -2503,11 +2574,11 @@ var Controller = (_class6 = class {
2503
2574
  duration: Date.now() - startTime,
2504
2575
  mode: mode === "streaming" ? "stream" : mode === "polling" ? "poll" : mode
2505
2576
  };
2506
- const configUpdatedAt = _optionalChain([this, 'access', _74 => _74.data, 'optionalAccess', _75 => _75.configUpdatedAt]);
2577
+ const configUpdatedAt = _optionalChain([this, 'access', _77 => _77.data, 'optionalAccess', _78 => _78.configUpdatedAt]);
2507
2578
  if (typeof configUpdatedAt === "number") {
2508
2579
  trackOptions.configUpdatedAt = configUpdatedAt;
2509
2580
  }
2510
- const revision = _optionalChain([this, 'access', _76 => _76.data, 'optionalAccess', _77 => _77.revision]);
2581
+ const revision = _optionalChain([this, 'access', _79 => _79.data, 'optionalAccess', _80 => _80.revision]);
2511
2582
  if (typeof revision === "number") {
2512
2583
  trackOptions.revision = revision;
2513
2584
  }
@@ -2540,7 +2611,7 @@ function parseSdkKeyFromFlagsConnectionString(text) {
2540
2611
  const params = new URLSearchParams(text.slice(6));
2541
2612
  const sdkKey = params.get("sdkKey");
2542
2613
  if (sdkKey && SDK_KEY_REGEX.test(sdkKey)) return sdkKey;
2543
- } catch (e16) {
2614
+ } catch (e18) {
2544
2615
  }
2545
2616
  return null;
2546
2617
  }
@@ -2549,7 +2620,7 @@ function parseSdkKeyFromFlagsConnectionString(text) {
2549
2620
  async function getOidcToken() {
2550
2621
  try {
2551
2622
  return await _oidc.getVercelOidcToken.call(void 0, );
2552
- } catch (e17) {
2623
+ } catch (e19) {
2553
2624
  throw new Error(
2554
2625
  [
2555
2626
  "@vercel/flags-core: Failed to get OIDC token.",
@@ -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 } = _nullishCoalesce(createClientOptions, () => ( {}));
2621
2692
  const auth = new Authentication(sdkKeyOrConnectionString);
2622
- const controller = new Controller({ auth, ...controllerOptions });
2693
+ const waitUntil3 = _nullishCoalesce(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: _functions.waitUntil });
2665
2742
 
2666
2743
 
2667
2744
 
@@ -2673,4 +2750,4 @@ var {
2673
2750
 
2674
2751
 
2675
2752
  exports.ResolutionReason = ResolutionReason; exports.evaluate = evaluate; exports.FallbackNotFoundError = FallbackNotFoundError; exports.FallbackEntryNotFoundError = FallbackEntryNotFoundError; exports.Controller = Controller; exports.flagsClient = flagsClient; exports.resetDefaultFlagsClient = resetDefaultFlagsClient; exports.createClient = createClient;
2676
- //# sourceMappingURL=chunk-CVDAHZHK.cjs.map
2753
+ //# sourceMappingURL=chunk-2WO37TKH.cjs.map