@vercel/prepare-flags-definitions 0.3.0 → 0.4.1-ea4d7de-20260922121332
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 -0
- package/dist/index.cjs +40 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +40 -7
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @vercel/prepare-flags-definitions
|
|
2
2
|
|
|
3
|
+
## 0.4.1-ea4d7de-20260922121332
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#498](https://github.com/vercel/flags/pull/498) [`75dcc62`](https://github.com/vercel/flags/commit/75dcc62ccaf6a0ef4cc3cf76158629510a563166) Thanks [@luismeyer](https://github.com/luismeyer)! - Record `fetchedAt` when a datafile fetch completes and preserve it in generated flag definitions. Loading the bundle retains the original timestamp so the Flags SDK can determine its age.
|
|
8
|
+
|
|
9
|
+
## 0.4.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [#421](https://github.com/vercel/flags/pull/421) [`d601dba`](https://github.com/vercel/flags/commit/d601dba20a6225c49617937ff11915374597bb48) Thanks [@luismeyer](https://github.com/luismeyer)! - Add fetch retries
|
|
14
|
+
|
|
3
15
|
## 0.3.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -4,11 +4,16 @@ var _promises = require('fs/promises');
|
|
|
4
4
|
var _path = require('path');
|
|
5
5
|
|
|
6
6
|
// package.json
|
|
7
|
-
var version = "0.
|
|
7
|
+
var version = "0.4.1-ea4d7de-20260922121332";
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
10
10
|
var FLAGS_HOST = "https://flags.vercel.com";
|
|
11
11
|
var FLAGS_DEFINITIONS_VERSION = "1.0.1";
|
|
12
|
+
var FETCH_MAX_RETRIES = 3;
|
|
13
|
+
var FETCH_RETRY_BASE_DELAY_MS = 200;
|
|
14
|
+
function wait(ms) {
|
|
15
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
16
|
+
}
|
|
12
17
|
function obfuscate(sdkKey, prefixLength = 18) {
|
|
13
18
|
if (prefixLength >= sdkKey.length) return sdkKey;
|
|
14
19
|
return sdkKey.slice(0, prefixLength) + "*".repeat(sdkKey.length - prefixLength);
|
|
@@ -56,7 +61,10 @@ function generateMap(lines, entries) {
|
|
|
56
61
|
lines.push(" return map[key]?.() ?? null;");
|
|
57
62
|
lines.push("}");
|
|
58
63
|
}
|
|
59
|
-
|
|
64
|
+
function isRetryableStatus(status) {
|
|
65
|
+
return status === 429 || status >= 500;
|
|
66
|
+
}
|
|
67
|
+
async function fetchDatafile(token, env, fetchFn, userAgentSuffix, output) {
|
|
60
68
|
const headers = {
|
|
61
69
|
authorization: `Bearer ${token}`,
|
|
62
70
|
"user-agent": [
|
|
@@ -76,16 +84,40 @@ async function fetchDatafile(token, env, fetchFn, userAgentSuffix) {
|
|
|
76
84
|
if (env.VERCEL_REGION) {
|
|
77
85
|
headers["x-vercel-region"] = env.VERCEL_REGION;
|
|
78
86
|
}
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
let lastError;
|
|
88
|
+
for (let attempt = 0; attempt <= FETCH_MAX_RETRIES; attempt++) {
|
|
89
|
+
if (attempt > 0) {
|
|
90
|
+
const delay = FETCH_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1);
|
|
91
|
+
output == null ? void 0 : output.debug(
|
|
92
|
+
`vercel-flags: retrying datafile fetch for ${obfuscate(token)} (attempt ${attempt}/${FETCH_MAX_RETRIES}) after ${delay}ms`
|
|
93
|
+
);
|
|
94
|
+
await wait(delay);
|
|
95
|
+
}
|
|
96
|
+
let res;
|
|
97
|
+
try {
|
|
98
|
+
res = await fetchFn(`${FLAGS_HOST}/v1/datafile`, { headers });
|
|
99
|
+
} catch (error2) {
|
|
100
|
+
lastError = error2;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (res.ok) {
|
|
104
|
+
const definitions = await res.json();
|
|
105
|
+
return { ...definitions, fetchedAt: Date.now() };
|
|
106
|
+
}
|
|
81
107
|
if (res.status === 404) {
|
|
82
108
|
return void 0;
|
|
83
109
|
}
|
|
84
|
-
|
|
110
|
+
const error = new Error(
|
|
85
111
|
`Failed to fetch flag definitions for ${obfuscate(token)}: ${res.status} ${res.statusText}`
|
|
86
112
|
);
|
|
113
|
+
if (!isRetryableStatus(res.status)) {
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
lastError = error;
|
|
87
117
|
}
|
|
88
|
-
|
|
118
|
+
throw lastError instanceof Error ? lastError : new Error(
|
|
119
|
+
`Failed to fetch flag definitions for ${obfuscate(token)} after ${FETCH_MAX_RETRIES} retries`
|
|
120
|
+
);
|
|
89
121
|
}
|
|
90
122
|
function generateDefinitionsModule(entries, output) {
|
|
91
123
|
output == null ? void 0 : output.debug(
|
|
@@ -154,7 +186,8 @@ async function prepareFlagsDefinitions(options) {
|
|
|
154
186
|
key,
|
|
155
187
|
env,
|
|
156
188
|
fetchFn,
|
|
157
|
-
userAgentSuffix
|
|
189
|
+
userAgentSuffix,
|
|
190
|
+
output
|
|
158
191
|
);
|
|
159
192
|
if (!definitions) {
|
|
160
193
|
return;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/flags/flags/packages/prepare-flags-definitions/dist/index.cjs","../src/index.ts","../package.json"],"names":[],"mappings":"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","file":"/home/runner/work/flags/flags/packages/prepare-flags-definitions/dist/index.cjs","sourcesContent":[null,"import { createHash } from 'node:crypto';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { version as PACKAGE_VERSION } from '../package.json';\n\nconst FLAGS_HOST = 'https://flags.vercel.com';\nconst FLAGS_DEFINITIONS_VERSION = '1.0.1';\n\ntype BundledDefinitions = Record<string, unknown>;\n\nexport interface Output {\n debug(message: string): void;\n time<T>(label: string, promise: Promise<T>): Promise<T>;\n}\n\nexport type PrepareFlagsDefinitionsResult =\n | { created: false; reason: 'no-flags-entries' }\n | { created: true; entryCount: number };\n\n/**\n * Obfuscates SDK key for logging (shows first 18 chars)\n */\nfunction obfuscate(sdkKey: string, prefixLength = 18): string {\n if (prefixLength >= sdkKey.length) return sdkKey;\n return (\n sdkKey.slice(0, prefixLength) + '*'.repeat(sdkKey.length - prefixLength)\n );\n}\n\n/**\n * Computes a SHA-256 hex digest of the given SDK key.\n */\nexport function hashSdkKey(sdkKey: string): string {\n return createHash('sha256').update(sdkKey).digest('hex');\n}\n\nexport function getProjectIdFromOidcToken(oidcToken: string) {\n const tokenParts = oidcToken.split('.');\n if (tokenParts.length !== 3 || !tokenParts[1]) {\n return;\n }\n\n const payload = JSON.parse(\n Buffer.from(tokenParts[1], 'base64url').toString('utf8'),\n ) as { project_id?: unknown };\n\n if (typeof payload.project_id !== 'string' || !payload.project_id) {\n return;\n }\n\n return payload.project_id;\n}\n\ntype DefinitionsEntry = {\n key: string;\n definitions: BundledDefinitions;\n};\n\ntype MapEntry = {\n key: string;\n value: string;\n};\n\n/**\n * Creates js constants pointing to memoized deduplicated flag definitions.\n * Output format:\n * ```js\n * const _d0 = memo(() => JSON.parse('...'));\n * const _d1 = memo(() => JSON.parse('...'));\n * ````\n */\nfunction generateDefinitionConstants(\n lines: string[],\n entries: DefinitionsEntry[],\n): MapEntry[] {\n const stringToConst = new Map<string, string>();\n\n return entries.map((entry) => {\n const stringified = JSON.stringify(entry.definitions);\n let definitionConst = stringToConst.get(stringified);\n\n if (!definitionConst) {\n definitionConst = `_d${stringToConst.size}`;\n stringToConst.set(stringified, definitionConst);\n lines.push(\n `const ${definitionConst} = memo(() => JSON.parse(${JSON.stringify(stringified)}));`,\n );\n }\n\n return { key: entry.key, value: definitionConst };\n });\n}\n\n/**\n * Creates a js map and getter function for exposing key value pairs.\n * Output format:\n * ```js\n * const map = { \"<sha256_hash_or_project_id>\": _d0 };\n * export function get(key) { return map[key]?.() ?? null; }\n * ````\n */\nfunction generateMap(lines: string[], entries: MapEntry[]): void {\n lines.push('');\n lines.push('const map = {');\n for (const entry of entries) {\n lines.push(` ${JSON.stringify(entry.key)}: ${entry.value},`);\n }\n lines.push('};');\n lines.push('');\n lines.push('export function get(key) {');\n lines.push(' return map[key]?.() ?? null;');\n lines.push('}');\n}\n\nasync function fetchDatafile(\n token: string,\n env: Record<string, string | undefined>,\n fetchFn: typeof globalThis.fetch,\n userAgentSuffix?: string,\n): Promise<BundledDefinitions | undefined> {\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n 'user-agent': [\n `@vercel/prepare-flags-definitions/${PACKAGE_VERSION}`,\n userAgentSuffix,\n ]\n .filter(Boolean)\n .join(' '),\n };\n\n // Add Vercel metadata headers if available\n if (env.VERCEL_PROJECT_ID) {\n headers['x-vercel-project-id'] = env.VERCEL_PROJECT_ID;\n }\n if (env.VERCEL_ENV) {\n headers['x-vercel-env'] = env.VERCEL_ENV;\n }\n if (env.VERCEL_DEPLOYMENT_ID) {\n headers['x-vercel-deployment-id'] = env.VERCEL_DEPLOYMENT_ID;\n }\n if (env.VERCEL_REGION) {\n headers['x-vercel-region'] = env.VERCEL_REGION;\n }\n\n const res = await fetchFn(`${FLAGS_HOST}/v1/datafile`, { headers });\n\n if (!res.ok) {\n if (res.status === 404) {\n return undefined;\n }\n\n throw new Error(\n `Failed to fetch flag definitions for ${obfuscate(token)}: ${res.status} ${res.statusText}`,\n );\n }\n\n return res.json() as Promise<BundledDefinitions>;\n}\n\n/**\n * Generates a JS module with deduplicated, lazily-parsed definitions.\n *\n * The map keys are SHA-256 hashes of the SDK keys so that raw keys\n * are not embedded in the output.\n *\n * Output format:\n * ```js\n * const memo = (fn) => { let cached; return () => (cached ??= fn()); };\n * const _d0 = memo(() => JSON.parse('...'));\n * const _d1 = memo(() => JSON.parse('...'));\n * const map = { \"<sha256_hash>\": _d0, \"project_id\": _d1 };\n * export function get(key) { return map[key]?.() ?? null; }\n * ```\n */\nexport function generateDefinitionsModule(\n entries: DefinitionsEntry[],\n output: Output | undefined,\n): string {\n output?.debug(\n `vercel-flags: writing flag definitions for \"${entries.map(({ key }) => obfuscate(key)).join(', ')}\"`,\n );\n\n // generate shared js\n const lines: string[] = [\n 'const memo = (fn) => { let cached; return () => (cached ??= fn()); };',\n '',\n ];\n\n // generate js const and capture the const names\n const generatedDefinitions = generateDefinitionConstants(lines, entries);\n\n // generate a map wiring keys to const names\n generateMap(lines, generatedDefinitions);\n\n lines.push(\n '',\n `export const version = ${JSON.stringify(FLAGS_DEFINITIONS_VERSION)};`,\n );\n\n return lines.join('\\n');\n}\n\ntype FlagEntry = {\n type: 'oidcToken' | 'sdkKey';\n key: string;\n};\n\n/**\n * Regex to match valid Vercel Flags SDK keys.\n * SDK keys must follow the format: vf_server_* or vf_client_*\n * This avoids false positives with third-party identifiers that happen\n * to start with 'vf_' (e.g., Stripe identity flow IDs like 'vf_1PyH...').\n */\nconst SDK_KEY_REGEX = /^vf_(?:server|client)_/;\n\n/**\n * Collect all possible flag entries the need embedding from the environment\n */\nfunction collectFlagEntries(\n env: Record<string, string | undefined>,\n output: Output | undefined,\n): FlagEntry[] {\n const entries: FlagEntry[] = [];\n\n // Collect unique SDK keys from environment variables\n // Supports both direct SDK keys (vf_server_*/vf_client_*) and flags: format\n const sdkKeys = Array.from(\n Object.values(env).reduce<Set<string>>((acc, value) => {\n if (typeof value === 'string') {\n if (SDK_KEY_REGEX.test(value)) {\n acc.add(value);\n } else if (value.startsWith('flags:')) {\n const params = new URLSearchParams(value.slice('flags:'.length));\n const sdkKey = params.get('sdkKey');\n if (sdkKey && SDK_KEY_REGEX.test(sdkKey)) {\n acc.add(sdkKey);\n }\n }\n }\n return acc;\n }, new Set<string>()),\n );\n\n if (sdkKeys.length > 0) {\n output?.debug(`vercel-flags: found ${sdkKeys.length} SDK keys`);\n\n for (const key of sdkKeys) {\n entries.push({ type: 'sdkKey', key });\n }\n }\n\n const oidcToken = env.VERCEL_OIDC_TOKEN;\n if (oidcToken && oidcToken?.length > 0) {\n output?.debug(`vercel-flags: found OIDC token`);\n\n entries.push({ type: 'oidcToken', key: oidcToken });\n }\n\n return entries;\n}\n\n/**\n * Prepares flag definitions by reading SDK keys from environment variables,\n * fetching definitions from flags.vercel.com, and writing them into a\n * synthetic `@vercel/flags-definitions` package inside `node_modules/`.\n */\nexport async function prepareFlagsDefinitions(options: {\n cwd: string;\n env: Record<string, string | undefined>;\n /**\n * Appended to the user-agent header to identify the caller.\n * Example: `\"vercel-cli/35.0.0\"`\n */\n userAgentSuffix?: string;\n fetch?: typeof globalThis.fetch;\n output?: Output;\n}): Promise<PrepareFlagsDefinitionsResult> {\n const {\n cwd,\n env,\n userAgentSuffix,\n fetch: fetchFn = globalThis.fetch,\n output,\n } = options;\n\n output?.debug('vercel-flags: checking env vars for SDK Keys and OIDC Token');\n\n const entries = collectFlagEntries(env, output);\n if (entries.length === 0) {\n return { created: false, reason: 'no-flags-entries' };\n }\n\n // fetch all datafiles for sdk keys and oidc tokens\n const resolvedEntries = await Promise.all(\n entries.map(async ({ key, type }) => {\n const definitions = await fetchDatafile(\n key,\n env,\n fetchFn,\n userAgentSuffix,\n );\n if (!definitions) {\n return;\n }\n\n if (type === 'oidcToken') {\n const projectId = getProjectIdFromOidcToken(key);\n\n if (projectId) {\n return {\n key: projectId,\n definitions,\n };\n }\n }\n\n if (type === 'sdkKey') {\n return {\n key: hashSdkKey(key),\n definitions,\n };\n }\n }),\n );\n\n const validEntries = resolvedEntries.filter((entry) => !!entry);\n\n const definitionsJs = generateDefinitionsModule(validEntries, output);\n\n // Write to node_modules/@vercel/flags-definitions/\n const storageDir = join(cwd, 'node_modules', '@vercel', 'flags-definitions');\n const indexPath = join(storageDir, 'index.js');\n const dtsPath = join(storageDir, 'index.d.ts');\n const packageJsonPath = join(storageDir, 'package.json');\n\n const dts = [\n 'export function get(key: string): Record<string, unknown> | null;',\n 'export const version: string;',\n '',\n ].join('\\n');\n\n const packageJson = {\n name: '@vercel/flags-definitions',\n version: FLAGS_DEFINITIONS_VERSION,\n type: 'module',\n main: './index.js',\n types: './index.d.ts',\n exports: {\n '.': {\n types: './index.d.ts',\n import: './index.js',\n },\n },\n };\n\n await mkdir(storageDir, { recursive: true });\n await Promise.all([\n writeFile(indexPath, definitionsJs),\n writeFile(dtsPath, dts),\n writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)),\n ]);\n\n output?.debug('vercel-flags: created module');\n output?.debug(` → ${indexPath}`);\n output?.debug(` → ${dtsPath}`);\n output?.debug(` → ${packageJsonPath}`);\n\n return { created: true, entryCount: entries.length };\n}\n","{\n \"name\": \"@vercel/prepare-flags-definitions\",\n \"version\": \"0.3.0\",\n \"description\": \"A utility for preparing flags definitions for embedding\",\n \"keywords\": [\n \"flags\",\n \"flags sdk\",\n \"experimentation\",\n \"ab testing\"\n ],\n \"license\": \"MIT\",\n \"author\": \"Dominik Ferber <dominik@vercel.com>\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/vercel/flags.git\"\n },\n \"sideEffects\": false,\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"typesVersions\": {\n \"*\": {\n \".\": [\n \"dist/*.d.ts\",\n \"dist/*.d.cts\"\n ]\n }\n },\n \"files\": [\n \"dist\",\n \"CHANGELOG.md\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"check\": \"biome check\",\n \"test\": \"vitest --run\",\n \"test:watch\": \"vitest\",\n \"type-check\": \"tsc --noEmit\"\n },\n \"devDependencies\": {\n \"@types/node\": \"20.11.17\",\n \"tsup\": \"8.5.1\",\n \"typescript\": \"5.6.3\",\n \"vite\": \"6.4.1\",\n \"vitest\": \"2.1.9\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/flags/flags/packages/prepare-flags-definitions/dist/index.cjs","../src/index.ts","../package.json"],"names":["error"],"mappings":"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","file":"/home/runner/work/flags/flags/packages/prepare-flags-definitions/dist/index.cjs","sourcesContent":[null,"import { createHash } from 'node:crypto';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { version as PACKAGE_VERSION } from '../package.json';\n\nconst FLAGS_HOST = 'https://flags.vercel.com';\nconst FLAGS_DEFINITIONS_VERSION = '1.0.1';\n\n/** Number of retry attempts for transient datafile fetch failures. */\nconst FETCH_MAX_RETRIES = 3;\n/** Base delay in milliseconds used for exponential backoff between retries. */\nconst FETCH_RETRY_BASE_DELAY_MS = 200;\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\ntype BundledDefinitions = Record<string, unknown>;\n\nexport interface Output {\n debug(message: string): void;\n time<T>(label: string, promise: Promise<T>): Promise<T>;\n}\n\nexport type PrepareFlagsDefinitionsResult =\n | { created: false; reason: 'no-flags-entries' }\n | { created: true; entryCount: number };\n\n/**\n * Obfuscates SDK key for logging (shows first 18 chars)\n */\nfunction obfuscate(sdkKey: string, prefixLength = 18): string {\n if (prefixLength >= sdkKey.length) return sdkKey;\n return (\n sdkKey.slice(0, prefixLength) + '*'.repeat(sdkKey.length - prefixLength)\n );\n}\n\n/**\n * Computes a SHA-256 hex digest of the given SDK key.\n */\nexport function hashSdkKey(sdkKey: string): string {\n return createHash('sha256').update(sdkKey).digest('hex');\n}\n\nexport function getProjectIdFromOidcToken(oidcToken: string) {\n const tokenParts = oidcToken.split('.');\n if (tokenParts.length !== 3 || !tokenParts[1]) {\n return;\n }\n\n const payload = JSON.parse(\n Buffer.from(tokenParts[1], 'base64url').toString('utf8'),\n ) as { project_id?: unknown };\n\n if (typeof payload.project_id !== 'string' || !payload.project_id) {\n return;\n }\n\n return payload.project_id;\n}\n\ntype DefinitionsEntry = {\n key: string;\n definitions: BundledDefinitions;\n};\n\ntype MapEntry = {\n key: string;\n value: string;\n};\n\n/**\n * Creates js constants pointing to memoized deduplicated flag definitions.\n * Output format:\n * ```js\n * const _d0 = memo(() => JSON.parse('...'));\n * const _d1 = memo(() => JSON.parse('...'));\n * ````\n */\nfunction generateDefinitionConstants(\n lines: string[],\n entries: DefinitionsEntry[],\n): MapEntry[] {\n const stringToConst = new Map<string, string>();\n\n return entries.map((entry) => {\n const stringified = JSON.stringify(entry.definitions);\n let definitionConst = stringToConst.get(stringified);\n\n if (!definitionConst) {\n definitionConst = `_d${stringToConst.size}`;\n stringToConst.set(stringified, definitionConst);\n lines.push(\n `const ${definitionConst} = memo(() => JSON.parse(${JSON.stringify(stringified)}));`,\n );\n }\n\n return { key: entry.key, value: definitionConst };\n });\n}\n\n/**\n * Creates a js map and getter function for exposing key value pairs.\n * Output format:\n * ```js\n * const map = { \"<sha256_hash_or_project_id>\": _d0 };\n * export function get(key) { return map[key]?.() ?? null; }\n * ````\n */\nfunction generateMap(lines: string[], entries: MapEntry[]): void {\n lines.push('');\n lines.push('const map = {');\n for (const entry of entries) {\n lines.push(` ${JSON.stringify(entry.key)}: ${entry.value},`);\n }\n lines.push('};');\n lines.push('');\n lines.push('export function get(key) {');\n lines.push(' return map[key]?.() ?? null;');\n lines.push('}');\n}\n\n/**\n * Determines whether an HTTP status is worth retrying. Transient server-side\n * failures (5xx) and rate limiting (429) are retryable; other 4xx responses\n * indicate a client error that won't be fixed by retrying.\n */\nfunction isRetryableStatus(status: number): boolean {\n return status === 429 || status >= 500;\n}\n\nasync function fetchDatafile(\n token: string,\n env: Record<string, string | undefined>,\n fetchFn: typeof globalThis.fetch,\n userAgentSuffix?: string,\n output?: Output,\n): Promise<BundledDefinitions | undefined> {\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n 'user-agent': [\n `@vercel/prepare-flags-definitions/${PACKAGE_VERSION}`,\n userAgentSuffix,\n ]\n .filter(Boolean)\n .join(' '),\n };\n\n // Add Vercel metadata headers if available\n if (env.VERCEL_PROJECT_ID) {\n headers['x-vercel-project-id'] = env.VERCEL_PROJECT_ID;\n }\n if (env.VERCEL_ENV) {\n headers['x-vercel-env'] = env.VERCEL_ENV;\n }\n if (env.VERCEL_DEPLOYMENT_ID) {\n headers['x-vercel-deployment-id'] = env.VERCEL_DEPLOYMENT_ID;\n }\n if (env.VERCEL_REGION) {\n headers['x-vercel-region'] = env.VERCEL_REGION;\n }\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= FETCH_MAX_RETRIES; attempt++) {\n if (attempt > 0) {\n // Exponential backoff: 200ms, 400ms, 800ms, ...\n const delay = FETCH_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1);\n output?.debug(\n `vercel-flags: retrying datafile fetch for ${obfuscate(token)} (attempt ${attempt}/${FETCH_MAX_RETRIES}) after ${delay}ms`,\n );\n await wait(delay);\n }\n\n let res: Response;\n try {\n res = await fetchFn(`${FLAGS_HOST}/v1/datafile`, { headers });\n } catch (error) {\n // Network-level failure (DNS, connection reset, etc.) — retryable.\n lastError = error;\n continue;\n }\n\n if (res.ok) {\n const definitions = (await res.json()) as BundledDefinitions;\n return { ...definitions, fetchedAt: Date.now() };\n }\n\n if (res.status === 404) {\n return undefined;\n }\n\n const error = new Error(\n `Failed to fetch flag definitions for ${obfuscate(token)}: ${res.status} ${res.statusText}`,\n );\n\n if (!isRetryableStatus(res.status)) {\n throw error;\n }\n\n lastError = error;\n }\n\n throw lastError instanceof Error\n ? lastError\n : new Error(\n `Failed to fetch flag definitions for ${obfuscate(token)} after ${FETCH_MAX_RETRIES} retries`,\n );\n}\n\n/**\n * Generates a JS module with deduplicated, lazily-parsed definitions.\n *\n * The map keys are SHA-256 hashes of the SDK keys so that raw keys\n * are not embedded in the output.\n *\n * Output format:\n * ```js\n * const memo = (fn) => { let cached; return () => (cached ??= fn()); };\n * const _d0 = memo(() => JSON.parse('...'));\n * const _d1 = memo(() => JSON.parse('...'));\n * const map = { \"<sha256_hash>\": _d0, \"project_id\": _d1 };\n * export function get(key) { return map[key]?.() ?? null; }\n * ```\n */\nexport function generateDefinitionsModule(\n entries: DefinitionsEntry[],\n output: Output | undefined,\n): string {\n output?.debug(\n `vercel-flags: writing flag definitions for \"${entries.map(({ key }) => obfuscate(key)).join(', ')}\"`,\n );\n\n // generate shared js\n const lines: string[] = [\n 'const memo = (fn) => { let cached; return () => (cached ??= fn()); };',\n '',\n ];\n\n // generate js const and capture the const names\n const generatedDefinitions = generateDefinitionConstants(lines, entries);\n\n // generate a map wiring keys to const names\n generateMap(lines, generatedDefinitions);\n\n lines.push(\n '',\n `export const version = ${JSON.stringify(FLAGS_DEFINITIONS_VERSION)};`,\n );\n\n return lines.join('\\n');\n}\n\ntype FlagEntry = {\n type: 'oidcToken' | 'sdkKey';\n key: string;\n};\n\n/**\n * Regex to match valid Vercel Flags SDK keys.\n * SDK keys must follow the format: vf_server_* or vf_client_*\n * This avoids false positives with third-party identifiers that happen\n * to start with 'vf_' (e.g., Stripe identity flow IDs like 'vf_1PyH...').\n */\nconst SDK_KEY_REGEX = /^vf_(?:server|client)_/;\n\n/**\n * Collect all possible flag entries the need embedding from the environment\n */\nfunction collectFlagEntries(\n env: Record<string, string | undefined>,\n output: Output | undefined,\n): FlagEntry[] {\n const entries: FlagEntry[] = [];\n\n // Collect unique SDK keys from environment variables\n // Supports both direct SDK keys (vf_server_*/vf_client_*) and flags: format\n const sdkKeys = Array.from(\n Object.values(env).reduce<Set<string>>((acc, value) => {\n if (typeof value === 'string') {\n if (SDK_KEY_REGEX.test(value)) {\n acc.add(value);\n } else if (value.startsWith('flags:')) {\n const params = new URLSearchParams(value.slice('flags:'.length));\n const sdkKey = params.get('sdkKey');\n if (sdkKey && SDK_KEY_REGEX.test(sdkKey)) {\n acc.add(sdkKey);\n }\n }\n }\n return acc;\n }, new Set<string>()),\n );\n\n if (sdkKeys.length > 0) {\n output?.debug(`vercel-flags: found ${sdkKeys.length} SDK keys`);\n\n for (const key of sdkKeys) {\n entries.push({ type: 'sdkKey', key });\n }\n }\n\n const oidcToken = env.VERCEL_OIDC_TOKEN;\n if (oidcToken && oidcToken?.length > 0) {\n output?.debug(`vercel-flags: found OIDC token`);\n\n entries.push({ type: 'oidcToken', key: oidcToken });\n }\n\n return entries;\n}\n\n/**\n * Prepares flag definitions by reading SDK keys from environment variables,\n * fetching definitions from flags.vercel.com, and writing them into a\n * synthetic `@vercel/flags-definitions` package inside `node_modules/`.\n */\nexport async function prepareFlagsDefinitions(options: {\n cwd: string;\n env: Record<string, string | undefined>;\n /**\n * Appended to the user-agent header to identify the caller.\n * Example: `\"vercel-cli/35.0.0\"`\n */\n userAgentSuffix?: string;\n fetch?: typeof globalThis.fetch;\n output?: Output;\n}): Promise<PrepareFlagsDefinitionsResult> {\n const {\n cwd,\n env,\n userAgentSuffix,\n fetch: fetchFn = globalThis.fetch,\n output,\n } = options;\n\n output?.debug('vercel-flags: checking env vars for SDK Keys and OIDC Token');\n\n const entries = collectFlagEntries(env, output);\n if (entries.length === 0) {\n return { created: false, reason: 'no-flags-entries' };\n }\n\n // fetch all datafiles for sdk keys and oidc tokens\n const resolvedEntries = await Promise.all(\n entries.map(async ({ key, type }) => {\n const definitions = await fetchDatafile(\n key,\n env,\n fetchFn,\n userAgentSuffix,\n output,\n );\n if (!definitions) {\n return;\n }\n\n if (type === 'oidcToken') {\n const projectId = getProjectIdFromOidcToken(key);\n\n if (projectId) {\n return {\n key: projectId,\n definitions,\n };\n }\n }\n\n if (type === 'sdkKey') {\n return {\n key: hashSdkKey(key),\n definitions,\n };\n }\n }),\n );\n\n const validEntries = resolvedEntries.filter((entry) => !!entry);\n\n const definitionsJs = generateDefinitionsModule(validEntries, output);\n\n // Write to node_modules/@vercel/flags-definitions/\n const storageDir = join(cwd, 'node_modules', '@vercel', 'flags-definitions');\n const indexPath = join(storageDir, 'index.js');\n const dtsPath = join(storageDir, 'index.d.ts');\n const packageJsonPath = join(storageDir, 'package.json');\n\n const dts = [\n 'export function get(key: string): Record<string, unknown> | null;',\n 'export const version: string;',\n '',\n ].join('\\n');\n\n const packageJson = {\n name: '@vercel/flags-definitions',\n version: FLAGS_DEFINITIONS_VERSION,\n type: 'module',\n main: './index.js',\n types: './index.d.ts',\n exports: {\n '.': {\n types: './index.d.ts',\n import: './index.js',\n },\n },\n };\n\n await mkdir(storageDir, { recursive: true });\n await Promise.all([\n writeFile(indexPath, definitionsJs),\n writeFile(dtsPath, dts),\n writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)),\n ]);\n\n output?.debug('vercel-flags: created module');\n output?.debug(` → ${indexPath}`);\n output?.debug(` → ${dtsPath}`);\n output?.debug(` → ${packageJsonPath}`);\n\n return { created: true, entryCount: entries.length };\n}\n","{\n \"name\": \"@vercel/prepare-flags-definitions\",\n \"version\": \"0.4.1-ea4d7de-20260922121332\",\n \"description\": \"A utility for preparing flags definitions for embedding\",\n \"keywords\": [\n \"flags\",\n \"flags sdk\",\n \"experimentation\",\n \"ab testing\"\n ],\n \"license\": \"MIT\",\n \"author\": \"Dominik Ferber <dominik@vercel.com>\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/vercel/flags.git\"\n },\n \"sideEffects\": false,\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"typesVersions\": {\n \"*\": {\n \".\": [\n \"dist/*.d.ts\",\n \"dist/*.d.cts\"\n ]\n }\n },\n \"files\": [\n \"dist\",\n \"CHANGELOG.md\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"check\": \"biome check\",\n \"test\": \"vitest --run\",\n \"test:watch\": \"vitest\",\n \"type-check\": \"tsc --noEmit\"\n },\n \"devDependencies\": {\n \"@types/node\": \"22.14.0\",\n \"tsup\": \"8.5.1\",\n \"typescript\": \"5.6.3\",\n \"vite\": \"8.1.5\",\n \"vitest\": \"4.1.10\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -4,11 +4,16 @@ import { mkdir, writeFile } from "fs/promises";
|
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
|
|
6
6
|
// package.json
|
|
7
|
-
var version = "0.
|
|
7
|
+
var version = "0.4.1-ea4d7de-20260922121332";
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
10
10
|
var FLAGS_HOST = "https://flags.vercel.com";
|
|
11
11
|
var FLAGS_DEFINITIONS_VERSION = "1.0.1";
|
|
12
|
+
var FETCH_MAX_RETRIES = 3;
|
|
13
|
+
var FETCH_RETRY_BASE_DELAY_MS = 200;
|
|
14
|
+
function wait(ms) {
|
|
15
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
16
|
+
}
|
|
12
17
|
function obfuscate(sdkKey, prefixLength = 18) {
|
|
13
18
|
if (prefixLength >= sdkKey.length) return sdkKey;
|
|
14
19
|
return sdkKey.slice(0, prefixLength) + "*".repeat(sdkKey.length - prefixLength);
|
|
@@ -56,7 +61,10 @@ function generateMap(lines, entries) {
|
|
|
56
61
|
lines.push(" return map[key]?.() ?? null;");
|
|
57
62
|
lines.push("}");
|
|
58
63
|
}
|
|
59
|
-
|
|
64
|
+
function isRetryableStatus(status) {
|
|
65
|
+
return status === 429 || status >= 500;
|
|
66
|
+
}
|
|
67
|
+
async function fetchDatafile(token, env, fetchFn, userAgentSuffix, output) {
|
|
60
68
|
const headers = {
|
|
61
69
|
authorization: `Bearer ${token}`,
|
|
62
70
|
"user-agent": [
|
|
@@ -76,16 +84,40 @@ async function fetchDatafile(token, env, fetchFn, userAgentSuffix) {
|
|
|
76
84
|
if (env.VERCEL_REGION) {
|
|
77
85
|
headers["x-vercel-region"] = env.VERCEL_REGION;
|
|
78
86
|
}
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
let lastError;
|
|
88
|
+
for (let attempt = 0; attempt <= FETCH_MAX_RETRIES; attempt++) {
|
|
89
|
+
if (attempt > 0) {
|
|
90
|
+
const delay = FETCH_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1);
|
|
91
|
+
output == null ? void 0 : output.debug(
|
|
92
|
+
`vercel-flags: retrying datafile fetch for ${obfuscate(token)} (attempt ${attempt}/${FETCH_MAX_RETRIES}) after ${delay}ms`
|
|
93
|
+
);
|
|
94
|
+
await wait(delay);
|
|
95
|
+
}
|
|
96
|
+
let res;
|
|
97
|
+
try {
|
|
98
|
+
res = await fetchFn(`${FLAGS_HOST}/v1/datafile`, { headers });
|
|
99
|
+
} catch (error2) {
|
|
100
|
+
lastError = error2;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (res.ok) {
|
|
104
|
+
const definitions = await res.json();
|
|
105
|
+
return { ...definitions, fetchedAt: Date.now() };
|
|
106
|
+
}
|
|
81
107
|
if (res.status === 404) {
|
|
82
108
|
return void 0;
|
|
83
109
|
}
|
|
84
|
-
|
|
110
|
+
const error = new Error(
|
|
85
111
|
`Failed to fetch flag definitions for ${obfuscate(token)}: ${res.status} ${res.statusText}`
|
|
86
112
|
);
|
|
113
|
+
if (!isRetryableStatus(res.status)) {
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
lastError = error;
|
|
87
117
|
}
|
|
88
|
-
|
|
118
|
+
throw lastError instanceof Error ? lastError : new Error(
|
|
119
|
+
`Failed to fetch flag definitions for ${obfuscate(token)} after ${FETCH_MAX_RETRIES} retries`
|
|
120
|
+
);
|
|
89
121
|
}
|
|
90
122
|
function generateDefinitionsModule(entries, output) {
|
|
91
123
|
output == null ? void 0 : output.debug(
|
|
@@ -154,7 +186,8 @@ async function prepareFlagsDefinitions(options) {
|
|
|
154
186
|
key,
|
|
155
187
|
env,
|
|
156
188
|
fetchFn,
|
|
157
|
-
userAgentSuffix
|
|
189
|
+
userAgentSuffix,
|
|
190
|
+
output
|
|
158
191
|
);
|
|
159
192
|
if (!definitions) {
|
|
160
193
|
return;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../package.json"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { version as PACKAGE_VERSION } from '../package.json';\n\nconst FLAGS_HOST = 'https://flags.vercel.com';\nconst FLAGS_DEFINITIONS_VERSION = '1.0.1';\n\ntype BundledDefinitions = Record<string, unknown>;\n\nexport interface Output {\n debug(message: string): void;\n time<T>(label: string, promise: Promise<T>): Promise<T>;\n}\n\nexport type PrepareFlagsDefinitionsResult =\n | { created: false; reason: 'no-flags-entries' }\n | { created: true; entryCount: number };\n\n/**\n * Obfuscates SDK key for logging (shows first 18 chars)\n */\nfunction obfuscate(sdkKey: string, prefixLength = 18): string {\n if (prefixLength >= sdkKey.length) return sdkKey;\n return (\n sdkKey.slice(0, prefixLength) + '*'.repeat(sdkKey.length - prefixLength)\n );\n}\n\n/**\n * Computes a SHA-256 hex digest of the given SDK key.\n */\nexport function hashSdkKey(sdkKey: string): string {\n return createHash('sha256').update(sdkKey).digest('hex');\n}\n\nexport function getProjectIdFromOidcToken(oidcToken: string) {\n const tokenParts = oidcToken.split('.');\n if (tokenParts.length !== 3 || !tokenParts[1]) {\n return;\n }\n\n const payload = JSON.parse(\n Buffer.from(tokenParts[1], 'base64url').toString('utf8'),\n ) as { project_id?: unknown };\n\n if (typeof payload.project_id !== 'string' || !payload.project_id) {\n return;\n }\n\n return payload.project_id;\n}\n\ntype DefinitionsEntry = {\n key: string;\n definitions: BundledDefinitions;\n};\n\ntype MapEntry = {\n key: string;\n value: string;\n};\n\n/**\n * Creates js constants pointing to memoized deduplicated flag definitions.\n * Output format:\n * ```js\n * const _d0 = memo(() => JSON.parse('...'));\n * const _d1 = memo(() => JSON.parse('...'));\n * ````\n */\nfunction generateDefinitionConstants(\n lines: string[],\n entries: DefinitionsEntry[],\n): MapEntry[] {\n const stringToConst = new Map<string, string>();\n\n return entries.map((entry) => {\n const stringified = JSON.stringify(entry.definitions);\n let definitionConst = stringToConst.get(stringified);\n\n if (!definitionConst) {\n definitionConst = `_d${stringToConst.size}`;\n stringToConst.set(stringified, definitionConst);\n lines.push(\n `const ${definitionConst} = memo(() => JSON.parse(${JSON.stringify(stringified)}));`,\n );\n }\n\n return { key: entry.key, value: definitionConst };\n });\n}\n\n/**\n * Creates a js map and getter function for exposing key value pairs.\n * Output format:\n * ```js\n * const map = { \"<sha256_hash_or_project_id>\": _d0 };\n * export function get(key) { return map[key]?.() ?? null; }\n * ````\n */\nfunction generateMap(lines: string[], entries: MapEntry[]): void {\n lines.push('');\n lines.push('const map = {');\n for (const entry of entries) {\n lines.push(` ${JSON.stringify(entry.key)}: ${entry.value},`);\n }\n lines.push('};');\n lines.push('');\n lines.push('export function get(key) {');\n lines.push(' return map[key]?.() ?? null;');\n lines.push('}');\n}\n\nasync function fetchDatafile(\n token: string,\n env: Record<string, string | undefined>,\n fetchFn: typeof globalThis.fetch,\n userAgentSuffix?: string,\n): Promise<BundledDefinitions | undefined> {\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n 'user-agent': [\n `@vercel/prepare-flags-definitions/${PACKAGE_VERSION}`,\n userAgentSuffix,\n ]\n .filter(Boolean)\n .join(' '),\n };\n\n // Add Vercel metadata headers if available\n if (env.VERCEL_PROJECT_ID) {\n headers['x-vercel-project-id'] = env.VERCEL_PROJECT_ID;\n }\n if (env.VERCEL_ENV) {\n headers['x-vercel-env'] = env.VERCEL_ENV;\n }\n if (env.VERCEL_DEPLOYMENT_ID) {\n headers['x-vercel-deployment-id'] = env.VERCEL_DEPLOYMENT_ID;\n }\n if (env.VERCEL_REGION) {\n headers['x-vercel-region'] = env.VERCEL_REGION;\n }\n\n const res = await fetchFn(`${FLAGS_HOST}/v1/datafile`, { headers });\n\n if (!res.ok) {\n if (res.status === 404) {\n return undefined;\n }\n\n throw new Error(\n `Failed to fetch flag definitions for ${obfuscate(token)}: ${res.status} ${res.statusText}`,\n );\n }\n\n return res.json() as Promise<BundledDefinitions>;\n}\n\n/**\n * Generates a JS module with deduplicated, lazily-parsed definitions.\n *\n * The map keys are SHA-256 hashes of the SDK keys so that raw keys\n * are not embedded in the output.\n *\n * Output format:\n * ```js\n * const memo = (fn) => { let cached; return () => (cached ??= fn()); };\n * const _d0 = memo(() => JSON.parse('...'));\n * const _d1 = memo(() => JSON.parse('...'));\n * const map = { \"<sha256_hash>\": _d0, \"project_id\": _d1 };\n * export function get(key) { return map[key]?.() ?? null; }\n * ```\n */\nexport function generateDefinitionsModule(\n entries: DefinitionsEntry[],\n output: Output | undefined,\n): string {\n output?.debug(\n `vercel-flags: writing flag definitions for \"${entries.map(({ key }) => obfuscate(key)).join(', ')}\"`,\n );\n\n // generate shared js\n const lines: string[] = [\n 'const memo = (fn) => { let cached; return () => (cached ??= fn()); };',\n '',\n ];\n\n // generate js const and capture the const names\n const generatedDefinitions = generateDefinitionConstants(lines, entries);\n\n // generate a map wiring keys to const names\n generateMap(lines, generatedDefinitions);\n\n lines.push(\n '',\n `export const version = ${JSON.stringify(FLAGS_DEFINITIONS_VERSION)};`,\n );\n\n return lines.join('\\n');\n}\n\ntype FlagEntry = {\n type: 'oidcToken' | 'sdkKey';\n key: string;\n};\n\n/**\n * Regex to match valid Vercel Flags SDK keys.\n * SDK keys must follow the format: vf_server_* or vf_client_*\n * This avoids false positives with third-party identifiers that happen\n * to start with 'vf_' (e.g., Stripe identity flow IDs like 'vf_1PyH...').\n */\nconst SDK_KEY_REGEX = /^vf_(?:server|client)_/;\n\n/**\n * Collect all possible flag entries the need embedding from the environment\n */\nfunction collectFlagEntries(\n env: Record<string, string | undefined>,\n output: Output | undefined,\n): FlagEntry[] {\n const entries: FlagEntry[] = [];\n\n // Collect unique SDK keys from environment variables\n // Supports both direct SDK keys (vf_server_*/vf_client_*) and flags: format\n const sdkKeys = Array.from(\n Object.values(env).reduce<Set<string>>((acc, value) => {\n if (typeof value === 'string') {\n if (SDK_KEY_REGEX.test(value)) {\n acc.add(value);\n } else if (value.startsWith('flags:')) {\n const params = new URLSearchParams(value.slice('flags:'.length));\n const sdkKey = params.get('sdkKey');\n if (sdkKey && SDK_KEY_REGEX.test(sdkKey)) {\n acc.add(sdkKey);\n }\n }\n }\n return acc;\n }, new Set<string>()),\n );\n\n if (sdkKeys.length > 0) {\n output?.debug(`vercel-flags: found ${sdkKeys.length} SDK keys`);\n\n for (const key of sdkKeys) {\n entries.push({ type: 'sdkKey', key });\n }\n }\n\n const oidcToken = env.VERCEL_OIDC_TOKEN;\n if (oidcToken && oidcToken?.length > 0) {\n output?.debug(`vercel-flags: found OIDC token`);\n\n entries.push({ type: 'oidcToken', key: oidcToken });\n }\n\n return entries;\n}\n\n/**\n * Prepares flag definitions by reading SDK keys from environment variables,\n * fetching definitions from flags.vercel.com, and writing them into a\n * synthetic `@vercel/flags-definitions` package inside `node_modules/`.\n */\nexport async function prepareFlagsDefinitions(options: {\n cwd: string;\n env: Record<string, string | undefined>;\n /**\n * Appended to the user-agent header to identify the caller.\n * Example: `\"vercel-cli/35.0.0\"`\n */\n userAgentSuffix?: string;\n fetch?: typeof globalThis.fetch;\n output?: Output;\n}): Promise<PrepareFlagsDefinitionsResult> {\n const {\n cwd,\n env,\n userAgentSuffix,\n fetch: fetchFn = globalThis.fetch,\n output,\n } = options;\n\n output?.debug('vercel-flags: checking env vars for SDK Keys and OIDC Token');\n\n const entries = collectFlagEntries(env, output);\n if (entries.length === 0) {\n return { created: false, reason: 'no-flags-entries' };\n }\n\n // fetch all datafiles for sdk keys and oidc tokens\n const resolvedEntries = await Promise.all(\n entries.map(async ({ key, type }) => {\n const definitions = await fetchDatafile(\n key,\n env,\n fetchFn,\n userAgentSuffix,\n );\n if (!definitions) {\n return;\n }\n\n if (type === 'oidcToken') {\n const projectId = getProjectIdFromOidcToken(key);\n\n if (projectId) {\n return {\n key: projectId,\n definitions,\n };\n }\n }\n\n if (type === 'sdkKey') {\n return {\n key: hashSdkKey(key),\n definitions,\n };\n }\n }),\n );\n\n const validEntries = resolvedEntries.filter((entry) => !!entry);\n\n const definitionsJs = generateDefinitionsModule(validEntries, output);\n\n // Write to node_modules/@vercel/flags-definitions/\n const storageDir = join(cwd, 'node_modules', '@vercel', 'flags-definitions');\n const indexPath = join(storageDir, 'index.js');\n const dtsPath = join(storageDir, 'index.d.ts');\n const packageJsonPath = join(storageDir, 'package.json');\n\n const dts = [\n 'export function get(key: string): Record<string, unknown> | null;',\n 'export const version: string;',\n '',\n ].join('\\n');\n\n const packageJson = {\n name: '@vercel/flags-definitions',\n version: FLAGS_DEFINITIONS_VERSION,\n type: 'module',\n main: './index.js',\n types: './index.d.ts',\n exports: {\n '.': {\n types: './index.d.ts',\n import: './index.js',\n },\n },\n };\n\n await mkdir(storageDir, { recursive: true });\n await Promise.all([\n writeFile(indexPath, definitionsJs),\n writeFile(dtsPath, dts),\n writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)),\n ]);\n\n output?.debug('vercel-flags: created module');\n output?.debug(` → ${indexPath}`);\n output?.debug(` → ${dtsPath}`);\n output?.debug(` → ${packageJsonPath}`);\n\n return { created: true, entryCount: entries.length };\n}\n","{\n \"name\": \"@vercel/prepare-flags-definitions\",\n \"version\": \"0.3.0\",\n \"description\": \"A utility for preparing flags definitions for embedding\",\n \"keywords\": [\n \"flags\",\n \"flags sdk\",\n \"experimentation\",\n \"ab testing\"\n ],\n \"license\": \"MIT\",\n \"author\": \"Dominik Ferber <dominik@vercel.com>\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/vercel/flags.git\"\n },\n \"sideEffects\": false,\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"typesVersions\": {\n \"*\": {\n \".\": [\n \"dist/*.d.ts\",\n \"dist/*.d.cts\"\n ]\n }\n },\n \"files\": [\n \"dist\",\n \"CHANGELOG.md\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"check\": \"biome check\",\n \"test\": \"vitest --run\",\n \"test:watch\": \"vitest\",\n \"type-check\": \"tsc --noEmit\"\n },\n \"devDependencies\": {\n \"@types/node\": \"20.11.17\",\n \"tsup\": \"8.5.1\",\n \"typescript\": \"5.6.3\",\n \"vite\": \"6.4.1\",\n \"vitest\": \"2.1.9\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAC3B,SAAS,OAAO,iBAAiB;AACjC,SAAS,YAAY;;;ACAnB,cAAW;;;ADGb,IAAM,aAAa;AACnB,IAAM,4BAA4B;AAgBlC,SAAS,UAAU,QAAgB,eAAe,IAAY;AAC5D,MAAI,gBAAgB,OAAO,OAAQ,QAAO;AAC1C,SACE,OAAO,MAAM,GAAG,YAAY,IAAI,IAAI,OAAO,OAAO,SAAS,YAAY;AAE3E;AAKO,SAAS,WAAW,QAAwB;AACjD,SAAO,WAAW,QAAQ,EAAE,OAAO,MAAM,EAAE,OAAO,KAAK;AACzD;AAEO,SAAS,0BAA0B,WAAmB;AAC3D,QAAM,aAAa,UAAU,MAAM,GAAG;AACtC,MAAI,WAAW,WAAW,KAAK,CAAC,WAAW,CAAC,GAAG;AAC7C;AAAA,EACF;AAEA,QAAM,UAAU,KAAK;AAAA,IACnB,OAAO,KAAK,WAAW,CAAC,GAAG,WAAW,EAAE,SAAS,MAAM;AAAA,EACzD;AAEA,MAAI,OAAO,QAAQ,eAAe,YAAY,CAAC,QAAQ,YAAY;AACjE;AAAA,EACF;AAEA,SAAO,QAAQ;AACjB;AAoBA,SAAS,4BACP,OACA,SACY;AACZ,QAAM,gBAAgB,oBAAI,IAAoB;AAE9C,SAAO,QAAQ,IAAI,CAAC,UAAU;AAC5B,UAAM,cAAc,KAAK,UAAU,MAAM,WAAW;AACpD,QAAI,kBAAkB,cAAc,IAAI,WAAW;AAEnD,QAAI,CAAC,iBAAiB;AACpB,wBAAkB,KAAK,cAAc,IAAI;AACzC,oBAAc,IAAI,aAAa,eAAe;AAC9C,YAAM;AAAA,QACJ,SAAS,eAAe,4BAA4B,KAAK,UAAU,WAAW,CAAC;AAAA,MACjF;AAAA,IACF;AAEA,WAAO,EAAE,KAAK,MAAM,KAAK,OAAO,gBAAgB;AAAA,EAClD,CAAC;AACH;AAUA,SAAS,YAAY,OAAiB,SAA2B;AAC/D,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,eAAe;AAC1B,aAAW,SAAS,SAAS;AAC3B,UAAM,KAAK,KAAK,KAAK,UAAU,MAAM,GAAG,CAAC,KAAK,MAAM,KAAK,GAAG;AAAA,EAC9D;AACA,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,4BAA4B;AACvC,QAAM,KAAK,gCAAgC;AAC3C,QAAM,KAAK,GAAG;AAChB;AAEA,eAAe,cACb,OACA,KACA,SACA,iBACyC;AACzC,QAAM,UAAkC;AAAA,IACtC,eAAe,UAAU,KAAK;AAAA,IAC9B,cAAc;AAAA,MACZ,qCAAqC,OAAe;AAAA,MACpD;AAAA,IACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,EACb;AAGA,MAAI,IAAI,mBAAmB;AACzB,YAAQ,qBAAqB,IAAI,IAAI;AAAA,EACvC;AACA,MAAI,IAAI,YAAY;AAClB,YAAQ,cAAc,IAAI,IAAI;AAAA,EAChC;AACA,MAAI,IAAI,sBAAsB;AAC5B,YAAQ,wBAAwB,IAAI,IAAI;AAAA,EAC1C;AACA,MAAI,IAAI,eAAe;AACrB,YAAQ,iBAAiB,IAAI,IAAI;AAAA,EACnC;AAEA,QAAM,MAAM,MAAM,QAAQ,GAAG,UAAU,gBAAgB,EAAE,QAAQ,CAAC;AAElE,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,KAAK;AACtB,aAAO;AAAA,IACT;AAEA,UAAM,IAAI;AAAA,MACR,wCAAwC,UAAU,KAAK,CAAC,KAAK,IAAI,MAAM,IAAI,IAAI,UAAU;AAAA,IAC3F;AAAA,EACF;AAEA,SAAO,IAAI,KAAK;AAClB;AAiBO,SAAS,0BACd,SACA,QACQ;AACR,mCAAQ;AAAA,IACN,+CAA+C,QAAQ,IAAI,CAAC,EAAE,IAAI,MAAM,UAAU,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA;AAIpG,QAAM,QAAkB;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AAGA,QAAM,uBAAuB,4BAA4B,OAAO,OAAO;AAGvE,cAAY,OAAO,oBAAoB;AAEvC,QAAM;AAAA,IACJ;AAAA,IACA,0BAA0B,KAAK,UAAU,yBAAyB,CAAC;AAAA,EACrE;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAaA,IAAM,gBAAgB;AAKtB,SAAS,mBACP,KACA,QACa;AACb,QAAM,UAAuB,CAAC;AAI9B,QAAM,UAAU,MAAM;AAAA,IACpB,OAAO,OAAO,GAAG,EAAE,OAAoB,CAAC,KAAK,UAAU;AACrD,UAAI,OAAO,UAAU,UAAU;AAC7B,YAAI,cAAc,KAAK,KAAK,GAAG;AAC7B,cAAI,IAAI,KAAK;AAAA,QACf,WAAW,MAAM,WAAW,QAAQ,GAAG;AACrC,gBAAM,SAAS,IAAI,gBAAgB,MAAM,MAAM,SAAS,MAAM,CAAC;AAC/D,gBAAM,SAAS,OAAO,IAAI,QAAQ;AAClC,cAAI,UAAU,cAAc,KAAK,MAAM,GAAG;AACxC,gBAAI,IAAI,MAAM;AAAA,UAChB;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT,GAAG,oBAAI,IAAY,CAAC;AAAA,EACtB;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,qCAAQ,MAAM,uBAAuB,QAAQ,MAAM;AAEnD,eAAW,OAAO,SAAS;AACzB,cAAQ,KAAK,EAAE,MAAM,UAAU,IAAI,CAAC;AAAA,IACtC;AAAA,EACF;AAEA,QAAM,YAAY,IAAI;AACtB,MAAI,cAAa,uCAAW,UAAS,GAAG;AACtC,qCAAQ,MAAM;AAEd,YAAQ,KAAK,EAAE,MAAM,aAAa,KAAK,UAAU,CAAC;AAAA,EACpD;AAEA,SAAO;AACT;AAOA,eAAsB,wBAAwB,SAUH;AACzC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,UAAU,WAAW;AAAA,IAC5B;AAAA,EACF,IAAI;AAEJ,mCAAQ,MAAM;AAEd,QAAM,UAAU,mBAAmB,KAAK,MAAM;AAC9C,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,EAAE,SAAS,OAAO,QAAQ,mBAAmB;AAAA,EACtD;AAGA,QAAM,kBAAkB,MAAM,QAAQ;AAAA,IACpC,QAAQ,IAAI,OAAO,EAAE,KAAK,KAAK,MAAM;AACnC,YAAM,cAAc,MAAM;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,UAAI,CAAC,aAAa;AAChB;AAAA,MACF;AAEA,UAAI,SAAS,aAAa;AACxB,cAAM,YAAY,0BAA0B,GAAG;AAE/C,YAAI,WAAW;AACb,iBAAO;AAAA,YACL,KAAK;AAAA,YACL;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,SAAS,UAAU;AACrB,eAAO;AAAA,UACL,KAAK,WAAW,GAAG;AAAA,UACnB;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,gBAAgB,OAAO,CAAC,UAAU,CAAC,CAAC,KAAK;AAE9D,QAAM,gBAAgB,0BAA0B,cAAc,MAAM;AAGpE,QAAM,aAAa,KAAK,KAAK,gBAAgB,WAAW,mBAAmB;AAC3E,QAAM,YAAY,KAAK,YAAY,UAAU;AAC7C,QAAM,UAAU,KAAK,YAAY,YAAY;AAC7C,QAAM,kBAAkB,KAAK,YAAY,cAAc;AAEvD,QAAM,MAAM;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AAEX,QAAM,cAAc;AAAA,IAClB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,MACP,KAAK;AAAA,QACH,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC3C,QAAM,QAAQ,IAAI;AAAA,IAChB,UAAU,WAAW,aAAa;AAAA,IAClC,UAAU,SAAS,GAAG;AAAA,IACtB,UAAU,iBAAiB,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;AAAA,EACjE,CAAC;AAED,mCAAQ,MAAM;AACd,mCAAQ,MAAM,YAAO,SAAS;AAC9B,mCAAQ,MAAM,YAAO,OAAO;AAC5B,mCAAQ,MAAM,YAAO,eAAe;AAEpC,SAAO,EAAE,SAAS,MAAM,YAAY,QAAQ,OAAO;AACrD;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../package.json"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { version as PACKAGE_VERSION } from '../package.json';\n\nconst FLAGS_HOST = 'https://flags.vercel.com';\nconst FLAGS_DEFINITIONS_VERSION = '1.0.1';\n\n/** Number of retry attempts for transient datafile fetch failures. */\nconst FETCH_MAX_RETRIES = 3;\n/** Base delay in milliseconds used for exponential backoff between retries. */\nconst FETCH_RETRY_BASE_DELAY_MS = 200;\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\ntype BundledDefinitions = Record<string, unknown>;\n\nexport interface Output {\n debug(message: string): void;\n time<T>(label: string, promise: Promise<T>): Promise<T>;\n}\n\nexport type PrepareFlagsDefinitionsResult =\n | { created: false; reason: 'no-flags-entries' }\n | { created: true; entryCount: number };\n\n/**\n * Obfuscates SDK key for logging (shows first 18 chars)\n */\nfunction obfuscate(sdkKey: string, prefixLength = 18): string {\n if (prefixLength >= sdkKey.length) return sdkKey;\n return (\n sdkKey.slice(0, prefixLength) + '*'.repeat(sdkKey.length - prefixLength)\n );\n}\n\n/**\n * Computes a SHA-256 hex digest of the given SDK key.\n */\nexport function hashSdkKey(sdkKey: string): string {\n return createHash('sha256').update(sdkKey).digest('hex');\n}\n\nexport function getProjectIdFromOidcToken(oidcToken: string) {\n const tokenParts = oidcToken.split('.');\n if (tokenParts.length !== 3 || !tokenParts[1]) {\n return;\n }\n\n const payload = JSON.parse(\n Buffer.from(tokenParts[1], 'base64url').toString('utf8'),\n ) as { project_id?: unknown };\n\n if (typeof payload.project_id !== 'string' || !payload.project_id) {\n return;\n }\n\n return payload.project_id;\n}\n\ntype DefinitionsEntry = {\n key: string;\n definitions: BundledDefinitions;\n};\n\ntype MapEntry = {\n key: string;\n value: string;\n};\n\n/**\n * Creates js constants pointing to memoized deduplicated flag definitions.\n * Output format:\n * ```js\n * const _d0 = memo(() => JSON.parse('...'));\n * const _d1 = memo(() => JSON.parse('...'));\n * ````\n */\nfunction generateDefinitionConstants(\n lines: string[],\n entries: DefinitionsEntry[],\n): MapEntry[] {\n const stringToConst = new Map<string, string>();\n\n return entries.map((entry) => {\n const stringified = JSON.stringify(entry.definitions);\n let definitionConst = stringToConst.get(stringified);\n\n if (!definitionConst) {\n definitionConst = `_d${stringToConst.size}`;\n stringToConst.set(stringified, definitionConst);\n lines.push(\n `const ${definitionConst} = memo(() => JSON.parse(${JSON.stringify(stringified)}));`,\n );\n }\n\n return { key: entry.key, value: definitionConst };\n });\n}\n\n/**\n * Creates a js map and getter function for exposing key value pairs.\n * Output format:\n * ```js\n * const map = { \"<sha256_hash_or_project_id>\": _d0 };\n * export function get(key) { return map[key]?.() ?? null; }\n * ````\n */\nfunction generateMap(lines: string[], entries: MapEntry[]): void {\n lines.push('');\n lines.push('const map = {');\n for (const entry of entries) {\n lines.push(` ${JSON.stringify(entry.key)}: ${entry.value},`);\n }\n lines.push('};');\n lines.push('');\n lines.push('export function get(key) {');\n lines.push(' return map[key]?.() ?? null;');\n lines.push('}');\n}\n\n/**\n * Determines whether an HTTP status is worth retrying. Transient server-side\n * failures (5xx) and rate limiting (429) are retryable; other 4xx responses\n * indicate a client error that won't be fixed by retrying.\n */\nfunction isRetryableStatus(status: number): boolean {\n return status === 429 || status >= 500;\n}\n\nasync function fetchDatafile(\n token: string,\n env: Record<string, string | undefined>,\n fetchFn: typeof globalThis.fetch,\n userAgentSuffix?: string,\n output?: Output,\n): Promise<BundledDefinitions | undefined> {\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n 'user-agent': [\n `@vercel/prepare-flags-definitions/${PACKAGE_VERSION}`,\n userAgentSuffix,\n ]\n .filter(Boolean)\n .join(' '),\n };\n\n // Add Vercel metadata headers if available\n if (env.VERCEL_PROJECT_ID) {\n headers['x-vercel-project-id'] = env.VERCEL_PROJECT_ID;\n }\n if (env.VERCEL_ENV) {\n headers['x-vercel-env'] = env.VERCEL_ENV;\n }\n if (env.VERCEL_DEPLOYMENT_ID) {\n headers['x-vercel-deployment-id'] = env.VERCEL_DEPLOYMENT_ID;\n }\n if (env.VERCEL_REGION) {\n headers['x-vercel-region'] = env.VERCEL_REGION;\n }\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= FETCH_MAX_RETRIES; attempt++) {\n if (attempt > 0) {\n // Exponential backoff: 200ms, 400ms, 800ms, ...\n const delay = FETCH_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1);\n output?.debug(\n `vercel-flags: retrying datafile fetch for ${obfuscate(token)} (attempt ${attempt}/${FETCH_MAX_RETRIES}) after ${delay}ms`,\n );\n await wait(delay);\n }\n\n let res: Response;\n try {\n res = await fetchFn(`${FLAGS_HOST}/v1/datafile`, { headers });\n } catch (error) {\n // Network-level failure (DNS, connection reset, etc.) — retryable.\n lastError = error;\n continue;\n }\n\n if (res.ok) {\n const definitions = (await res.json()) as BundledDefinitions;\n return { ...definitions, fetchedAt: Date.now() };\n }\n\n if (res.status === 404) {\n return undefined;\n }\n\n const error = new Error(\n `Failed to fetch flag definitions for ${obfuscate(token)}: ${res.status} ${res.statusText}`,\n );\n\n if (!isRetryableStatus(res.status)) {\n throw error;\n }\n\n lastError = error;\n }\n\n throw lastError instanceof Error\n ? lastError\n : new Error(\n `Failed to fetch flag definitions for ${obfuscate(token)} after ${FETCH_MAX_RETRIES} retries`,\n );\n}\n\n/**\n * Generates a JS module with deduplicated, lazily-parsed definitions.\n *\n * The map keys are SHA-256 hashes of the SDK keys so that raw keys\n * are not embedded in the output.\n *\n * Output format:\n * ```js\n * const memo = (fn) => { let cached; return () => (cached ??= fn()); };\n * const _d0 = memo(() => JSON.parse('...'));\n * const _d1 = memo(() => JSON.parse('...'));\n * const map = { \"<sha256_hash>\": _d0, \"project_id\": _d1 };\n * export function get(key) { return map[key]?.() ?? null; }\n * ```\n */\nexport function generateDefinitionsModule(\n entries: DefinitionsEntry[],\n output: Output | undefined,\n): string {\n output?.debug(\n `vercel-flags: writing flag definitions for \"${entries.map(({ key }) => obfuscate(key)).join(', ')}\"`,\n );\n\n // generate shared js\n const lines: string[] = [\n 'const memo = (fn) => { let cached; return () => (cached ??= fn()); };',\n '',\n ];\n\n // generate js const and capture the const names\n const generatedDefinitions = generateDefinitionConstants(lines, entries);\n\n // generate a map wiring keys to const names\n generateMap(lines, generatedDefinitions);\n\n lines.push(\n '',\n `export const version = ${JSON.stringify(FLAGS_DEFINITIONS_VERSION)};`,\n );\n\n return lines.join('\\n');\n}\n\ntype FlagEntry = {\n type: 'oidcToken' | 'sdkKey';\n key: string;\n};\n\n/**\n * Regex to match valid Vercel Flags SDK keys.\n * SDK keys must follow the format: vf_server_* or vf_client_*\n * This avoids false positives with third-party identifiers that happen\n * to start with 'vf_' (e.g., Stripe identity flow IDs like 'vf_1PyH...').\n */\nconst SDK_KEY_REGEX = /^vf_(?:server|client)_/;\n\n/**\n * Collect all possible flag entries the need embedding from the environment\n */\nfunction collectFlagEntries(\n env: Record<string, string | undefined>,\n output: Output | undefined,\n): FlagEntry[] {\n const entries: FlagEntry[] = [];\n\n // Collect unique SDK keys from environment variables\n // Supports both direct SDK keys (vf_server_*/vf_client_*) and flags: format\n const sdkKeys = Array.from(\n Object.values(env).reduce<Set<string>>((acc, value) => {\n if (typeof value === 'string') {\n if (SDK_KEY_REGEX.test(value)) {\n acc.add(value);\n } else if (value.startsWith('flags:')) {\n const params = new URLSearchParams(value.slice('flags:'.length));\n const sdkKey = params.get('sdkKey');\n if (sdkKey && SDK_KEY_REGEX.test(sdkKey)) {\n acc.add(sdkKey);\n }\n }\n }\n return acc;\n }, new Set<string>()),\n );\n\n if (sdkKeys.length > 0) {\n output?.debug(`vercel-flags: found ${sdkKeys.length} SDK keys`);\n\n for (const key of sdkKeys) {\n entries.push({ type: 'sdkKey', key });\n }\n }\n\n const oidcToken = env.VERCEL_OIDC_TOKEN;\n if (oidcToken && oidcToken?.length > 0) {\n output?.debug(`vercel-flags: found OIDC token`);\n\n entries.push({ type: 'oidcToken', key: oidcToken });\n }\n\n return entries;\n}\n\n/**\n * Prepares flag definitions by reading SDK keys from environment variables,\n * fetching definitions from flags.vercel.com, and writing them into a\n * synthetic `@vercel/flags-definitions` package inside `node_modules/`.\n */\nexport async function prepareFlagsDefinitions(options: {\n cwd: string;\n env: Record<string, string | undefined>;\n /**\n * Appended to the user-agent header to identify the caller.\n * Example: `\"vercel-cli/35.0.0\"`\n */\n userAgentSuffix?: string;\n fetch?: typeof globalThis.fetch;\n output?: Output;\n}): Promise<PrepareFlagsDefinitionsResult> {\n const {\n cwd,\n env,\n userAgentSuffix,\n fetch: fetchFn = globalThis.fetch,\n output,\n } = options;\n\n output?.debug('vercel-flags: checking env vars for SDK Keys and OIDC Token');\n\n const entries = collectFlagEntries(env, output);\n if (entries.length === 0) {\n return { created: false, reason: 'no-flags-entries' };\n }\n\n // fetch all datafiles for sdk keys and oidc tokens\n const resolvedEntries = await Promise.all(\n entries.map(async ({ key, type }) => {\n const definitions = await fetchDatafile(\n key,\n env,\n fetchFn,\n userAgentSuffix,\n output,\n );\n if (!definitions) {\n return;\n }\n\n if (type === 'oidcToken') {\n const projectId = getProjectIdFromOidcToken(key);\n\n if (projectId) {\n return {\n key: projectId,\n definitions,\n };\n }\n }\n\n if (type === 'sdkKey') {\n return {\n key: hashSdkKey(key),\n definitions,\n };\n }\n }),\n );\n\n const validEntries = resolvedEntries.filter((entry) => !!entry);\n\n const definitionsJs = generateDefinitionsModule(validEntries, output);\n\n // Write to node_modules/@vercel/flags-definitions/\n const storageDir = join(cwd, 'node_modules', '@vercel', 'flags-definitions');\n const indexPath = join(storageDir, 'index.js');\n const dtsPath = join(storageDir, 'index.d.ts');\n const packageJsonPath = join(storageDir, 'package.json');\n\n const dts = [\n 'export function get(key: string): Record<string, unknown> | null;',\n 'export const version: string;',\n '',\n ].join('\\n');\n\n const packageJson = {\n name: '@vercel/flags-definitions',\n version: FLAGS_DEFINITIONS_VERSION,\n type: 'module',\n main: './index.js',\n types: './index.d.ts',\n exports: {\n '.': {\n types: './index.d.ts',\n import: './index.js',\n },\n },\n };\n\n await mkdir(storageDir, { recursive: true });\n await Promise.all([\n writeFile(indexPath, definitionsJs),\n writeFile(dtsPath, dts),\n writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)),\n ]);\n\n output?.debug('vercel-flags: created module');\n output?.debug(` → ${indexPath}`);\n output?.debug(` → ${dtsPath}`);\n output?.debug(` → ${packageJsonPath}`);\n\n return { created: true, entryCount: entries.length };\n}\n","{\n \"name\": \"@vercel/prepare-flags-definitions\",\n \"version\": \"0.4.1-ea4d7de-20260922121332\",\n \"description\": \"A utility for preparing flags definitions for embedding\",\n \"keywords\": [\n \"flags\",\n \"flags sdk\",\n \"experimentation\",\n \"ab testing\"\n ],\n \"license\": \"MIT\",\n \"author\": \"Dominik Ferber <dominik@vercel.com>\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/vercel/flags.git\"\n },\n \"sideEffects\": false,\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"typesVersions\": {\n \"*\": {\n \".\": [\n \"dist/*.d.ts\",\n \"dist/*.d.cts\"\n ]\n }\n },\n \"files\": [\n \"dist\",\n \"CHANGELOG.md\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"check\": \"biome check\",\n \"test\": \"vitest --run\",\n \"test:watch\": \"vitest\",\n \"type-check\": \"tsc --noEmit\"\n },\n \"devDependencies\": {\n \"@types/node\": \"22.14.0\",\n \"tsup\": \"8.5.1\",\n \"typescript\": \"5.6.3\",\n \"vite\": \"8.1.5\",\n \"vitest\": \"4.1.10\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAC3B,SAAS,OAAO,iBAAiB;AACjC,SAAS,YAAY;;;ACAnB,cAAW;;;ADGb,IAAM,aAAa;AACnB,IAAM,4BAA4B;AAGlC,IAAM,oBAAoB;AAE1B,IAAM,4BAA4B;AAElC,SAAS,KAAK,IAA2B;AACvC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAgBA,SAAS,UAAU,QAAgB,eAAe,IAAY;AAC5D,MAAI,gBAAgB,OAAO,OAAQ,QAAO;AAC1C,SACE,OAAO,MAAM,GAAG,YAAY,IAAI,IAAI,OAAO,OAAO,SAAS,YAAY;AAE3E;AAKO,SAAS,WAAW,QAAwB;AACjD,SAAO,WAAW,QAAQ,EAAE,OAAO,MAAM,EAAE,OAAO,KAAK;AACzD;AAEO,SAAS,0BAA0B,WAAmB;AAC3D,QAAM,aAAa,UAAU,MAAM,GAAG;AACtC,MAAI,WAAW,WAAW,KAAK,CAAC,WAAW,CAAC,GAAG;AAC7C;AAAA,EACF;AAEA,QAAM,UAAU,KAAK;AAAA,IACnB,OAAO,KAAK,WAAW,CAAC,GAAG,WAAW,EAAE,SAAS,MAAM;AAAA,EACzD;AAEA,MAAI,OAAO,QAAQ,eAAe,YAAY,CAAC,QAAQ,YAAY;AACjE;AAAA,EACF;AAEA,SAAO,QAAQ;AACjB;AAoBA,SAAS,4BACP,OACA,SACY;AACZ,QAAM,gBAAgB,oBAAI,IAAoB;AAE9C,SAAO,QAAQ,IAAI,CAAC,UAAU;AAC5B,UAAM,cAAc,KAAK,UAAU,MAAM,WAAW;AACpD,QAAI,kBAAkB,cAAc,IAAI,WAAW;AAEnD,QAAI,CAAC,iBAAiB;AACpB,wBAAkB,KAAK,cAAc,IAAI;AACzC,oBAAc,IAAI,aAAa,eAAe;AAC9C,YAAM;AAAA,QACJ,SAAS,eAAe,4BAA4B,KAAK,UAAU,WAAW,CAAC;AAAA,MACjF;AAAA,IACF;AAEA,WAAO,EAAE,KAAK,MAAM,KAAK,OAAO,gBAAgB;AAAA,EAClD,CAAC;AACH;AAUA,SAAS,YAAY,OAAiB,SAA2B;AAC/D,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,eAAe;AAC1B,aAAW,SAAS,SAAS;AAC3B,UAAM,KAAK,KAAK,KAAK,UAAU,MAAM,GAAG,CAAC,KAAK,MAAM,KAAK,GAAG;AAAA,EAC9D;AACA,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,4BAA4B;AACvC,QAAM,KAAK,gCAAgC;AAC3C,QAAM,KAAK,GAAG;AAChB;AAOA,SAAS,kBAAkB,QAAyB;AAClD,SAAO,WAAW,OAAO,UAAU;AACrC;AAEA,eAAe,cACb,OACA,KACA,SACA,iBACA,QACyC;AACzC,QAAM,UAAkC;AAAA,IACtC,eAAe,UAAU,KAAK;AAAA,IAC9B,cAAc;AAAA,MACZ,qCAAqC,OAAe;AAAA,MACpD;AAAA,IACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,EACb;AAGA,MAAI,IAAI,mBAAmB;AACzB,YAAQ,qBAAqB,IAAI,IAAI;AAAA,EACvC;AACA,MAAI,IAAI,YAAY;AAClB,YAAQ,cAAc,IAAI,IAAI;AAAA,EAChC;AACA,MAAI,IAAI,sBAAsB;AAC5B,YAAQ,wBAAwB,IAAI,IAAI;AAAA,EAC1C;AACA,MAAI,IAAI,eAAe;AACrB,YAAQ,iBAAiB,IAAI,IAAI;AAAA,EACnC;AAEA,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,mBAAmB,WAAW;AAC7D,QAAI,UAAU,GAAG;AAEf,YAAM,QAAQ,4BAA4B,MAAM,UAAU;AAC1D,uCAAQ;AAAA,QACN,6CAA6C,UAAU,KAAK,CAAC,aAAa,OAAO,IAAI,iBAAiB,WAAW,KAAK;AAAA;AAExH,YAAM,KAAK,KAAK;AAAA,IAClB;AAEA,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,QAAQ,GAAG,UAAU,gBAAgB,EAAE,QAAQ,CAAC;AAAA,IAC9D,SAASA,QAAO;AAEd,kBAAYA;AACZ;AAAA,IACF;AAEA,QAAI,IAAI,IAAI;AACV,YAAM,cAAe,MAAM,IAAI,KAAK;AACpC,aAAO,EAAE,GAAG,aAAa,WAAW,KAAK,IAAI,EAAE;AAAA,IACjD;AAEA,QAAI,IAAI,WAAW,KAAK;AACtB,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,IAAI;AAAA,MAChB,wCAAwC,UAAU,KAAK,CAAC,KAAK,IAAI,MAAM,IAAI,IAAI,UAAU;AAAA,IAC3F;AAEA,QAAI,CAAC,kBAAkB,IAAI,MAAM,GAAG;AAClC,YAAM;AAAA,IACR;AAEA,gBAAY;AAAA,EACd;AAEA,QAAM,qBAAqB,QACvB,YACA,IAAI;AAAA,IACF,wCAAwC,UAAU,KAAK,CAAC,UAAU,iBAAiB;AAAA,EACrF;AACN;AAiBO,SAAS,0BACd,SACA,QACQ;AACR,mCAAQ;AAAA,IACN,+CAA+C,QAAQ,IAAI,CAAC,EAAE,IAAI,MAAM,UAAU,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA;AAIpG,QAAM,QAAkB;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AAGA,QAAM,uBAAuB,4BAA4B,OAAO,OAAO;AAGvE,cAAY,OAAO,oBAAoB;AAEvC,QAAM;AAAA,IACJ;AAAA,IACA,0BAA0B,KAAK,UAAU,yBAAyB,CAAC;AAAA,EACrE;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAaA,IAAM,gBAAgB;AAKtB,SAAS,mBACP,KACA,QACa;AACb,QAAM,UAAuB,CAAC;AAI9B,QAAM,UAAU,MAAM;AAAA,IACpB,OAAO,OAAO,GAAG,EAAE,OAAoB,CAAC,KAAK,UAAU;AACrD,UAAI,OAAO,UAAU,UAAU;AAC7B,YAAI,cAAc,KAAK,KAAK,GAAG;AAC7B,cAAI,IAAI,KAAK;AAAA,QACf,WAAW,MAAM,WAAW,QAAQ,GAAG;AACrC,gBAAM,SAAS,IAAI,gBAAgB,MAAM,MAAM,SAAS,MAAM,CAAC;AAC/D,gBAAM,SAAS,OAAO,IAAI,QAAQ;AAClC,cAAI,UAAU,cAAc,KAAK,MAAM,GAAG;AACxC,gBAAI,IAAI,MAAM;AAAA,UAChB;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT,GAAG,oBAAI,IAAY,CAAC;AAAA,EACtB;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,qCAAQ,MAAM,uBAAuB,QAAQ,MAAM;AAEnD,eAAW,OAAO,SAAS;AACzB,cAAQ,KAAK,EAAE,MAAM,UAAU,IAAI,CAAC;AAAA,IACtC;AAAA,EACF;AAEA,QAAM,YAAY,IAAI;AACtB,MAAI,cAAa,uCAAW,UAAS,GAAG;AACtC,qCAAQ,MAAM;AAEd,YAAQ,KAAK,EAAE,MAAM,aAAa,KAAK,UAAU,CAAC;AAAA,EACpD;AAEA,SAAO;AACT;AAOA,eAAsB,wBAAwB,SAUH;AACzC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,UAAU,WAAW;AAAA,IAC5B;AAAA,EACF,IAAI;AAEJ,mCAAQ,MAAM;AAEd,QAAM,UAAU,mBAAmB,KAAK,MAAM;AAC9C,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,EAAE,SAAS,OAAO,QAAQ,mBAAmB;AAAA,EACtD;AAGA,QAAM,kBAAkB,MAAM,QAAQ;AAAA,IACpC,QAAQ,IAAI,OAAO,EAAE,KAAK,KAAK,MAAM;AACnC,YAAM,cAAc,MAAM;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,UAAI,CAAC,aAAa;AAChB;AAAA,MACF;AAEA,UAAI,SAAS,aAAa;AACxB,cAAM,YAAY,0BAA0B,GAAG;AAE/C,YAAI,WAAW;AACb,iBAAO;AAAA,YACL,KAAK;AAAA,YACL;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,SAAS,UAAU;AACrB,eAAO;AAAA,UACL,KAAK,WAAW,GAAG;AAAA,UACnB;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,gBAAgB,OAAO,CAAC,UAAU,CAAC,CAAC,KAAK;AAE9D,QAAM,gBAAgB,0BAA0B,cAAc,MAAM;AAGpE,QAAM,aAAa,KAAK,KAAK,gBAAgB,WAAW,mBAAmB;AAC3E,QAAM,YAAY,KAAK,YAAY,UAAU;AAC7C,QAAM,UAAU,KAAK,YAAY,YAAY;AAC7C,QAAM,kBAAkB,KAAK,YAAY,cAAc;AAEvD,QAAM,MAAM;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AAEX,QAAM,cAAc;AAAA,IAClB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,MACP,KAAK;AAAA,QACH,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC3C,QAAM,QAAQ,IAAI;AAAA,IAChB,UAAU,WAAW,aAAa;AAAA,IAClC,UAAU,SAAS,GAAG;AAAA,IACtB,UAAU,iBAAiB,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;AAAA,EACjE,CAAC;AAED,mCAAQ,MAAM;AACd,mCAAQ,MAAM,YAAO,SAAS;AAC9B,mCAAQ,MAAM,YAAO,OAAO;AAC5B,mCAAQ,MAAM,YAAO,eAAe;AAEpC,SAAO,EAAE,SAAS,MAAM,YAAY,QAAQ,OAAO;AACrD;","names":["error"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/prepare-flags-definitions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1-ea4d7de-20260922121332",
|
|
4
4
|
"description": "A utility for preparing flags definitions for embedding",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"flags",
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
"CHANGELOG.md"
|
|
39
39
|
],
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@types/node": "
|
|
41
|
+
"@types/node": "22.14.0",
|
|
42
42
|
"tsup": "8.5.1",
|
|
43
43
|
"typescript": "5.6.3",
|
|
44
|
-
"vite": "
|
|
45
|
-
"vitest": "
|
|
44
|
+
"vite": "8.1.5",
|
|
45
|
+
"vitest": "4.1.10"
|
|
46
46
|
},
|
|
47
47
|
"publishConfig": {
|
|
48
48
|
"access": "public"
|