diff --git a/.changeset/old-fishes-sing.md b/.changeset/old-fishes-sing.md new file mode 100644 index 00000000..976ee921 --- /dev/null +++ b/.changeset/old-fishes-sing.md @@ -0,0 +1,5 @@ +--- +"@t3-oss/env-core": minor +--- + +support multiple client prefixes diff --git a/docs/src/app/docs/core/page.mdx b/docs/src/app/docs/core/page.mdx index 5d2c5a03..026950d7 100644 --- a/docs/src/app/docs/core/page.mdx +++ b/docs/src/app/docs/core/page.mdx @@ -58,9 +58,10 @@ export const env = createEnv({ /** * The prefix that client-side variables must have. This is enforced both at - * a type-level and at runtime. + * a type-level and at runtime. Can be a single string or an array of strings + * if you want to use multiple client prefixes. */ - clientPrefix: "PUBLIC_", + clientPrefix: "PUBLIC_", // or ["PUBLIC_", "NEXT_PUBLIC_"], client: { PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1), diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 814c5020..8d38fd4c 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -130,7 +130,7 @@ export interface StrictOptions< } export interface ClientOptions< - TPrefix extends string | undefined, + TPrefix extends string | string[] | undefined, TClient extends Record, > { /** @@ -144,11 +144,21 @@ export interface ClientOptions< * built with invalid env vars. */ client: Partial<{ - [TKey in keyof TClient]: TKey extends `${TPrefix}${string}` - ? TClient[TKey] - : ErrorMessage<`${TKey extends string - ? TKey - : never} is not prefixed with ${TPrefix}.`>; + [TKey in keyof TClient]: TPrefix extends string + ? TKey extends `${TPrefix}${string}` + ? TClient[TKey] + : ErrorMessage<`${TKey extends string + ? TKey + : never} is not prefixed with ${TPrefix}.`> + : TPrefix extends string[] + ? TKey extends `${infer Prefix}${string}` + ? Prefix extends TPrefix[number] + ? TClient[TKey] + : ErrorMessage<`${TKey extends string + ? TKey + : never} is not prefixed with any of ${TPrefix[number]}.`> + : never + : never; }>; } @@ -278,7 +288,14 @@ export function createEnv< const isServerAccess = (prop: string) => { if (!opts.clientPrefix) return true; - return !prop.startsWith(opts.clientPrefix) && !(prop in _shared); + + const prefixes = Array.isArray(opts.clientPrefix) + ? opts.clientPrefix + : [opts.clientPrefix]; + + return !prefixes.some( + (prefix) => prop.startsWith(prefix) && prop in _shared, + ); }; const isValidServerAccess = (prop: string) => { return isServer || !isServerAccess(prop);