@tmrp/env 0.1.1 → 0.1.3

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
@@ -3,11 +3,11 @@
3
3
  Type-safe environment variable parsing for TypeScript projects, powered by
4
4
  [Zod](https://zod.dev/).
5
5
 
6
- `@tmrp/env` reads environment variables from the current runtime or an
7
- explicit env-like object, validates them with Zod schemas, and returns a
8
- strongly typed object for application code. It supports Node.js, Deno, Bun,
9
- Cloudflare Workers, Vercel Edge, Netlify, browser-injected config, and
10
- `import.meta.env`-based toolchains.
6
+ `@tmrp/env` reads environment variables from the current runtime or an explicit
7
+ env-like object, validates them with Zod schemas, and returns a strongly typed
8
+ object for application code. It supports Node.js, Deno, Bun, Cloudflare Workers,
9
+ Vercel Edge, Netlify, browser-injected config, and `import.meta.env`-based
10
+ toolchains.
11
11
 
12
12
  ## Features
13
13
 
@@ -278,6 +278,36 @@ const env = createImportMetaEnv(
278
278
  );
279
279
  ```
280
280
 
281
+ ### Client-exposed Variables (`clientPrefix`)
282
+
283
+ When building applications that run code on both the server and client-side
284
+ (e.g. Next.js, Vite, Nuxt), the bundler statically injects environment variables
285
+ that are prefixed with a specific keyword (like `NEXT_PUBLIC_` or `VITE_`).
286
+
287
+ On the client-side, trying to validate server-only environment variables will
288
+ normally throw errors because those variables are not exposed. To prevent this,
289
+ you can pass the `clientPrefix` option:
290
+
291
+ ```ts
292
+ import { createEnv } from "@tmrp/env";
293
+ import z from "zod";
294
+
295
+ const env = createEnv(
296
+ {
297
+ DATABASE_URL: z.string(), // Server-only
298
+ NEXT_PUBLIC_API_URL: z.url(), // Client-exposed
299
+ },
300
+ {
301
+ clientPrefix: "NEXT_PUBLIC_",
302
+ }
303
+ );
304
+ ```
305
+
306
+ When running in a client environment (determined automatically by checking if
307
+ `"window" in globalThis` is false, or overridden by passing `isServer: false`),
308
+ only keys that start with the `clientPrefix` will be read and validated. All
309
+ other keys will bypass validation and return `undefined` on the client.
310
+
281
311
  ## Defining Schemas
282
312
 
283
313
  Pass an object whose keys are environment variable names and whose values are
@@ -366,8 +396,9 @@ env.PORT; // raw string value, or undefined when unavailable
366
396
  env.SECRET_KEY; // raw string value, or undefined when unavailable
367
397
  ```
368
398
 
369
- This option is intended for non-runtime code paths. Do not use it for application
370
- startup if the returned values will be used as validated configuration.
399
+ This option is intended for non-runtime code paths. Do not use it for
400
+ application startup if the returned values will be used as validated
401
+ configuration.
371
402
 
372
403
  ## Error Behavior
373
404
 
@@ -408,6 +439,8 @@ Reads variables from the detected Bun, Node, or Deno runtime and validates them.
408
439
 
409
440
  ```ts
410
441
  type Options = {
442
+ clientPrefix?: string;
443
+ isServer?: boolean;
411
444
  skipValidation?: boolean;
412
445
  };
413
446
 
@@ -1 +1 @@
1
- {"version":3,"file":"create-env-effect.d.ts","sourceRoot":"","sources":["../../src/effects/create-env-effect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAK7D,wBAAgB,eAAe,CAAC,KAAK,CAAC,QAAQ,SAAS,OAAO,EAC5D,OAAO,EAAE,QAAQ,EACjB,aAAa,EAAE,wBAAwB,EACvC,oBAAoB,EAAE,CACpB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,wBAAwB,KAC7B,OAAO,EACZ,OAAO,CAAC,EAAE,OAAO,GAiBH,GAAG,CAAC,QAAQ,CAAC,CAC5B"}
1
+ {"version":3,"file":"create-env-effect.d.ts","sourceRoot":"","sources":["../../src/effects/create-env-effect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAK7D,wBAAgB,eAAe,CAAC,KAAK,CAAC,QAAQ,SAAS,OAAO,EAC5D,OAAO,EAAE,QAAQ,EACjB,aAAa,EAAE,wBAAwB,EACvC,oBAAoB,EAAE,CACpB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,wBAAwB,KAC7B,OAAO,EACZ,OAAO,CAAC,EAAE,OAAO,GA4BH,GAAG,CAAC,QAAQ,CAAC,CAC5B"}
@@ -2,7 +2,16 @@ import { Effect } from "effect";
2
2
  import { envParseValueEffect } from "./env-parse-value-effect.js";
3
3
  import { envReadValueEffect } from "./env-read-value-effect.js";
4
4
  export function createEnvEffect(envKeys, runtimeSchema, runtimeEnvReadEffect, options) {
5
- const env = Effect.runSync(Effect.forEach(Object.entries(envKeys), ([key, schema]) => envReadValueEffect(key, (env) => runtimeEnvReadEffect(env, runtimeSchema), options).pipe(Effect.flatMap((value) => envParseValueEffect(key, schema, value, options)), Effect.map((value) => [key, value]))).pipe(Effect.map((entries) => Object.fromEntries(entries))));
5
+ const isServer = options?.isServer ?? !("window" in globalThis);
6
+ const clientPrefix = options?.clientPrefix;
7
+ const env = Effect.runSync(Effect.forEach(Object.entries(envKeys), ([key, schema]) => {
8
+ if (!isServer &&
9
+ clientPrefix !== undefined &&
10
+ !key.startsWith(clientPrefix)) {
11
+ return Effect.succeed([key, undefined]);
12
+ }
13
+ return envReadValueEffect(key, (env) => runtimeEnvReadEffect(env, runtimeSchema), options).pipe(Effect.flatMap((value) => envParseValueEffect(key, schema, value, options)), Effect.map((value) => [key, value]));
14
+ }).pipe(Effect.map((entries) => Object.fromEntries(entries))));
6
15
  return env;
7
16
  }
8
17
  //# sourceMappingURL=create-env-effect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-env-effect.js","sourceRoot":"","sources":["../../src/effects/create-env-effect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAKhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,MAAM,UAAU,eAAe,CAC7B,OAAiB,EACjB,aAAuC,EACvC,oBAGY,EACZ,OAAiB;IAEjB,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CACxB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE,CACxD,kBAAkB,CAChB,GAAG,EACH,CAAC,GAAG,EAAE,EAAE,CAAC,oBAAoB,CAAC,GAAG,EAAE,aAAa,CAAC,EACjD,OAAO,CACR,CAAC,IAAI,CACJ,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACvB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CACjD,EACD,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,CAAU,CAAC,CAC7C,CACF,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAC7D,CAAC;IAEF,OAAO,GAAoB,CAAC;AAC9B,CAAC"}
1
+ {"version":3,"file":"create-env-effect.js","sourceRoot":"","sources":["../../src/effects/create-env-effect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAKhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,MAAM,UAAU,eAAe,CAC7B,OAAiB,EACjB,aAAuC,EACvC,oBAGY,EACZ,OAAiB;IAEjB,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,CAAC,CAAC,QAAQ,IAAI,UAAU,CAAC,CAAC;IAChE,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,CAAC;IAE3C,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CACxB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE;QACxD,IACE,CAAC,QAAQ;YACT,YAAY,KAAK,SAAS;YAC1B,CAAC,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,EAC7B,CAAC;YACD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,SAAS,CAAU,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,kBAAkB,CACvB,GAAG,EACH,CAAC,GAAG,EAAE,EAAE,CAAC,oBAAoB,CAAC,GAAG,EAAE,aAAa,CAAC,EACjD,OAAO,CACR,CAAC,IAAI,CACJ,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACvB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CACjD,EACD,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,CAAU,CAAC,CAC7C,CAAC;IACJ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAC9D,CAAC;IAEF,OAAO,GAAoB,CAAC;AAC9B,CAAC"}
@@ -5,6 +5,8 @@ export type Env<TEnvKeys extends EnvKeys> = {
5
5
  export type EnvKeys = Record<string, ZodType>;
6
6
  export type EnvRecord = object;
7
7
  export type Options = {
8
+ clientPrefix?: string;
9
+ isServer?: boolean;
8
10
  skipValidation?: boolean;
9
11
  };
10
12
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAEtC,MAAM,MAAM,GAAG,CAAC,QAAQ,SAAS,OAAO,IAAI;KACzC,CAAC,IAAI,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE9C,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B,MAAM,MAAM,OAAO,GAAG;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAEtC,MAAM,MAAM,GAAG,CAAC,QAAQ,SAAS,OAAO,IAAI;KACzC,CAAC,IAAI,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE9C,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B,MAAM,MAAM,OAAO,GAAG;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"create-record-env.d.ts","sourceRoot":"","sources":["../../../src/runtime/record/create-record-env.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAM3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,eAAe,CAAC,KAAK,CAAC,QAAQ,SAAS,OAAO,EAC5D,OAAO,EAAE,QAAQ,EACjB,MAAM,EAAE,SAAS,EACjB,OAAO,CAAC,EAAE,OAAO,GAiBH,GAAG,CAAC,QAAQ,CAAC,CAC5B"}
1
+ {"version":3,"file":"create-record-env.d.ts","sourceRoot":"","sources":["../../../src/runtime/record/create-record-env.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAM3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,eAAe,CAAC,KAAK,CAAC,QAAQ,SAAS,OAAO,EAC5D,OAAO,EAAE,QAAQ,EACjB,MAAM,EAAE,SAAS,EACjB,OAAO,CAAC,EAAE,OAAO,GA4BH,GAAG,CAAC,QAAQ,CAAC,CAC5B"}
@@ -33,7 +33,16 @@ import { readRecordEnv } from "./lib/read-record-env.js";
33
33
  * `options.skipValidation` is enabled.
34
34
  */
35
35
  export function createRecordEnv(envKeys, record, options) {
36
- const env = Effect.runSync(Effect.forEach(Object.entries(envKeys), ([key, schema]) => envReadValueEffect(key, (env) => readRecordEnv(env, record), options).pipe(Effect.flatMap((value) => envParseValueEffect(key, schema, value, options)), Effect.map((value) => [key, value]))).pipe(Effect.map((entries) => Object.fromEntries(entries))));
36
+ const isServer = options?.isServer ?? !("window" in globalThis);
37
+ const clientPrefix = options?.clientPrefix;
38
+ const env = Effect.runSync(Effect.forEach(Object.entries(envKeys), ([key, schema]) => {
39
+ if (!isServer &&
40
+ clientPrefix !== undefined &&
41
+ !key.startsWith(clientPrefix)) {
42
+ return Effect.succeed([key, undefined]);
43
+ }
44
+ return envReadValueEffect(key, (env) => readRecordEnv(env, record), options).pipe(Effect.flatMap((value) => envParseValueEffect(key, schema, value, options)), Effect.map((value) => [key, value]));
45
+ }).pipe(Effect.map((entries) => Object.fromEntries(entries))));
37
46
  return env;
38
47
  }
39
48
  //# sourceMappingURL=create-record-env.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-record-env.js","sourceRoot":"","sources":["../../../src/runtime/record/create-record-env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAiB,EACjB,MAAiB,EACjB,OAAiB;IAEjB,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CACxB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE,CACxD,kBAAkB,CAChB,GAAG,EACH,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,EACnC,OAAO,CACR,CAAC,IAAI,CACJ,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACvB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CACjD,EACD,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,CAAU,CAAC,CAC7C,CACF,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAC7D,CAAC;IAEF,OAAO,GAAoB,CAAC;AAC9B,CAAC"}
1
+ {"version":3,"file":"create-record-env.js","sourceRoot":"","sources":["../../../src/runtime/record/create-record-env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAiB,EACjB,MAAiB,EACjB,OAAiB;IAEjB,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,CAAC,CAAC,QAAQ,IAAI,UAAU,CAAC,CAAC;IAChE,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,CAAC;IAE3C,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CACxB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE;QACxD,IACE,CAAC,QAAQ;YACT,YAAY,KAAK,SAAS;YAC1B,CAAC,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,EAC7B,CAAC;YACD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,SAAS,CAAU,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,kBAAkB,CACvB,GAAG,EACH,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,EACnC,OAAO,CACR,CAAC,IAAI,CACJ,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACvB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CACjD,EACD,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,CAAU,CAAC,CAC7C,CAAC;IACJ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAC9D,CAAC;IAEF,OAAO,GAAoB,CAAC;AAC9B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmrp/env",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Type-safe environment variable parsing, powered by Zod.",
5
5
  "keywords": [
6
6
  "env",