@vercel/flags-core 1.9.0-da0b044-20260915081607 → 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 +27 -2
- package/README.md +0 -12
- package/dist/{chunk-KF4BZUWK.cjs → chunk-PGKH4XQ7.cjs} +424 -331
- package/dist/chunk-PGKH4XQ7.cjs.map +1 -0
- package/dist/{chunk-4MFMRMFV.js → chunk-SLNLPFKU.js} +384 -291
- package/dist/chunk-SLNLPFKU.js.map +1 -0
- package/dist/{chunk-P6CH6LXX.js → chunk-TDZBUVBG.js} +383 -291
- package/dist/chunk-TDZBUVBG.js.map +1 -0
- package/dist/{chunk-3OXPYLQR.cjs → chunk-W742GGZU.cjs} +422 -330
- package/dist/chunk-W742GGZU.cjs.map +1 -0
- package/dist/index.default.cjs +2 -2
- package/dist/index.default.d.cts +17 -10
- package/dist/index.default.d.ts +17 -10
- package/dist/index.default.js +1 -1
- package/dist/index.next-js.cjs +2 -2
- package/dist/index.next-js.d.cts +17 -10
- package/dist/index.next-js.d.ts +17 -10
- 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-w8u8i22D.d.cts → types-1OK52NNi.d.cts} +13 -3
- package/dist/{types-w8u8i22D.d.ts → types-1OK52NNi.d.ts} +13 -3
- package/package.json +4 -4
- package/dist/chunk-3OXPYLQR.cjs.map +0 -1
- package/dist/chunk-4MFMRMFV.js.map +0 -1
- package/dist/chunk-KF4BZUWK.cjs.map +0 -1
- package/dist/chunk-P6CH6LXX.js.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
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;// src/index.next-js.ts
|
|
2
2
|
var _cache = require('next/cache');
|
|
3
|
+
var _server = require('next/server');
|
|
3
4
|
|
|
4
5
|
// src/evaluate.ts
|
|
5
6
|
var _jsxxhash = require('js-xxhash');
|
|
@@ -55,7 +56,7 @@ function getSplitBoundaries(outcome) {
|
|
|
55
56
|
function getCompiledRegex(rhs) {
|
|
56
57
|
const cached = compiledRegexCache.get(rhs);
|
|
57
58
|
if (cached) return cached;
|
|
58
|
-
const compiled = new RegExp(rhs.pattern, rhs.flags.replace(/[gy]
|
|
59
|
+
const compiled = new RegExp(rhs.pattern, rhs.flags.replace(/[gy]/g, ""));
|
|
59
60
|
compiledRegexCache.set(rhs, compiled);
|
|
60
61
|
return compiled;
|
|
61
62
|
}
|
|
@@ -432,7 +433,7 @@ function resolveOutcome(params, outcome) {
|
|
|
432
433
|
break;
|
|
433
434
|
}
|
|
434
435
|
}
|
|
435
|
-
if (exhausted) currentPromille = PROMILLE_SCALE;
|
|
436
|
+
if (exhausted) currentPromille = _nullishCoalesce(outcome.finalPromille, () => ( PROMILLE_SCALE));
|
|
436
437
|
if (currentPromille <= 0) {
|
|
437
438
|
return {
|
|
438
439
|
...rollFromVariant,
|
|
@@ -558,7 +559,7 @@ function bulkEvaluate(flags, shared) {
|
|
|
558
559
|
}
|
|
559
560
|
|
|
560
561
|
// package.json
|
|
561
|
-
var version = "1.9.0
|
|
562
|
+
var version = "1.9.0";
|
|
562
563
|
|
|
563
564
|
// src/lib/report-value.ts
|
|
564
565
|
function internalReportValue(key, value, data) {
|
|
@@ -793,9 +794,11 @@ function createCreateRawClient(fns) {
|
|
|
793
794
|
return function createRawClient({
|
|
794
795
|
controller,
|
|
795
796
|
origin,
|
|
796
|
-
experimental_reportExposures
|
|
797
|
+
experimental_reportExposures,
|
|
798
|
+
waitUntil: waitUntil2 = _functions.waitUntil
|
|
797
799
|
}) {
|
|
798
800
|
const id = idCount++;
|
|
801
|
+
const pendingExposureReports = /* @__PURE__ */ new Set();
|
|
799
802
|
controllerInstanceMap.set(id, {
|
|
800
803
|
controller,
|
|
801
804
|
initialized: false,
|
|
@@ -813,8 +816,10 @@ function createCreateRawClient(fns) {
|
|
|
813
816
|
);
|
|
814
817
|
}
|
|
815
818
|
})();
|
|
819
|
+
pendingExposureReports.add(pending);
|
|
820
|
+
void pending.finally(() => pendingExposureReports.delete(pending));
|
|
816
821
|
try {
|
|
817
|
-
|
|
822
|
+
waitUntil2(pending);
|
|
818
823
|
} catch (e) {
|
|
819
824
|
}
|
|
820
825
|
}
|
|
@@ -849,6 +854,9 @@ function createCreateRawClient(fns) {
|
|
|
849
854
|
},
|
|
850
855
|
shutdown: async () => {
|
|
851
856
|
await fns.shutdown(id);
|
|
857
|
+
while (pendingExposureReports.size > 0) {
|
|
858
|
+
await Promise.all(pendingExposureReports);
|
|
859
|
+
}
|
|
852
860
|
controllerInstanceMap.delete(id);
|
|
853
861
|
},
|
|
854
862
|
getDatafile: async () => {
|
|
@@ -1015,6 +1023,126 @@ async function readBundledDefinitions(auth) {
|
|
|
1015
1023
|
// src/utils/ingest.ts
|
|
1016
1024
|
var _oidc = require('@vercel/oidc');
|
|
1017
1025
|
|
|
1026
|
+
// src/controller/auth.ts
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
// src/utils/sdk-keys.ts
|
|
1030
|
+
var SDK_KEY_REGEX = /^vf_(?:server|client)_/;
|
|
1031
|
+
function isValidSdkKey(value) {
|
|
1032
|
+
return SDK_KEY_REGEX.test(value);
|
|
1033
|
+
}
|
|
1034
|
+
var PROJECT_ID_REGEX = /^[A-Za-z0-9_]{1,64}$/;
|
|
1035
|
+
function isValidProjectId(value) {
|
|
1036
|
+
return PROJECT_ID_REGEX.test(value);
|
|
1037
|
+
}
|
|
1038
|
+
function parseFlagsConnectionString(text) {
|
|
1039
|
+
if (SDK_KEY_REGEX.test(text)) return { sdkKey: text, projectId: null };
|
|
1040
|
+
if (!text.startsWith("flags:")) return null;
|
|
1041
|
+
try {
|
|
1042
|
+
const params = new URLSearchParams(text.slice(6));
|
|
1043
|
+
return {
|
|
1044
|
+
sdkKey: params.get("sdkKey") || null,
|
|
1045
|
+
projectId: params.get("projectId") || null
|
|
1046
|
+
};
|
|
1047
|
+
} catch (e5) {
|
|
1048
|
+
return null;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// src/controller/auth.ts
|
|
1053
|
+
var SOURCE_PROJECT_HEADER = "X-Vercel-Flags-Project-Id";
|
|
1054
|
+
function authHeaders(token, sourceProjectId) {
|
|
1055
|
+
return {
|
|
1056
|
+
Authorization: `Bearer ${token}`,
|
|
1057
|
+
...sourceProjectId ? { [SOURCE_PROJECT_HEADER]: sourceProjectId } : null
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
function unauthorizedMessage(sourceProjectId) {
|
|
1061
|
+
if (!sourceProjectId) return "unauthorized (401)";
|
|
1062
|
+
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.`;
|
|
1063
|
+
}
|
|
1064
|
+
async function getOidcToken() {
|
|
1065
|
+
try {
|
|
1066
|
+
return await _oidc.getVercelOidcToken.call(void 0, );
|
|
1067
|
+
} catch (e6) {
|
|
1068
|
+
throw new Error(
|
|
1069
|
+
[
|
|
1070
|
+
"@vercel/flags-core: Failed to get OIDC token.",
|
|
1071
|
+
"Are you running in a Vercel Environment where OIDC tokens are available?",
|
|
1072
|
+
"Did you mean to use an SDK Key instead? Use the environment variable FLAGS or pass it directly to the client."
|
|
1073
|
+
].join(" ")
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
function getProjectIdFromOidcToken(oidcToken) {
|
|
1078
|
+
const tokenParts = oidcToken.split(".");
|
|
1079
|
+
if (tokenParts.length !== 3 || !tokenParts[1]) {
|
|
1080
|
+
throw new Error("@vercel/flags-core: Invalid OIDC token");
|
|
1081
|
+
}
|
|
1082
|
+
const payload = JSON.parse(
|
|
1083
|
+
Buffer.from(tokenParts[1], "base64url").toString("utf8")
|
|
1084
|
+
);
|
|
1085
|
+
if (typeof payload.project_id !== "string" || !payload.project_id) {
|
|
1086
|
+
throw new Error(
|
|
1087
|
+
"@vercel/flags-core: Missing project_id claim in OIDC token"
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
return payload.project_id;
|
|
1091
|
+
}
|
|
1092
|
+
var Authentication = class {
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
constructor(sdkKeyOrConnectionString) {
|
|
1096
|
+
if (sdkKeyOrConnectionString !== void 0) {
|
|
1097
|
+
if (typeof sdkKeyOrConnectionString !== "string") {
|
|
1098
|
+
throw new Error(
|
|
1099
|
+
`@vercel/flags-core: Invalid sdkKey. Expected string, got ${typeof sdkKeyOrConnectionString}`
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
const parsed = parseFlagsConnectionString(sdkKeyOrConnectionString);
|
|
1103
|
+
if (_optionalChain([parsed, 'optionalAccess', _31 => _31.sdkKey]) && parsed.projectId) {
|
|
1104
|
+
throw new Error(
|
|
1105
|
+
"@vercel/flags-core: A connection string must contain either sdkKey or projectId, not both"
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
if (_optionalChain([parsed, 'optionalAccess', _32 => _32.sdkKey])) {
|
|
1109
|
+
if (!isValidSdkKey(parsed.sdkKey)) {
|
|
1110
|
+
throw new Error("@vercel/flags-core: Missing sdkKey");
|
|
1111
|
+
}
|
|
1112
|
+
this.sdkKey = parsed.sdkKey;
|
|
1113
|
+
} else if (_optionalChain([parsed, 'optionalAccess', _33 => _33.projectId])) {
|
|
1114
|
+
if (!isValidProjectId(parsed.projectId)) {
|
|
1115
|
+
throw new Error(
|
|
1116
|
+
"@vercel/flags-core: Invalid projectId in connection string"
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
this.sourceProjectId = parsed.projectId;
|
|
1120
|
+
} else {
|
|
1121
|
+
throw new Error("@vercel/flags-core: Missing sdkKey");
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
async resolveToken() {
|
|
1126
|
+
if (this.sdkKey) {
|
|
1127
|
+
return this.sdkKey;
|
|
1128
|
+
}
|
|
1129
|
+
return await getOidcToken();
|
|
1130
|
+
}
|
|
1131
|
+
async resolveBundledDefinitionsLookup() {
|
|
1132
|
+
if (this.sdkKey) {
|
|
1133
|
+
return { type: "sdk-key", sdkKey: this.sdkKey };
|
|
1134
|
+
}
|
|
1135
|
+
if (this.sourceProjectId) {
|
|
1136
|
+
return { type: "project-id", projectId: this.sourceProjectId };
|
|
1137
|
+
}
|
|
1138
|
+
const oidcToken = await this.resolveToken();
|
|
1139
|
+
return {
|
|
1140
|
+
type: "project-id",
|
|
1141
|
+
projectId: getProjectIdFromOidcToken(oidcToken)
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1018
1146
|
// src/utils/backoff.ts
|
|
1019
1147
|
var DEFAULT_BASE_MS = 250;
|
|
1020
1148
|
var DEFAULT_CAP_MS = 5e3;
|
|
@@ -1030,12 +1158,26 @@ function getJitteredWaitMs(baseMs, ratio) {
|
|
|
1030
1158
|
return Math.floor(min + Math.random() * span);
|
|
1031
1159
|
}
|
|
1032
1160
|
|
|
1161
|
+
// src/utils/runtime-ingest.ts
|
|
1162
|
+
var FLAGS_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("@vercel/flags-context");
|
|
1163
|
+
function getRuntimeIngestFor(auth) {
|
|
1164
|
+
return auth.sourceProjectId ? void 0 : getRuntimeIngest();
|
|
1165
|
+
}
|
|
1166
|
+
function getRuntimeIngest() {
|
|
1167
|
+
try {
|
|
1168
|
+
const context = globalThis[FLAGS_CONTEXT_SYMBOL];
|
|
1169
|
+
return typeof _optionalChain([context, 'optionalAccess', _34 => _34.ingest]) === "function" ? context.ingest : void 0;
|
|
1170
|
+
} catch (e7) {
|
|
1171
|
+
return void 0;
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1033
1175
|
// src/utils/ingest.ts
|
|
1034
1176
|
var MAX_RETRIES = 3;
|
|
1035
1177
|
var MAX_EVENTS_PER_REQUEST = 2e3;
|
|
1036
1178
|
var EVALUATING_OIDC_TOKEN_HEADER = "X-Vercel-Flags-OIDC-Token";
|
|
1037
1179
|
var FLUSH_REASON_HEADER = "X-Vercel-Flags-Flush-Reason";
|
|
1038
|
-
var isDebugMode = _optionalChain([process, 'access',
|
|
1180
|
+
var isDebugMode = _optionalChain([process, 'access', _35 => _35.env, 'access', _36 => _36.DEBUG, 'optionalAccess', _37 => _37.includes, 'call', _38 => _38("@vercel/flags-core")]);
|
|
1039
1181
|
var debugLog = (...args) => {
|
|
1040
1182
|
if (!isDebugMode) return;
|
|
1041
1183
|
console.log(...args);
|
|
@@ -1044,7 +1186,7 @@ async function getEvaluatingOidcToken(auth) {
|
|
|
1044
1186
|
if (!auth.sdkKey) return void 0;
|
|
1045
1187
|
try {
|
|
1046
1188
|
return await _oidc.getVercelOidcToken.call(void 0, );
|
|
1047
|
-
} catch (
|
|
1189
|
+
} catch (e8) {
|
|
1048
1190
|
return void 0;
|
|
1049
1191
|
}
|
|
1050
1192
|
}
|
|
@@ -1053,7 +1195,7 @@ async function getIngestHeaders(options, flushReason) {
|
|
|
1053
1195
|
const evaluatingOidcToken = await getEvaluatingOidcToken(options.auth);
|
|
1054
1196
|
return {
|
|
1055
1197
|
"Content-Type": "application/json",
|
|
1056
|
-
|
|
1198
|
+
...authHeaders(token, options.auth.sourceProjectId),
|
|
1057
1199
|
"User-Agent": `VercelFlagsCore/${version}`,
|
|
1058
1200
|
[FLUSH_REASON_HEADER]: flushReason,
|
|
1059
1201
|
..._nullishCoalesce(options.metricEnvironment, () => ( process.env.VERCEL_ENV)) ? {
|
|
@@ -1063,8 +1205,28 @@ async function getIngestHeaders(options, flushReason) {
|
|
|
1063
1205
|
...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
|
|
1064
1206
|
};
|
|
1065
1207
|
}
|
|
1208
|
+
function getRuntimeIngestHeaders(options, flushReason) {
|
|
1209
|
+
return {
|
|
1210
|
+
"Content-Type": "application/json",
|
|
1211
|
+
...options.auth.sdkKey ? { Authorization: `Bearer ${options.auth.sdkKey}` } : null,
|
|
1212
|
+
"User-Agent": `VercelFlagsCore/${version}`,
|
|
1213
|
+
[FLUSH_REASON_HEADER]: flushReason,
|
|
1214
|
+
..._nullishCoalesce(options.metricEnvironment, () => ( process.env.VERCEL_ENV)) ? {
|
|
1215
|
+
"X-Vercel-Env": _nullishCoalesce(options.metricEnvironment, () => ( process.env.VERCEL_ENV))
|
|
1216
|
+
} : null,
|
|
1217
|
+
...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1066
1220
|
async function sendIngestEvents(options, events, flushId, flushReason) {
|
|
1067
|
-
|
|
1221
|
+
let eventsToSend = events.map((event) => event.ingestEvent());
|
|
1222
|
+
const runtimeIngest = getRuntimeIngestFor(options.auth);
|
|
1223
|
+
if (runtimeIngest) {
|
|
1224
|
+
const headers = getRuntimeIngestHeaders(options, flushReason);
|
|
1225
|
+
eventsToSend = eventsToSend.filter(
|
|
1226
|
+
(event) => !runtimeIngest({ headers, body: [event] })
|
|
1227
|
+
);
|
|
1228
|
+
if (eventsToSend.length === 0) return;
|
|
1229
|
+
}
|
|
1068
1230
|
for (let i = 0; i < eventsToSend.length; i += MAX_EVENTS_PER_REQUEST) {
|
|
1069
1231
|
await sendIngestChunk(
|
|
1070
1232
|
options,
|
|
@@ -1114,7 +1276,7 @@ var SYMBOL_FOR_REQ_CONTEXT = /* @__PURE__ */ Symbol.for("@vercel/request-context
|
|
|
1114
1276
|
var fromSymbol = globalThis;
|
|
1115
1277
|
function getRequestContext() {
|
|
1116
1278
|
try {
|
|
1117
|
-
const ctx = _optionalChain([fromSymbol, 'access',
|
|
1279
|
+
const ctx = _optionalChain([fromSymbol, 'access', _39 => _39[SYMBOL_FOR_REQ_CONTEXT], 'optionalAccess', _40 => _40.get, 'optionalCall', _41 => _41()]);
|
|
1118
1280
|
if (ctx && Object.hasOwn(ctx, "headers")) {
|
|
1119
1281
|
return {
|
|
1120
1282
|
ctx,
|
|
@@ -1122,7 +1284,7 @@ function getRequestContext() {
|
|
|
1122
1284
|
};
|
|
1123
1285
|
}
|
|
1124
1286
|
return { ctx, headers: void 0 };
|
|
1125
|
-
} catch (
|
|
1287
|
+
} catch (e9) {
|
|
1126
1288
|
return { ctx: void 0, headers: void 0 };
|
|
1127
1289
|
}
|
|
1128
1290
|
}
|
|
@@ -1133,10 +1295,12 @@ var IDLE_FLUSH_WAIT_MS = 5e3;
|
|
|
1133
1295
|
var IDLE_FLUSH_JITTER_RATIO = 0.2;
|
|
1134
1296
|
var MAX_FLUSH_WAIT_MS = 6e4;
|
|
1135
1297
|
var Scheduler = (_class = class {
|
|
1136
|
-
constructor(onFlush) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
|
|
1298
|
+
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);
|
|
1137
1299
|
this.onFlush = onFlush;
|
|
1300
|
+
this.scheduleTask = scheduleTask;
|
|
1138
1301
|
}
|
|
1139
1302
|
|
|
1303
|
+
|
|
1140
1304
|
__init() {this.resolveWait = null}
|
|
1141
1305
|
__init2() {this.pending = null}
|
|
1142
1306
|
__init3() {this.idleTimeout = null}
|
|
@@ -1151,8 +1315,8 @@ var Scheduler = (_class = class {
|
|
|
1151
1315
|
await this.onFlush(reason);
|
|
1152
1316
|
})();
|
|
1153
1317
|
try {
|
|
1154
|
-
|
|
1155
|
-
} catch (
|
|
1318
|
+
this.scheduleTask(this.pending);
|
|
1319
|
+
} catch (e10) {
|
|
1156
1320
|
}
|
|
1157
1321
|
this.maxTimeout = setTimeout(
|
|
1158
1322
|
() => this.resolveScheduledFlush("max_timeout"),
|
|
@@ -1179,7 +1343,7 @@ var Scheduler = (_class = class {
|
|
|
1179
1343
|
}
|
|
1180
1344
|
resolveScheduledFlush(reason) {
|
|
1181
1345
|
this.clearTimeouts();
|
|
1182
|
-
_optionalChain([this, 'access',
|
|
1346
|
+
_optionalChain([this, 'access', _42 => _42.resolveWait, 'optionalCall', _43 => _43(reason)]);
|
|
1183
1347
|
}
|
|
1184
1348
|
reset() {
|
|
1185
1349
|
this.pending = null;
|
|
@@ -1298,12 +1462,18 @@ var UsageTracker = (_class4 = class {
|
|
|
1298
1462
|
__init7() {this.flushCount = 0}
|
|
1299
1463
|
|
|
1300
1464
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1465
|
+
|
|
1466
|
+
__init8() {this.inflightFlushes = /* @__PURE__ */ new Set()}
|
|
1467
|
+
__init9() {this.trackedRequests = /* @__PURE__ */ new WeakSet()}
|
|
1468
|
+
__init10() {this.readEvents = []}
|
|
1469
|
+
__init11() {this.evaluationEvents = /* @__PURE__ */ new Map()}
|
|
1470
|
+
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);
|
|
1305
1471
|
this.options = options;
|
|
1306
|
-
this.
|
|
1472
|
+
this.waitUntil = options.waitUntil;
|
|
1473
|
+
this.scheduler = new Scheduler(
|
|
1474
|
+
(reason) => this.flushEvents(reason),
|
|
1475
|
+
options.waitUntil
|
|
1476
|
+
);
|
|
1307
1477
|
}
|
|
1308
1478
|
/**
|
|
1309
1479
|
* Triggers an immediate flush of any pending events.
|
|
@@ -1312,6 +1482,7 @@ var UsageTracker = (_class4 = class {
|
|
|
1312
1482
|
async shutdown() {
|
|
1313
1483
|
await this.scheduler.shutdown();
|
|
1314
1484
|
await this.flushEvents("shutdown");
|
|
1485
|
+
await Promise.all([...this.inflightFlushes]);
|
|
1315
1486
|
}
|
|
1316
1487
|
/**
|
|
1317
1488
|
* Tracks a config read event. Deduplicates by request context.
|
|
@@ -1323,7 +1494,7 @@ var UsageTracker = (_class4 = class {
|
|
|
1323
1494
|
if (this.trackedRequests.has(ctx)) return;
|
|
1324
1495
|
this.trackedRequests.add(ctx);
|
|
1325
1496
|
this.readEvents.push(new FlagsConfigReadEvent(headers, options));
|
|
1326
|
-
this.
|
|
1497
|
+
this.requestFlush();
|
|
1327
1498
|
} catch (error) {
|
|
1328
1499
|
console.error("@vercel/flags-core: Failed to record event:", error);
|
|
1329
1500
|
}
|
|
@@ -1347,7 +1518,7 @@ var UsageTracker = (_class4 = class {
|
|
|
1347
1518
|
new FlagsEvaluationEvent(bucketedOptions)
|
|
1348
1519
|
);
|
|
1349
1520
|
}
|
|
1350
|
-
this.
|
|
1521
|
+
this.requestFlush();
|
|
1351
1522
|
} catch (error) {
|
|
1352
1523
|
console.error(
|
|
1353
1524
|
"@vercel/flags-core: Failed to record evaluation event:",
|
|
@@ -1355,6 +1526,25 @@ var UsageTracker = (_class4 = class {
|
|
|
1355
1526
|
);
|
|
1356
1527
|
}
|
|
1357
1528
|
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Flushes immediately when the runtime provides an ingest transport,
|
|
1531
|
+
* otherwise falls back to the time-based scheduler.
|
|
1532
|
+
*/
|
|
1533
|
+
requestFlush() {
|
|
1534
|
+
if (getRuntimeIngestFor(this.options.auth)) {
|
|
1535
|
+
const flush = this.flushEvents("immediate").catch((error) => {
|
|
1536
|
+
console.error("@vercel/flags-core: Failed to flush events:", error);
|
|
1537
|
+
});
|
|
1538
|
+
this.inflightFlushes.add(flush);
|
|
1539
|
+
void flush.finally(() => this.inflightFlushes.delete(flush));
|
|
1540
|
+
try {
|
|
1541
|
+
_optionalChain([this, 'access', _44 => _44.waitUntil, 'optionalCall', _45 => _45(flush)]);
|
|
1542
|
+
} catch (e11) {
|
|
1543
|
+
}
|
|
1544
|
+
} else {
|
|
1545
|
+
this.scheduler.scheduleFlush();
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1358
1548
|
/**
|
|
1359
1549
|
* Send all events to the ingest service
|
|
1360
1550
|
*/
|
|
@@ -1427,7 +1617,7 @@ var BundledSource = class {
|
|
|
1427
1617
|
*/
|
|
1428
1618
|
async tryLoad() {
|
|
1429
1619
|
const result = await this.getResult();
|
|
1430
|
-
if (_optionalChain([result, 'optionalAccess',
|
|
1620
|
+
if (_optionalChain([result, 'optionalAccess', _46 => _46.state]) === "ok" && result.definitions) {
|
|
1431
1621
|
return result.definitions;
|
|
1432
1622
|
}
|
|
1433
1623
|
return void 0;
|
|
@@ -1442,169 +1632,94 @@ var BundledSource = class {
|
|
|
1442
1632
|
|
|
1443
1633
|
// src/controller/fetch-datafile.ts
|
|
1444
1634
|
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
1635
|
+
var DEFAULT_MAX_ATTEMPTS = 3;
|
|
1636
|
+
var DatafileHttpError = class extends Error {
|
|
1637
|
+
constructor(status, statusText) {
|
|
1638
|
+
super(`Failed to fetch data: ${statusText}`);
|
|
1639
|
+
this.status = status;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
get retryable() {
|
|
1643
|
+
return this.status === 408 || this.status === 429 || this.status >= 500;
|
|
1644
|
+
}
|
|
1645
|
+
};
|
|
1445
1646
|
async function fetchDatafile(options) {
|
|
1446
|
-
|
|
1647
|
+
_optionalChain([options, 'access', _47 => _47.signal, 'optionalAccess', _48 => _48.throwIfAborted, 'call', _49 => _49()]);
|
|
1648
|
+
const maxAttempts = _nullishCoalesce(options.maxAttempts, () => ( DEFAULT_MAX_ATTEMPTS));
|
|
1649
|
+
if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {
|
|
1650
|
+
throw new Error("maxAttempts must be a positive integer");
|
|
1651
|
+
}
|
|
1447
1652
|
const controller = new AbortController();
|
|
1653
|
+
const { signal } = controller;
|
|
1654
|
+
let rejectAbort;
|
|
1655
|
+
const aborted = new Promise((_, reject) => {
|
|
1656
|
+
rejectAbort = reject;
|
|
1657
|
+
});
|
|
1658
|
+
const onAbort = () => rejectAbort(signal.reason);
|
|
1659
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1660
|
+
const onExternalAbort = () => controller.abort(_optionalChain([options, 'access', _50 => _50.signal, 'optionalAccess', _51 => _51.reason]));
|
|
1661
|
+
_optionalChain([options, 'access', _52 => _52.signal, 'optionalAccess', _53 => _53.addEventListener, 'call', _54 => _54("abort", onExternalAbort, { once: true })]);
|
|
1448
1662
|
const timeoutId = setTimeout(
|
|
1449
|
-
() => controller.abort(
|
|
1663
|
+
() => controller.abort(
|
|
1664
|
+
new Error("@vercel/flags-core: Datafile fetch deadline exceeded")
|
|
1665
|
+
),
|
|
1450
1666
|
DEFAULT_FETCH_TIMEOUT_MS
|
|
1451
1667
|
);
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
throw new Error("Fetch aborted");
|
|
1457
|
-
}
|
|
1458
|
-
options.signal.addEventListener("abort", onExternalAbort, { once: true });
|
|
1459
|
-
}
|
|
1460
|
-
try {
|
|
1668
|
+
let delay;
|
|
1669
|
+
const fetchAttempt = async () => {
|
|
1670
|
+
const token = await options.auth.resolveToken();
|
|
1671
|
+
signal.throwIfAborted();
|
|
1461
1672
|
const res = await options.fetch(`${options.host}/v1/datafile`, {
|
|
1462
1673
|
headers: {
|
|
1463
|
-
|
|
1674
|
+
...authHeaders(token, options.auth.sourceProjectId),
|
|
1464
1675
|
"User-Agent": `VercelFlagsCore/${version}`,
|
|
1465
1676
|
...process.env.VERCEL_ENV ? { "X-Vercel-Env": process.env.VERCEL_ENV } : null
|
|
1466
1677
|
},
|
|
1467
|
-
signal
|
|
1678
|
+
signal
|
|
1468
1679
|
});
|
|
1469
|
-
|
|
1470
|
-
_optionalChain([options, 'access', _41 => _41.signal, 'optionalAccess', _42 => _42.removeEventListener, 'call', _43 => _43("abort", onExternalAbort)]);
|
|
1680
|
+
signal.throwIfAborted();
|
|
1471
1681
|
if (!res.ok) {
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
throw error instanceof Error ? error : new Error("Unknown fetch error");
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
// src/controller/header-source.ts
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
// src/controller/tagged-data.ts
|
|
1486
|
-
function tagData(data, origin) {
|
|
1487
|
-
return Object.assign(data, { _origin: origin });
|
|
1488
|
-
}
|
|
1489
|
-
function originToMetricsSource(origin) {
|
|
1490
|
-
switch (origin) {
|
|
1491
|
-
case "stream":
|
|
1492
|
-
case "poll":
|
|
1493
|
-
case "provided":
|
|
1494
|
-
return "in-memory";
|
|
1495
|
-
case "fetched":
|
|
1496
|
-
return "remote";
|
|
1497
|
-
case "bundled":
|
|
1498
|
-
return "embedded";
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
|
-
// src/controller/typed-emitter.ts
|
|
1503
|
-
var TypedEmitter = (_class5 = class {constructor() { _class5.prototype.__init11.call(this); }
|
|
1504
|
-
__init11() {this.handlers = /* @__PURE__ */ new Map()}
|
|
1505
|
-
on(event, handler) {
|
|
1506
|
-
let set = this.handlers.get(event);
|
|
1507
|
-
if (!set) {
|
|
1508
|
-
set = /* @__PURE__ */ new Set();
|
|
1509
|
-
this.handlers.set(event, set);
|
|
1510
|
-
}
|
|
1511
|
-
set.add(handler);
|
|
1512
|
-
}
|
|
1513
|
-
off(event, handler) {
|
|
1514
|
-
_optionalChain([this, 'access', _47 => _47.handlers, 'access', _48 => _48.get, 'call', _49 => _49(event), 'optionalAccess', _50 => _50.delete, 'call', _51 => _51(handler)]);
|
|
1515
|
-
}
|
|
1516
|
-
emit(event, ...args) {
|
|
1517
|
-
const set = this.handlers.get(event);
|
|
1518
|
-
if (set) {
|
|
1519
|
-
for (const handler of set) {
|
|
1520
|
-
handler(...args);
|
|
1521
|
-
}
|
|
1682
|
+
void _optionalChain([res, 'access', _55 => _55.body, 'optionalAccess', _56 => _56.cancel, 'call', _57 => _57(), 'access', _58 => _58.catch, 'call', _59 => _59(() => {
|
|
1683
|
+
})]);
|
|
1684
|
+
throw new DatafileHttpError(
|
|
1685
|
+
res.status,
|
|
1686
|
+
res.status === 401 && options.auth.sourceProjectId ? unauthorizedMessage(options.auth.sourceProjectId) : res.statusText
|
|
1687
|
+
);
|
|
1522
1688
|
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
if (this.promise) return this.promise;
|
|
1537
|
-
const abortController = new AbortController();
|
|
1538
|
-
this.abortController = abortController;
|
|
1539
|
-
this.promise = fetchDatafile({
|
|
1540
|
-
...this.options,
|
|
1541
|
-
signal: abortController.signal
|
|
1542
|
-
}).then((data) => {
|
|
1543
|
-
abortController.signal.throwIfAborted();
|
|
1544
|
-
this.emit("data", data);
|
|
1545
|
-
return data;
|
|
1546
|
-
}).finally(() => {
|
|
1547
|
-
if (this.abortController === abortController) {
|
|
1548
|
-
this.promise = void 0;
|
|
1549
|
-
this.abortController = void 0;
|
|
1689
|
+
const data = await res.json();
|
|
1690
|
+
signal.throwIfAborted();
|
|
1691
|
+
return data;
|
|
1692
|
+
};
|
|
1693
|
+
try {
|
|
1694
|
+
for (let attempt = 0; ; attempt++) {
|
|
1695
|
+
if (attempt > 0) {
|
|
1696
|
+
await Promise.race([
|
|
1697
|
+
new Promise((resolve) => {
|
|
1698
|
+
delay = setTimeout(resolve, 100 * 2 ** (attempt - 1));
|
|
1699
|
+
}),
|
|
1700
|
+
aborted
|
|
1701
|
+
]);
|
|
1550
1702
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
return;
|
|
1559
|
-
}
|
|
1560
|
-
const prefix = `flags_${projectId}=`;
|
|
1561
|
-
const value = _optionalChain([header, 'access', _56 => _56.split, 'call', _57 => _57(";"), 'access', _58 => _58.map, 'call', _59 => _59((part) => part.trim()), 'access', _60 => _60.find, 'call', _61 => _61((part) => part.startsWith(prefix)), 'optionalAccess', _62 => _62.slice, 'call', _63 => _63(prefix.length)]);
|
|
1562
|
-
const timestamp = Number(value);
|
|
1563
|
-
return Number.isFinite(timestamp) && timestamp > 0 ? timestamp : void 0;
|
|
1564
|
-
}
|
|
1565
|
-
async resolveData(currentData) {
|
|
1566
|
-
if (!currentData.configUpdatedAt) {
|
|
1567
|
-
return;
|
|
1568
|
-
}
|
|
1569
|
-
const updatedAtHeader = this.getUpdatedAtHeader(currentData.projectId);
|
|
1570
|
-
if (!updatedAtHeader) {
|
|
1571
|
-
return;
|
|
1572
|
-
}
|
|
1573
|
-
const currentUpdatedAt = Number(currentData.configUpdatedAt);
|
|
1574
|
-
if (updatedAtHeader <= currentUpdatedAt) {
|
|
1575
|
-
return [currentData, "HIT"];
|
|
1576
|
-
}
|
|
1577
|
-
if (updatedAtHeader <= currentUpdatedAt + 1e4) {
|
|
1578
|
-
const pending = this.fetchDatafile();
|
|
1579
|
-
const signal = _optionalChain([this, 'access', _64 => _64.abortController, 'optionalAccess', _65 => _65.signal]);
|
|
1580
|
-
const background = pending.catch((error) => {
|
|
1581
|
-
if (!_optionalChain([signal, 'optionalAccess', _66 => _66.aborted])) {
|
|
1582
|
-
console.error("@vercel/flags-core: Header refresh failed:", error);
|
|
1703
|
+
signal.throwIfAborted();
|
|
1704
|
+
try {
|
|
1705
|
+
return await Promise.race([fetchAttempt(), aborted]);
|
|
1706
|
+
} catch (error) {
|
|
1707
|
+
signal.throwIfAborted();
|
|
1708
|
+
if (attempt === maxAttempts - 1 || error instanceof DatafileHttpError && !error.retryable) {
|
|
1709
|
+
throw error instanceof Error ? error : new Error("Unknown fetch error");
|
|
1583
1710
|
}
|
|
1584
|
-
}
|
|
1585
|
-
_functions.waitUntil.call(void 0, background);
|
|
1586
|
-
return [currentData, "STALE"];
|
|
1711
|
+
}
|
|
1587
1712
|
}
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
}
|
|
1594
|
-
isAvailable(projectId) {
|
|
1595
|
-
return !!this.getUpdatedAtHeader(projectId);
|
|
1596
|
-
}
|
|
1597
|
-
/**
|
|
1598
|
-
* Abort the current header-driven fetch and discard its pending work.
|
|
1599
|
-
*/
|
|
1600
|
-
stop() {
|
|
1601
|
-
_optionalChain([this, 'access', _67 => _67.abortController, 'optionalAccess', _68 => _68.abort, 'call', _69 => _69()]);
|
|
1602
|
-
this.abortController = void 0;
|
|
1603
|
-
this.promise = void 0;
|
|
1713
|
+
} finally {
|
|
1714
|
+
clearTimeout(timeoutId);
|
|
1715
|
+
clearTimeout(delay);
|
|
1716
|
+
signal.removeEventListener("abort", onAbort);
|
|
1717
|
+
_optionalChain([options, 'access', _60 => _60.signal, 'optionalAccess', _61 => _61.removeEventListener, 'call', _62 => _62("abort", onExternalAbort)]);
|
|
1604
1718
|
}
|
|
1605
|
-
}
|
|
1719
|
+
}
|
|
1606
1720
|
|
|
1607
1721
|
// src/controller/normalized-options.ts
|
|
1722
|
+
|
|
1608
1723
|
var DEFAULT_STREAM_INIT_TIMEOUT_MS = 3e3;
|
|
1609
1724
|
var DEFAULT_POLLING_INTERVAL_MS = 3e4;
|
|
1610
1725
|
var MIN_POLLING_INTERVAL_MS = 3e4;
|
|
@@ -1648,6 +1763,7 @@ function normalizeOptions(options) {
|
|
|
1648
1763
|
polling,
|
|
1649
1764
|
buildStep,
|
|
1650
1765
|
fetch: _nullishCoalesce(options.fetch, () => ( globalThis.fetch)),
|
|
1766
|
+
waitUntil: _nullishCoalesce(options.waitUntil, () => ( _functions.waitUntil)),
|
|
1651
1767
|
host: "https://flags.vercel.com",
|
|
1652
1768
|
metricEnvironment: options.metricEnvironment,
|
|
1653
1769
|
clientName: options.clientName,
|
|
@@ -1655,6 +1771,35 @@ function normalizeOptions(options) {
|
|
|
1655
1771
|
};
|
|
1656
1772
|
}
|
|
1657
1773
|
|
|
1774
|
+
// src/controller/typed-emitter.ts
|
|
1775
|
+
var TypedEmitter = (_class5 = class {constructor() { _class5.prototype.__init12.call(this); }
|
|
1776
|
+
__init12() {this.handlers = /* @__PURE__ */ new Map()}
|
|
1777
|
+
on(event, handler) {
|
|
1778
|
+
let set = this.handlers.get(event);
|
|
1779
|
+
if (!set) {
|
|
1780
|
+
set = /* @__PURE__ */ new Set();
|
|
1781
|
+
this.handlers.set(event, set);
|
|
1782
|
+
}
|
|
1783
|
+
set.add(handler);
|
|
1784
|
+
}
|
|
1785
|
+
off(event, handler) {
|
|
1786
|
+
const set = this.handlers.get(event);
|
|
1787
|
+
if (!set) return;
|
|
1788
|
+
set.delete(handler);
|
|
1789
|
+
if (set.size === 0) {
|
|
1790
|
+
this.handlers.delete(event);
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
emit(event, ...args) {
|
|
1794
|
+
const set = this.handlers.get(event);
|
|
1795
|
+
if (set) {
|
|
1796
|
+
for (const handler of set) {
|
|
1797
|
+
handler(...args);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
}, _class5);
|
|
1802
|
+
|
|
1658
1803
|
// src/controller/polling-source.ts
|
|
1659
1804
|
var PollingSource = class extends TypedEmitter {
|
|
1660
1805
|
|
|
@@ -1669,14 +1814,17 @@ var PollingSource = class extends TypedEmitter {
|
|
|
1669
1814
|
* Emits 'data' on success, 'error' on failure.
|
|
1670
1815
|
*/
|
|
1671
1816
|
async poll() {
|
|
1672
|
-
if (_optionalChain([this, 'access',
|
|
1817
|
+
if (_optionalChain([this, 'access', _63 => _63.abortController, 'optionalAccess', _64 => _64.signal, 'access', _65 => _65.aborted])) return;
|
|
1818
|
+
this.abortController ??= new AbortController();
|
|
1819
|
+
const controller = this.abortController;
|
|
1673
1820
|
try {
|
|
1674
1821
|
const data = await fetchDatafile({
|
|
1675
1822
|
...this.config,
|
|
1676
|
-
signal:
|
|
1823
|
+
signal: controller.signal
|
|
1677
1824
|
});
|
|
1678
1825
|
this.emit("data", data);
|
|
1679
1826
|
} catch (error) {
|
|
1827
|
+
controller.signal.throwIfAborted();
|
|
1680
1828
|
const err = error instanceof Error ? error : new Error("Unknown poll error");
|
|
1681
1829
|
this.emit("error", err);
|
|
1682
1830
|
}
|
|
@@ -1688,9 +1836,9 @@ var PollingSource = class extends TypedEmitter {
|
|
|
1688
1836
|
*/
|
|
1689
1837
|
startInterval() {
|
|
1690
1838
|
if (this.intervalId) return;
|
|
1691
|
-
this.abortController = new AbortController();
|
|
1692
1839
|
this.intervalId = setInterval(
|
|
1693
|
-
() => void this.poll()
|
|
1840
|
+
() => void this.poll().catch(() => {
|
|
1841
|
+
}),
|
|
1694
1842
|
this.config.polling.intervalMs
|
|
1695
1843
|
);
|
|
1696
1844
|
}
|
|
@@ -1702,7 +1850,7 @@ var PollingSource = class extends TypedEmitter {
|
|
|
1702
1850
|
clearInterval(this.intervalId);
|
|
1703
1851
|
this.intervalId = void 0;
|
|
1704
1852
|
}
|
|
1705
|
-
_optionalChain([this, 'access',
|
|
1853
|
+
_optionalChain([this, 'access', _66 => _66.abortController, 'optionalAccess', _67 => _67.abort, 'call', _68 => _68()]);
|
|
1706
1854
|
this.abortController = void 0;
|
|
1707
1855
|
}
|
|
1708
1856
|
};
|
|
@@ -1731,8 +1879,8 @@ function backoff(retryCount) {
|
|
|
1731
1879
|
return delay + Math.random() * 1e3;
|
|
1732
1880
|
}
|
|
1733
1881
|
var UnauthorizedError = class extends Error {
|
|
1734
|
-
constructor() {
|
|
1735
|
-
super(
|
|
1882
|
+
constructor(sourceProjectId) {
|
|
1883
|
+
super(`stream: ${unauthorizedMessage(sourceProjectId)}`);
|
|
1736
1884
|
this.name = "UnauthorizedError";
|
|
1737
1885
|
}
|
|
1738
1886
|
};
|
|
@@ -1781,7 +1929,7 @@ async function connectStream(config, callbacks) {
|
|
|
1781
1929
|
if (pingTimeoutId !== void 0) clearTimeout(pingTimeoutId);
|
|
1782
1930
|
if (!initialDataReceived) return;
|
|
1783
1931
|
pingTimeoutId = setTimeout(() => {
|
|
1784
|
-
_optionalChain([responseBody, 'optionalAccess',
|
|
1932
|
+
_optionalChain([responseBody, 'optionalAccess', _69 => _69.cancel, 'call', _70 => _70(), 'access', _71 => _71.catch, 'call', _72 => _72(() => {
|
|
1785
1933
|
})]);
|
|
1786
1934
|
connectionAbort.abort();
|
|
1787
1935
|
}, PING_TIMEOUT_MS);
|
|
@@ -1792,7 +1940,7 @@ async function connectStream(config, callbacks) {
|
|
|
1792
1940
|
throw new TokenResolutionError(error);
|
|
1793
1941
|
});
|
|
1794
1942
|
const headers = {
|
|
1795
|
-
|
|
1943
|
+
...authHeaders(token, config.sourceProjectId),
|
|
1796
1944
|
"User-Agent": `VercelFlagsCore/${version}`,
|
|
1797
1945
|
"X-Retry-Attempt": String(retryCount)
|
|
1798
1946
|
};
|
|
@@ -1803,7 +1951,7 @@ async function connectStream(config, callbacks) {
|
|
|
1803
1951
|
if (vercelEnv) {
|
|
1804
1952
|
headers["X-Vercel-Env"] = vercelEnv;
|
|
1805
1953
|
}
|
|
1806
|
-
const revision = _optionalChain([config, 'access',
|
|
1954
|
+
const revision = _optionalChain([config, 'access', _73 => _73.revision, 'optionalCall', _74 => _74()]);
|
|
1807
1955
|
if (revision !== void 0) {
|
|
1808
1956
|
headers["X-Revision"] = String(revision);
|
|
1809
1957
|
}
|
|
@@ -1814,7 +1962,7 @@ async function connectStream(config, callbacks) {
|
|
|
1814
1962
|
if (!response.ok) {
|
|
1815
1963
|
if (response.status === 401) {
|
|
1816
1964
|
if (!initialDataReceived) {
|
|
1817
|
-
rejectInit(new UnauthorizedError());
|
|
1965
|
+
rejectInit(new UnauthorizedError(config.sourceProjectId));
|
|
1818
1966
|
}
|
|
1819
1967
|
abortController.abort();
|
|
1820
1968
|
break;
|
|
@@ -1849,7 +1997,7 @@ async function connectStream(config, callbacks) {
|
|
|
1849
1997
|
let message;
|
|
1850
1998
|
try {
|
|
1851
1999
|
message = JSON.parse(line);
|
|
1852
|
-
} catch (
|
|
2000
|
+
} catch (e12) {
|
|
1853
2001
|
console.warn(
|
|
1854
2002
|
"@vercel/flags-core: Failed to parse stream message, skipping"
|
|
1855
2003
|
);
|
|
@@ -1865,7 +2013,7 @@ async function connectStream(config, callbacks) {
|
|
|
1865
2013
|
resetPingTimeout();
|
|
1866
2014
|
}
|
|
1867
2015
|
if (message.type === "primed") {
|
|
1868
|
-
_optionalChain([onPrimed, 'optionalCall',
|
|
2016
|
+
_optionalChain([onPrimed, 'optionalCall', _75 => _75(message)]);
|
|
1869
2017
|
retryCount = 0;
|
|
1870
2018
|
if (!initialDataReceived) {
|
|
1871
2019
|
initialDataReceived = true;
|
|
@@ -1888,7 +2036,7 @@ async function connectStream(config, callbacks) {
|
|
|
1888
2036
|
clearTimeout(pingTimeoutId);
|
|
1889
2037
|
abortController.signal.removeEventListener("abort", onMainAbort);
|
|
1890
2038
|
if (!abortController.signal.aborted) {
|
|
1891
|
-
_optionalChain([onDisconnect, 'optionalCall',
|
|
2039
|
+
_optionalChain([onDisconnect, 'optionalCall', _76 => _76()]);
|
|
1892
2040
|
retryCount++;
|
|
1893
2041
|
const elapsed = Date.now() - lastAttemptTime;
|
|
1894
2042
|
const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
|
|
@@ -1909,7 +2057,7 @@ async function connectStream(config, callbacks) {
|
|
|
1909
2057
|
if (!connectionAbort.signal.aborted) {
|
|
1910
2058
|
lastError = error;
|
|
1911
2059
|
}
|
|
1912
|
-
_optionalChain([onDisconnect, 'optionalCall',
|
|
2060
|
+
_optionalChain([onDisconnect, 'optionalCall', _77 => _77()]);
|
|
1913
2061
|
retryCount++;
|
|
1914
2062
|
const elapsed = Date.now() - lastAttemptTime;
|
|
1915
2063
|
const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);
|
|
@@ -1958,6 +2106,7 @@ var StreamSource = class extends TypedEmitter {
|
|
|
1958
2106
|
{
|
|
1959
2107
|
host: this.options.host,
|
|
1960
2108
|
resolveToken: () => this.options.auth.resolveToken(),
|
|
2109
|
+
sourceProjectId: this.options.auth.sourceProjectId,
|
|
1961
2110
|
abortController,
|
|
1962
2111
|
fetch: this.options.fetch,
|
|
1963
2112
|
revision: this.revision
|
|
@@ -1988,12 +2137,29 @@ var StreamSource = class extends TypedEmitter {
|
|
|
1988
2137
|
* Stop the stream connection.
|
|
1989
2138
|
*/
|
|
1990
2139
|
stop() {
|
|
1991
|
-
_optionalChain([this, 'access',
|
|
2140
|
+
_optionalChain([this, 'access', _78 => _78.abortController, 'optionalAccess', _79 => _79.abort, 'call', _80 => _80()]);
|
|
1992
2141
|
this.abortController = void 0;
|
|
1993
2142
|
this.promise = void 0;
|
|
1994
2143
|
}
|
|
1995
2144
|
};
|
|
1996
2145
|
|
|
2146
|
+
// src/controller/tagged-data.ts
|
|
2147
|
+
function tagData(data, origin) {
|
|
2148
|
+
return Object.assign(data, { _origin: origin });
|
|
2149
|
+
}
|
|
2150
|
+
function originToMetricsSource(origin) {
|
|
2151
|
+
switch (origin) {
|
|
2152
|
+
case "stream":
|
|
2153
|
+
case "poll":
|
|
2154
|
+
case "provided":
|
|
2155
|
+
return "in-memory";
|
|
2156
|
+
case "fetched":
|
|
2157
|
+
return "remote";
|
|
2158
|
+
case "bundled":
|
|
2159
|
+
return "embedded";
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
|
|
1997
2163
|
// src/controller/index.ts
|
|
1998
2164
|
function parseConfigUpdatedAt(value) {
|
|
1999
2165
|
if (typeof value === "number") return value;
|
|
@@ -2006,39 +2172,38 @@ function parseConfigUpdatedAt(value) {
|
|
|
2006
2172
|
var Controller = (_class6 = class {
|
|
2007
2173
|
|
|
2008
2174
|
// State machine
|
|
2009
|
-
|
|
2175
|
+
__init13() {this.state = "idle"}
|
|
2010
2176
|
// Data state — tagged with origin
|
|
2011
2177
|
|
|
2012
2178
|
// Memoized data spread for read() / getDatafile().
|
|
2013
2179
|
// Rebuilt only when `this.data` reference changes (e.g. on stream/poll update).
|
|
2014
2180
|
// Holds the result of stripping `_origin`; metrics are appended per-call.
|
|
2015
|
-
|
|
2016
|
-
|
|
2181
|
+
__init14() {this.dataViewSource = void 0}
|
|
2182
|
+
__init15() {this.dataViewBase = void 0}
|
|
2017
2183
|
// Sources (I/O delegates)
|
|
2018
2184
|
|
|
2019
2185
|
|
|
2020
2186
|
|
|
2021
|
-
|
|
2022
2187
|
// Usage tracking
|
|
2023
2188
|
|
|
2024
|
-
|
|
2189
|
+
__init16() {this.isFirstGetData = true}
|
|
2025
2190
|
// Build-step deduplication
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
// Suppresses usage tracking
|
|
2029
|
-
|
|
2030
|
-
|
|
2191
|
+
__init17() {this.buildDataPromise = null}
|
|
2192
|
+
__init18() {this.buildReadTracked = false}
|
|
2193
|
+
// Suppresses usage tracking while the credential is rejected. Set on a 401
|
|
2194
|
+
// from any source, cleared as soon as a source delivers data again.
|
|
2195
|
+
__init19() {this.unauthorized = false}
|
|
2196
|
+
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);
|
|
2031
2197
|
this.options = normalizeOptions(options);
|
|
2032
2198
|
this.streamSource = new StreamSource(
|
|
2033
2199
|
this.options,
|
|
2034
|
-
() => _optionalChain([this, 'access',
|
|
2200
|
+
() => _optionalChain([this, 'access', _81 => _81.data, 'optionalAccess', _82 => _82.revision])
|
|
2035
2201
|
);
|
|
2036
2202
|
this.pollingSource = new PollingSource(this.options);
|
|
2037
2203
|
this.bundledSource = new BundledSource({
|
|
2038
2204
|
auth: this.options.auth,
|
|
2039
2205
|
readBundledDefinitions
|
|
2040
2206
|
});
|
|
2041
|
-
this.headerSource = new HeaderSource(this.options);
|
|
2042
2207
|
this.wireSourceEvents();
|
|
2043
2208
|
if (this.options.datafile) {
|
|
2044
2209
|
this.data = tagData(this.options.datafile, "provided");
|
|
@@ -2046,39 +2211,38 @@ var Controller = (_class6 = class {
|
|
|
2046
2211
|
this.usageTracker = new UsageTracker(this.options);
|
|
2047
2212
|
}
|
|
2048
2213
|
// Source event handlers (stored for cleanup)
|
|
2049
|
-
|
|
2214
|
+
__init20() {this.onStreamData = (data) => {
|
|
2215
|
+
this.unauthorized = false;
|
|
2050
2216
|
if (this.isNewerData(data)) {
|
|
2051
2217
|
this.data = tagData(data, "stream");
|
|
2052
2218
|
}
|
|
2053
2219
|
}}
|
|
2054
|
-
|
|
2220
|
+
__init21() {this.onStreamPrimed = () => {
|
|
2221
|
+
this.unauthorized = false;
|
|
2055
2222
|
if (this.state === "degraded" || this.state === "initializing:stream") {
|
|
2056
2223
|
this.transition("streaming");
|
|
2057
2224
|
}
|
|
2058
2225
|
}}
|
|
2059
|
-
|
|
2226
|
+
__init22() {this.onStreamConnected = () => {
|
|
2060
2227
|
if (this.state === "degraded" || this.state === "initializing:stream") {
|
|
2061
2228
|
this.transition("streaming");
|
|
2062
2229
|
}
|
|
2063
2230
|
}}
|
|
2064
|
-
|
|
2231
|
+
__init23() {this.onStreamDisconnected = () => {
|
|
2065
2232
|
if (this.state === "streaming") {
|
|
2066
2233
|
this.transition("degraded");
|
|
2067
2234
|
}
|
|
2068
2235
|
}}
|
|
2069
|
-
|
|
2236
|
+
__init24() {this.onPollData = (data) => {
|
|
2237
|
+
this.unauthorized = false;
|
|
2070
2238
|
if (this.isNewerData(data)) {
|
|
2071
2239
|
this.data = tagData(data, "poll");
|
|
2072
2240
|
}
|
|
2073
2241
|
}}
|
|
2074
|
-
|
|
2242
|
+
__init25() {this.onPollError = (error) => {
|
|
2243
|
+
this.noteUnauthorized(error);
|
|
2075
2244
|
console.error("@vercel/flags-core: Poll failed:", error);
|
|
2076
2245
|
}}
|
|
2077
|
-
__init25() {this.onFetchedData = (data) => {
|
|
2078
|
-
if (this.isNewerData(data)) {
|
|
2079
|
-
this.data = tagData(data, "fetched");
|
|
2080
|
-
}
|
|
2081
|
-
}}
|
|
2082
2246
|
// ---------------------------------------------------------------------------
|
|
2083
2247
|
// Source event wiring
|
|
2084
2248
|
// ---------------------------------------------------------------------------
|
|
@@ -2089,7 +2253,6 @@ var Controller = (_class6 = class {
|
|
|
2089
2253
|
this.streamSource.on("disconnected", this.onStreamDisconnected);
|
|
2090
2254
|
this.pollingSource.on("data", this.onPollData);
|
|
2091
2255
|
this.pollingSource.on("error", this.onPollError);
|
|
2092
|
-
this.headerSource.on("data", this.onFetchedData);
|
|
2093
2256
|
}
|
|
2094
2257
|
unwireSourceEvents() {
|
|
2095
2258
|
this.streamSource.off("data", this.onStreamData);
|
|
@@ -2098,7 +2261,6 @@ var Controller = (_class6 = class {
|
|
|
2098
2261
|
this.streamSource.off("disconnected", this.onStreamDisconnected);
|
|
2099
2262
|
this.pollingSource.off("data", this.onPollData);
|
|
2100
2263
|
this.pollingSource.off("error", this.onPollError);
|
|
2101
|
-
this.headerSource.off("data", this.onFetchedData);
|
|
2102
2264
|
}
|
|
2103
2265
|
// ---------------------------------------------------------------------------
|
|
2104
2266
|
// State machine
|
|
@@ -2116,8 +2278,6 @@ var Controller = (_class6 = class {
|
|
|
2116
2278
|
return "streaming";
|
|
2117
2279
|
case "polling":
|
|
2118
2280
|
return "polling";
|
|
2119
|
-
case "vercel":
|
|
2120
|
-
return "vercel";
|
|
2121
2281
|
default:
|
|
2122
2282
|
return "offline";
|
|
2123
2283
|
}
|
|
@@ -2149,13 +2309,11 @@ var Controller = (_class6 = class {
|
|
|
2149
2309
|
if (bundled) {
|
|
2150
2310
|
this.data = tagData(bundled, "bundled");
|
|
2151
2311
|
}
|
|
2152
|
-
} catch (
|
|
2312
|
+
} catch (e13) {
|
|
2153
2313
|
}
|
|
2154
2314
|
}
|
|
2155
2315
|
if (this.data) {
|
|
2156
|
-
if (this.
|
|
2157
|
-
this.transition("vercel");
|
|
2158
|
-
} else if (this.options.stream.enabled) {
|
|
2316
|
+
if (this.options.stream.enabled) {
|
|
2159
2317
|
this.transition("initializing:stream");
|
|
2160
2318
|
await this.tryInitializeStream();
|
|
2161
2319
|
} else if (this.options.polling.enabled) {
|
|
@@ -2218,7 +2376,6 @@ var Controller = (_class6 = class {
|
|
|
2218
2376
|
this.unwireSourceEvents();
|
|
2219
2377
|
this.streamSource.stop();
|
|
2220
2378
|
this.pollingSource.stop();
|
|
2221
|
-
this.headerSource.stop();
|
|
2222
2379
|
this.data = this.options.datafile ? tagData(this.options.datafile, "provided") : void 0;
|
|
2223
2380
|
this.transition("shutdown");
|
|
2224
2381
|
await this.usageTracker.shutdown();
|
|
@@ -2254,9 +2411,10 @@ var Controller = (_class6 = class {
|
|
|
2254
2411
|
this.data = tagData(fetched, "fetched");
|
|
2255
2412
|
result = this.data;
|
|
2256
2413
|
cacheStatus = "MISS";
|
|
2257
|
-
} catch (
|
|
2258
|
-
|
|
2259
|
-
|
|
2414
|
+
} catch (error) {
|
|
2415
|
+
this.noteUnauthorized(error);
|
|
2416
|
+
throw this.noDefinitionsError(
|
|
2417
|
+
". Initialize the client or provide a datafile."
|
|
2260
2418
|
);
|
|
2261
2419
|
}
|
|
2262
2420
|
}
|
|
@@ -2300,12 +2458,6 @@ var Controller = (_class6 = class {
|
|
|
2300
2458
|
return this.resolveDataForBuildStep();
|
|
2301
2459
|
}
|
|
2302
2460
|
if (this.data) {
|
|
2303
|
-
if (this.mode === "vercel") {
|
|
2304
|
-
const data = await this.headerSource.read(this.data);
|
|
2305
|
-
if (data) {
|
|
2306
|
-
return data;
|
|
2307
|
-
}
|
|
2308
|
-
}
|
|
2309
2461
|
const cacheStatus = this.isConnected ? "HIT" : "STALE";
|
|
2310
2462
|
return [this.data, cacheStatus];
|
|
2311
2463
|
}
|
|
@@ -2324,9 +2476,7 @@ var Controller = (_class6 = class {
|
|
|
2324
2476
|
await this.streamSource.start();
|
|
2325
2477
|
return true;
|
|
2326
2478
|
} catch (error) {
|
|
2327
|
-
|
|
2328
|
-
this.unauthorized = true;
|
|
2329
|
-
}
|
|
2479
|
+
this.noteUnauthorized(error);
|
|
2330
2480
|
return false;
|
|
2331
2481
|
}
|
|
2332
2482
|
}
|
|
@@ -2354,9 +2504,7 @@ var Controller = (_class6 = class {
|
|
|
2354
2504
|
return true;
|
|
2355
2505
|
} catch (error) {
|
|
2356
2506
|
clearTimeout(timeoutId);
|
|
2357
|
-
|
|
2358
|
-
this.unauthorized = true;
|
|
2359
|
-
}
|
|
2507
|
+
this.noteUnauthorized(error);
|
|
2360
2508
|
return false;
|
|
2361
2509
|
}
|
|
2362
2510
|
}
|
|
@@ -2379,7 +2527,7 @@ var Controller = (_class6 = class {
|
|
|
2379
2527
|
return true;
|
|
2380
2528
|
}
|
|
2381
2529
|
return false;
|
|
2382
|
-
} catch (
|
|
2530
|
+
} catch (e14) {
|
|
2383
2531
|
return false;
|
|
2384
2532
|
}
|
|
2385
2533
|
}
|
|
@@ -2404,11 +2552,16 @@ var Controller = (_class6 = class {
|
|
|
2404
2552
|
return true;
|
|
2405
2553
|
}
|
|
2406
2554
|
return false;
|
|
2407
|
-
} catch (
|
|
2555
|
+
} catch (e15) {
|
|
2408
2556
|
clearTimeout(timeoutId);
|
|
2409
2557
|
return false;
|
|
2410
2558
|
}
|
|
2411
2559
|
}
|
|
2560
|
+
noteUnauthorized(error) {
|
|
2561
|
+
if (error instanceof UnauthorizedError || error instanceof Error && error.message.includes("401")) {
|
|
2562
|
+
this.unauthorized = true;
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2412
2565
|
// ---------------------------------------------------------------------------
|
|
2413
2566
|
// Build step helpers
|
|
2414
2567
|
// ---------------------------------------------------------------------------
|
|
@@ -2454,10 +2607,11 @@ var Controller = (_class6 = class {
|
|
|
2454
2607
|
fetch: this.options.fetch
|
|
2455
2608
|
});
|
|
2456
2609
|
return tagData(fetched, "fetched");
|
|
2457
|
-
} catch (
|
|
2610
|
+
} catch (error) {
|
|
2611
|
+
this.noteUnauthorized(error);
|
|
2458
2612
|
}
|
|
2459
|
-
throw
|
|
2460
|
-
"
|
|
2613
|
+
throw this.noDefinitionsError(
|
|
2614
|
+
" during build. Provide a datafile or bundled definitions."
|
|
2461
2615
|
);
|
|
2462
2616
|
}
|
|
2463
2617
|
// ---------------------------------------------------------------------------
|
|
@@ -2488,11 +2642,20 @@ var Controller = (_class6 = class {
|
|
|
2488
2642
|
this.data = tagData(fetched, "fetched");
|
|
2489
2643
|
this.transition("degraded");
|
|
2490
2644
|
return;
|
|
2491
|
-
} catch (
|
|
2645
|
+
} catch (e16) {
|
|
2492
2646
|
}
|
|
2493
2647
|
}
|
|
2494
|
-
throw
|
|
2495
|
-
|
|
2648
|
+
throw this.noDefinitionsError(". Bundled definitions not found.");
|
|
2649
|
+
}
|
|
2650
|
+
/**
|
|
2651
|
+
* `detail` continues the sentence "No flag definitions available", so it
|
|
2652
|
+
* starts with either "." or " during build.".
|
|
2653
|
+
*/
|
|
2654
|
+
noDefinitionsError(detail) {
|
|
2655
|
+
const { sourceProjectId } = this.options.auth;
|
|
2656
|
+
const reason = this.unauthorized && sourceProjectId ? ` Request was ${unauthorizedMessage(sourceProjectId)}` : "";
|
|
2657
|
+
return new Error(
|
|
2658
|
+
`@vercel/flags-core: No flag definitions available${detail}${reason}`
|
|
2496
2659
|
);
|
|
2497
2660
|
}
|
|
2498
2661
|
/**
|
|
@@ -2540,11 +2703,11 @@ var Controller = (_class6 = class {
|
|
|
2540
2703
|
this.data = tagData(fetched, "fetched");
|
|
2541
2704
|
this.transition("degraded");
|
|
2542
2705
|
return [this.data, "MISS"];
|
|
2543
|
-
} catch (
|
|
2706
|
+
} catch (e17) {
|
|
2544
2707
|
}
|
|
2545
2708
|
}
|
|
2546
|
-
throw
|
|
2547
|
-
"
|
|
2709
|
+
throw this.noDefinitionsError(
|
|
2710
|
+
". Provide a datafile or bundled definitions."
|
|
2548
2711
|
);
|
|
2549
2712
|
}
|
|
2550
2713
|
// ---------------------------------------------------------------------------
|
|
@@ -2592,11 +2755,11 @@ var Controller = (_class6 = class {
|
|
|
2592
2755
|
duration: Date.now() - startTime,
|
|
2593
2756
|
mode: mode === "streaming" ? "stream" : mode === "polling" ? "poll" : mode
|
|
2594
2757
|
};
|
|
2595
|
-
const configUpdatedAt = _optionalChain([this, 'access',
|
|
2758
|
+
const configUpdatedAt = _optionalChain([this, 'access', _83 => _83.data, 'optionalAccess', _84 => _84.configUpdatedAt]);
|
|
2596
2759
|
if (typeof configUpdatedAt === "number") {
|
|
2597
2760
|
trackOptions.configUpdatedAt = configUpdatedAt;
|
|
2598
2761
|
}
|
|
2599
|
-
const revision = _optionalChain([this, 'access',
|
|
2762
|
+
const revision = _optionalChain([this, 'access', _85 => _85.data, 'optionalAccess', _86 => _86.revision]);
|
|
2600
2763
|
if (typeof revision === "number") {
|
|
2601
2764
|
trackOptions.revision = revision;
|
|
2602
2765
|
}
|
|
@@ -2617,90 +2780,8 @@ var Controller = (_class6 = class {
|
|
|
2617
2780
|
}
|
|
2618
2781
|
}, _class6);
|
|
2619
2782
|
|
|
2620
|
-
// src/controller/auth.ts
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
// src/utils/sdk-keys.ts
|
|
2624
|
-
var SDK_KEY_REGEX = /^vf_(?:server|client)_/;
|
|
2625
|
-
function parseSdkKeyFromFlagsConnectionString(text) {
|
|
2626
|
-
if (SDK_KEY_REGEX.test(text)) return text;
|
|
2627
|
-
try {
|
|
2628
|
-
if (!text.startsWith("flags:")) return null;
|
|
2629
|
-
const params = new URLSearchParams(text.slice(6));
|
|
2630
|
-
const sdkKey = params.get("sdkKey");
|
|
2631
|
-
if (sdkKey && SDK_KEY_REGEX.test(sdkKey)) return sdkKey;
|
|
2632
|
-
} catch (e16) {
|
|
2633
|
-
}
|
|
2634
|
-
return null;
|
|
2635
|
-
}
|
|
2636
|
-
|
|
2637
|
-
// src/controller/auth.ts
|
|
2638
|
-
async function getOidcToken() {
|
|
2639
|
-
try {
|
|
2640
|
-
return await _oidc.getVercelOidcToken.call(void 0, );
|
|
2641
|
-
} catch (e17) {
|
|
2642
|
-
throw new Error(
|
|
2643
|
-
[
|
|
2644
|
-
"@vercel/flags-core: Failed to get OIDC token.",
|
|
2645
|
-
"Are you running in a Vercel Environment where OIDC tokens are available?",
|
|
2646
|
-
"Did you mean to use an SDK Key instead? Use the environment variable FLAGS or pass it directly to the client."
|
|
2647
|
-
].join(" ")
|
|
2648
|
-
);
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
function getProjectIdFromOidcToken(oidcToken) {
|
|
2652
|
-
const tokenParts = oidcToken.split(".");
|
|
2653
|
-
if (tokenParts.length !== 3 || !tokenParts[1]) {
|
|
2654
|
-
throw new Error("@vercel/flags-core: Invalid OIDC token");
|
|
2655
|
-
}
|
|
2656
|
-
const payload = JSON.parse(
|
|
2657
|
-
Buffer.from(tokenParts[1], "base64url").toString("utf8")
|
|
2658
|
-
);
|
|
2659
|
-
if (typeof payload.project_id !== "string" || !payload.project_id) {
|
|
2660
|
-
throw new Error(
|
|
2661
|
-
"@vercel/flags-core: Missing project_id claim in OIDC token"
|
|
2662
|
-
);
|
|
2663
|
-
}
|
|
2664
|
-
return payload.project_id;
|
|
2665
|
-
}
|
|
2666
|
-
var Authentication = class {
|
|
2667
|
-
|
|
2668
|
-
constructor(sdkKeyOrConnectionString) {
|
|
2669
|
-
if (sdkKeyOrConnectionString !== void 0) {
|
|
2670
|
-
if (typeof sdkKeyOrConnectionString !== "string") {
|
|
2671
|
-
throw new Error(
|
|
2672
|
-
`@vercel/flags-core: Invalid sdkKey. Expected string, got ${typeof sdkKeyOrConnectionString}`
|
|
2673
|
-
);
|
|
2674
|
-
}
|
|
2675
|
-
const parsed = parseSdkKeyFromFlagsConnectionString(
|
|
2676
|
-
sdkKeyOrConnectionString
|
|
2677
|
-
);
|
|
2678
|
-
if (!parsed) {
|
|
2679
|
-
throw new Error("@vercel/flags-core: Missing sdkKey");
|
|
2680
|
-
}
|
|
2681
|
-
this.sdkKey = parsed;
|
|
2682
|
-
}
|
|
2683
|
-
}
|
|
2684
|
-
async resolveToken() {
|
|
2685
|
-
if (this.sdkKey) {
|
|
2686
|
-
return this.sdkKey;
|
|
2687
|
-
}
|
|
2688
|
-
return await getOidcToken();
|
|
2689
|
-
}
|
|
2690
|
-
async resolveBundledDefinitionsLookup() {
|
|
2691
|
-
if (this.sdkKey) {
|
|
2692
|
-
return { type: "sdk-key", sdkKey: this.sdkKey };
|
|
2693
|
-
}
|
|
2694
|
-
const oidcToken = await this.resolveToken();
|
|
2695
|
-
return {
|
|
2696
|
-
type: "project-id",
|
|
2697
|
-
projectId: getProjectIdFromOidcToken(oidcToken)
|
|
2698
|
-
};
|
|
2699
|
-
}
|
|
2700
|
-
};
|
|
2701
|
-
|
|
2702
2783
|
// src/index.make.ts
|
|
2703
|
-
function make(createRawClient) {
|
|
2784
|
+
function make(createRawClient, defaults) {
|
|
2704
2785
|
let _defaultFlagsClient = null;
|
|
2705
2786
|
function createClient2(sdkKeyOrConnectionStringOrOptions, options) {
|
|
2706
2787
|
const optionsOnly = typeof sdkKeyOrConnectionStringOrOptions === "object" && sdkKeyOrConnectionStringOrOptions !== null;
|
|
@@ -2708,10 +2789,20 @@ function make(createRawClient) {
|
|
|
2708
2789
|
const createClientOptions = optionsOnly ? sdkKeyOrConnectionStringOrOptions : options;
|
|
2709
2790
|
const { experimental_reportExposures, ...controllerOptions } = _nullishCoalesce(createClientOptions, () => ( {}));
|
|
2710
2791
|
const auth = new Authentication(sdkKeyOrConnectionString);
|
|
2711
|
-
const
|
|
2792
|
+
const waitUntil2 = _nullishCoalesce(controllerOptions.waitUntil, () => ( defaults.waitUntil));
|
|
2793
|
+
const controller = new Controller({
|
|
2794
|
+
auth,
|
|
2795
|
+
...controllerOptions,
|
|
2796
|
+
waitUntil: waitUntil2
|
|
2797
|
+
});
|
|
2712
2798
|
return createRawClient({
|
|
2713
2799
|
controller,
|
|
2714
|
-
origin: {
|
|
2800
|
+
origin: {
|
|
2801
|
+
provider: "vercel",
|
|
2802
|
+
sdkKey: auth.sdkKey,
|
|
2803
|
+
...auth.sourceProjectId ? { projectId: auth.sourceProjectId } : null
|
|
2804
|
+
},
|
|
2805
|
+
waitUntil: waitUntil2,
|
|
2715
2806
|
...experimental_reportExposures ? { experimental_reportExposures } : {}
|
|
2716
2807
|
});
|
|
2717
2808
|
}
|
|
@@ -2774,7 +2865,8 @@ var cachedFns = {
|
|
|
2774
2865
|
}
|
|
2775
2866
|
};
|
|
2776
2867
|
var { flagsClient, resetDefaultFlagsClient, createClient } = make(
|
|
2777
|
-
createCreateRawClient(cachedFns)
|
|
2868
|
+
createCreateRawClient(cachedFns),
|
|
2869
|
+
{ waitUntil: _server.after }
|
|
2778
2870
|
);
|
|
2779
2871
|
|
|
2780
2872
|
|
|
@@ -2787,4 +2879,4 @@ var { flagsClient, resetDefaultFlagsClient, createClient } = make(
|
|
|
2787
2879
|
|
|
2788
2880
|
|
|
2789
2881
|
exports.ResolutionReason = ResolutionReason; exports.evaluate = evaluate; exports.FallbackNotFoundError = FallbackNotFoundError; exports.FallbackEntryNotFoundError = FallbackEntryNotFoundError; exports.Controller = Controller; exports.flagsClient = flagsClient; exports.resetDefaultFlagsClient = resetDefaultFlagsClient; exports.createClient = createClient;
|
|
2790
|
-
//# sourceMappingURL=chunk-
|
|
2882
|
+
//# sourceMappingURL=chunk-W742GGZU.cjs.map
|