@t3-oss/env-core
Advanced tools
Comparing version 0.11.1 to 0.12.0-canary.1
@@ -1,2 +0,71 @@ | ||
import { ZodType, ZodError, TypeOf, ZodObject } from 'zod'; | ||
/** The Standard Schema interface. */ | ||
interface StandardSchemaV1<Input = unknown, Output = Input> { | ||
/** The Standard Schema properties. */ | ||
readonly "~standard": StandardSchemaV1.Props<Input, Output>; | ||
} | ||
declare namespace StandardSchemaV1 { | ||
/** The Standard Schema properties interface. */ | ||
interface Props<Input = unknown, Output = Input> { | ||
/** The version number of the standard. */ | ||
readonly version: 1; | ||
/** The vendor name of the schema library. */ | ||
readonly vendor: string; | ||
/** Validates unknown input values. */ | ||
readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>; | ||
/** Inferred types associated with the schema. */ | ||
readonly types?: Types<Input, Output> | undefined; | ||
} | ||
/** The result interface of the validate function. */ | ||
type Result<Output> = SuccessResult<Output> | FailureResult; | ||
/** The result interface if validation succeeds. */ | ||
interface SuccessResult<Output> { | ||
/** The typed output value. */ | ||
readonly value: Output; | ||
/** The non-existent issues. */ | ||
readonly issues?: undefined; | ||
} | ||
/** The result interface if validation fails. */ | ||
interface FailureResult { | ||
/** The issues of failed validation. */ | ||
readonly issues: ReadonlyArray<Issue>; | ||
} | ||
/** The issue interface of the failure output. */ | ||
interface Issue { | ||
/** The error message of the issue. */ | ||
readonly message: string; | ||
/** The path of the issue, if any. */ | ||
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined; | ||
} | ||
/** The path segment interface of the issue. */ | ||
interface PathSegment { | ||
/** The key representing a path segment. */ | ||
readonly key: PropertyKey; | ||
} | ||
/** The Standard Schema types interface. */ | ||
interface Types<Input = unknown, Output = Input> { | ||
/** The input type of the schema. */ | ||
readonly input: Input; | ||
/** The output type of the schema. */ | ||
readonly output: Output; | ||
} | ||
/** Infers the input type of a Standard Schema. */ | ||
type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["input"]; | ||
/** Infers the output type of a Standard Schema. */ | ||
type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["output"]; | ||
} | ||
type StandardSchemaDictionary = Record<string, StandardSchemaV1>; | ||
declare namespace StandardSchemaDictionary { | ||
/** | ||
* A dictionary of Standard Schemas that match the input and output types. | ||
*/ | ||
type Matching<Input, Output extends Record<keyof Input, unknown> = Input> = { | ||
[K in keyof Input]-?: StandardSchemaV1<Input[K], Output[K]>; | ||
}; | ||
type InferInput<T extends StandardSchemaDictionary> = { | ||
[K in keyof T]: StandardSchemaV1.InferInput<T[K]>; | ||
}; | ||
type InferOutput<T extends StandardSchemaDictionary> = { | ||
[K in keyof T]: StandardSchemaV1.InferOutput<T[K]>; | ||
}; | ||
} | ||
@@ -10,3 +79,3 @@ type ErrorMessage<T extends string> = T; | ||
type Reduce<TArr extends Array<Record<string, unknown>>, TAcc = {}> = TArr extends [] ? TAcc : TArr extends [infer Head, ...infer Tail] ? Tail extends Array<Record<string, unknown>> ? Head & Reduce<Tail, TAcc> : never : never; | ||
interface BaseOptions<TShared extends Record<string, ZodType>, TExtends extends Array<Record<string, unknown>>> { | ||
interface BaseOptions<TShared extends Record<string, StandardSchemaV1>, TExtends extends Array<Record<string, unknown>>> { | ||
/** | ||
@@ -30,3 +99,3 @@ * How to determine whether the app is running on the server or the client. | ||
*/ | ||
onValidationError?: (error: ZodError) => never; | ||
onValidationError?: (issues: readonly StandardSchemaV1.Issue[]) => never; | ||
/** | ||
@@ -57,3 +126,3 @@ * Called when a server-side environment variable is accessed on the client. | ||
} | ||
interface LooseOptions<TShared extends Record<string, ZodType>, TExtends extends Array<Record<string, unknown>>> extends BaseOptions<TShared, TExtends> { | ||
interface LooseOptions<TShared extends Record<string, StandardSchemaV1>, TExtends extends Array<Record<string, unknown>>> extends BaseOptions<TShared, TExtends> { | ||
runtimeEnvStrict?: never; | ||
@@ -66,3 +135,3 @@ /** | ||
} | ||
interface StrictOptions<TPrefix extends string | undefined, TServer extends Record<string, ZodType>, TClient extends Record<string, ZodType>, TShared extends Record<string, ZodType>, TExtends extends Array<Record<string, unknown>>> extends BaseOptions<TShared, TExtends> { | ||
interface StrictOptions<TPrefix extends string | undefined, TServer extends Record<string, StandardSchemaV1>, TClient extends Record<string, StandardSchemaV1>, TShared extends Record<string, StandardSchemaV1>, TExtends extends Array<Record<string, unknown>>> extends BaseOptions<TShared, TExtends> { | ||
/** | ||
@@ -81,3 +150,3 @@ * Runtime Environment variables to use for validation - `process.env`, `import.meta.env` or similar. | ||
} | ||
interface ClientOptions<TPrefix extends string | undefined, TClient extends Record<string, ZodType>> { | ||
interface ClientOptions<TPrefix extends string | undefined, TClient extends Record<string, StandardSchemaV1>> { | ||
/** | ||
@@ -96,3 +165,3 @@ * The prefix that client-side variables must have. This is enforced both at | ||
} | ||
interface ServerOptions<TPrefix extends string | undefined, TServer extends Record<string, ZodType>> { | ||
interface ServerOptions<TPrefix extends string | undefined, TServer extends Record<string, StandardSchemaV1>> { | ||
/** | ||
@@ -106,12 +175,12 @@ * Specify your server-side environment variables schema here. This way you can ensure the app isn't | ||
} | ||
type ServerClientOptions<TPrefix extends string | undefined, TServer extends Record<string, ZodType>, TClient extends Record<string, ZodType>> = (ClientOptions<TPrefix, TClient> & ServerOptions<TPrefix, TServer>) | (ServerOptions<TPrefix, TServer> & Impossible<ClientOptions<never, never>>) | (ClientOptions<TPrefix, TClient> & Impossible<ServerOptions<never, never>>); | ||
type EnvOptions<TPrefix extends string | undefined, TServer extends Record<string, ZodType>, TClient extends Record<string, ZodType>, TShared extends Record<string, ZodType>, TExtends extends Array<Record<string, unknown>>> = (LooseOptions<TShared, TExtends> & ServerClientOptions<TPrefix, TServer, TClient>) | (StrictOptions<TPrefix, TServer, TClient, TShared, TExtends> & ServerClientOptions<TPrefix, TServer, TClient>); | ||
type ServerClientOptions<TPrefix extends string | undefined, TServer extends Record<string, StandardSchemaV1>, TClient extends Record<string, StandardSchemaV1>> = (ClientOptions<TPrefix, TClient> & ServerOptions<TPrefix, TServer>) | (ServerOptions<TPrefix, TServer> & Impossible<ClientOptions<never, never>>) | (ClientOptions<TPrefix, TClient> & Impossible<ServerOptions<never, never>>); | ||
type EnvOptions<TPrefix extends string | undefined, TServer extends Record<string, StandardSchemaV1>, TClient extends Record<string, StandardSchemaV1>, TShared extends Record<string, StandardSchemaV1>, TExtends extends Array<Record<string, unknown>>> = (LooseOptions<TShared, TExtends> & ServerClientOptions<TPrefix, TServer, TClient>) | (StrictOptions<TPrefix, TServer, TClient, TShared, TExtends> & ServerClientOptions<TPrefix, TServer, TClient>); | ||
type TPrefixFormat = string | undefined; | ||
type TServerFormat = Record<string, ZodType>; | ||
type TClientFormat = Record<string, ZodType>; | ||
type TSharedFormat = Record<string, ZodType>; | ||
type TServerFormat = Record<string, StandardSchemaV1>; | ||
type TClientFormat = Record<string, StandardSchemaV1>; | ||
type TSharedFormat = Record<string, StandardSchemaV1>; | ||
type TExtendsFormat = Array<Record<string, unknown>>; | ||
type CreateEnv<TServer extends TServerFormat, TClient extends TClientFormat, TShared extends TSharedFormat, TExtends extends TExtendsFormat> = Readonly<Simplify<TypeOf<ZodObject<TServer>> & TypeOf<ZodObject<TClient>> & TypeOf<ZodObject<TShared>> & UnReadonlyObject<Reduce<TExtends>>>>; | ||
type CreateEnv<TServer extends TServerFormat, TClient extends TClientFormat, TShared extends TSharedFormat, TExtends extends TExtendsFormat> = Readonly<Simplify<StandardSchemaDictionary.InferOutput<TServer> & StandardSchemaDictionary.InferOutput<TClient> & StandardSchemaDictionary.InferOutput<TShared> & UnReadonlyObject<Reduce<TExtends>>>>; | ||
declare function createEnv<TPrefix extends TPrefixFormat, TServer extends TServerFormat = NonNullable<unknown>, TClient extends TClientFormat = NonNullable<unknown>, TShared extends TSharedFormat = NonNullable<unknown>, const TExtends extends TExtendsFormat = []>(opts: EnvOptions<TPrefix, TServer, TClient, TShared, TExtends>): CreateEnv<TServer, TClient, TShared, TExtends>; | ||
export { type BaseOptions, type ClientOptions, type CreateEnv, type EnvOptions, type ErrorMessage, type LooseOptions, type ServerClientOptions, type ServerOptions, type Simplify, type StrictOptions, createEnv }; | ||
export { type BaseOptions, type ClientOptions, type CreateEnv, type EnvOptions, type ErrorMessage, type LooseOptions, type ServerClientOptions, type ServerOptions, type Simplify, StandardSchemaDictionary, StandardSchemaV1, type StrictOptions, createEnv }; |
@@ -1,2 +0,32 @@ | ||
import { object } from 'zod'; | ||
/** The Standard Schema interface. */ function parseWithDictionary(dictionary, value) { | ||
const result = {}; | ||
const issues = []; | ||
for(const key in dictionary){ | ||
const schema = dictionary[key]; | ||
const prop = value[key]; | ||
const propResult = schema["~standard"].validate(prop); | ||
if (propResult instanceof Promise) { | ||
throw new Error(`Validation must be synchronous, but ${key} returned a Promise.`); | ||
} | ||
if (propResult.issues) { | ||
issues.push(...propResult.issues.map((issue)=>({ | ||
...issue, | ||
path: [ | ||
key, | ||
...issue.path ?? [] | ||
] | ||
}))); | ||
continue; | ||
} | ||
result[key] = propResult.value; | ||
} | ||
if (issues.length) { | ||
return { | ||
issues | ||
}; | ||
} | ||
return { | ||
value: result | ||
}; | ||
} | ||
@@ -19,23 +49,25 @@ function createEnv(opts) { | ||
const _shared = typeof opts.shared === "object" ? opts.shared : {}; | ||
const client = object(_client); | ||
const server = object(_server); | ||
const shared = object(_shared); | ||
const isServer = opts.isServer ?? (typeof window === "undefined" || "Deno" in window); | ||
const allClient = client.merge(shared); | ||
const allServer = server.merge(shared).merge(client); | ||
const parsed = isServer ? allServer.safeParse(runtimeEnv) // on server we can validate all env vars | ||
: allClient.safeParse(runtimeEnv); // on client we can only validate the ones that are exposed | ||
const onValidationError = opts.onValidationError ?? ((error)=>{ | ||
console.error("❌ Invalid environment variables:", error.flatten().fieldErrors); | ||
const finalSchema = isServer ? { | ||
..._server, | ||
..._shared, | ||
..._client | ||
} : { | ||
..._client, | ||
..._shared | ||
}; | ||
const parsed = parseWithDictionary(finalSchema, runtimeEnv); | ||
const onValidationError = opts.onValidationError ?? ((issues)=>{ | ||
console.error("❌ Invalid environment variables:", issues); | ||
throw new Error("Invalid environment variables"); | ||
}); | ||
const onInvalidAccess = opts.onInvalidAccess ?? ((_variable)=>{ | ||
const onInvalidAccess = opts.onInvalidAccess ?? (()=>{ | ||
throw new Error("❌ Attempted to access a server-side environment variable on the client"); | ||
}); | ||
if (parsed.success === false) { | ||
return onValidationError(parsed.error); | ||
if (parsed.issues) { | ||
return onValidationError(parsed.issues); | ||
} | ||
const isServerAccess = (prop)=>{ | ||
if (!opts.clientPrefix) return true; | ||
return !prop.startsWith(opts.clientPrefix) && !(prop in shared.shape); | ||
return !prop.startsWith(opts.clientPrefix) && !(prop in _shared); | ||
}; | ||
@@ -51,3 +83,3 @@ const isValidServerAccess = (prop)=>{ | ||
}, {}); | ||
const fullObj = Object.assign(parsed.data, extendedObj); | ||
const fullObj = Object.assign(parsed.value, extendedObj); | ||
const env = new Proxy(fullObj, { | ||
@@ -54,0 +86,0 @@ get (target, prop) { |
{ | ||
"name": "@t3-oss/env-core", | ||
"version": "0.11.1", | ||
"version": "0.12.0-canary.1", | ||
"type": "module", | ||
@@ -19,5 +19,9 @@ "keywords": ["create-t3-app", "environment variables", "zod"], | ||
}, | ||
"./presets": { | ||
"types": "./dist/presets.d.ts", | ||
"default": "./dist/presets.js" | ||
"./presets-zod": { | ||
"types": "./dist/presets-zod.d.ts", | ||
"default": "./dist/presets-zod.js" | ||
}, | ||
"./presets-valibot": { | ||
"types": "./dist/presets-valibot.d.ts", | ||
"default": "./dist/presets-valibot.js" | ||
} | ||
@@ -33,3 +37,4 @@ }, | ||
"typescript": ">=5.0.0", | ||
"zod": "^3.0.0" | ||
"zod": "^3.24.0", | ||
"valibot": "^1.0.0-beta.7 || ^1.0.0" | ||
}, | ||
@@ -39,9 +44,16 @@ "peerDependenciesMeta": { | ||
"optional": true | ||
}, | ||
"zod": { | ||
"optional": true | ||
}, | ||
"valibot": { | ||
"optional": true | ||
} | ||
}, | ||
"devDependencies": { | ||
"bunchee": "^5.1.2", | ||
"typescript": "^5.4.5", | ||
"zod": "^3.23.0" | ||
"bunchee": "^6.3.2", | ||
"typescript": "^5.7.3", | ||
"zod": "^3.24.1", | ||
"valibot": "^1.0.0-beta.14" | ||
} | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
40611
7
909
3
4
16
1