@vercel/flags-core 1.11.0-31e6462-20261026142820 → 1.11.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 +12 -2
- package/dist/{chunk-WMVRYZBA.js → chunk-755SFCGD.js} +64 -22
- package/dist/chunk-755SFCGD.js.map +1 -0
- package/dist/{chunk-RHQBMBPA.cjs → chunk-EMMJNPVW.cjs} +75 -33
- package/dist/chunk-EMMJNPVW.cjs.map +1 -0
- package/dist/{chunk-OGNDEHBK.js → chunk-OI7WI6QP.js} +64 -22
- package/dist/chunk-OI7WI6QP.js.map +1 -0
- package/dist/{chunk-J73HQYK2.cjs → chunk-SDWWHUKB.cjs} +76 -34
- package/dist/chunk-SDWWHUKB.cjs.map +1 -0
- package/dist/index.default.cjs +2 -2
- package/dist/index.default.d.cts +5 -2
- package/dist/index.default.d.ts +5 -2
- package/dist/index.default.js +1 -1
- package/dist/index.next-js.cjs +2 -2
- package/dist/index.next-js.d.cts +5 -2
- package/dist/index.next-js.d.ts +5 -2
- package/dist/index.next-js.js +1 -1
- package/dist/openfeature.default.cjs +2 -2
- package/dist/openfeature.default.js +1 -1
- package/dist/openfeature.next-js.cjs +2 -2
- package/dist/openfeature.next-js.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-J73HQYK2.cjs.map +0 -1
- package/dist/chunk-OGNDEHBK.js.map +0 -1
- package/dist/chunk-RHQBMBPA.cjs.map +0 -1
- package/dist/chunk-WMVRYZBA.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
# @vercel/flags-core
|
|
2
2
|
|
|
3
|
-
## 1.11.0
|
|
3
|
+
## 1.11.0
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
7
|
-
- [#539](https://github.com/vercel/flags/pull/539) [`
|
|
7
|
+
- [#539](https://github.com/vercel/flags/pull/539) [`24983f9`](https://github.com/vercel/flags/commit/24983f90eec9789fbed1a7a556d41070218aecb6) Thanks [@vincent-derks](https://github.com/vincent-derks)! - Prepare for upcoming Vercel Flags Project Connections with a new `projectId` option.
|
|
8
|
+
|
|
9
|
+
## 1.10.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#578](https://github.com/vercel/flags/pull/578) [`63d13be`](https://github.com/vercel/flags/commit/63d13be0193102a4f0cbcbf48e7f67efad96a462) Thanks [@luismeyer](https://github.com/luismeyer)! - Fail fast on authentication errors. When the stream gets a 401 or no OIDC token is available, the client no longer falls back to polling, which would fail the same way, and goes straight to revalidating over HTTP on reads. When polling is the primary source, its first auth failure stops it, and a missing OIDC token is no longer retried. A failed OIDC token refresh is still retried. A client without credentials no longer keeps scripts and test files running.
|
|
14
|
+
|
|
15
|
+
## 1.10.2
|
|
8
16
|
|
|
9
17
|
### Patch Changes
|
|
10
18
|
|
|
11
19
|
- [#567](https://github.com/vercel/flags/pull/567) [`5f738f0`](https://github.com/vercel/flags/commit/5f738f08f182838d0efb13fd3efb89751d48cb0a) Thanks [@vincent-derks](https://github.com/vincent-derks)! - Improve guidance for authorization errors.
|
|
12
20
|
|
|
21
|
+
- [#573](https://github.com/vercel/flags/pull/573) [`e2a1a39`](https://github.com/vercel/flags/commit/e2a1a394e3de4b271c8305bae6cbcf053814ab70) Thanks [@luismeyer](https://github.com/luismeyer)! - Read embedded flag definitions when the client is created, using the SDK key or `VERCEL_PROJECT_ID` in OIDC mode.
|
|
22
|
+
|
|
13
23
|
## 1.10.1
|
|
14
24
|
|
|
15
25
|
### Patch Changes
|
|
@@ -576,7 +576,7 @@ function bulkEvaluate(flags, shared) {
|
|
|
576
576
|
}
|
|
577
577
|
|
|
578
578
|
// package.json
|
|
579
|
-
var version = "1.11.0
|
|
579
|
+
var version = "1.11.0";
|
|
580
580
|
|
|
581
581
|
// src/lib/report-value.ts
|
|
582
582
|
function internalReportValue(key, value, data) {
|
|
@@ -1110,17 +1110,26 @@ function unauthorizedMessage(sourceProjectId) {
|
|
|
1110
1110
|
}
|
|
1111
1111
|
return `unauthorized (401): this deployment is not allowed to read the flags of project "${sourceProjectId}". Check that the project is connected. See https://vercel.com/docs/flags/vercel-flags/project-connections`;
|
|
1112
1112
|
}
|
|
1113
|
+
var oidcTokenMessage = [
|
|
1114
|
+
"@vercel/flags-core: Failed to get OIDC token.",
|
|
1115
|
+
"Are you running in a Vercel Environment where OIDC tokens are available?",
|
|
1116
|
+
"Did you mean to use an SDK Key instead? Use the environment variable FLAGS or pass it directly to the client."
|
|
1117
|
+
].join(" ");
|
|
1118
|
+
var missingCredentials = /`vc link|vercel login|^The 'x-vercel-oidc-token' header is missing from the request\.$/;
|
|
1119
|
+
var OidcTokenError = class extends Error {
|
|
1120
|
+
constructor(cause) {
|
|
1121
|
+
super(oidcTokenMessage, { cause });
|
|
1122
|
+
this.name = "OidcTokenError";
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1113
1125
|
async function getOidcToken() {
|
|
1114
1126
|
try {
|
|
1115
1127
|
return await getVercelOidcToken();
|
|
1116
|
-
} catch {
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
"Did you mean to use an SDK Key instead? Use the environment variable FLAGS or pass it directly to the client."
|
|
1122
|
-
].join(" ")
|
|
1123
|
-
);
|
|
1128
|
+
} catch (error) {
|
|
1129
|
+
if (error instanceof Error && missingCredentials.test(error.message)) {
|
|
1130
|
+
throw new OidcTokenError(error);
|
|
1131
|
+
}
|
|
1132
|
+
throw new Error(oidcTokenMessage, { cause: error });
|
|
1124
1133
|
}
|
|
1125
1134
|
}
|
|
1126
1135
|
function getProjectIdFromOidcToken(oidcToken) {
|
|
@@ -1177,13 +1186,24 @@ var Authentication = class {
|
|
|
1177
1186
|
}
|
|
1178
1187
|
return await getOidcToken();
|
|
1179
1188
|
}
|
|
1180
|
-
|
|
1189
|
+
getStaticBundledDefinitionsLookup() {
|
|
1181
1190
|
if (this.sdkKey) {
|
|
1182
1191
|
return { type: "sdk-key", sdkKey: this.sdkKey };
|
|
1183
1192
|
}
|
|
1184
1193
|
if (this.sourceProjectId) {
|
|
1185
1194
|
return { type: "project-id", projectId: this.sourceProjectId };
|
|
1186
1195
|
}
|
|
1196
|
+
const projectId = process.env.VERCEL_PROJECT_ID;
|
|
1197
|
+
if (projectId) {
|
|
1198
|
+
return { type: "project-id", projectId };
|
|
1199
|
+
}
|
|
1200
|
+
return void 0;
|
|
1201
|
+
}
|
|
1202
|
+
async resolveBundledDefinitionsLookup() {
|
|
1203
|
+
const lookup = this.getStaticBundledDefinitionsLookup();
|
|
1204
|
+
if (lookup) {
|
|
1205
|
+
return lookup;
|
|
1206
|
+
}
|
|
1187
1207
|
const oidcToken = await this.resolveToken();
|
|
1188
1208
|
return {
|
|
1189
1209
|
type: "project-id",
|
|
@@ -1693,6 +1713,13 @@ var BundledSource = class {
|
|
|
1693
1713
|
}
|
|
1694
1714
|
return void 0;
|
|
1695
1715
|
}
|
|
1716
|
+
/**
|
|
1717
|
+
* Starts reading bundled definitions without waiting for the result.
|
|
1718
|
+
*/
|
|
1719
|
+
preload() {
|
|
1720
|
+
this.getResult().catch(() => {
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1696
1723
|
getResult() {
|
|
1697
1724
|
if (!this.promise) {
|
|
1698
1725
|
this.promise = this.options.readBundledDefinitions(this.options.auth);
|
|
@@ -2160,7 +2187,8 @@ async function fetchDatafile(options) {
|
|
|
2160
2187
|
return await Promise.race([fetchAttempt(attempt + 1), aborted]);
|
|
2161
2188
|
} catch (error) {
|
|
2162
2189
|
signal.throwIfAborted();
|
|
2163
|
-
if (attempt === maxAttempts - 1 || error instanceof DatafileHttpError && !error.retryable
|
|
2190
|
+
if (attempt === maxAttempts - 1 || error instanceof DatafileHttpError && !error.retryable || // Retrying will not produce a token that is not there.
|
|
2191
|
+
error instanceof OidcTokenError) {
|
|
2164
2192
|
throw error instanceof Error ? error : new Error("Unknown fetch error");
|
|
2165
2193
|
}
|
|
2166
2194
|
debug(options.clientName, "datafile.fetch.retry", () => ({
|
|
@@ -2578,7 +2606,7 @@ async function connectStream(config, callbacks) {
|
|
|
2578
2606
|
rejectInit(error);
|
|
2579
2607
|
}
|
|
2580
2608
|
onDisconnect?.();
|
|
2581
|
-
abortController.abort();
|
|
2609
|
+
abortController.abort(error);
|
|
2582
2610
|
break;
|
|
2583
2611
|
}
|
|
2584
2612
|
if (!response.ok) {
|
|
@@ -2680,7 +2708,7 @@ async function connectStream(config, callbacks) {
|
|
|
2680
2708
|
}
|
|
2681
2709
|
if (error instanceof TokenResolutionError && !initialDataReceived) {
|
|
2682
2710
|
rejectInit(error.cause);
|
|
2683
|
-
abortController.abort();
|
|
2711
|
+
abortController.abort(error.cause);
|
|
2684
2712
|
break;
|
|
2685
2713
|
}
|
|
2686
2714
|
if (connectionAbort.signal.reason !== PING_TIMEOUT) {
|
|
@@ -2754,7 +2782,7 @@ var StreamSource = class extends TypedEmitter {
|
|
|
2754
2782
|
if (this.abortController === abortController) {
|
|
2755
2783
|
this.promise = void 0;
|
|
2756
2784
|
this.abortController = void 0;
|
|
2757
|
-
this.emit("exhausted");
|
|
2785
|
+
this.emit("exhausted", abortController.signal.reason);
|
|
2758
2786
|
}
|
|
2759
2787
|
},
|
|
2760
2788
|
{ once: true }
|
|
@@ -2815,7 +2843,7 @@ var UNKNOWN_FRESHNESS = {
|
|
|
2815
2843
|
assess: () => ({ status: "unknown" })
|
|
2816
2844
|
};
|
|
2817
2845
|
function isUnauthorizedError(error) {
|
|
2818
|
-
if (error instanceof UnauthorizedError) {
|
|
2846
|
+
if (error instanceof UnauthorizedError || error instanceof OidcTokenError) {
|
|
2819
2847
|
return true;
|
|
2820
2848
|
}
|
|
2821
2849
|
if (!(error instanceof Error)) {
|
|
@@ -2850,8 +2878,9 @@ var Controller = class {
|
|
|
2850
2878
|
// Build-step deduplication
|
|
2851
2879
|
buildDataPromise = null;
|
|
2852
2880
|
buildReadTracked = false;
|
|
2853
|
-
// Suppresses usage tracking while the credential is rejected
|
|
2854
|
-
//
|
|
2881
|
+
// Suppresses usage tracking while the credential is rejected or no OIDC
|
|
2882
|
+
// token is available. Set on an auth failure from any source, cleared as
|
|
2883
|
+
// soon as a source delivers data again.
|
|
2855
2884
|
unauthorized = false;
|
|
2856
2885
|
constructor(options) {
|
|
2857
2886
|
this.options = normalizeOptions(options);
|
|
@@ -2888,6 +2917,9 @@ var Controller = class {
|
|
|
2888
2917
|
auth: this.options.auth,
|
|
2889
2918
|
readBundledDefinitions
|
|
2890
2919
|
});
|
|
2920
|
+
if (!this.options.datafile && this.options.auth.getStaticBundledDefinitionsLookup()) {
|
|
2921
|
+
this.bundledSource.preload();
|
|
2922
|
+
}
|
|
2891
2923
|
this.wireSourceEvents();
|
|
2892
2924
|
if (this.options.datafile) {
|
|
2893
2925
|
this.cache.seed(tagData({ ...this.options.datafile }, "provided"));
|
|
@@ -2944,10 +2976,16 @@ var Controller = class {
|
|
|
2944
2976
|
this.transition("degraded", "stream-disconnected");
|
|
2945
2977
|
}
|
|
2946
2978
|
};
|
|
2947
|
-
onStreamExhausted = () => {
|
|
2979
|
+
onStreamExhausted = (reason) => {
|
|
2948
2980
|
if (this.state !== "streaming" && this.state !== "degraded") {
|
|
2949
2981
|
return;
|
|
2950
2982
|
}
|
|
2983
|
+
if (isUnauthorizedError(reason)) {
|
|
2984
|
+
if (this.state === "streaming") {
|
|
2985
|
+
this.transition("degraded", "stream-auth-failed");
|
|
2986
|
+
}
|
|
2987
|
+
return;
|
|
2988
|
+
}
|
|
2951
2989
|
void this.activateFallbackSource().catch(() => {
|
|
2952
2990
|
});
|
|
2953
2991
|
};
|
|
@@ -2959,7 +2997,7 @@ var Controller = class {
|
|
|
2959
2997
|
this.onSourceError(error);
|
|
2960
2998
|
if (this.state === "polling" && isUnauthorizedError(error)) {
|
|
2961
2999
|
this.pollingSource.stop();
|
|
2962
|
-
this.transition("degraded", "polling-
|
|
3000
|
+
this.transition("degraded", "polling-auth-failed");
|
|
2963
3001
|
}
|
|
2964
3002
|
};
|
|
2965
3003
|
// ---------------------------------------------------------------------------
|
|
@@ -3369,6 +3407,10 @@ var Controller = class {
|
|
|
3369
3407
|
this.transition("degraded", "stream-still-connecting");
|
|
3370
3408
|
return;
|
|
3371
3409
|
}
|
|
3410
|
+
if (outcome === "auth-failed") {
|
|
3411
|
+
this.transition("degraded", "stream-auth-failed");
|
|
3412
|
+
return;
|
|
3413
|
+
}
|
|
3372
3414
|
}
|
|
3373
3415
|
if (this.options.polling.enabled) {
|
|
3374
3416
|
if (this.state !== "polling") {
|
|
@@ -3401,7 +3443,7 @@ var Controller = class {
|
|
|
3401
3443
|
return "connected";
|
|
3402
3444
|
} catch (error) {
|
|
3403
3445
|
this.noteUnauthorized(error);
|
|
3404
|
-
return "failed";
|
|
3446
|
+
return isUnauthorizedError(error) ? "auth-failed" : "failed";
|
|
3405
3447
|
}
|
|
3406
3448
|
}
|
|
3407
3449
|
let timeoutId;
|
|
@@ -3435,7 +3477,7 @@ var Controller = class {
|
|
|
3435
3477
|
} catch (error) {
|
|
3436
3478
|
clearTimeout(timeoutId);
|
|
3437
3479
|
this.noteUnauthorized(error);
|
|
3438
|
-
return "failed";
|
|
3480
|
+
return isUnauthorizedError(error) ? "auth-failed" : "failed";
|
|
3439
3481
|
}
|
|
3440
3482
|
}
|
|
3441
3483
|
// ---------------------------------------------------------------------------
|
|
@@ -3763,4 +3805,4 @@ export {
|
|
|
3763
3805
|
resetDefaultFlagsClient,
|
|
3764
3806
|
createClient
|
|
3765
3807
|
};
|
|
3766
|
-
//# sourceMappingURL=chunk-
|
|
3808
|
+
//# sourceMappingURL=chunk-755SFCGD.js.map
|