Skip to content

Commit f41081c

Browse files
authored
chore(shared,react-router,tanstack-start): Create shared environment variable retrieval function (#4985)
1 parent 6ce705c commit f41081c

File tree

13 files changed

+120
-109
lines changed

13 files changed

+120
-109
lines changed

.changeset/eleven-cougars-film.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@clerk/shared": minor
3+
---
4+
5+
Introduce unified environment variable handling across all supported platforms
6+
7+
Usage:
8+
9+
```ts
10+
import { getEnvVariable } from '@clerk/shared/getEnvVariable'
11+
12+
const publishableKey = getEnvVariable('CLERK_PUBLISHABLE_KEY')
13+
```

.changeset/unlucky-gifts-obey.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/tanstack-start': patch
3+
'@clerk/react-router': patch
4+
---
5+
6+
Internal changes to use new `getEnvVariable` utility from `@clerk/shared`

packages/react-router/src/ssr/loadOptions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { createClerkRequest } from '@clerk/backend/internal';
22
import { apiUrlFromPublishableKey } from '@clerk/shared/apiUrlFromPublishableKey';
3+
import { getEnvVariable } from '@clerk/shared/getEnvVariable';
34
import { isDevelopmentFromSecretKey } from '@clerk/shared/keys';
45
import { isHttpOrHttps, isProxyUrlRelative } from '@clerk/shared/proxy';
56
import { handleValueOrFn } from '@clerk/shared/utils';
67

7-
import { getEnvVariable, getPublicEnvVariables } from '../utils/env';
8+
import { getPublicEnvVariables } from '../utils/env';
89
import { noSecretKeyError, satelliteAndMissingProxyUrlAndDomain, satelliteAndMissingSignInUrl } from '../utils/errors';
910
import type { LoaderFunctionArgs, RootAuthLoaderOptions } from './types';
1011
import { patchRequest } from './utils';

packages/react-router/src/utils/env.ts

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,7 @@
1+
import { getEnvVariable } from '@clerk/shared/getEnvVariable';
12
import { isTruthy } from '@clerk/shared/underscore';
23
import type { AppLoadContext } from 'react-router';
34

4-
type CloudflareEnv = { env: Record<string, string> };
5-
6-
const hasCloudflareProxyContext = (context: any): context is { cloudflare: CloudflareEnv } => {
7-
return !!context?.cloudflare?.env;
8-
};
9-
10-
const hasCloudflareContext = (context: any): context is CloudflareEnv => {
11-
return !!context?.env;
12-
};
13-
14-
/**
15-
*
16-
* Utility function to get env variables across Node and Edge runtimes.
17-
*
18-
* @param name
19-
* @returns string
20-
*/
21-
export const getEnvVariable = (name: string, context: AppLoadContext | undefined): string => {
22-
// Node envs
23-
if (typeof process !== 'undefined' && process.env && typeof process.env[name] === 'string') {
24-
return process.env[name];
25-
}
26-
27-
// @ts-expect-error - Vite specific
28-
if (typeof import.meta !== 'undefined' && import.meta.env && typeof import.meta.env[name] === 'string') {
29-
// @ts-expect-error - Vite specific
30-
return import.meta.env[name];
31-
}
32-
33-
if (hasCloudflareProxyContext(context)) {
34-
return context.cloudflare.env[name] || '';
35-
}
36-
37-
// Cloudflare
38-
if (hasCloudflareContext(context)) {
39-
return context.env[name] || '';
40-
}
41-
42-
// Check whether the value exists in the context object directly
43-
if (context && typeof context[name] === 'string') {
44-
return context[name];
45-
}
46-
47-
// Cloudflare workers
48-
try {
49-
return globalThis[name as keyof typeof globalThis];
50-
} catch {
51-
// This will raise an error in Cloudflare Pages
52-
}
53-
54-
return '';
55-
};
56-
575
export const getPublicEnvVariables = (context: AppLoadContext | undefined) => {
586
return {
597
publishableKey:

packages/shared/global.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,11 @@ declare const PACKAGE_NAME: string;
22
declare const PACKAGE_VERSION: string;
33
declare const JS_PACKAGE_VERSION: string;
44
declare const __DEV__: boolean;
5+
6+
interface ImportMetaEnv {
7+
readonly [key: string]: string;
8+
}
9+
10+
interface ImportMeta {
11+
readonly env: ImportMetaEnv;
12+
}

packages/shared/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
"object",
116116
"oauth",
117117
"web3",
118+
"getEnvVariable",
118119
"pathMatcher"
119120
],
120121
"scripts": {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
type CloudflareEnv = { env: Record<string, string> };
2+
3+
const hasCloudflareProxyContext = (context: any): context is { cloudflare: CloudflareEnv } => {
4+
return !!context?.cloudflare?.env;
5+
};
6+
7+
const hasCloudflareContext = (context: any): context is CloudflareEnv => {
8+
return !!context?.env;
9+
};
10+
11+
/**
12+
* Retrieves an environment variable across runtime environments.
13+
* @param name - The environment variable name to retrieve
14+
* @param context - Optional context object that may contain environment values
15+
* @returns The environment variable value or empty string if not found
16+
*/
17+
export const getEnvVariable = (name: string, context?: Record<string, any>): string => {
18+
// Node envs
19+
if (typeof process !== 'undefined' && process.env && typeof process.env[name] === 'string') {
20+
return process.env[name];
21+
}
22+
23+
// Vite specific
24+
if (typeof import.meta !== 'undefined' && import.meta.env && typeof import.meta.env[name] === 'string') {
25+
return import.meta.env[name];
26+
}
27+
28+
if (hasCloudflareProxyContext(context)) {
29+
return context.cloudflare.env[name] || '';
30+
}
31+
32+
// Cloudflare
33+
if (hasCloudflareContext(context)) {
34+
return context.env[name] || '';
35+
}
36+
37+
// Check whether the value exists in the context object directly
38+
if (context && typeof context[name] === 'string') {
39+
return context[name];
40+
}
41+
42+
// Cloudflare workers
43+
try {
44+
return globalThis[name as keyof typeof globalThis];
45+
} catch {
46+
// This will raise an error in Cloudflare Pages
47+
}
48+
49+
return '';
50+
};

packages/shared/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,5 @@ export * from './object';
3535
export * from './logger';
3636
export { createWorkerTimers } from './workerTimers';
3737
export { DEV_BROWSER_JWT_KEY, extractDevBrowserJWTFromURL, setDevBrowserJWTInURL } from './devBrowser';
38+
export { getEnvVariable } from './getEnvVariable';
3839
export * from './pathMatcher';

packages/shared/tsup.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default defineConfig(overrideOptions => {
2424
minify: false,
2525
sourcemap: true,
2626
dts: true,
27+
target: 'es2020',
2728
external: ['react', 'react-dom'],
2829
esbuildPlugins: [WebWorkerMinifyPlugin as any],
2930
define: {
Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { apiUrlFromPublishableKey } from '@clerk/shared/apiUrlFromPublishableKey';
2+
import { getEnvVariable } from '@clerk/shared/getEnvVariable';
23
import { getEvent } from 'vinxi/http';
34

4-
import { getEnvVariable, getPublicEnvVariables } from '../utils/env';
5+
import { getPublicEnvVariables } from '../utils/env';
56

67
export const commonEnvs = () => {
78
const event = getEvent();
8-
const publicEnvs = getPublicEnvVariables(event);
9+
const publicEnvs = getPublicEnvVariables(event.context);
910

1011
return {
1112
// Public environment variables
@@ -21,16 +22,16 @@ export const commonEnvs = () => {
2122
TELEMETRY_DEBUG: publicEnvs.telemetryDebug,
2223

2324
// Server-only environment variables
24-
API_VERSION: getEnvVariable('CLERK_API_VERSION', 'v1', event),
25-
SECRET_KEY: getEnvVariable('CLERK_SECRET_KEY', '', event),
26-
ENCRYPTION_KEY: getEnvVariable('CLERK_ENCRYPTION_KEY', '', event),
27-
CLERK_JWT_KEY: getEnvVariable('CLERK_JWT_KEY', '', event),
28-
API_URL: getEnvVariable('CLERK_API_URL', '', event) || apiUrlFromPublishableKey(publicEnvs.publishableKey),
25+
API_VERSION: getEnvVariable('CLERK_API_VERSION', event.context) || 'v1',
26+
SECRET_KEY: getEnvVariable('CLERK_SECRET_KEY', event.context),
27+
ENCRYPTION_KEY: getEnvVariable('CLERK_ENCRYPTION_KEY', event.context),
28+
CLERK_JWT_KEY: getEnvVariable('CLERK_JWT_KEY', event.context),
29+
API_URL: getEnvVariable('CLERK_API_URL', event.context) || apiUrlFromPublishableKey(publicEnvs.publishableKey),
2930

3031
SDK_METADATA: {
3132
name: PACKAGE_NAME,
3233
version: PACKAGE_VERSION,
33-
environment: getEnvVariable('NODE_ENV', '', event),
34+
environment: getEnvVariable('NODE_ENV', event.context),
3435
},
3536
} as const;
3637
};

0 commit comments

Comments
 (0)