@g14o/env-core 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -88,6 +88,9 @@ export const env = createEnv({
88
88
  When your framework only inlines env vars you reference explicitly, use `runtimeEnvStrict`:
89
89
 
90
90
  ```ts
91
+ import { createEnv } from "@g14o/env-core";
92
+ import * as z from "zod";
93
+
91
94
  export const env = createEnv({
92
95
  server: { DATABASE_URL: z.url() },
93
96
  clientPrefix: "NEXT_PUBLIC_",
@@ -99,6 +102,52 @@ export const env = createEnv({
99
102
  });
100
103
  ```
101
104
 
105
+ ### Overriding default error handler
106
+
107
+ ```ts
108
+ import { createEnv } from "@g14o/env-core";
109
+
110
+ export const env = createEnv({
111
+ // ...
112
+ // Called when schema validation fails.
113
+ onValidationError: (issues) => {
114
+ console.error("Invalid environment variables:", issues);
115
+ throw new Error("Invalid environment variables");
116
+ },
117
+ // Called when a server variable is accessed on the client.
118
+ onInvalidAccess: (variable) => {
119
+ console.error("Invalid access to server variable:", variable);
120
+ throw new Error("Invalid access to server variable");
121
+ },
122
+ });
123
+ ```
124
+
125
+ ### Tell when we're in a server context
126
+
127
+ ```ts
128
+ import { createEnv } from "@g14o/env-core";
129
+
130
+ export const env = createEnv({
131
+ // ...
132
+ // Tell when we're in a server context
133
+ isServer: typeof window === "undefined", // or straight up `true` or `false`
134
+ });
135
+ ```
136
+
137
+ ### Skip validation
138
+
139
+ `skipValidation` bypasses both schema validation and the client-access proxy. Only use it when exposing the picked keys acceptable for the current runtime.
140
+
141
+ ```ts
142
+ import { createEnv } from "@g14o/env-core";
143
+
144
+ export const env = createEnv({
145
+ // ...
146
+ // Tell the library to skip validation and return picked runtime values only
147
+ skipValidation: true,
148
+ });
149
+ ```
150
+
102
151
  ## Options
103
152
 
104
153
  | Option | Description |
@@ -110,7 +159,9 @@ export const env = createEnv({
110
159
  | `runtimeEnvStrict` | Explicit per-key mapping; mutually exclusive with `runtimeEnv` |
111
160
  | `emptyStringAsUndefined` | Treat `""` as `undefined` before validation |
112
161
  | `isServer` | Override server detection (default: `typeof window === "undefined"`) |
113
- | `onInvalidAccess` | Hook before throwing when a server key is read on the client |
162
+ | `onValidationError` | Hook when schema validation fails; may throw custom error, otherwise default `InvalidEnvironmentVariablesError` is thrown |
163
+ | `onInvalidAccess` | Hook when a server key is accessed on the client; may throw custom error, otherwise default server-access error is thrown |
164
+ | `skipValidation` | Bypasses both schema validation and the client-access proxy. Only use it when exposing the picked keys acceptable for the current runtime (default: `false`) |
114
165
 
115
166
  ## Security
116
167
 
@@ -1,5 +1,6 @@
1
1
  //#region src/client-guard.d.ts
2
- type OnInvalidAccessHandler = (variable: string) => void;
2
+ /** Called when a server key is read on the client. May throw a custom error; otherwise the default is thrown. */
3
+ type OnInvalidAccessHandler = (variable: string) => never;
3
4
  //#endregion
4
5
  //#region src/standard-schema.d.ts
5
6
  /** @see https://standardschema.dev */
@@ -33,6 +34,10 @@ declare namespace StandardSchemaV1 {
33
34
  }
34
35
  }
35
36
  //#endregion
37
+ //#region src/validate.d.ts
38
+ /** Called when schema validation fails. May throw a custom error; otherwise the default is thrown. */
39
+ type OnValidationErrorHandler = (issues: readonly StandardSchemaV1.Issue[]) => never;
40
+ //#endregion
36
41
  //#region src/types.d.ts
37
42
  /**
38
43
  * Record mapping environment variable names to
@@ -159,8 +164,9 @@ type CreateEnvOptions<TServer extends SchemaShape | undefined = undefined, TClie
159
164
  client?: TClient; /** Prefix every `client` key must use (enforced at compile time and runtime). */
160
165
  clientPrefix?: TPrefix; /** Treat `""` as `undefined` before validation. Default `false`. */
161
166
  emptyStringAsUndefined?: boolean; /** Override server detection. Default: no `window` on `globalThis`. */
162
- isServer?: boolean; /** Called before throwing when a non-client key is read on the client. */
163
- onInvalidAccess?: OnInvalidAccessHandler; /** @internal Skip schema validation and return picked runtime values only. */
167
+ isServer?: boolean; /** Called when a non-client key is read on the client. May throw a custom error; otherwise the default is thrown. */
168
+ onInvalidAccess?: OnInvalidAccessHandler; /** Called when schema validation fails. May throw a custom error; otherwise the default is thrown. */
169
+ onValidationError?: OnValidationErrorHandler; /** @internal Skip schema validation and return picked runtime values only. */
164
170
  skipValidation?: boolean;
165
171
  } & RuntimeEnvSource<TServer, TClient>;
166
172
  /**
@@ -195,7 +201,8 @@ type CreateEnvOutput<TServer extends SchemaShape | undefined, TClient extends Sc
195
201
  * @param options.runtimeEnvStrict - Explicit per-key mapping; mutually exclusive with `runtimeEnv`.
196
202
  * @param options.emptyStringAsUndefined - Treat `""` as `undefined` before validation. Default `false`.
197
203
  * @param options.isServer - Override server detection. Default: no `window` on `globalThis`.
198
- * @param options.onInvalidAccess - Called before throwing when a non-client key is read on the client.
204
+ * @param options.onInvalidAccess - Called when a non-client key is read on the client. May throw a custom error; otherwise the default is thrown.
205
+ * @param options.onValidationError - Called when schema validation fails. May throw a custom error; otherwise the default is thrown.
199
206
  * @param options.skipValidation - Skip schema validation and return picked runtime values only. Default `false`.
200
207
  * @returns Readonly, frozen env object typed from inferred schema outputs; client reads are guarded by `Proxy`.
201
208
  *
@@ -227,4 +234,4 @@ declare class InvalidEnvironmentVariablesError extends Error {
227
234
  constructor(issues: readonly string[], scope: string);
228
235
  }
229
236
  //#endregion
230
- export { type AssertValidClientPrefix, type CreateEnvOptions, type CreateEnvOutput, type InferSchemaOutput, type InferShapeOutput, type InvalidClientKeys, InvalidEnvironmentVariablesError, type OnInvalidAccessHandler, type PrefixedClientShape, type RuntimeEnvInput, type RuntimeEnvValue, type SchemaShape, type Simplify, type StandardSchemaV1, type StrictRuntimeEnv, createEnv };
237
+ export { type AssertValidClientPrefix, type CreateEnvOptions, type CreateEnvOutput, type InferSchemaOutput, type InferShapeOutput, type InvalidClientKeys, InvalidEnvironmentVariablesError, type OnInvalidAccessHandler, type OnValidationErrorHandler, type PrefixedClientShape, type RuntimeEnvInput, type RuntimeEnvValue, type SchemaShape, type Simplify, type StandardSchemaV1, type StrictRuntimeEnv, createEnv };
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ var e=class extends Error{issues;constructor(e,t){super(`Invalid environment variables (${t}):\n${e.map(e=>` - ${e}`).join(`
2
+ `)}`),this.name=`InvalidEnvironmentVariablesError`,this.issues=e}};function t(e){return`${e.path&&e.path.length>0?`${e.path.map(e=>n(e)).join(`.`)}: `:``}${e.message}`}function n(e){return typeof e==`object`&&e&&`key`in e?String(e.key):String(e)}function r(e){return Error(`Attempted to access server environment variable(s) on the client: ${e}`)}const i=new Set([`__esModule`,`$$typeof`]);function a(e,t){let{isServer:n,clientKeys:a,onInvalidAccess:o}=t,s=e=>{throw o&&o(e),r(e)};return new Proxy(e,{get(e,t,r){return typeof t==`string`?n||i.has(t)||a.has(t)?Reflect.get(e,t,r):s(t):Reflect.get(e,t,r)}})}function o(e,t,n){let r={};for(let i of e){let e=Object.hasOwn(t,i)?t[i]:void 0;n&&e===``&&(e=void 0),r[i]=e}return r}function s(e,t){let n=[];for(let r of e)Object.hasOwn(t,r)||n.push(r);if(n.length>0)throw Error(`runtimeEnvStrict is missing required keys: ${n.join(`, `)}`)}function c(e,t){if(e!==void 0){for(let n of t)if(!n.startsWith(e))throw Error(`Environment variable "${n}" must start with "${e}" to be exposed on the client`)}}function l(e,t){for(let n of e)if(t.includes(n))throw Error(`Environment variable "${n}" cannot be defined in both server and client`)}function u(e,t){return{message:t.message,path:[{key:e},...t.path??[]]}}function d(t,n,r,i){let a=Object.keys(t);if(a.length===0)return{};let o={},s=[],c=[],l=[];for(let e of a){let r=t[e];if(!r)continue;let i=r[`~standard`].validate(n[e]);if(i instanceof Promise){l.push({key:e,promise:i});continue}f(e,i,o,s,c)}if(l.length>0)throw Error(`Async Standard Schema validation is not supported in createEnv (${r}). Use synchronous validators.`);return c.length>0?(t=>{throw i&&i(t),console.error(`❌ Invalid environment variables:`,t),new e(s,r)})(c):o}function f(e,n,r,i,a){if(n.issues){for(let r of n.issues){let n=u(e,r);a.push(n),i.push(`${e}: ${t(r)}`)}return}r[e]=n.value}function p(e){return e===void 0?globalThis.window===void 0:e}function m(e){if(`runtimeEnvStrict`in e&&e.runtimeEnvStrict!==void 0)return e.runtimeEnvStrict;if(`runtimeEnv`in e&&e.runtimeEnv!==void 0)return e.runtimeEnv;throw Error(`createEnv requires either runtimeEnv or runtimeEnvStrict`)}function h(e,t){return{server:e??{},client:t??{}}}function g(e){let{server:t,client:n}=h(e.server,e.client),r=Object.keys(t),i=Object.keys(n),a=[...r,...i];l(r,i),c(e.clientPrefix,i);let o=m(e);return`runtimeEnvStrict`in e&&e.runtimeEnvStrict!==void 0&&s(a,o),{server:t,client:n,clientPrefix:e.clientPrefix,emptyStringAsUndefined:e.emptyStringAsUndefined??!1,isServer:p(e.isServer),onInvalidAccess:e.onInvalidAccess,onValidationError:e.onValidationError,skipValidation:e.skipValidation??!1,runtime:o}}function _(e){let t=g(e),n=Object.keys(t.server),r=Object.keys(t.client),i=[...n,...r],s=new Set(r),c=o(i,t.runtime,t.emptyStringAsUndefined);if(t.skipValidation)return Object.freeze({...c});let l=o(r,t.runtime,t.emptyStringAsUndefined),u=d(t.client,l,`client`,t.onValidationError),f={};if(t.isServer&&n.length>0){let e=o(n,t.runtime,t.emptyStringAsUndefined);f=d(t.server,e,`server`,t.onValidationError)}return a(Object.freeze(t.isServer?{...f,...u}:{...u}),{isServer:t.isServer,clientKeys:s,onInvalidAccess:t.onInvalidAccess})}export{e as InvalidEnvironmentVariablesError,_ as createEnv};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@g14o/env-core",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Framework-agnostic, typesafe environment variables via Standard Schema (Zod, Valibot, ArkType).",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -47,10 +47,21 @@
47
47
  "zod": "^4.0.0",
48
48
  "@workspace/typescript-config": "0.0.0"
49
49
  },
50
+ "main": "./dist/index.mjs",
51
+ "module": "./dist/index.mjs",
52
+ "types": "./dist/index.d.mts",
50
53
  "exports": {
51
54
  ".": {
52
- "types": "./dist/index.d.ts",
53
- "import": "./dist/index.js"
55
+ "dev-source": "./src/index.ts",
56
+ "types": "./dist/index.d.mts",
57
+ "default": "./dist/index.mjs"
58
+ }
59
+ },
60
+ "typesVersions": {
61
+ "*": {
62
+ "*": [
63
+ "./dist/index.d.mts"
64
+ ]
54
65
  }
55
66
  },
56
67
  "publishConfig": {
package/dist/index.js DELETED
@@ -1,2 +0,0 @@
1
- var e=class extends Error{issues;constructor(e,t){super(`Invalid environment variables (${t}):\n${e.map(e=>` - ${e}`).join(`
2
- `)}`),this.name=`InvalidEnvironmentVariablesError`,this.issues=e}};function t(e){return`${e.path&&e.path.length>0?`${e.path.map(e=>n(e)).join(`.`)}: `:``}${e.message}`}function n(e){return typeof e==`object`&&e&&`key`in e?String(e.key):String(e)}function r(e){return Error(`Attempted to access server environment variable(s) on the client: ${e}`)}const i=new Set([`__esModule`,`$$typeof`]);function a(e,t){let{isServer:n,clientKeys:a,onInvalidAccess:o}=t;return new Proxy(e,{get(e,t,s){if(typeof t!=`string`)return Reflect.get(e,t,s);if(!(n||i.has(t)||a.has(t)))throw o?.(t),r(t);return Reflect.get(e,t,s)}})}function o(e,t,n){let r={};for(let i of e){let e=Object.hasOwn(t,i)?t[i]:void 0;n&&e===``&&(e=void 0),r[i]=e}return r}function s(e,t){let n=[];for(let r of e)Object.hasOwn(t,r)||n.push(r);if(n.length>0)throw Error(`runtimeEnvStrict is missing required keys: ${n.join(`, `)}`)}function c(e,t){if(e!==void 0){for(let n of t)if(!n.startsWith(e))throw Error(`Environment variable "${n}" must start with "${e}" to be exposed on the client`)}}function l(e,t){for(let n of e)if(t.includes(n))throw Error(`Environment variable "${n}" cannot be defined in both server and client`)}function u(t,n,r){let i=Object.keys(t);if(i.length===0)return{};let a={},o=[],s=[];for(let e of i){let r=t[e];if(!r)continue;let i=r[`~standard`].validate(n[e]);if(i instanceof Promise){s.push({key:e,promise:i});continue}d(e,i,a,o)}if(s.length>0)throw Error(`Async Standard Schema validation is not supported in createEnv (${r}). Use synchronous validators.`);if(o.length>0)throw new e(o,r);return a}function d(e,n,r,i){if(n.issues){for(let r of n.issues)i.push(`${e}: ${t(r)}`);return}r[e]=n.value}function f(e){return e===void 0?globalThis.window===void 0:e}function p(e){if(`runtimeEnvStrict`in e&&e.runtimeEnvStrict!==void 0)return e.runtimeEnvStrict;if(`runtimeEnv`in e&&e.runtimeEnv!==void 0)return e.runtimeEnv;throw Error(`createEnv requires either runtimeEnv or runtimeEnvStrict`)}function m(e,t){return{server:e??{},client:t??{}}}function h(e){let{server:t,client:n}=m(e.server,e.client),r=Object.keys(t),i=Object.keys(n),a=[...r,...i];l(r,i),c(e.clientPrefix,i);let o=p(e);return`runtimeEnvStrict`in e&&e.runtimeEnvStrict!==void 0&&s(a,o),{server:t,client:n,clientPrefix:e.clientPrefix,emptyStringAsUndefined:e.emptyStringAsUndefined??!1,isServer:f(e.isServer),onInvalidAccess:e.onInvalidAccess,skipValidation:e.skipValidation??!1,runtime:o}}function g(e){let t=h(e),n=Object.keys(t.server),r=Object.keys(t.client),i=[...n,...r],s=new Set(r),c=o(i,t.runtime,t.emptyStringAsUndefined);if(t.skipValidation)return Object.freeze({...c});let l=o(r,t.runtime,t.emptyStringAsUndefined),d=u(t.client,l,`client`),f={};if(t.isServer&&n.length>0){let e=o(n,t.runtime,t.emptyStringAsUndefined);f=u(t.server,e,`server`)}return a(Object.freeze(t.isServer?{...f,...d}:{...d}),{isServer:t.isServer,clientKeys:s,onInvalidAccess:t.onInvalidAccess})}export{e as InvalidEnvironmentVariablesError,g as createEnv};