@vercel/flags-core 1.9.0-ea4d7de-20260922121332 → 1.9.0
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 +20 -2
- package/dist/{chunk-L6QJOXJN.cjs → chunk-PGKH4XQ7.cjs} +385 -353
- package/dist/chunk-PGKH4XQ7.cjs.map +1 -0
- package/dist/{chunk-FKI4L3NY.js → chunk-SLNLPFKU.js} +354 -322
- package/dist/chunk-SLNLPFKU.js.map +1 -0
- package/dist/{chunk-C22F472K.js → chunk-TDZBUVBG.js} +354 -322
- package/dist/chunk-TDZBUVBG.js.map +1 -0
- package/dist/{chunk-VQ3WFG4Y.cjs → chunk-W742GGZU.cjs} +386 -354
- package/dist/chunk-W742GGZU.cjs.map +1 -0
- package/dist/index.default.cjs +2 -2
- package/dist/index.default.d.cts +10 -28
- package/dist/index.default.d.ts +10 -28
- package/dist/index.default.js +1 -1
- package/dist/index.next-js.cjs +2 -2
- package/dist/index.next-js.d.cts +10 -28
- package/dist/index.next-js.d.ts +10 -28
- package/dist/index.next-js.js +1 -1
- package/dist/openfeature.default.cjs +2 -2
- package/dist/openfeature.default.d.cts +1 -1
- package/dist/openfeature.default.d.ts +1 -1
- package/dist/openfeature.default.js +1 -1
- package/dist/openfeature.next-js.cjs +2 -2
- package/dist/openfeature.next-js.d.cts +1 -1
- package/dist/openfeature.next-js.d.ts +1 -1
- package/dist/openfeature.next-js.js +1 -1
- package/dist/{types-WeTcbSQF.d.cts → types-1OK52NNi.d.cts} +10 -8
- package/dist/{types-WeTcbSQF.d.ts → types-1OK52NNi.d.ts} +10 -8
- package/package.json +4 -4
- package/dist/chunk-C22F472K.js.map +0 -1
- package/dist/chunk-FKI4L3NY.js.map +0 -1
- package/dist/chunk-L6QJOXJN.cjs.map +0 -1
- package/dist/chunk-VQ3WFG4Y.cjs.map +0 -1
|
@@ -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(); } }
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6;var __defProp = Object.defineProperty;
|
|
2
2
|
var __export = (target, all) => {
|
|
3
3
|
for (var name in all)
|
|
4
4
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -73,7 +73,7 @@ function getSplitBoundaries(outcome) {
|
|
|
73
73
|
function getCompiledRegex(rhs) {
|
|
74
74
|
const cached = compiledRegexCache.get(rhs);
|
|
75
75
|
if (cached) return cached;
|
|
76
|
-
const compiled = new RegExp(rhs.pattern, rhs.flags.replace(/[gy]
|
|
76
|
+
const compiled = new RegExp(rhs.pattern, rhs.flags.replace(/[gy]/g, ""));
|
|
77
77
|
compiledRegexCache.set(rhs, compiled);
|
|
78
78
|
return compiled;
|
|
79
79
|
}
|
|
@@ -450,7 +450,7 @@ function resolveOutcome(params, outcome) {
|
|
|
450
450
|
break;
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
|
-
if (exhausted) currentPromille = PROMILLE_SCALE;
|
|
453
|
+
if (exhausted) currentPromille = _nullishCoalesce(outcome.finalPromille, () => ( PROMILLE_SCALE));
|
|
454
454
|
if (currentPromille <= 0) {
|
|
455
455
|
return {
|
|
456
456
|
...rollFromVariant,
|
|
@@ -576,7 +576,7 @@ function bulkEvaluate(flags, shared) {
|
|
|
576
576
|
}
|
|
577
577
|
|
|
578
578
|
// package.json
|
|
579
|
-
var version = "1.9.0
|
|
579
|
+
var version = "1.9.0";
|
|
580
580
|
|
|
581
581
|
// src/lib/report-value.ts
|
|
582
582
|
function internalReportValue(key, value, data) {
|
|
@@ -1040,6 +1040,126 @@ async function readBundledDefinitions(auth) {
|
|
|
1040
1040
|
// src/utils/ingest.ts
|
|
1041
1041
|
var _oidc = require('@vercel/oidc');
|
|
1042
1042
|
|
|
1043
|
+
// src/controller/auth.ts
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
// src/utils/sdk-keys.ts
|
|
1047
|
+
var SDK_KEY_REGEX = /^vf_(?:server|client)_/;
|
|
1048
|
+
function isValidSdkKey(value) {
|
|
1049
|
+
return SDK_KEY_REGEX.test(value);
|
|
1050
|
+
}
|
|
1051
|
+
var PROJECT_ID_REGEX = /^[A-Za-z0-9_]{1,64}$/;
|
|
1052
|
+
function isValidProjectId(value) {
|
|
1053
|
+
return PROJECT_ID_REGEX.test(value);
|
|
1054
|
+
}
|
|
1055
|
+
function parseFlagsConnectionString(text) {
|
|
1056
|
+
if (SDK_KEY_REGEX.test(text)) return { sdkKey: text, projectId: null };
|
|
1057
|
+
if (!text.startsWith("flags:")) return null;
|
|
1058
|
+
try {
|
|
1059
|
+
const params = new URLSearchParams(text.slice(6));
|
|
1060
|
+
return {
|
|
1061
|
+
sdkKey: params.get("sdkKey") || null,
|
|
1062
|
+
projectId: params.get("projectId") || null
|
|
1063
|
+
};
|
|
1064
|
+
} catch (e5) {
|
|
1065
|
+
return null;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// src/controller/auth.ts
|
|
1070
|
+
var SOURCE_PROJECT_HEADER = "X-Vercel-Flags-Project-Id";
|
|
1071
|
+
function authHeaders(token, sourceProjectId) {
|
|
1072
|
+
return {
|
|
1073
|
+
Authorization: `Bearer ${token}`,
|
|
1074
|
+
...sourceProjectId ? { [SOURCE_PROJECT_HEADER]: sourceProjectId } : null
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
function unauthorizedMessage(sourceProjectId) {
|
|
1078
|
+
if (!sourceProjectId) return "unauthorized (401)";
|
|
1079
|
+
return `unauthorized (401): this deployment is not allowed to read the flags of project "${sourceProjectId}". Check the connection string and that the project is connected.`;
|
|
1080
|
+
}
|
|
1081
|
+
async function getOidcToken() {
|
|
1082
|
+
try {
|
|
1083
|
+
return await _oidc.getVercelOidcToken.call(void 0, );
|
|
1084
|
+
} catch (e6) {
|
|
1085
|
+
throw new Error(
|
|
1086
|
+
[
|
|
1087
|
+
"@vercel/flags-core: Failed to get OIDC token.",
|
|
1088
|
+
"Are you running in a Vercel Environment where OIDC tokens are available?",
|
|
1089
|
+
"Did you mean to use an SDK Key instead? Use the environment variable FLAGS or pass it directly to the client."
|
|
1090
|
+
].join(" ")
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
function getProjectIdFromOidcToken(oidcToken) {
|
|
1095
|
+
const tokenParts = oidcToken.split(".");
|
|
1096
|
+
if (tokenParts.length !== 3 || !tokenParts[1]) {
|
|
1097
|
+
throw new Error("@vercel/flags-core: Invalid OIDC token");
|
|
1098
|
+
}
|
|
1099
|
+
const payload = JSON.parse(
|
|
1100
|
+
Buffer.from(tokenParts[1], "base64url").toString("utf8")
|
|
1101
|
+
);
|
|
1102
|
+
if (typeof payload.project_id !== "string" || !payload.project_id) {
|
|
1103
|
+
throw new Error(
|
|
1104
|
+
"@vercel/flags-core: Missing project_id claim in OIDC token"
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
return payload.project_id;
|
|
1108
|
+
}
|
|
1109
|
+
var Authentication = class {
|
|
1110
|
+
|
|
1111
|
+
|
|
1112
|
+
constructor(sdkKeyOrConnectionString) {
|
|
1113
|
+
if (sdkKeyOrConnectionString !== void 0) {
|
|
1114
|
+
if (typeof sdkKeyOrConnectionString !== "string") {
|
|
1115
|
+
throw new Error(
|
|
1116
|
+
`@vercel/flags-core: Invalid sdkKey. Expected string, got ${typeof sdkKeyOrConnectionString}`
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
const parsed = parseFlagsConnectionString(sdkKeyOrConnectionString);
|
|
1120
|
+
if (_optionalChain([parsed, 'optionalAccess', _31 => _31.sdkKey]) && parsed.projectId) {
|
|
1121
|
+
throw new Error(
|
|
1122
|
+
"@vercel/flags-core: A connection string must contain either sdkKey or projectId, not both"
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
if (_optionalChain([parsed, 'optionalAccess', _32 => _32.sdkKey])) {
|
|
1126
|
+
if (!isValidSdkKey(parsed.sdkKey)) {
|
|
1127
|
+
throw new Error("@vercel/flags-core: Missing sdkKey");
|
|
1128
|
+
}
|
|
1129
|
+
this.sdkKey = parsed.sdkKey;
|
|
1130
|
+
} else if (_optionalChain([parsed, 'optionalAccess', _33 => _33.projectId])) {
|
|
1131
|
+
if (!isValidProjectId(parsed.projectId)) {
|
|
1132
|
+
throw new Error(
|
|
1133
|
+
"@vercel/flags-core: Invalid projectId in connection string"
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
this.sourceProjectId = parsed.projectId;
|
|
1137
|
+
} else {
|
|
1138
|
+
throw new Error("@vercel/flags-core: Missing sdkKey");
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
async resolveToken() {
|
|
1143
|
+
if (this.sdkKey) {
|
|
1144
|
+
return this.sdkKey;
|
|
1145
|
+
}
|
|
1146
|
+
return await getOidcToken();
|
|
1147
|
+
}
|
|
1148
|
+
async resolveBundledDefinitionsLookup() {
|
|
1149
|
+
if (this.sdkKey) {
|
|
1150
|
+
return { type: "sdk-key", sdkKey: this.sdkKey };
|
|
1151
|
+
}
|
|
1152
|
+
if (this.sourceProjectId) {
|
|
1153
|
+
return { type: "project-id", projectId: this.sourceProjectId };
|
|
1154
|
+
}
|
|
1155
|
+
const oidcToken = await this.resolveToken();
|
|
1156
|
+
return {
|
|
1157
|
+
type: "project-id",
|
|
1158
|
+
projectId: getProjectIdFromOidcToken(oidcToken)
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1043
1163
|
// src/utils/backoff.ts
|
|
1044
1164
|
var DEFAULT_BASE_MS = 250;
|
|
1045
1165
|
var DEFAULT_CAP_MS = 5e3;
|
|
@@ -1055,12 +1175,26 @@ function getJitteredWaitMs(baseMs, ratio) {
|
|
|
1055
1175
|
return Math.floor(min + Math.random() * span);
|
|
1056
1176
|
}
|
|
1057
1177
|
|
|
1178
|
+
// src/utils/runtime-ingest.ts
|
|
1179
|
+
var FLAGS_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("@vercel/flags-context");
|
|
1180
|
+
function getRuntimeIngestFor(auth) {
|
|
1181
|
+
return auth.sourceProjectId ? void 0 : getRuntimeIngest();
|
|
1182
|
+
}
|
|
1183
|
+
function getRuntimeIngest() {
|
|
1184
|
+
try {
|
|
1185
|
+
const context = globalThis[FLAGS_CONTEXT_SYMBOL];
|
|
1186
|
+
return typeof _optionalChain([context, 'optionalAccess', _34 => _34.ingest]) === "function" ? context.ingest : void 0;
|
|
1187
|
+
} catch (e7) {
|
|
1188
|
+
return void 0;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1058
1192
|
// src/utils/ingest.ts
|
|
1059
1193
|
var MAX_RETRIES = 3;
|
|
1060
1194
|
var MAX_EVENTS_PER_REQUEST = 2e3;
|
|
1061
1195
|
var EVALUATING_OIDC_TOKEN_HEADER = "X-Vercel-Flags-OIDC-Token";
|
|
1062
1196
|
var FLUSH_REASON_HEADER = "X-Vercel-Flags-Flush-Reason";
|
|
1063
|
-
var isDebugMode = _optionalChain([process, 'access',
|
|
1197
|
+
var isDebugMode = _optionalChain([process, 'access', _35 => _35.env, 'access', _36 => _36.DEBUG, 'optionalAccess', _37 => _37.includes, 'call', _38 => _38("@vercel/flags-core")]);
|
|
1064
1198
|
var debugLog = (...args) => {
|
|
1065
1199
|
if (!isDebugMode) return;
|
|
1066
1200
|
console.log(...args);
|
|
@@ -1069,7 +1203,7 @@ async function getEvaluatingOidcToken(auth) {
|
|
|
1069
1203
|
if (!auth.sdkKey) return void 0;
|
|
1070
1204
|
try {
|
|
1071
1205
|
return await _oidc.getVercelOidcToken.call(void 0, );
|
|
1072
|
-
} catch (
|
|
1206
|
+
} catch (e8) {
|
|
1073
1207
|
return void 0;
|
|
1074
1208
|
}
|
|
1075
1209
|
}
|
|
@@ -1078,7 +1212,7 @@ async function getIngestHeaders(options, flushReason) {
|
|
|
1078
1212
|
const evaluatingOidcToken = await getEvaluatingOidcToken(options.auth);
|
|
1079
1213
|
return {
|
|
1080
1214
|
"Content-Type": "application/json",
|
|
1081
|
-
|
|
1215
|
+
...authHeaders(token, options.auth.sourceProjectId),
|
|
1082
1216
|
"User-Agent": `VercelFlagsCore/${version}`,
|
|
1083
1217
|
[FLUSH_REASON_HEADER]: flushReason,
|
|
1084
1218
|
..._nullishCoalesce(options.metricEnvironment, () => ( process.env.VERCEL_ENV)) ? {
|
|
@@ -1088,8 +1222,28 @@ async function getIngestHeaders(options, flushReason) {
|
|
|
1088
1222
|
...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
|
|
1089
1223
|
};
|
|
1090
1224
|
}
|
|
1225
|
+
function getRuntimeIngestHeaders(options, flushReason) {
|
|
1226
|
+
return {
|
|
1227
|
+
"Content-Type": "application/json",
|
|
1228
|
+
...options.auth.sdkKey ? { Authorization: `Bearer ${options.auth.sdkKey}` } : null,
|
|
1229
|
+
"User-Agent": `VercelFlagsCore/${version}`,
|
|
1230
|
+
[FLUSH_REASON_HEADER]: flushReason,
|
|
1231
|
+
..._nullishCoalesce(options.metricEnvironment, () => ( process.env.VERCEL_ENV)) ? {
|
|
1232
|
+
"X-Vercel-Env": _nullishCoalesce(options.metricEnvironment, () => ( process.env.VERCEL_ENV))
|
|
1233
|
+
} : null,
|
|
1234
|
+
...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1091
1237
|
async function sendIngestEvents(options, events, flushId, flushReason) {
|
|
1092
|
-
|
|
1238
|
+
let eventsToSend = events.map((event) => event.ingestEvent());
|
|
1239
|
+
const runtimeIngest = getRuntimeIngestFor(options.auth);
|
|
1240
|
+
if (runtimeIngest) {
|
|
1241
|
+
const headers = getRuntimeIngestHeaders(options, flushReason);
|
|
1242
|
+
eventsToSend = eventsToSend.filter(
|
|
1243
|
+
(event) => !runtimeIngest({ headers, body: [event] })
|
|
1244
|
+
);
|
|
1245
|
+
if (eventsToSend.length === 0) return;
|
|
1246
|
+
}
|
|
1093
1247
|
for (let i = 0; i < eventsToSend.length; i += MAX_EVENTS_PER_REQUEST) {
|
|
1094
1248
|
await sendIngestChunk(
|
|
1095
1249
|
options,
|
|
@@ -1139,7 +1293,7 @@ var SYMBOL_FOR_REQ_CONTEXT = /* @__PURE__ */ Symbol.for("@vercel/request-context
|
|
|
1139
1293
|
var fromSymbol = globalThis;
|
|
1140
1294
|
function getRequestContext() {
|
|
1141
1295
|
try {
|
|
1142
|
-
const ctx = _optionalChain([fromSymbol, 'access',
|
|
1296
|
+
const ctx = _optionalChain([fromSymbol, 'access', _39 => _39[SYMBOL_FOR_REQ_CONTEXT], 'optionalAccess', _40 => _40.get, 'optionalCall', _41 => _41()]);
|
|
1143
1297
|
if (ctx && Object.hasOwn(ctx, "headers")) {
|
|
1144
1298
|
return {
|
|
1145
1299
|
ctx,
|
|
@@ -1147,7 +1301,7 @@ function getRequestContext() {
|
|
|
1147
1301
|
};
|
|
1148
1302
|
}
|
|
1149
1303
|
return { ctx, headers: void 0 };
|
|
1150
|
-
} catch (
|
|
1304
|
+
} catch (e9) {
|
|
1151
1305
|
return { ctx: void 0, headers: void 0 };
|
|
1152
1306
|
}
|
|
1153
1307
|
}
|
|
@@ -1179,7 +1333,7 @@ var Scheduler = (_class = class {
|
|
|
1179
1333
|
})();
|
|
1180
1334
|
try {
|
|
1181
1335
|
this.scheduleTask(this.pending);
|
|
1182
|
-
} catch (
|
|
1336
|
+
} catch (e10) {
|
|
1183
1337
|
}
|
|
1184
1338
|
this.maxTimeout = setTimeout(
|
|
1185
1339
|
() => this.resolveScheduledFlush("max_timeout"),
|
|
@@ -1206,7 +1360,7 @@ var Scheduler = (_class = class {
|
|
|
1206
1360
|
}
|
|
1207
1361
|
resolveScheduledFlush(reason) {
|
|
1208
1362
|
this.clearTimeouts();
|
|
1209
|
-
_optionalChain([this, 'access',
|
|
1363
|
+
_optionalChain([this, 'access', _42 => _42.resolveWait, 'optionalCall', _43 => _43(reason)]);
|
|
1210
1364
|
}
|
|
1211
1365
|
reset() {
|
|
1212
1366
|
this.pending = null;
|
|
@@ -1325,11 +1479,14 @@ var UsageTracker = (_class4 = class {
|
|
|
1325
1479
|
__init7() {this.flushCount = 0}
|
|
1326
1480
|
|
|
1327
1481
|
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1482
|
+
|
|
1483
|
+
__init8() {this.inflightFlushes = /* @__PURE__ */ new Set()}
|
|
1484
|
+
__init9() {this.trackedRequests = /* @__PURE__ */ new WeakSet()}
|
|
1485
|
+
__init10() {this.readEvents = []}
|
|
1486
|
+
__init11() {this.evaluationEvents = /* @__PURE__ */ new Map()}
|
|
1487
|
+
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);
|
|
1332
1488
|
this.options = options;
|
|
1489
|
+
this.waitUntil = options.waitUntil;
|
|
1333
1490
|
this.scheduler = new Scheduler(
|
|
1334
1491
|
(reason) => this.flushEvents(reason),
|
|
1335
1492
|
options.waitUntil
|
|
@@ -1342,6 +1499,7 @@ var UsageTracker = (_class4 = class {
|
|
|
1342
1499
|
async shutdown() {
|
|
1343
1500
|
await this.scheduler.shutdown();
|
|
1344
1501
|
await this.flushEvents("shutdown");
|
|
1502
|
+
await Promise.all([...this.inflightFlushes]);
|
|
1345
1503
|
}
|
|
1346
1504
|
/**
|
|
1347
1505
|
* Tracks a config read event. Deduplicates by request context.
|
|
@@ -1353,7 +1511,7 @@ var UsageTracker = (_class4 = class {
|
|
|
1353
1511
|
if (this.trackedRequests.has(ctx)) return;
|
|
1354
1512
|
this.trackedRequests.add(ctx);
|
|
1355
1513
|
this.readEvents.push(new FlagsConfigReadEvent(headers, options));
|
|
1356
|
-
this.
|
|
1514
|
+
this.requestFlush();
|
|
1357
1515
|
} catch (error) {
|
|
1358
1516
|
console.error("@vercel/flags-core: Failed to record event:", error);
|
|
1359
1517
|
}
|
|
@@ -1377,7 +1535,7 @@ var UsageTracker = (_class4 = class {
|
|
|
1377
1535
|
new FlagsEvaluationEvent(bucketedOptions)
|
|
1378
1536
|
);
|
|
1379
1537
|
}
|
|
1380
|
-
this.
|
|
1538
|
+
this.requestFlush();
|
|
1381
1539
|
} catch (error) {
|
|
1382
1540
|
console.error(
|
|
1383
1541
|
"@vercel/flags-core: Failed to record evaluation event:",
|
|
@@ -1385,6 +1543,25 @@ var UsageTracker = (_class4 = class {
|
|
|
1385
1543
|
);
|
|
1386
1544
|
}
|
|
1387
1545
|
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Flushes immediately when the runtime provides an ingest transport,
|
|
1548
|
+
* otherwise falls back to the time-based scheduler.
|
|
1549
|
+
*/
|
|
1550
|
+
requestFlush() {
|
|
1551
|
+
if (getRuntimeIngestFor(this.options.auth)) {
|
|
1552
|
+
const flush = this.flushEvents("immediate").catch((error) => {
|
|
1553
|
+
console.error("@vercel/flags-core: Failed to flush events:", error);
|
|
1554
|
+
});
|
|
1555
|
+
this.inflightFlushes.add(flush);
|
|
1556
|
+
void flush.finally(() => this.inflightFlushes.delete(flush));
|
|
1557
|
+
try {
|
|
1558
|
+
_optionalChain([this, 'access', _44 => _44.waitUntil, 'optionalCall', _45 => _45(flush)]);
|
|
1559
|
+
} catch (e11) {
|
|
1560
|
+
}
|
|
1561
|
+
} else {
|
|
1562
|
+
this.scheduler.scheduleFlush();
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1388
1565
|
/**
|
|
1389
1566
|
* Send all events to the ingest service
|
|
1390
1567
|
*/
|
|
@@ -1457,7 +1634,7 @@ var BundledSource = class {
|
|
|
1457
1634
|
*/
|
|
1458
1635
|
async tryLoad() {
|
|
1459
1636
|
const result = await this.getResult();
|
|
1460
|
-
if (_optionalChain([result, 'optionalAccess',
|
|
1637
|
+
if (_optionalChain([result, 'optionalAccess', _46 => _46.state]) === "ok" && result.definitions) {
|
|
1461
1638
|
return result.definitions;
|
|
1462
1639
|
}
|
|
1463
1640
|
return void 0;
|
|
@@ -1472,192 +1649,91 @@ var BundledSource = class {
|
|
|
1472
1649
|
|
|
1473
1650
|
// src/controller/fetch-datafile.ts
|
|
1474
1651
|
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
1652
|
+
var DEFAULT_MAX_ATTEMPTS = 3;
|
|
1653
|
+
var DatafileHttpError = class extends Error {
|
|
1654
|
+
constructor(status, statusText) {
|
|
1655
|
+
super(`Failed to fetch data: ${statusText}`);
|
|
1656
|
+
this.status = status;
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
get retryable() {
|
|
1660
|
+
return this.status === 408 || this.status === 429 || this.status >= 500;
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1475
1663
|
async function fetchDatafile(options) {
|
|
1476
|
-
|
|
1664
|
+
_optionalChain([options, 'access', _47 => _47.signal, 'optionalAccess', _48 => _48.throwIfAborted, 'call', _49 => _49()]);
|
|
1665
|
+
const maxAttempts = _nullishCoalesce(options.maxAttempts, () => ( DEFAULT_MAX_ATTEMPTS));
|
|
1666
|
+
if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {
|
|
1667
|
+
throw new Error("maxAttempts must be a positive integer");
|
|
1668
|
+
}
|
|
1477
1669
|
const controller = new AbortController();
|
|
1670
|
+
const { signal } = controller;
|
|
1671
|
+
let rejectAbort;
|
|
1672
|
+
const aborted = new Promise((_, reject) => {
|
|
1673
|
+
rejectAbort = reject;
|
|
1674
|
+
});
|
|
1675
|
+
const onAbort = () => rejectAbort(signal.reason);
|
|
1676
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1677
|
+
const onExternalAbort = () => controller.abort(_optionalChain([options, 'access', _50 => _50.signal, 'optionalAccess', _51 => _51.reason]));
|
|
1678
|
+
_optionalChain([options, 'access', _52 => _52.signal, 'optionalAccess', _53 => _53.addEventListener, 'call', _54 => _54("abort", onExternalAbort, { once: true })]);
|
|
1478
1679
|
const timeoutId = setTimeout(
|
|
1479
|
-
() => controller.abort(
|
|
1680
|
+
() => controller.abort(
|
|
1681
|
+
new Error("@vercel/flags-core: Datafile fetch deadline exceeded")
|
|
1682
|
+
),
|
|
1480
1683
|
DEFAULT_FETCH_TIMEOUT_MS
|
|
1481
1684
|
);
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
throw new Error("Fetch aborted");
|
|
1487
|
-
}
|
|
1488
|
-
options.signal.addEventListener("abort", onExternalAbort, { once: true });
|
|
1489
|
-
}
|
|
1490
|
-
try {
|
|
1685
|
+
let delay;
|
|
1686
|
+
const fetchAttempt = async () => {
|
|
1687
|
+
const token = await options.auth.resolveToken();
|
|
1688
|
+
signal.throwIfAborted();
|
|
1491
1689
|
const res = await options.fetch(`${options.host}/v1/datafile`, {
|
|
1492
1690
|
headers: {
|
|
1493
|
-
|
|
1691
|
+
...authHeaders(token, options.auth.sourceProjectId),
|
|
1494
1692
|
"User-Agent": `VercelFlagsCore/${version}`,
|
|
1495
1693
|
...process.env.VERCEL_ENV ? { "X-Vercel-Env": process.env.VERCEL_ENV } : null
|
|
1496
1694
|
},
|
|
1497
|
-
signal
|
|
1695
|
+
signal
|
|
1498
1696
|
});
|
|
1499
|
-
|
|
1500
|
-
_optionalChain([options, 'access', _41 => _41.signal, 'optionalAccess', _42 => _42.removeEventListener, 'call', _43 => _43("abort", onExternalAbort)]);
|
|
1697
|
+
signal.throwIfAborted();
|
|
1501
1698
|
if (!res.ok) {
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
throw error instanceof Error ? error : new Error("Unknown fetch error");
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
// src/controller/tagged-data.ts
|
|
1513
|
-
function tagData(data, origin) {
|
|
1514
|
-
const tagged = { ...data, _origin: origin };
|
|
1515
|
-
if (origin === "fetched" || origin === "poll" || origin === "stream") {
|
|
1516
|
-
tagged.fetchedAt = Date.now();
|
|
1517
|
-
} else if (typeof data.fetchedAt !== "number" || !Number.isFinite(data.fetchedAt) || data.fetchedAt < 0) {
|
|
1518
|
-
delete tagged.fetchedAt;
|
|
1519
|
-
}
|
|
1520
|
-
return tagged;
|
|
1521
|
-
}
|
|
1522
|
-
function originToMetricsSource(origin) {
|
|
1523
|
-
switch (origin) {
|
|
1524
|
-
case "stream":
|
|
1525
|
-
case "poll":
|
|
1526
|
-
case "provided":
|
|
1527
|
-
return "in-memory";
|
|
1528
|
-
case "fetched":
|
|
1529
|
-
return "remote";
|
|
1530
|
-
case "bundled":
|
|
1531
|
-
return "embedded";
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
// src/controller/typed-emitter.ts
|
|
1536
|
-
var TypedEmitter = (_class5 = class {constructor() { _class5.prototype.__init11.call(this); }
|
|
1537
|
-
__init11() {this.handlers = /* @__PURE__ */ new Map()}
|
|
1538
|
-
on(event, handler) {
|
|
1539
|
-
let set = this.handlers.get(event);
|
|
1540
|
-
if (!set) {
|
|
1541
|
-
set = /* @__PURE__ */ new Set();
|
|
1542
|
-
this.handlers.set(event, set);
|
|
1543
|
-
}
|
|
1544
|
-
set.add(handler);
|
|
1545
|
-
}
|
|
1546
|
-
off(event, handler) {
|
|
1547
|
-
_optionalChain([this, 'access', _47 => _47.handlers, 'access', _48 => _48.get, 'call', _49 => _49(event), 'optionalAccess', _50 => _50.delete, 'call', _51 => _51(handler)]);
|
|
1548
|
-
}
|
|
1549
|
-
emit(event, ...args) {
|
|
1550
|
-
const set = this.handlers.get(event);
|
|
1551
|
-
if (set) {
|
|
1552
|
-
for (const handler of set) {
|
|
1553
|
-
handler(...args);
|
|
1554
|
-
}
|
|
1699
|
+
void _optionalChain([res, 'access', _55 => _55.body, 'optionalAccess', _56 => _56.cancel, 'call', _57 => _57(), 'access', _58 => _58.catch, 'call', _59 => _59(() => {
|
|
1700
|
+
})]);
|
|
1701
|
+
throw new DatafileHttpError(
|
|
1702
|
+
res.status,
|
|
1703
|
+
res.status === 401 && options.auth.sourceProjectId ? unauthorizedMessage(options.auth.sourceProjectId) : res.statusText
|
|
1704
|
+
);
|
|
1555
1705
|
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
}
|
|
1570
|
-
fetchDatafile() {
|
|
1571
|
-
if (this.promise) return this.promise;
|
|
1572
|
-
const abortController = new AbortController();
|
|
1573
|
-
this.abortController = abortController;
|
|
1574
|
-
this.promise = fetchDatafile({
|
|
1575
|
-
...this.options,
|
|
1576
|
-
signal: abortController.signal
|
|
1577
|
-
}).then((data) => {
|
|
1578
|
-
abortController.signal.throwIfAborted();
|
|
1579
|
-
this.emit("data", data);
|
|
1580
|
-
return data;
|
|
1581
|
-
}).finally(() => {
|
|
1582
|
-
if (this.abortController === abortController) {
|
|
1583
|
-
this.promise = void 0;
|
|
1584
|
-
this.abortController = void 0;
|
|
1706
|
+
const data = await res.json();
|
|
1707
|
+
signal.throwIfAborted();
|
|
1708
|
+
return data;
|
|
1709
|
+
};
|
|
1710
|
+
try {
|
|
1711
|
+
for (let attempt = 0; ; attempt++) {
|
|
1712
|
+
if (attempt > 0) {
|
|
1713
|
+
await Promise.race([
|
|
1714
|
+
new Promise((resolve) => {
|
|
1715
|
+
delay = setTimeout(resolve, 100 * 2 ** (attempt - 1));
|
|
1716
|
+
}),
|
|
1717
|
+
aborted
|
|
1718
|
+
]);
|
|
1585
1719
|
}
|
|
1586
|
-
|
|
1587
|
-
return this.promise;
|
|
1588
|
-
}
|
|
1589
|
-
getUpdatedAtHeader(projectId, header) {
|
|
1590
|
-
if (!header) {
|
|
1591
|
-
return;
|
|
1592
|
-
}
|
|
1593
|
-
const prefix = `flags_${projectId}=`;
|
|
1594
|
-
const value = _optionalChain([header, 'access', _52 => _52.split, 'call', _53 => _53(";"), 'access', _54 => _54.map, 'call', _55 => _55((part) => part.trim()), 'access', _56 => _56.find, 'call', _57 => _57((part) => part.startsWith(prefix)), 'optionalAccess', _58 => _58.slice, 'call', _59 => _59(prefix.length)]);
|
|
1595
|
-
const timestamp = Number(value);
|
|
1596
|
-
return Number.isFinite(timestamp) && timestamp > 0 ? timestamp : void 0;
|
|
1597
|
-
}
|
|
1598
|
-
async resolveData(currentData, updatedAtHeader) {
|
|
1599
|
-
if (!currentData.configUpdatedAt) {
|
|
1600
|
-
return;
|
|
1601
|
-
}
|
|
1602
|
-
if (!updatedAtHeader) {
|
|
1603
|
-
return;
|
|
1604
|
-
}
|
|
1605
|
-
const currentUpdatedAt = Number(currentData.configUpdatedAt);
|
|
1606
|
-
if (updatedAtHeader <= currentUpdatedAt) {
|
|
1607
|
-
return [currentData, "HIT"];
|
|
1608
|
-
}
|
|
1609
|
-
const freshAt = Math.max(
|
|
1610
|
-
_nullishCoalesce(currentData.fetchedAt, () => ( -Infinity)),
|
|
1611
|
-
_optionalChain([this, 'access', _60 => _60.lastSeen, 'optionalAccess', _61 => _61.version]) === currentUpdatedAt ? this.lastSeen.at : -Infinity
|
|
1612
|
-
);
|
|
1613
|
-
const { staleWhileRevalidateMs } = this.options;
|
|
1614
|
-
if (staleWhileRevalidateMs > 0 && Date.now() - freshAt <= staleWhileRevalidateMs) {
|
|
1615
|
-
const pending = this.fetchDatafile();
|
|
1616
|
-
const signal = _optionalChain([this, 'access', _62 => _62.abortController, 'optionalAccess', _63 => _63.signal]);
|
|
1617
|
-
const background = pending.catch((error) => {
|
|
1618
|
-
if (!_optionalChain([signal, 'optionalAccess', _64 => _64.aborted])) {
|
|
1619
|
-
console.error("@vercel/flags-core: Header refresh failed:", error);
|
|
1620
|
-
}
|
|
1621
|
-
});
|
|
1720
|
+
signal.throwIfAborted();
|
|
1622
1721
|
try {
|
|
1623
|
-
|
|
1624
|
-
} catch (
|
|
1722
|
+
return await Promise.race([fetchAttempt(), aborted]);
|
|
1723
|
+
} catch (error) {
|
|
1724
|
+
signal.throwIfAborted();
|
|
1725
|
+
if (attempt === maxAttempts - 1 || error instanceof DatafileHttpError && !error.retryable) {
|
|
1726
|
+
throw error instanceof Error ? error : new Error("Unknown fetch error");
|
|
1727
|
+
}
|
|
1625
1728
|
}
|
|
1626
|
-
return [currentData, "STALE"];
|
|
1627
|
-
}
|
|
1628
|
-
const data = await this.fetchDatafile();
|
|
1629
|
-
return [tagData(data, "fetched"), "MISS"];
|
|
1630
|
-
}
|
|
1631
|
-
observe(version2, currentVersion) {
|
|
1632
|
-
this.highestObserved = Math.max(this.highestObserved, version2);
|
|
1633
|
-
if (version2 !== currentVersion || version2 !== this.highestObserved) return;
|
|
1634
|
-
this.lastSeen = { version: version2, at: Date.now() };
|
|
1635
|
-
}
|
|
1636
|
-
async read(currentData) {
|
|
1637
|
-
const { headers } = getRequestContext();
|
|
1638
|
-
const header = _nullishCoalesce(_optionalChain([headers, 'optionalAccess', _65 => _65["x-vercel-flags-config-versions"]]), () => ( _optionalChain([headers, 'optionalAccess', _66 => _66["flags-config-versions"]])));
|
|
1639
|
-
const data = await _asyncNullishCoalesce(currentData, async () => ( tagData(await this.fetchDatafile(), "fetched")));
|
|
1640
|
-
const updatedAtHeader = this.getUpdatedAtHeader(data.projectId, header);
|
|
1641
|
-
if (updatedAtHeader) {
|
|
1642
|
-
this.observe(updatedAtHeader, Number(data.configUpdatedAt));
|
|
1643
1729
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
}
|
|
1650
|
-
/**
|
|
1651
|
-
* Abort the current header-driven fetch and discard its pending work.
|
|
1652
|
-
*/
|
|
1653
|
-
stop() {
|
|
1654
|
-
_optionalChain([this, 'access', _67 => _67.abortController, 'optionalAccess', _68 => _68.abort, 'call', _69 => _69()]);
|
|
1655
|
-
this.abortController = void 0;
|
|
1656
|
-
this.promise = void 0;
|
|
1657
|
-
this.lastSeen = void 0;
|
|
1658
|
-
this.highestObserved = 0;
|
|
1730
|
+
} finally {
|
|
1731
|
+
clearTimeout(timeoutId);
|
|
1732
|
+
clearTimeout(delay);
|
|
1733
|
+
signal.removeEventListener("abort", onAbort);
|
|
1734
|
+
_optionalChain([options, 'access', _60 => _60.signal, 'optionalAccess', _61 => _61.removeEventListener, 'call', _62 => _62("abort", onExternalAbort)]);
|
|
1659
1735
|
}
|
|
1660
|
-
}
|
|
1736
|
+
}
|
|
1661
1737
|
|
|
1662
1738
|
// src/controller/normalized-options.ts
|
|
1663
1739
|
|
|
@@ -1665,7 +1741,6 @@ var DEFAULT_STREAM_INIT_TIMEOUT_MS = 3e3;
|
|
|
1665
1741
|
var DEFAULT_POLLING_INTERVAL_MS = 3e4;
|
|
1666
1742
|
var MIN_POLLING_INTERVAL_MS = 3e4;
|
|
1667
1743
|
var DEFAULT_POLLING_INIT_TIMEOUT_MS = 3e3;
|
|
1668
|
-
var DEFAULT_STALE_WHILE_REVALIDATE_MS = 1e4;
|
|
1669
1744
|
function normalizeOptions(options) {
|
|
1670
1745
|
const autoDetectedBuildStep = process.env.CI === "1" || process.env.NEXT_PHASE === "phase-production-build";
|
|
1671
1746
|
const buildStep = _nullishCoalesce(options.buildStep, () => ( autoDetectedBuildStep));
|
|
@@ -1698,19 +1773,11 @@ function normalizeOptions(options) {
|
|
|
1698
1773
|
initTimeoutMs: options.polling.initTimeoutMs
|
|
1699
1774
|
};
|
|
1700
1775
|
}
|
|
1701
|
-
const staleWhileRevalidateMs = _nullishCoalesce(options.staleWhileRevalidateMs, () => ( DEFAULT_STALE_WHILE_REVALIDATE_MS));
|
|
1702
|
-
if (!Number.isFinite(staleWhileRevalidateMs) || staleWhileRevalidateMs < 0) {
|
|
1703
|
-
throw new Error(
|
|
1704
|
-
"@vercel/flags-core: staleWhileRevalidateMs must be a finite, non-negative number."
|
|
1705
|
-
);
|
|
1706
|
-
}
|
|
1707
1776
|
return {
|
|
1708
1777
|
auth: options.auth,
|
|
1709
1778
|
datafile: options.datafile,
|
|
1710
1779
|
stream,
|
|
1711
1780
|
polling,
|
|
1712
|
-
vercel: _nullishCoalesce(options.vercel, () => ( process.env.VERCEL === "1")),
|
|
1713
|
-
staleWhileRevalidateMs,
|
|
1714
1781
|
buildStep,
|
|
1715
1782
|
fetch: _nullishCoalesce(options.fetch, () => ( globalThis.fetch)),
|
|
1716
1783
|
waitUntil: _nullishCoalesce(options.waitUntil, () => ( _functions.waitUntil)),
|
|
@@ -1721,6 +1788,35 @@ function normalizeOptions(options) {
|
|
|
1721
1788
|
};
|
|
1722
1789
|
}
|
|
1723
1790
|
|
|
1791
|
+
// src/controller/typed-emitter.ts
|
|
1792
|
+
var TypedEmitter = (_class5 = class {constructor() { _class5.prototype.__init12.call(this); }
|
|
1793
|
+
__init12() {this.handlers = /* @__PURE__ */ new Map()}
|
|
1794
|
+
on(event, handler) {
|
|
1795
|
+
let set = this.handlers.get(event);
|
|
1796
|
+
if (!set) {
|
|
1797
|
+
set = /* @__PURE__ */ new Set();
|
|
1798
|
+
this.handlers.set(event, set);
|
|
1799
|
+
}
|
|
1800
|
+
set.add(handler);
|
|
1801
|
+
}
|
|
1802
|
+
off(event, handler) {
|
|
1803
|
+
const set = this.handlers.get(event);
|
|
1804
|
+
if (!set) return;
|
|
1805
|
+
set.delete(handler);
|
|
1806
|
+
if (set.size === 0) {
|
|
1807
|
+
this.handlers.delete(event);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
emit(event, ...args) {
|
|
1811
|
+
const set = this.handlers.get(event);
|
|
1812
|
+
if (set) {
|
|
1813
|
+
for (const handler of set) {
|
|
1814
|
+
handler(...args);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
}, _class5);
|
|
1819
|
+
|
|
1724
1820
|
// src/controller/polling-source.ts
|
|
1725
1821
|
var PollingSource = class extends TypedEmitter {
|
|
1726
1822
|
|
|
@@ -1735,14 +1831,17 @@ var PollingSource = class extends TypedEmitter {
|
|
|
1735
1831
|
* Emits 'data' on success, 'error' on failure.
|
|
1736
1832
|
*/
|
|
1737
1833
|
async poll() {
|
|
1738
|
-
if (_optionalChain([this, 'access',
|
|
1834
|
+
if (_optionalChain([this, 'access', _63 => _63.abortController, 'optionalAccess', _64 => _64.signal, 'access', _65 => _65.aborted])) return;
|
|
1835
|
+
this.abortController ??= new AbortController();
|
|
1836
|
+
const controller = this.abortController;
|
|
1739
1837
|
try {
|
|
1740
1838
|
const data = await fetchDatafile({
|
|
1741
1839
|
...this.config,
|
|
1742
|
-
signal:
|
|
1840
|
+
signal: controller.signal
|
|
1743
1841
|
});
|
|
1744
1842
|
this.emit("data", data);
|
|
1745
1843
|
} catch (error) {
|
|
1844
|
+
controller.signal.throwIfAborted();
|
|
1746
1845
|
const err = error instanceof Error ? error : new Error("Unknown poll error");
|
|
1747
1846
|
this.emit("error", err);
|
|
1748
1847
|
}
|
|
@@ -1754,9 +1853,9 @@ var PollingSource = class extends TypedEmitter {
|
|
|
1754
1853
|
*/
|
|
1755
1854
|
startInterval() {
|
|
1756
1855
|
if (this.intervalId) return;
|
|
1757
|
-
this.abortController = new AbortController();
|
|
1758
1856
|
this.intervalId = setInterval(
|
|
1759
|
-
() => void this.poll()
|
|
1857
|
+
() => void this.poll().catch(() => {
|
|
1858
|
+
}),
|
|
1760
1859
|
this.config.polling.intervalMs
|
|
1761
1860
|
);
|
|
1762
1861
|
}
|
|
@@ -1768,7 +1867,7 @@ var PollingSource = class extends TypedEmitter {
|
|
|
1768
1867
|
clearInterval(this.intervalId);
|
|
1769
1868
|
this.intervalId = void 0;
|
|
1770
1869
|
}
|
|
1771
|
-
_optionalChain([this, 'access',
|
|
1870
|
+
_optionalChain([this, 'access', _66 => _66.abortController, 'optionalAccess', _67 => _67.abort, 'call', _68 => _68()]);
|
|
1772
1871
|
this.abortController = void 0;
|
|
1773
1872
|
}
|
|
1774
1873
|
};
|
|
@@ -1797,8 +1896,8 @@ function backoff(retryCount) {
|
|
|
1797
1896
|
return delay + Math.random() * 1e3;
|
|
1798
1897
|
}
|
|
1799
1898
|
var UnauthorizedError = class extends Error {
|
|
1800
|
-
constructor() {
|
|
1801
|
-
super(
|
|
1899
|
+
constructor(sourceProjectId) {
|
|
1900
|
+
super(`stream: ${unauthorizedMessage(sourceProjectId)}`);
|
|
1802
1901
|
this.name = "UnauthorizedError";
|
|
1803
1902
|
}
|
|
1804
1903
|
};
|
|
@@ -1847,7 +1946,7 @@ async function connectStream(config, callbacks) {
|
|
|
1847
1946
|
if (pingTimeoutId !== void 0) clearTimeout(pingTimeoutId);
|
|
1848
1947
|
if (!initialDataReceived) return;
|
|
1849
1948
|
pingTimeoutId = setTimeout(() => {
|
|
1850
|
-
_optionalChain([responseBody, 'optionalAccess',
|
|
1949
|
+
_optionalChain([responseBody, 'optionalAccess', _69 => _69.cancel, 'call', _70 => _70(), 'access', _71 => _71.catch, 'call', _72 => _72(() => {
|
|
1851
1950
|
})]);
|
|
1852
1951
|
connectionAbort.abort();
|
|
1853
1952
|
}, PING_TIMEOUT_MS);
|
|
@@ -1858,7 +1957,7 @@ async function connectStream(config, callbacks) {
|
|
|
1858
1957
|
throw new TokenResolutionError(error);
|
|
1859
1958
|
});
|
|
1860
1959
|
const headers = {
|
|
1861
|
-
|
|
1960
|
+
...authHeaders(token, config.sourceProjectId),
|
|
1862
1961
|
"User-Agent": `VercelFlagsCore/${version}`,
|
|
1863
1962
|
"X-Retry-Attempt": String(retryCount)
|
|
1864
1963
|
};
|
|
@@ -1869,7 +1968,7 @@ async function connectStream(config, callbacks) {
|
|
|
1869
1968
|
if (vercelEnv) {
|
|
1870
1969
|
headers["X-Vercel-Env"] = vercelEnv;
|
|
1871
1970
|
}
|
|
1872
|
-
const revision = _optionalChain([config, 'access',
|
|
1971
|
+
const revision = _optionalChain([config, 'access', _73 => _73.revision, 'optionalCall', _74 => _74()]);
|
|
1873
1972
|
if (revision !== void 0) {
|
|
1874
1973
|
headers["X-Revision"] = String(revision);
|
|
1875
1974
|
}
|
|
@@ -1880,7 +1979,7 @@ async function connectStream(config, callbacks) {
|
|
|
1880
1979
|
if (!response.ok) {
|
|
1881
1980
|
if (response.status === 401) {
|
|
1882
1981
|
if (!initialDataReceived) {
|
|
1883
|
-
rejectInit(new UnauthorizedError());
|
|
1982
|
+
rejectInit(new UnauthorizedError(config.sourceProjectId));
|
|
1884
1983
|
}
|
|
1885
1984
|
abortController.abort();
|
|
1886
1985
|
break;
|
|
@@ -1915,7 +2014,7 @@ async function connectStream(config, callbacks) {
|
|
|
1915
2014
|
let message;
|
|
1916
2015
|
try {
|
|
1917
2016
|
message = JSON.parse(line);
|
|
1918
|
-
} catch (
|
|
2017
|
+
} catch (e12) {
|
|
1919
2018
|
console.warn(
|
|
1920
2019
|
"@vercel/flags-core: Failed to parse stream message, skipping"
|
|
1921
2020
|
);
|
|
@@ -1931,7 +2030,7 @@ async function connectStream(config, callbacks) {
|
|
|
1931
2030
|
resetPingTimeout();
|
|
1932
2031
|
}
|
|
1933
2032
|
if (message.type === "primed") {
|
|
1934
|
-
_optionalChain([onPrimed, 'optionalCall',
|
|
2033
|
+
_optionalChain([onPrimed, 'optionalCall', _75 => _75(message)]);
|
|
1935
2034
|
retryCount = 0;
|
|
1936
2035
|
if (!initialDataReceived) {
|
|
1937
2036
|
initialDataReceived = true;
|
|
@@ -1954,7 +2053,7 @@ async function connectStream(config, callbacks) {
|
|
|
1954
2053
|
clearTimeout(pingTimeoutId);
|
|
1955
2054
|
abortController.signal.removeEventListener("abort", onMainAbort);
|
|
1956
2055
|
if (!abortController.signal.aborted) {
|
|
1957
|
-
_optionalChain([onDisconnect, 'optionalCall',
|
|
2056
|
+
_optionalChain([onDisconnect, 'optionalCall', _76 => _76()]);
|
|
1958
2057
|
retryCount++;
|
|
1959
2058
|
const elapsed = Date.now() - lastAttemptTime;
|
|
1960
2059
|
const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
|
|
@@ -1975,7 +2074,7 @@ async function connectStream(config, callbacks) {
|
|
|
1975
2074
|
if (!connectionAbort.signal.aborted) {
|
|
1976
2075
|
lastError = error;
|
|
1977
2076
|
}
|
|
1978
|
-
_optionalChain([onDisconnect, 'optionalCall',
|
|
2077
|
+
_optionalChain([onDisconnect, 'optionalCall', _77 => _77()]);
|
|
1979
2078
|
retryCount++;
|
|
1980
2079
|
const elapsed = Date.now() - lastAttemptTime;
|
|
1981
2080
|
const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
|
|
@@ -2024,6 +2123,7 @@ var StreamSource = class extends TypedEmitter {
|
|
|
2024
2123
|
{
|
|
2025
2124
|
host: this.options.host,
|
|
2026
2125
|
resolveToken: () => this.options.auth.resolveToken(),
|
|
2126
|
+
sourceProjectId: this.options.auth.sourceProjectId,
|
|
2027
2127
|
abortController,
|
|
2028
2128
|
fetch: this.options.fetch,
|
|
2029
2129
|
revision: this.revision
|
|
@@ -2054,12 +2154,29 @@ var StreamSource = class extends TypedEmitter {
|
|
|
2054
2154
|
* Stop the stream connection.
|
|
2055
2155
|
*/
|
|
2056
2156
|
stop() {
|
|
2057
|
-
_optionalChain([this, 'access',
|
|
2157
|
+
_optionalChain([this, 'access', _78 => _78.abortController, 'optionalAccess', _79 => _79.abort, 'call', _80 => _80()]);
|
|
2058
2158
|
this.abortController = void 0;
|
|
2059
2159
|
this.promise = void 0;
|
|
2060
2160
|
}
|
|
2061
2161
|
};
|
|
2062
2162
|
|
|
2163
|
+
// src/controller/tagged-data.ts
|
|
2164
|
+
function tagData(data, origin) {
|
|
2165
|
+
return Object.assign(data, { _origin: origin });
|
|
2166
|
+
}
|
|
2167
|
+
function originToMetricsSource(origin) {
|
|
2168
|
+
switch (origin) {
|
|
2169
|
+
case "stream":
|
|
2170
|
+
case "poll":
|
|
2171
|
+
case "provided":
|
|
2172
|
+
return "in-memory";
|
|
2173
|
+
case "fetched":
|
|
2174
|
+
return "remote";
|
|
2175
|
+
case "bundled":
|
|
2176
|
+
return "embedded";
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2063
2180
|
// src/controller/index.ts
|
|
2064
2181
|
function parseConfigUpdatedAt(value) {
|
|
2065
2182
|
if (typeof value === "number") return value;
|
|
@@ -2069,7 +2186,7 @@ function parseConfigUpdatedAt(value) {
|
|
|
2069
2186
|
}
|
|
2070
2187
|
return void 0;
|
|
2071
2188
|
}
|
|
2072
|
-
var Controller = (
|
|
2189
|
+
var Controller = (_class6 = class {
|
|
2073
2190
|
|
|
2074
2191
|
// State machine
|
|
2075
2192
|
__init13() {this.state = "idle"}
|
|
@@ -2077,34 +2194,33 @@ var Controller = (_class7 = class {
|
|
|
2077
2194
|
|
|
2078
2195
|
// Memoized data spread for read() / getDatafile().
|
|
2079
2196
|
// Rebuilt only when `this.data` reference changes (e.g. on stream/poll update).
|
|
2080
|
-
// Holds the result of stripping
|
|
2197
|
+
// Holds the result of stripping `_origin`; metrics are appended per-call.
|
|
2081
2198
|
__init14() {this.dataViewSource = void 0}
|
|
2082
2199
|
__init15() {this.dataViewBase = void 0}
|
|
2083
2200
|
// Sources (I/O delegates)
|
|
2084
2201
|
|
|
2085
2202
|
|
|
2086
2203
|
|
|
2087
|
-
|
|
2088
2204
|
// Usage tracking
|
|
2089
2205
|
|
|
2090
2206
|
__init16() {this.isFirstGetData = true}
|
|
2091
2207
|
// Build-step deduplication
|
|
2092
2208
|
__init17() {this.buildDataPromise = null}
|
|
2093
2209
|
__init18() {this.buildReadTracked = false}
|
|
2094
|
-
// Suppresses usage tracking
|
|
2210
|
+
// Suppresses usage tracking while the credential is rejected. Set on a 401
|
|
2211
|
+
// from any source, cleared as soon as a source delivers data again.
|
|
2095
2212
|
__init19() {this.unauthorized = false}
|
|
2096
|
-
constructor(options) {;
|
|
2213
|
+
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);
|
|
2097
2214
|
this.options = normalizeOptions(options);
|
|
2098
2215
|
this.streamSource = new StreamSource(
|
|
2099
2216
|
this.options,
|
|
2100
|
-
() => _optionalChain([this, 'access',
|
|
2217
|
+
() => _optionalChain([this, 'access', _81 => _81.data, 'optionalAccess', _82 => _82.revision])
|
|
2101
2218
|
);
|
|
2102
2219
|
this.pollingSource = new PollingSource(this.options);
|
|
2103
2220
|
this.bundledSource = new BundledSource({
|
|
2104
2221
|
auth: this.options.auth,
|
|
2105
2222
|
readBundledDefinitions
|
|
2106
2223
|
});
|
|
2107
|
-
this.headerSource = new HeaderSource(this.options);
|
|
2108
2224
|
this.wireSourceEvents();
|
|
2109
2225
|
if (this.options.datafile) {
|
|
2110
2226
|
this.data = tagData(this.options.datafile, "provided");
|
|
@@ -2113,11 +2229,13 @@ var Controller = (_class7 = class {
|
|
|
2113
2229
|
}
|
|
2114
2230
|
// Source event handlers (stored for cleanup)
|
|
2115
2231
|
__init20() {this.onStreamData = (data) => {
|
|
2232
|
+
this.unauthorized = false;
|
|
2116
2233
|
if (this.isNewerData(data)) {
|
|
2117
2234
|
this.data = tagData(data, "stream");
|
|
2118
2235
|
}
|
|
2119
2236
|
}}
|
|
2120
2237
|
__init21() {this.onStreamPrimed = () => {
|
|
2238
|
+
this.unauthorized = false;
|
|
2121
2239
|
if (this.state === "degraded" || this.state === "initializing:stream") {
|
|
2122
2240
|
this.transition("streaming");
|
|
2123
2241
|
}
|
|
@@ -2133,18 +2251,15 @@ var Controller = (_class7 = class {
|
|
|
2133
2251
|
}
|
|
2134
2252
|
}}
|
|
2135
2253
|
__init24() {this.onPollData = (data) => {
|
|
2254
|
+
this.unauthorized = false;
|
|
2136
2255
|
if (this.isNewerData(data)) {
|
|
2137
2256
|
this.data = tagData(data, "poll");
|
|
2138
2257
|
}
|
|
2139
2258
|
}}
|
|
2140
2259
|
__init25() {this.onPollError = (error) => {
|
|
2260
|
+
this.noteUnauthorized(error);
|
|
2141
2261
|
console.error("@vercel/flags-core: Poll failed:", error);
|
|
2142
2262
|
}}
|
|
2143
|
-
__init26() {this.onFetchedData = (data) => {
|
|
2144
|
-
if (this.isNewerData(data)) {
|
|
2145
|
-
this.data = tagData(data, "fetched");
|
|
2146
|
-
}
|
|
2147
|
-
}}
|
|
2148
2263
|
// ---------------------------------------------------------------------------
|
|
2149
2264
|
// Source event wiring
|
|
2150
2265
|
// ---------------------------------------------------------------------------
|
|
@@ -2155,7 +2270,6 @@ var Controller = (_class7 = class {
|
|
|
2155
2270
|
this.streamSource.on("disconnected", this.onStreamDisconnected);
|
|
2156
2271
|
this.pollingSource.on("data", this.onPollData);
|
|
2157
2272
|
this.pollingSource.on("error", this.onPollError);
|
|
2158
|
-
this.headerSource.on("data", this.onFetchedData);
|
|
2159
2273
|
}
|
|
2160
2274
|
unwireSourceEvents() {
|
|
2161
2275
|
this.streamSource.off("data", this.onStreamData);
|
|
@@ -2164,7 +2278,6 @@ var Controller = (_class7 = class {
|
|
|
2164
2278
|
this.streamSource.off("disconnected", this.onStreamDisconnected);
|
|
2165
2279
|
this.pollingSource.off("data", this.onPollData);
|
|
2166
2280
|
this.pollingSource.off("error", this.onPollError);
|
|
2167
|
-
this.headerSource.off("data", this.onFetchedData);
|
|
2168
2281
|
}
|
|
2169
2282
|
// ---------------------------------------------------------------------------
|
|
2170
2283
|
// State machine
|
|
@@ -2182,8 +2295,6 @@ var Controller = (_class7 = class {
|
|
|
2182
2295
|
return "streaming";
|
|
2183
2296
|
case "polling":
|
|
2184
2297
|
return "polling";
|
|
2185
|
-
case "vercel":
|
|
2186
|
-
return "vercel";
|
|
2187
2298
|
default:
|
|
2188
2299
|
return "offline";
|
|
2189
2300
|
}
|
|
@@ -2197,7 +2308,6 @@ var Controller = (_class7 = class {
|
|
|
2197
2308
|
* Build step: datafile → bundled → one-time fetch
|
|
2198
2309
|
* Streaming mode: stream → datafile → bundled
|
|
2199
2310
|
* Polling mode (no stream): poll → datafile → bundled
|
|
2200
|
-
* Vercel mode: datafile → bundled; fetch only on a read
|
|
2201
2311
|
* Offline mode (neither): datafile → bundled → one-time fetch
|
|
2202
2312
|
*/
|
|
2203
2313
|
async initialize() {
|
|
@@ -2211,20 +2321,13 @@ var Controller = (_class7 = class {
|
|
|
2211
2321
|
this.data = tagData(this.options.datafile, "provided");
|
|
2212
2322
|
}
|
|
2213
2323
|
if (!this.data) {
|
|
2214
|
-
this.transition("initializing:fallback");
|
|
2215
|
-
let bundled;
|
|
2216
2324
|
try {
|
|
2217
|
-
bundled = await this.bundledSource.tryLoad();
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2325
|
+
const bundled = await this.bundledSource.tryLoad();
|
|
2326
|
+
if (bundled) {
|
|
2327
|
+
this.data = tagData(bundled, "bundled");
|
|
2328
|
+
}
|
|
2329
|
+
} catch (e13) {
|
|
2222
2330
|
}
|
|
2223
|
-
if (bundled) this.data = tagData(bundled, "bundled");
|
|
2224
|
-
}
|
|
2225
|
-
if (this.headerSource.isAvailable()) {
|
|
2226
|
-
this.transition("vercel");
|
|
2227
|
-
return;
|
|
2228
2331
|
}
|
|
2229
2332
|
if (this.data) {
|
|
2230
2333
|
if (this.options.stream.enabled) {
|
|
@@ -2290,7 +2393,6 @@ var Controller = (_class7 = class {
|
|
|
2290
2393
|
this.unwireSourceEvents();
|
|
2291
2394
|
this.streamSource.stop();
|
|
2292
2395
|
this.pollingSource.stop();
|
|
2293
|
-
this.headerSource.stop();
|
|
2294
2396
|
this.data = this.options.datafile ? tagData(this.options.datafile, "provided") : void 0;
|
|
2295
2397
|
this.transition("shutdown");
|
|
2296
2398
|
await this.usageTracker.shutdown();
|
|
@@ -2326,9 +2428,10 @@ var Controller = (_class7 = class {
|
|
|
2326
2428
|
this.data = tagData(fetched, "fetched");
|
|
2327
2429
|
result = this.data;
|
|
2328
2430
|
cacheStatus = "MISS";
|
|
2329
|
-
} catch (
|
|
2330
|
-
|
|
2331
|
-
|
|
2431
|
+
} catch (error) {
|
|
2432
|
+
this.noteUnauthorized(error);
|
|
2433
|
+
throw this.noDefinitionsError(
|
|
2434
|
+
". Initialize the client or provide a datafile."
|
|
2332
2435
|
);
|
|
2333
2436
|
}
|
|
2334
2437
|
}
|
|
@@ -2371,10 +2474,6 @@ var Controller = (_class7 = class {
|
|
|
2371
2474
|
if (this.options.buildStep) {
|
|
2372
2475
|
return this.resolveDataForBuildStep();
|
|
2373
2476
|
}
|
|
2374
|
-
if (this.state === "vercel") {
|
|
2375
|
-
const result = await this.headerSource.read(this.data);
|
|
2376
|
-
if (result) return result;
|
|
2377
|
-
}
|
|
2378
2477
|
if (this.data) {
|
|
2379
2478
|
const cacheStatus = this.isConnected ? "HIT" : "STALE";
|
|
2380
2479
|
return [this.data, cacheStatus];
|
|
@@ -2394,9 +2493,7 @@ var Controller = (_class7 = class {
|
|
|
2394
2493
|
await this.streamSource.start();
|
|
2395
2494
|
return true;
|
|
2396
2495
|
} catch (error) {
|
|
2397
|
-
|
|
2398
|
-
this.unauthorized = true;
|
|
2399
|
-
}
|
|
2496
|
+
this.noteUnauthorized(error);
|
|
2400
2497
|
return false;
|
|
2401
2498
|
}
|
|
2402
2499
|
}
|
|
@@ -2424,9 +2521,7 @@ var Controller = (_class7 = class {
|
|
|
2424
2521
|
return true;
|
|
2425
2522
|
} catch (error) {
|
|
2426
2523
|
clearTimeout(timeoutId);
|
|
2427
|
-
|
|
2428
|
-
this.unauthorized = true;
|
|
2429
|
-
}
|
|
2524
|
+
this.noteUnauthorized(error);
|
|
2430
2525
|
return false;
|
|
2431
2526
|
}
|
|
2432
2527
|
}
|
|
@@ -2449,7 +2544,7 @@ var Controller = (_class7 = class {
|
|
|
2449
2544
|
return true;
|
|
2450
2545
|
}
|
|
2451
2546
|
return false;
|
|
2452
|
-
} catch (
|
|
2547
|
+
} catch (e14) {
|
|
2453
2548
|
return false;
|
|
2454
2549
|
}
|
|
2455
2550
|
}
|
|
@@ -2474,11 +2569,16 @@ var Controller = (_class7 = class {
|
|
|
2474
2569
|
return true;
|
|
2475
2570
|
}
|
|
2476
2571
|
return false;
|
|
2477
|
-
} catch (
|
|
2572
|
+
} catch (e15) {
|
|
2478
2573
|
clearTimeout(timeoutId);
|
|
2479
2574
|
return false;
|
|
2480
2575
|
}
|
|
2481
2576
|
}
|
|
2577
|
+
noteUnauthorized(error) {
|
|
2578
|
+
if (error instanceof UnauthorizedError || error instanceof Error && error.message.includes("401")) {
|
|
2579
|
+
this.unauthorized = true;
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2482
2582
|
// ---------------------------------------------------------------------------
|
|
2483
2583
|
// Build step helpers
|
|
2484
2584
|
// ---------------------------------------------------------------------------
|
|
@@ -2524,10 +2624,11 @@ var Controller = (_class7 = class {
|
|
|
2524
2624
|
fetch: this.options.fetch
|
|
2525
2625
|
});
|
|
2526
2626
|
return tagData(fetched, "fetched");
|
|
2527
|
-
} catch (
|
|
2627
|
+
} catch (error) {
|
|
2628
|
+
this.noteUnauthorized(error);
|
|
2528
2629
|
}
|
|
2529
|
-
throw
|
|
2530
|
-
"
|
|
2630
|
+
throw this.noDefinitionsError(
|
|
2631
|
+
" during build. Provide a datafile or bundled definitions."
|
|
2531
2632
|
);
|
|
2532
2633
|
}
|
|
2533
2634
|
// ---------------------------------------------------------------------------
|
|
@@ -2558,11 +2659,20 @@ var Controller = (_class7 = class {
|
|
|
2558
2659
|
this.data = tagData(fetched, "fetched");
|
|
2559
2660
|
this.transition("degraded");
|
|
2560
2661
|
return;
|
|
2561
|
-
} catch (
|
|
2662
|
+
} catch (e16) {
|
|
2562
2663
|
}
|
|
2563
2664
|
}
|
|
2564
|
-
throw
|
|
2565
|
-
|
|
2665
|
+
throw this.noDefinitionsError(". Bundled definitions not found.");
|
|
2666
|
+
}
|
|
2667
|
+
/**
|
|
2668
|
+
* `detail` continues the sentence "No flag definitions available", so it
|
|
2669
|
+
* starts with either "." or " during build.".
|
|
2670
|
+
*/
|
|
2671
|
+
noDefinitionsError(detail) {
|
|
2672
|
+
const { sourceProjectId } = this.options.auth;
|
|
2673
|
+
const reason = this.unauthorized && sourceProjectId ? ` Request was ${unauthorizedMessage(sourceProjectId)}` : "";
|
|
2674
|
+
return new Error(
|
|
2675
|
+
`@vercel/flags-core: No flag definitions available${detail}${reason}`
|
|
2566
2676
|
);
|
|
2567
2677
|
}
|
|
2568
2678
|
/**
|
|
@@ -2610,11 +2720,11 @@ var Controller = (_class7 = class {
|
|
|
2610
2720
|
this.data = tagData(fetched, "fetched");
|
|
2611
2721
|
this.transition("degraded");
|
|
2612
2722
|
return [this.data, "MISS"];
|
|
2613
|
-
} catch (
|
|
2723
|
+
} catch (e17) {
|
|
2614
2724
|
}
|
|
2615
2725
|
}
|
|
2616
|
-
throw
|
|
2617
|
-
"
|
|
2726
|
+
throw this.noDefinitionsError(
|
|
2727
|
+
". Provide a datafile or bundled definitions."
|
|
2618
2728
|
);
|
|
2619
2729
|
}
|
|
2620
2730
|
// ---------------------------------------------------------------------------
|
|
@@ -2662,11 +2772,11 @@ var Controller = (_class7 = class {
|
|
|
2662
2772
|
duration: Date.now() - startTime,
|
|
2663
2773
|
mode: mode === "streaming" ? "stream" : mode === "polling" ? "poll" : mode
|
|
2664
2774
|
};
|
|
2665
|
-
const configUpdatedAt = _optionalChain([this, 'access',
|
|
2775
|
+
const configUpdatedAt = _optionalChain([this, 'access', _83 => _83.data, 'optionalAccess', _84 => _84.configUpdatedAt]);
|
|
2666
2776
|
if (typeof configUpdatedAt === "number") {
|
|
2667
2777
|
trackOptions.configUpdatedAt = configUpdatedAt;
|
|
2668
2778
|
}
|
|
2669
|
-
const revision = _optionalChain([this, 'access',
|
|
2779
|
+
const revision = _optionalChain([this, 'access', _85 => _85.data, 'optionalAccess', _86 => _86.revision]);
|
|
2670
2780
|
if (typeof revision === "number") {
|
|
2671
2781
|
trackOptions.revision = revision;
|
|
2672
2782
|
}
|
|
@@ -2685,89 +2795,7 @@ var Controller = (_class7 = class {
|
|
|
2685
2795
|
clientName: _nullishCoalesce(options.clientName, () => ( this.options.clientName))
|
|
2686
2796
|
});
|
|
2687
2797
|
}
|
|
2688
|
-
},
|
|
2689
|
-
|
|
2690
|
-
// src/controller/auth.ts
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
// src/utils/sdk-keys.ts
|
|
2694
|
-
var SDK_KEY_REGEX = /^vf_(?:server|client)_/;
|
|
2695
|
-
function parseSdkKeyFromFlagsConnectionString(text) {
|
|
2696
|
-
if (SDK_KEY_REGEX.test(text)) return text;
|
|
2697
|
-
try {
|
|
2698
|
-
if (!text.startsWith("flags:")) return null;
|
|
2699
|
-
const params = new URLSearchParams(text.slice(6));
|
|
2700
|
-
const sdkKey = params.get("sdkKey");
|
|
2701
|
-
if (sdkKey && SDK_KEY_REGEX.test(sdkKey)) return sdkKey;
|
|
2702
|
-
} catch (e17) {
|
|
2703
|
-
}
|
|
2704
|
-
return null;
|
|
2705
|
-
}
|
|
2706
|
-
|
|
2707
|
-
// src/controller/auth.ts
|
|
2708
|
-
async function getOidcToken() {
|
|
2709
|
-
try {
|
|
2710
|
-
return await _oidc.getVercelOidcToken.call(void 0, );
|
|
2711
|
-
} catch (e18) {
|
|
2712
|
-
throw new Error(
|
|
2713
|
-
[
|
|
2714
|
-
"@vercel/flags-core: Failed to get OIDC token.",
|
|
2715
|
-
"Are you running in a Vercel Environment where OIDC tokens are available?",
|
|
2716
|
-
"Did you mean to use an SDK Key instead? Use the environment variable FLAGS or pass it directly to the client."
|
|
2717
|
-
].join(" ")
|
|
2718
|
-
);
|
|
2719
|
-
}
|
|
2720
|
-
}
|
|
2721
|
-
function getProjectIdFromOidcToken(oidcToken) {
|
|
2722
|
-
const tokenParts = oidcToken.split(".");
|
|
2723
|
-
if (tokenParts.length !== 3 || !tokenParts[1]) {
|
|
2724
|
-
throw new Error("@vercel/flags-core: Invalid OIDC token");
|
|
2725
|
-
}
|
|
2726
|
-
const payload = JSON.parse(
|
|
2727
|
-
Buffer.from(tokenParts[1], "base64url").toString("utf8")
|
|
2728
|
-
);
|
|
2729
|
-
if (typeof payload.project_id !== "string" || !payload.project_id) {
|
|
2730
|
-
throw new Error(
|
|
2731
|
-
"@vercel/flags-core: Missing project_id claim in OIDC token"
|
|
2732
|
-
);
|
|
2733
|
-
}
|
|
2734
|
-
return payload.project_id;
|
|
2735
|
-
}
|
|
2736
|
-
var Authentication = class {
|
|
2737
|
-
|
|
2738
|
-
constructor(sdkKeyOrConnectionString) {
|
|
2739
|
-
if (sdkKeyOrConnectionString !== void 0) {
|
|
2740
|
-
if (typeof sdkKeyOrConnectionString !== "string") {
|
|
2741
|
-
throw new Error(
|
|
2742
|
-
`@vercel/flags-core: Invalid sdkKey. Expected string, got ${typeof sdkKeyOrConnectionString}`
|
|
2743
|
-
);
|
|
2744
|
-
}
|
|
2745
|
-
const parsed = parseSdkKeyFromFlagsConnectionString(
|
|
2746
|
-
sdkKeyOrConnectionString
|
|
2747
|
-
);
|
|
2748
|
-
if (!parsed) {
|
|
2749
|
-
throw new Error("@vercel/flags-core: Missing sdkKey");
|
|
2750
|
-
}
|
|
2751
|
-
this.sdkKey = parsed;
|
|
2752
|
-
}
|
|
2753
|
-
}
|
|
2754
|
-
async resolveToken() {
|
|
2755
|
-
if (this.sdkKey) {
|
|
2756
|
-
return this.sdkKey;
|
|
2757
|
-
}
|
|
2758
|
-
return await getOidcToken();
|
|
2759
|
-
}
|
|
2760
|
-
async resolveBundledDefinitionsLookup() {
|
|
2761
|
-
if (this.sdkKey) {
|
|
2762
|
-
return { type: "sdk-key", sdkKey: this.sdkKey };
|
|
2763
|
-
}
|
|
2764
|
-
const oidcToken = await this.resolveToken();
|
|
2765
|
-
return {
|
|
2766
|
-
type: "project-id",
|
|
2767
|
-
projectId: getProjectIdFromOidcToken(oidcToken)
|
|
2768
|
-
};
|
|
2769
|
-
}
|
|
2770
|
-
};
|
|
2798
|
+
}, _class6);
|
|
2771
2799
|
|
|
2772
2800
|
// src/index.make.ts
|
|
2773
2801
|
function make(createRawClient, defaults) {
|
|
@@ -2786,7 +2814,11 @@ function make(createRawClient, defaults) {
|
|
|
2786
2814
|
});
|
|
2787
2815
|
return createRawClient({
|
|
2788
2816
|
controller,
|
|
2789
|
-
origin: {
|
|
2817
|
+
origin: {
|
|
2818
|
+
provider: "vercel",
|
|
2819
|
+
sdkKey: auth.sdkKey,
|
|
2820
|
+
...auth.sourceProjectId ? { projectId: auth.sourceProjectId } : null
|
|
2821
|
+
},
|
|
2790
2822
|
waitUntil: waitUntil3,
|
|
2791
2823
|
...experimental_reportExposures ? { experimental_reportExposures } : {}
|
|
2792
2824
|
});
|
|
@@ -2838,4 +2870,4 @@ var {
|
|
|
2838
2870
|
|
|
2839
2871
|
|
|
2840
2872
|
exports.ResolutionReason = ResolutionReason; exports.evaluate = evaluate; exports.FallbackNotFoundError = FallbackNotFoundError; exports.FallbackEntryNotFoundError = FallbackEntryNotFoundError; exports.Controller = Controller; exports.flagsClient = flagsClient; exports.resetDefaultFlagsClient = resetDefaultFlagsClient; exports.createClient = createClient;
|
|
2841
|
-
//# sourceMappingURL=chunk-
|
|
2873
|
+
//# sourceMappingURL=chunk-PGKH4XQ7.cjs.map
|