@vite-env/core 0.2.2 → 0.3.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.
Files changed (45) hide show
  1. package/README.md +3 -1
  2. package/dist/config.cjs +1 -1
  3. package/dist/config.d.cts +1 -1
  4. package/dist/config.d.mts +1 -1
  5. package/dist/dts-BgHTl6hC.cjs +114 -0
  6. package/dist/dts-BgHTl6hC.cjs.map +1 -0
  7. package/dist/dts.cjs +2 -1
  8. package/dist/dts.d.cts +10 -5
  9. package/dist/dts.d.mts +10 -5
  10. package/dist/dts.mjs +31 -23
  11. package/dist/dts.mjs.map +1 -1
  12. package/dist/format.cjs +6 -0
  13. package/dist/format.cjs.map +1 -1
  14. package/dist/format.d.cts +3 -1
  15. package/dist/format.d.mts +3 -1
  16. package/dist/format.mjs +6 -1
  17. package/dist/format.mjs.map +1 -1
  18. package/dist/index.cjs +2 -8
  19. package/dist/index.d.cts +3 -3
  20. package/dist/index.d.mts +3 -3
  21. package/dist/index.mjs +2 -2
  22. package/dist/leak.cjs.map +1 -1
  23. package/dist/leak.d.cts +2 -2
  24. package/dist/leak.d.mts +2 -2
  25. package/dist/leak.mjs.map +1 -1
  26. package/dist/plugin.cjs +30 -13
  27. package/dist/plugin.cjs.map +1 -1
  28. package/dist/plugin.mjs +31 -14
  29. package/dist/plugin.mjs.map +1 -1
  30. package/dist/schema.cjs +1 -1
  31. package/dist/schema.d.cts +1 -1
  32. package/dist/schema.d.mts +1 -1
  33. package/dist/standard.cjs +46 -0
  34. package/dist/standard.cjs.map +1 -0
  35. package/dist/standard.d.cts +11 -0
  36. package/dist/standard.d.mts +11 -0
  37. package/dist/standard.mjs +43 -0
  38. package/dist/standard.mjs.map +1 -0
  39. package/dist/types-1okexcwM.d.cts +43 -0
  40. package/dist/types-DuWT_251.d.mts +43 -0
  41. package/package.json +19 -2
  42. package/dist/dts-DF71HNdJ.cjs +0 -100
  43. package/dist/dts-DF71HNdJ.cjs.map +0 -1
  44. package/dist/types-CluiDKAQ.d.mts +0 -22
  45. package/dist/types-DqTMuWwc.d.cts +0 -22
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs","names":["process","path","loadEnvConfig","validateEnv","formatZodError","generateDts","detectServerLeak"],"sources":["../src/sources.ts","../src/virtual.ts","../src/plugin.ts"],"sourcesContent":["// @env node\nimport type { ResolvedConfig } from 'vite'\nimport process from 'node:process'\nimport { loadEnv } from 'vite'\n\n/**\n * Merge priority (highest → lowest):\n * 1. process.env (CI pipeline secrets win)\n * 2. .env.[mode].local\n * 3. .env.[mode]\n * 4. .env.local\n * 5. .env\n *\n * Prefix '' = load everything, schema decides what's valid.\n */\nexport async function loadEnvSources(\n config: ResolvedConfig,\n): Promise<Record<string, string>> {\n const fileEnv = loadEnv(\n config.mode,\n config.envDir || config.root,\n '', // no prefix filter — schema is the filter\n )\n\n return {\n ...fileEnv,\n ...filterStrings(process.env),\n }\n}\n\nfunction filterStrings(env: NodeJS.ProcessEnv): Record<string, string> {\n return Object.fromEntries(\n Object.entries(env).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n ),\n )\n}\n","import type { EnvDefinition } from './types'\n\nexport function buildClientModule(\n def: EnvDefinition,\n data: Record<string, unknown>,\n): { code: string, moduleType: 'js' } {\n const clientKeys = new Set(Object.keys(def.client ?? {}))\n\n const clientData = Object.fromEntries(\n Object.entries(data).filter(([k]) => clientKeys.has(k)),\n )\n\n return {\n moduleType: 'js', // Required: Vite 8 / Rolldown explicit moduleType\n code: `// Auto-generated by @vite-env/core — do not edit\nexport const env = Object.freeze(${JSON.stringify(clientData, null, 2)});\nexport default env;`,\n }\n}\n\nexport function buildServerModule(\n _def: EnvDefinition,\n data: Record<string, unknown>,\n): { code: string, moduleType: 'js' } {\n return {\n moduleType: 'js',\n code: `// Auto-generated by @vite-env/core — do not edit\nexport const env = Object.freeze(${JSON.stringify(data, null, 2)});\nexport default env;`,\n }\n}\n","// @env node\nimport type { Plugin, ResolvedConfig } from 'vite'\nimport type { EnvDefinition } from './types'\nimport path from 'node:path'\nimport { loadEnvConfig } from './config'\nimport { generateDts } from './dts'\nimport { formatZodError } from './format'\nimport { detectServerLeak } from './leak'\nimport { validateEnv } from './schema'\nimport { loadEnvSources } from './sources'\nimport { buildClientModule, buildServerModule } from './virtual'\n\nexport interface ViteEnvOptions {\n /**\n * Path to env definition file.\n * @default './env.ts' (resolved from project root)\n */\n configFile?: string\n}\n\nexport default function ViteEnv(options: ViteEnvOptions = {}): Plugin {\n let resolvedConfig: ResolvedConfig\n let envDefinition: EnvDefinition\n let lastValidated: Record<string, unknown> = {}\n\n return {\n name: 'vite-env',\n enforce: 'pre',\n\n async configResolved(config) {\n resolvedConfig = config\n\n const configPath = path.resolve(\n config.root,\n options.configFile ?? 'env.ts',\n )\n\n try {\n envDefinition = await loadEnvConfig(configPath)\n }\n catch (e) {\n throw new Error(\n `[vite-env] Could not load env definition file at: ${configPath}\\n`\n + ` Create an env.ts file and export default defineEnv({ ... })`,\n { cause: e },\n )\n }\n },\n\n async buildStart() {\n const rawEnv = await loadEnvSources(resolvedConfig)\n const result = validateEnv(envDefinition, rawEnv)\n\n if (!result.success) {\n const formatted = formatZodError(result.errors)\n throw new Error(\n `[vite-env] Environment validation failed:\\n\\n${formatted}`,\n )\n }\n\n lastValidated = result.data\n\n await generateDts(envDefinition, resolvedConfig.root)\n\n const count = Object.keys(result.data).length\n resolvedConfig.logger.info(\n ` \\x1B[32m✓\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m ${count} variables validated`,\n )\n },\n\n resolveId(id) {\n if (id === 'virtual:env/client')\n return '\\0virtual:env/client'\n if (id === 'virtual:env/server')\n return '\\0virtual:env/server'\n },\n\n load(id) {\n if (id === '\\0virtual:env/client')\n return buildClientModule(envDefinition, lastValidated)\n if (id === '\\0virtual:env/server')\n return buildServerModule(envDefinition, lastValidated)\n },\n\n generateBundle(_options, bundle) {\n if (resolvedConfig.build.ssr)\n return\n\n const leaks = detectServerLeak(\n envDefinition,\n lastValidated,\n bundle as Record<string, { type: string, code?: string }>,\n (keys) => {\n resolvedConfig.logger.warn(\n ` \\x1B[33m⚠\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Leak detection skipped ${keys.length} server variable(s) with values shorter than 8 chars: ${keys.join(', ')}`,\n )\n },\n )\n\n if (leaks.length > 0) {\n const details = leaks.map(l => ` ✗ ${l.key} found in ${l.chunk}`).join('\\n')\n throw new Error(\n `[vite-env] Server environment variables detected in client bundle!\\n\\n${details}\\n\\n These variables are marked as server-only and must never reach the browser.`,\n )\n }\n },\n\n configureServer(server) {\n const envDir = resolvedConfig.envDir || resolvedConfig.root\n server.watcher.add(path.join(envDir, '.env*'))\n\n let debounceTimer: ReturnType<typeof setTimeout>\n\n server.watcher.on('change', async (file) => {\n if (!path.basename(file).startsWith('.env'))\n return\n\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(async () => {\n try {\n const rawEnv = await loadEnvSources(resolvedConfig)\n const result = validateEnv(envDefinition, rawEnv)\n\n if (!result.success) {\n const formatted = formatZodError(result.errors)\n resolvedConfig.logger.warn(\n `\\n \\x1B[33m⚠\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Env revalidation failed:\\n${formatted}`,\n )\n return\n }\n\n lastValidated = result.data\n\n const clientMod = server.moduleGraph.getModuleById('\\0virtual:env/client')\n const serverMod = server.moduleGraph.getModuleById('\\0virtual:env/server')\n if (clientMod)\n server.moduleGraph.invalidateModule(clientMod)\n if (serverMod)\n server.moduleGraph.invalidateModule(serverMod)\n if (clientMod || serverMod) {\n server.hot.send({ type: 'full-reload' })\n resolvedConfig.logger.info(\n ` \\x1B[32m✓\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Env revalidated`,\n )\n }\n }\n catch (e) {\n resolvedConfig.logger.error(\n `\\n \\x1B[31m✗\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Failed to reload env files: ${e instanceof Error ? e.message : String(e)}`,\n )\n }\n }, 150) // 150ms debounce\n })\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.cjs","names":["process","isStandardEnvDefinition","validateStandardEnv","formatStandardSchemaError","path","loadEnvConfig","generateStandardDts","detectServerLeak"],"sources":["../src/sources.ts","../src/virtual.ts","../src/plugin.ts"],"sourcesContent":["// @env node\nimport type { ResolvedConfig } from 'vite'\nimport process from 'node:process'\nimport { loadEnv } from 'vite'\n\n/**\n * Merge priority (highest → lowest):\n * 1. process.env (CI pipeline secrets win)\n * 2. .env.[mode].local\n * 3. .env.[mode]\n * 4. .env.local\n * 5. .env\n *\n * Prefix '' = load everything, schema decides what's valid.\n */\nexport async function loadEnvSources(\n config: ResolvedConfig,\n): Promise<Record<string, string>> {\n const fileEnv = loadEnv(\n config.mode,\n config.envDir || config.root,\n '', // no prefix filter — schema is the filter\n )\n\n return {\n ...fileEnv,\n ...filterStrings(process.env),\n }\n}\n\nfunction filterStrings(env: NodeJS.ProcessEnv): Record<string, string> {\n return Object.fromEntries(\n Object.entries(env).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n ),\n )\n}\n","import type { AnyEnvDefinition } from './types'\n\nexport function buildClientModule(\n def: AnyEnvDefinition,\n data: Record<string, unknown>,\n): { code: string, moduleType: 'js' } {\n const clientKeys = new Set(Object.keys(def.client ?? {}))\n\n const clientData = Object.fromEntries(\n Object.entries(data).filter(([k]) => clientKeys.has(k)),\n )\n\n return {\n moduleType: 'js', // Required: Vite 8 / Rolldown explicit moduleType\n code: `// Auto-generated by @vite-env/core — do not edit\nexport const env = Object.freeze(${JSON.stringify(clientData, null, 2)});\nexport default env;`,\n }\n}\n\nexport function buildServerModule(\n _def: AnyEnvDefinition,\n data: Record<string, unknown>,\n): { code: string, moduleType: 'js' } {\n return {\n moduleType: 'js',\n code: `// Auto-generated by @vite-env/core — do not edit\nexport const env = Object.freeze(${JSON.stringify(data, null, 2)});\nexport default env;`,\n }\n}\n","// @env node\nimport type { Plugin, ResolvedConfig } from 'vite'\nimport type { AnyEnvDefinition } from './types'\nimport path from 'node:path'\nimport { loadEnvConfig } from './config'\nimport { generateStandardDts } from './dts'\nimport { formatStandardSchemaError } from './format'\nimport { detectServerLeak } from './leak'\nimport { loadEnvSources } from './sources'\nimport { isStandardEnvDefinition, validateStandardEnv } from './standard'\nimport { buildClientModule, buildServerModule } from './virtual'\n\nexport interface ViteEnvOptions {\n /**\n * Path to env definition file.\n * @default './env.ts' (resolved from project root)\n */\n configFile?: string\n}\n\n/**\n * Validates environment variables against the definition.\n * Routes to Zod or Standard Schema path based on definition type.\n * Zod modules are loaded dynamically to avoid requiring zod for Standard Schema users.\n */\nasync function validateAndFormat(\n def: AnyEnvDefinition,\n rawEnv: Record<string, string>,\n): Promise<{ data: Record<string, unknown> } | { error: string }> {\n if (isStandardEnvDefinition(def)) {\n const result = await validateStandardEnv(def, rawEnv)\n if (!result.success) {\n return { error: formatStandardSchemaError(result.errors) }\n }\n return { data: result.data }\n }\n\n const { validateEnv } = await import('./schema')\n const { formatZodError } = await import('./format')\n const result = validateEnv(def, rawEnv)\n if (!result.success) {\n return { error: formatZodError(result.errors) }\n }\n return { data: result.data }\n}\n\nexport default function ViteEnv(options: ViteEnvOptions = {}): Plugin {\n let resolvedConfig: ResolvedConfig\n let envDefinition: AnyEnvDefinition\n let lastValidated: Record<string, unknown> = {}\n\n return {\n name: 'vite-env',\n enforce: 'pre',\n\n async configResolved(config) {\n resolvedConfig = config\n\n const configPath = path.resolve(\n config.root,\n options.configFile ?? 'env.ts',\n )\n\n try {\n envDefinition = await loadEnvConfig(configPath)\n }\n catch (e) {\n throw new Error(\n `[vite-env] Could not load env definition file at: ${configPath}\\n`\n + ` Create an env.ts file and export default defineEnv({ ... })`,\n { cause: e },\n )\n }\n },\n\n async buildStart() {\n const rawEnv = await loadEnvSources(resolvedConfig)\n const result = await validateAndFormat(envDefinition, rawEnv)\n\n if ('error' in result) {\n throw new Error(\n `[vite-env] Environment validation failed:\\n\\n${result.error}`,\n )\n }\n\n lastValidated = result.data\n\n if (isStandardEnvDefinition(envDefinition)) {\n await generateStandardDts(envDefinition, resolvedConfig.root)\n }\n else {\n const { generateDts } = await import('./dts')\n await generateDts(envDefinition, resolvedConfig.root)\n }\n\n const count = Object.keys(lastValidated).length\n resolvedConfig.logger.info(\n ` \\x1B[32m✓\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m ${count} variables validated`,\n )\n },\n\n resolveId(id) {\n if (id === 'virtual:env/client')\n return '\\0virtual:env/client'\n if (id === 'virtual:env/server')\n return '\\0virtual:env/server'\n },\n\n load(id) {\n if (id === '\\0virtual:env/client')\n return buildClientModule(envDefinition, lastValidated)\n if (id === '\\0virtual:env/server')\n return buildServerModule(envDefinition, lastValidated)\n },\n\n generateBundle(_options, bundle) {\n if (resolvedConfig.build.ssr)\n return\n\n const leaks = detectServerLeak(\n envDefinition,\n lastValidated,\n bundle as Record<string, { type: string, code?: string }>,\n (keys) => {\n resolvedConfig.logger.warn(\n ` \\x1B[33m⚠\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Leak detection skipped ${keys.length} server variable(s) with values shorter than 8 chars: ${keys.join(', ')}`,\n )\n },\n )\n\n if (leaks.length > 0) {\n const details = leaks.map(l => ` ✗ ${l.key} found in ${l.chunk}`).join('\\n')\n throw new Error(\n `[vite-env] Server environment variables detected in client bundle!\\n\\n${details}\\n\\n These variables are marked as server-only and must never reach the browser.`,\n )\n }\n },\n\n configureServer(server) {\n const envDir = resolvedConfig.envDir || resolvedConfig.root\n server.watcher.add(path.join(envDir, '.env*'))\n\n let debounceTimer: ReturnType<typeof setTimeout>\n\n server.watcher.on('change', async (file) => {\n if (!path.basename(file).startsWith('.env'))\n return\n\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(async () => {\n try {\n const rawEnv = await loadEnvSources(resolvedConfig)\n const result = await validateAndFormat(envDefinition, rawEnv)\n\n if ('error' in result) {\n resolvedConfig.logger.warn(\n `\\n \\x1B[33m⚠\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Env revalidation failed:\\n${result.error}`,\n )\n return\n }\n\n lastValidated = result.data\n\n const clientMod = server.moduleGraph.getModuleById('\\0virtual:env/client')\n const serverMod = server.moduleGraph.getModuleById('\\0virtual:env/server')\n if (clientMod)\n server.moduleGraph.invalidateModule(clientMod)\n if (serverMod)\n server.moduleGraph.invalidateModule(serverMod)\n if (clientMod || serverMod) {\n server.hot.send({ type: 'full-reload' })\n resolvedConfig.logger.info(\n ` \\x1B[32m✓\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Env revalidated`,\n )\n }\n }\n catch (e) {\n resolvedConfig.logger.error(\n `\\n \\x1B[31m✗\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Failed to reload env files: ${e instanceof Error ? e.message : String(e)}`,\n )\n }\n }, 150) // 150ms debounce\n })\n },\n }\n}\n"],"mappings":"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"}
package/dist/plugin.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { validateEnv } from "./schema.mjs";
1
+ import { isStandardEnvDefinition, validateStandardEnv } from "./standard.mjs";
2
2
  import { loadEnvConfig } from "./config.mjs";
3
- import { generateDts } from "./dts.mjs";
4
- import { formatZodError } from "./format.mjs";
3
+ import { generateStandardDts } from "./dts.mjs";
4
+ import { formatStandardSchemaError } from "./format.mjs";
5
5
  import { detectServerLeak } from "./leak.mjs";
6
6
  import path from "node:path";
7
7
  import process from "node:process";
@@ -48,6 +48,23 @@ export default env;`
48
48
  }
49
49
  //#endregion
50
50
  //#region src/plugin.ts
51
+ /**
52
+ * Validates environment variables against the definition.
53
+ * Routes to Zod or Standard Schema path based on definition type.
54
+ * Zod modules are loaded dynamically to avoid requiring zod for Standard Schema users.
55
+ */
56
+ async function validateAndFormat(def, rawEnv) {
57
+ if (isStandardEnvDefinition(def)) {
58
+ const result = await validateStandardEnv(def, rawEnv);
59
+ if (!result.success) return { error: formatStandardSchemaError(result.errors) };
60
+ return { data: result.data };
61
+ }
62
+ const { validateEnv } = await import("./schema.mjs");
63
+ const { formatZodError } = await import("./format.mjs");
64
+ const result = validateEnv(def, rawEnv);
65
+ if (!result.success) return { error: formatZodError(result.errors) };
66
+ return { data: result.data };
67
+ }
51
68
  function ViteEnv(options = {}) {
52
69
  let resolvedConfig;
53
70
  let envDefinition;
@@ -66,14 +83,15 @@ function ViteEnv(options = {}) {
66
83
  },
67
84
  async buildStart() {
68
85
  const rawEnv = await loadEnvSources(resolvedConfig);
69
- const result = validateEnv(envDefinition, rawEnv);
70
- if (!result.success) {
71
- const formatted = formatZodError(result.errors);
72
- throw new Error(`[vite-env] Environment validation failed:\n\n${formatted}`);
73
- }
86
+ const result = await validateAndFormat(envDefinition, rawEnv);
87
+ if ("error" in result) throw new Error(`[vite-env] Environment validation failed:\n\n${result.error}`);
74
88
  lastValidated = result.data;
75
- await generateDts(envDefinition, resolvedConfig.root);
76
- const count = Object.keys(result.data).length;
89
+ if (isStandardEnvDefinition(envDefinition)) await generateStandardDts(envDefinition, resolvedConfig.root);
90
+ else {
91
+ const { generateDts } = await import("./dts.mjs");
92
+ await generateDts(envDefinition, resolvedConfig.root);
93
+ }
94
+ const count = Object.keys(lastValidated).length;
77
95
  resolvedConfig.logger.info(` \x1B[32m✓\x1B[0m \x1B[36m[vite-env]\x1B[0m ${count} variables validated`);
78
96
  },
79
97
  resolveId(id) {
@@ -104,10 +122,9 @@ function ViteEnv(options = {}) {
104
122
  debounceTimer = setTimeout(async () => {
105
123
  try {
106
124
  const rawEnv = await loadEnvSources(resolvedConfig);
107
- const result = validateEnv(envDefinition, rawEnv);
108
- if (!result.success) {
109
- const formatted = formatZodError(result.errors);
110
- resolvedConfig.logger.warn(`\n \x1B[33m⚠\x1B[0m \x1B[36m[vite-env]\x1B[0m Env revalidation failed:\n${formatted}`);
125
+ const result = await validateAndFormat(envDefinition, rawEnv);
126
+ if ("error" in result) {
127
+ resolvedConfig.logger.warn(`\n \x1B[33m⚠\x1B[0m \x1B[36m[vite-env]\x1B[0m Env revalidation failed:\n${result.error}`);
111
128
  return;
112
129
  }
113
130
  lastValidated = result.data;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.mjs","names":[],"sources":["../src/sources.ts","../src/virtual.ts","../src/plugin.ts"],"sourcesContent":["// @env node\nimport type { ResolvedConfig } from 'vite'\nimport process from 'node:process'\nimport { loadEnv } from 'vite'\n\n/**\n * Merge priority (highest → lowest):\n * 1. process.env (CI pipeline secrets win)\n * 2. .env.[mode].local\n * 3. .env.[mode]\n * 4. .env.local\n * 5. .env\n *\n * Prefix '' = load everything, schema decides what's valid.\n */\nexport async function loadEnvSources(\n config: ResolvedConfig,\n): Promise<Record<string, string>> {\n const fileEnv = loadEnv(\n config.mode,\n config.envDir || config.root,\n '', // no prefix filter — schema is the filter\n )\n\n return {\n ...fileEnv,\n ...filterStrings(process.env),\n }\n}\n\nfunction filterStrings(env: NodeJS.ProcessEnv): Record<string, string> {\n return Object.fromEntries(\n Object.entries(env).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n ),\n )\n}\n","import type { EnvDefinition } from './types'\n\nexport function buildClientModule(\n def: EnvDefinition,\n data: Record<string, unknown>,\n): { code: string, moduleType: 'js' } {\n const clientKeys = new Set(Object.keys(def.client ?? {}))\n\n const clientData = Object.fromEntries(\n Object.entries(data).filter(([k]) => clientKeys.has(k)),\n )\n\n return {\n moduleType: 'js', // Required: Vite 8 / Rolldown explicit moduleType\n code: `// Auto-generated by @vite-env/core — do not edit\nexport const env = Object.freeze(${JSON.stringify(clientData, null, 2)});\nexport default env;`,\n }\n}\n\nexport function buildServerModule(\n _def: EnvDefinition,\n data: Record<string, unknown>,\n): { code: string, moduleType: 'js' } {\n return {\n moduleType: 'js',\n code: `// Auto-generated by @vite-env/core — do not edit\nexport const env = Object.freeze(${JSON.stringify(data, null, 2)});\nexport default env;`,\n }\n}\n","// @env node\nimport type { Plugin, ResolvedConfig } from 'vite'\nimport type { EnvDefinition } from './types'\nimport path from 'node:path'\nimport { loadEnvConfig } from './config'\nimport { generateDts } from './dts'\nimport { formatZodError } from './format'\nimport { detectServerLeak } from './leak'\nimport { validateEnv } from './schema'\nimport { loadEnvSources } from './sources'\nimport { buildClientModule, buildServerModule } from './virtual'\n\nexport interface ViteEnvOptions {\n /**\n * Path to env definition file.\n * @default './env.ts' (resolved from project root)\n */\n configFile?: string\n}\n\nexport default function ViteEnv(options: ViteEnvOptions = {}): Plugin {\n let resolvedConfig: ResolvedConfig\n let envDefinition: EnvDefinition\n let lastValidated: Record<string, unknown> = {}\n\n return {\n name: 'vite-env',\n enforce: 'pre',\n\n async configResolved(config) {\n resolvedConfig = config\n\n const configPath = path.resolve(\n config.root,\n options.configFile ?? 'env.ts',\n )\n\n try {\n envDefinition = await loadEnvConfig(configPath)\n }\n catch (e) {\n throw new Error(\n `[vite-env] Could not load env definition file at: ${configPath}\\n`\n + ` Create an env.ts file and export default defineEnv({ ... })`,\n { cause: e },\n )\n }\n },\n\n async buildStart() {\n const rawEnv = await loadEnvSources(resolvedConfig)\n const result = validateEnv(envDefinition, rawEnv)\n\n if (!result.success) {\n const formatted = formatZodError(result.errors)\n throw new Error(\n `[vite-env] Environment validation failed:\\n\\n${formatted}`,\n )\n }\n\n lastValidated = result.data\n\n await generateDts(envDefinition, resolvedConfig.root)\n\n const count = Object.keys(result.data).length\n resolvedConfig.logger.info(\n ` \\x1B[32m✓\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m ${count} variables validated`,\n )\n },\n\n resolveId(id) {\n if (id === 'virtual:env/client')\n return '\\0virtual:env/client'\n if (id === 'virtual:env/server')\n return '\\0virtual:env/server'\n },\n\n load(id) {\n if (id === '\\0virtual:env/client')\n return buildClientModule(envDefinition, lastValidated)\n if (id === '\\0virtual:env/server')\n return buildServerModule(envDefinition, lastValidated)\n },\n\n generateBundle(_options, bundle) {\n if (resolvedConfig.build.ssr)\n return\n\n const leaks = detectServerLeak(\n envDefinition,\n lastValidated,\n bundle as Record<string, { type: string, code?: string }>,\n (keys) => {\n resolvedConfig.logger.warn(\n ` \\x1B[33m⚠\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Leak detection skipped ${keys.length} server variable(s) with values shorter than 8 chars: ${keys.join(', ')}`,\n )\n },\n )\n\n if (leaks.length > 0) {\n const details = leaks.map(l => ` ✗ ${l.key} found in ${l.chunk}`).join('\\n')\n throw new Error(\n `[vite-env] Server environment variables detected in client bundle!\\n\\n${details}\\n\\n These variables are marked as server-only and must never reach the browser.`,\n )\n }\n },\n\n configureServer(server) {\n const envDir = resolvedConfig.envDir || resolvedConfig.root\n server.watcher.add(path.join(envDir, '.env*'))\n\n let debounceTimer: ReturnType<typeof setTimeout>\n\n server.watcher.on('change', async (file) => {\n if (!path.basename(file).startsWith('.env'))\n return\n\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(async () => {\n try {\n const rawEnv = await loadEnvSources(resolvedConfig)\n const result = validateEnv(envDefinition, rawEnv)\n\n if (!result.success) {\n const formatted = formatZodError(result.errors)\n resolvedConfig.logger.warn(\n `\\n \\x1B[33m⚠\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Env revalidation failed:\\n${formatted}`,\n )\n return\n }\n\n lastValidated = result.data\n\n const clientMod = server.moduleGraph.getModuleById('\\0virtual:env/client')\n const serverMod = server.moduleGraph.getModuleById('\\0virtual:env/server')\n if (clientMod)\n server.moduleGraph.invalidateModule(clientMod)\n if (serverMod)\n server.moduleGraph.invalidateModule(serverMod)\n if (clientMod || serverMod) {\n server.hot.send({ type: 'full-reload' })\n resolvedConfig.logger.info(\n ` \\x1B[32m✓\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Env revalidated`,\n )\n }\n }\n catch (e) {\n resolvedConfig.logger.error(\n `\\n \\x1B[31m✗\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Failed to reload env files: ${e instanceof Error ? e.message : String(e)}`,\n )\n }\n }, 150) // 150ms debounce\n })\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.mjs","names":[],"sources":["../src/sources.ts","../src/virtual.ts","../src/plugin.ts"],"sourcesContent":["// @env node\nimport type { ResolvedConfig } from 'vite'\nimport process from 'node:process'\nimport { loadEnv } from 'vite'\n\n/**\n * Merge priority (highest → lowest):\n * 1. process.env (CI pipeline secrets win)\n * 2. .env.[mode].local\n * 3. .env.[mode]\n * 4. .env.local\n * 5. .env\n *\n * Prefix '' = load everything, schema decides what's valid.\n */\nexport async function loadEnvSources(\n config: ResolvedConfig,\n): Promise<Record<string, string>> {\n const fileEnv = loadEnv(\n config.mode,\n config.envDir || config.root,\n '', // no prefix filter — schema is the filter\n )\n\n return {\n ...fileEnv,\n ...filterStrings(process.env),\n }\n}\n\nfunction filterStrings(env: NodeJS.ProcessEnv): Record<string, string> {\n return Object.fromEntries(\n Object.entries(env).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n ),\n )\n}\n","import type { AnyEnvDefinition } from './types'\n\nexport function buildClientModule(\n def: AnyEnvDefinition,\n data: Record<string, unknown>,\n): { code: string, moduleType: 'js' } {\n const clientKeys = new Set(Object.keys(def.client ?? {}))\n\n const clientData = Object.fromEntries(\n Object.entries(data).filter(([k]) => clientKeys.has(k)),\n )\n\n return {\n moduleType: 'js', // Required: Vite 8 / Rolldown explicit moduleType\n code: `// Auto-generated by @vite-env/core — do not edit\nexport const env = Object.freeze(${JSON.stringify(clientData, null, 2)});\nexport default env;`,\n }\n}\n\nexport function buildServerModule(\n _def: AnyEnvDefinition,\n data: Record<string, unknown>,\n): { code: string, moduleType: 'js' } {\n return {\n moduleType: 'js',\n code: `// Auto-generated by @vite-env/core — do not edit\nexport const env = Object.freeze(${JSON.stringify(data, null, 2)});\nexport default env;`,\n }\n}\n","// @env node\nimport type { Plugin, ResolvedConfig } from 'vite'\nimport type { AnyEnvDefinition } from './types'\nimport path from 'node:path'\nimport { loadEnvConfig } from './config'\nimport { generateStandardDts } from './dts'\nimport { formatStandardSchemaError } from './format'\nimport { detectServerLeak } from './leak'\nimport { loadEnvSources } from './sources'\nimport { isStandardEnvDefinition, validateStandardEnv } from './standard'\nimport { buildClientModule, buildServerModule } from './virtual'\n\nexport interface ViteEnvOptions {\n /**\n * Path to env definition file.\n * @default './env.ts' (resolved from project root)\n */\n configFile?: string\n}\n\n/**\n * Validates environment variables against the definition.\n * Routes to Zod or Standard Schema path based on definition type.\n * Zod modules are loaded dynamically to avoid requiring zod for Standard Schema users.\n */\nasync function validateAndFormat(\n def: AnyEnvDefinition,\n rawEnv: Record<string, string>,\n): Promise<{ data: Record<string, unknown> } | { error: string }> {\n if (isStandardEnvDefinition(def)) {\n const result = await validateStandardEnv(def, rawEnv)\n if (!result.success) {\n return { error: formatStandardSchemaError(result.errors) }\n }\n return { data: result.data }\n }\n\n const { validateEnv } = await import('./schema')\n const { formatZodError } = await import('./format')\n const result = validateEnv(def, rawEnv)\n if (!result.success) {\n return { error: formatZodError(result.errors) }\n }\n return { data: result.data }\n}\n\nexport default function ViteEnv(options: ViteEnvOptions = {}): Plugin {\n let resolvedConfig: ResolvedConfig\n let envDefinition: AnyEnvDefinition\n let lastValidated: Record<string, unknown> = {}\n\n return {\n name: 'vite-env',\n enforce: 'pre',\n\n async configResolved(config) {\n resolvedConfig = config\n\n const configPath = path.resolve(\n config.root,\n options.configFile ?? 'env.ts',\n )\n\n try {\n envDefinition = await loadEnvConfig(configPath)\n }\n catch (e) {\n throw new Error(\n `[vite-env] Could not load env definition file at: ${configPath}\\n`\n + ` Create an env.ts file and export default defineEnv({ ... })`,\n { cause: e },\n )\n }\n },\n\n async buildStart() {\n const rawEnv = await loadEnvSources(resolvedConfig)\n const result = await validateAndFormat(envDefinition, rawEnv)\n\n if ('error' in result) {\n throw new Error(\n `[vite-env] Environment validation failed:\\n\\n${result.error}`,\n )\n }\n\n lastValidated = result.data\n\n if (isStandardEnvDefinition(envDefinition)) {\n await generateStandardDts(envDefinition, resolvedConfig.root)\n }\n else {\n const { generateDts } = await import('./dts')\n await generateDts(envDefinition, resolvedConfig.root)\n }\n\n const count = Object.keys(lastValidated).length\n resolvedConfig.logger.info(\n ` \\x1B[32m✓\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m ${count} variables validated`,\n )\n },\n\n resolveId(id) {\n if (id === 'virtual:env/client')\n return '\\0virtual:env/client'\n if (id === 'virtual:env/server')\n return '\\0virtual:env/server'\n },\n\n load(id) {\n if (id === '\\0virtual:env/client')\n return buildClientModule(envDefinition, lastValidated)\n if (id === '\\0virtual:env/server')\n return buildServerModule(envDefinition, lastValidated)\n },\n\n generateBundle(_options, bundle) {\n if (resolvedConfig.build.ssr)\n return\n\n const leaks = detectServerLeak(\n envDefinition,\n lastValidated,\n bundle as Record<string, { type: string, code?: string }>,\n (keys) => {\n resolvedConfig.logger.warn(\n ` \\x1B[33m⚠\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Leak detection skipped ${keys.length} server variable(s) with values shorter than 8 chars: ${keys.join(', ')}`,\n )\n },\n )\n\n if (leaks.length > 0) {\n const details = leaks.map(l => ` ✗ ${l.key} found in ${l.chunk}`).join('\\n')\n throw new Error(\n `[vite-env] Server environment variables detected in client bundle!\\n\\n${details}\\n\\n These variables are marked as server-only and must never reach the browser.`,\n )\n }\n },\n\n configureServer(server) {\n const envDir = resolvedConfig.envDir || resolvedConfig.root\n server.watcher.add(path.join(envDir, '.env*'))\n\n let debounceTimer: ReturnType<typeof setTimeout>\n\n server.watcher.on('change', async (file) => {\n if (!path.basename(file).startsWith('.env'))\n return\n\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(async () => {\n try {\n const rawEnv = await loadEnvSources(resolvedConfig)\n const result = await validateAndFormat(envDefinition, rawEnv)\n\n if ('error' in result) {\n resolvedConfig.logger.warn(\n `\\n \\x1B[33m⚠\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Env revalidation failed:\\n${result.error}`,\n )\n return\n }\n\n lastValidated = result.data\n\n const clientMod = server.moduleGraph.getModuleById('\\0virtual:env/client')\n const serverMod = server.moduleGraph.getModuleById('\\0virtual:env/server')\n if (clientMod)\n server.moduleGraph.invalidateModule(clientMod)\n if (serverMod)\n server.moduleGraph.invalidateModule(serverMod)\n if (clientMod || serverMod) {\n server.hot.send({ type: 'full-reload' })\n resolvedConfig.logger.info(\n ` \\x1B[32m✓\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Env revalidated`,\n )\n }\n }\n catch (e) {\n resolvedConfig.logger.error(\n `\\n \\x1B[31m✗\\x1B[0m \\x1B[36m[vite-env]\\x1B[0m Failed to reload env files: ${e instanceof Error ? e.message : String(e)}`,\n )\n }\n }, 150) // 150ms debounce\n })\n },\n }\n}\n"],"mappings":"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"}
package/dist/schema.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- require("./dts-DF71HNdJ.cjs");
2
+ require("./dts-BgHTl6hC.cjs");
3
3
  let zod = require("zod");
4
4
  //#region src/schema.ts
5
5
  function defineEnv(definition) {
package/dist/schema.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as ValidationResult, t as EnvDefinition } from "./types-DqTMuWwc.cjs";
1
+ import { c as ValidationResult, n as EnvDefinition } from "./types-1okexcwM.cjs";
2
2
 
3
3
  //#region src/schema.d.ts
4
4
  declare function defineEnv<T extends EnvDefinition>(definition: T): T;
package/dist/schema.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as ValidationResult, t as EnvDefinition } from "./types-CluiDKAQ.mjs";
1
+ import { c as ValidationResult, n as EnvDefinition } from "./types-DuWT_251.mjs";
2
2
 
3
3
  //#region src/schema.d.ts
4
4
  declare function defineEnv<T extends EnvDefinition>(definition: T): T;
@@ -0,0 +1,46 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/standard.ts
3
+ function defineStandardEnv(definition) {
4
+ if (definition.client) {
5
+ for (const key of Object.keys(definition.client)) if (!key.startsWith("VITE_")) throw new Error(`[vite-env] Client env var "${key}" must be prefixed with VITE_.\n Rename it to "VITE_${key}" or move it to "server" if it's secret.`);
6
+ }
7
+ return {
8
+ ...definition,
9
+ _standard: true
10
+ };
11
+ }
12
+ function isStandardEnvDefinition(def) {
13
+ return def != null && typeof def === "object" && "_standard" in def && def._standard === true;
14
+ }
15
+ async function validateStandardEnv(def, rawEnv) {
16
+ const combinedShape = {
17
+ ...def.server,
18
+ ...def.client
19
+ };
20
+ const errors = [];
21
+ const data = {};
22
+ for (const [key, schema] of Object.entries(combinedShape)) {
23
+ const result = await schema["~standard"].validate(rawEnv[key]);
24
+ if ("issues" in result && result.issues) for (const issue of result.issues) errors.push({
25
+ message: issue.message,
26
+ path: [key, ...issue.path ?? []]
27
+ });
28
+ else data[key] = result.value;
29
+ }
30
+ if (errors.length > 0) return {
31
+ success: false,
32
+ data: null,
33
+ errors
34
+ };
35
+ return {
36
+ success: true,
37
+ data,
38
+ errors: []
39
+ };
40
+ }
41
+ //#endregion
42
+ exports.defineStandardEnv = defineStandardEnv;
43
+ exports.isStandardEnvDefinition = isStandardEnvDefinition;
44
+ exports.validateStandardEnv = validateStandardEnv;
45
+
46
+ //# sourceMappingURL=standard.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standard.cjs","names":[],"sources":["../src/standard.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { StandardEnvDefinition, StandardValidationIssue, StandardValidationResult } from './types'\n\nexport function defineStandardEnv<T extends Omit<StandardEnvDefinition, '_standard'>>(\n definition: T,\n): T & { readonly _standard: true } {\n if (definition.client) {\n for (const key of Object.keys(definition.client)) {\n if (!key.startsWith('VITE_')) {\n throw new Error(\n `[vite-env] Client env var \"${key}\" must be prefixed with VITE_.\\n`\n + ` Rename it to \"VITE_${key}\" or move it to \"server\" if it's secret.`,\n )\n }\n }\n }\n\n return { ...definition, _standard: true as const }\n}\n\nexport function isStandardEnvDefinition(\n def: unknown,\n): def is StandardEnvDefinition {\n return def != null && typeof def === 'object' && '_standard' in def && (def as any)._standard === true\n}\n\nexport async function validateStandardEnv(\n def: StandardEnvDefinition,\n rawEnv: Record<string, string>,\n): Promise<StandardValidationResult> {\n const combinedShape: Record<string, StandardSchemaV1> = {\n ...def.server,\n ...def.client,\n }\n\n const errors: StandardValidationIssue[] = []\n const data: Record<string, unknown> = {}\n\n for (const [key, schema] of Object.entries(combinedShape)) {\n const result = await schema['~standard'].validate(rawEnv[key])\n\n if ('issues' in result && result.issues) {\n for (const issue of result.issues) {\n errors.push({\n message: issue.message,\n path: [key, ...(issue.path ?? [])],\n })\n }\n }\n else {\n data[key] = (result as { value: unknown }).value\n }\n }\n\n if (errors.length > 0) {\n return { success: false, data: null, errors }\n }\n\n return { success: true, data, errors: [] as const }\n}\n"],"mappings":";;AAGA,SAAgB,kBACd,YACkC;AAClC,KAAI,WAAW;OACR,MAAM,OAAO,OAAO,KAAK,WAAW,OAAO,CAC9C,KAAI,CAAC,IAAI,WAAW,QAAQ,CAC1B,OAAM,IAAI,MACR,8BAA8B,IAAI,uDACR,IAAI,0CAC/B;;AAKP,QAAO;EAAE,GAAG;EAAY,WAAW;EAAe;;AAGpD,SAAgB,wBACd,KAC8B;AAC9B,QAAO,OAAO,QAAQ,OAAO,QAAQ,YAAY,eAAe,OAAQ,IAAY,cAAc;;AAGpG,eAAsB,oBACpB,KACA,QACmC;CACnC,MAAM,gBAAkD;EACtD,GAAG,IAAI;EACP,GAAG,IAAI;EACR;CAED,MAAM,SAAoC,EAAE;CAC5C,MAAM,OAAgC,EAAE;AAExC,MAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,cAAc,EAAE;EACzD,MAAM,SAAS,MAAM,OAAO,aAAa,SAAS,OAAO,KAAK;AAE9D,MAAI,YAAY,UAAU,OAAO,OAC/B,MAAK,MAAM,SAAS,OAAO,OACzB,QAAO,KAAK;GACV,SAAS,MAAM;GACf,MAAM,CAAC,KAAK,GAAI,MAAM,QAAQ,EAAE,CAAE;GACnC,CAAC;MAIJ,MAAK,OAAQ,OAA8B;;AAI/C,KAAI,OAAO,SAAS,EAClB,QAAO;EAAE,SAAS;EAAO,MAAM;EAAM;EAAQ;AAG/C,QAAO;EAAE,SAAS;EAAM;EAAM,QAAQ,EAAE;EAAW"}
@@ -0,0 +1,11 @@
1
+ import { a as StandardEnvDefinition, s as StandardValidationResult } from "./types-1okexcwM.cjs";
2
+
3
+ //#region src/standard.d.ts
4
+ declare function defineStandardEnv<T extends Omit<StandardEnvDefinition, '_standard'>>(definition: T): T & {
5
+ readonly _standard: true;
6
+ };
7
+ declare function isStandardEnvDefinition(def: unknown): def is StandardEnvDefinition;
8
+ declare function validateStandardEnv(def: StandardEnvDefinition, rawEnv: Record<string, string>): Promise<StandardValidationResult>;
9
+ //#endregion
10
+ export { defineStandardEnv, isStandardEnvDefinition, validateStandardEnv };
11
+ //# sourceMappingURL=standard.d.cts.map
@@ -0,0 +1,11 @@
1
+ import { a as StandardEnvDefinition, s as StandardValidationResult } from "./types-DuWT_251.mjs";
2
+
3
+ //#region src/standard.d.ts
4
+ declare function defineStandardEnv<T extends Omit<StandardEnvDefinition, '_standard'>>(definition: T): T & {
5
+ readonly _standard: true;
6
+ };
7
+ declare function isStandardEnvDefinition(def: unknown): def is StandardEnvDefinition;
8
+ declare function validateStandardEnv(def: StandardEnvDefinition, rawEnv: Record<string, string>): Promise<StandardValidationResult>;
9
+ //#endregion
10
+ export { defineStandardEnv, isStandardEnvDefinition, validateStandardEnv };
11
+ //# sourceMappingURL=standard.d.mts.map
@@ -0,0 +1,43 @@
1
+ //#region src/standard.ts
2
+ function defineStandardEnv(definition) {
3
+ if (definition.client) {
4
+ for (const key of Object.keys(definition.client)) if (!key.startsWith("VITE_")) throw new Error(`[vite-env] Client env var "${key}" must be prefixed with VITE_.\n Rename it to "VITE_${key}" or move it to "server" if it's secret.`);
5
+ }
6
+ return {
7
+ ...definition,
8
+ _standard: true
9
+ };
10
+ }
11
+ function isStandardEnvDefinition(def) {
12
+ return def != null && typeof def === "object" && "_standard" in def && def._standard === true;
13
+ }
14
+ async function validateStandardEnv(def, rawEnv) {
15
+ const combinedShape = {
16
+ ...def.server,
17
+ ...def.client
18
+ };
19
+ const errors = [];
20
+ const data = {};
21
+ for (const [key, schema] of Object.entries(combinedShape)) {
22
+ const result = await schema["~standard"].validate(rawEnv[key]);
23
+ if ("issues" in result && result.issues) for (const issue of result.issues) errors.push({
24
+ message: issue.message,
25
+ path: [key, ...issue.path ?? []]
26
+ });
27
+ else data[key] = result.value;
28
+ }
29
+ if (errors.length > 0) return {
30
+ success: false,
31
+ data: null,
32
+ errors
33
+ };
34
+ return {
35
+ success: true,
36
+ data,
37
+ errors: []
38
+ };
39
+ }
40
+ //#endregion
41
+ export { defineStandardEnv, isStandardEnvDefinition, validateStandardEnv };
42
+
43
+ //# sourceMappingURL=standard.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standard.mjs","names":[],"sources":["../src/standard.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { StandardEnvDefinition, StandardValidationIssue, StandardValidationResult } from './types'\n\nexport function defineStandardEnv<T extends Omit<StandardEnvDefinition, '_standard'>>(\n definition: T,\n): T & { readonly _standard: true } {\n if (definition.client) {\n for (const key of Object.keys(definition.client)) {\n if (!key.startsWith('VITE_')) {\n throw new Error(\n `[vite-env] Client env var \"${key}\" must be prefixed with VITE_.\\n`\n + ` Rename it to \"VITE_${key}\" or move it to \"server\" if it's secret.`,\n )\n }\n }\n }\n\n return { ...definition, _standard: true as const }\n}\n\nexport function isStandardEnvDefinition(\n def: unknown,\n): def is StandardEnvDefinition {\n return def != null && typeof def === 'object' && '_standard' in def && (def as any)._standard === true\n}\n\nexport async function validateStandardEnv(\n def: StandardEnvDefinition,\n rawEnv: Record<string, string>,\n): Promise<StandardValidationResult> {\n const combinedShape: Record<string, StandardSchemaV1> = {\n ...def.server,\n ...def.client,\n }\n\n const errors: StandardValidationIssue[] = []\n const data: Record<string, unknown> = {}\n\n for (const [key, schema] of Object.entries(combinedShape)) {\n const result = await schema['~standard'].validate(rawEnv[key])\n\n if ('issues' in result && result.issues) {\n for (const issue of result.issues) {\n errors.push({\n message: issue.message,\n path: [key, ...(issue.path ?? [])],\n })\n }\n }\n else {\n data[key] = (result as { value: unknown }).value\n }\n }\n\n if (errors.length > 0) {\n return { success: false, data: null, errors }\n }\n\n return { success: true, data, errors: [] as const }\n}\n"],"mappings":";AAGA,SAAgB,kBACd,YACkC;AAClC,KAAI,WAAW;OACR,MAAM,OAAO,OAAO,KAAK,WAAW,OAAO,CAC9C,KAAI,CAAC,IAAI,WAAW,QAAQ,CAC1B,OAAM,IAAI,MACR,8BAA8B,IAAI,uDACR,IAAI,0CAC/B;;AAKP,QAAO;EAAE,GAAG;EAAY,WAAW;EAAe;;AAGpD,SAAgB,wBACd,KAC8B;AAC9B,QAAO,OAAO,QAAQ,OAAO,QAAQ,YAAY,eAAe,OAAQ,IAAY,cAAc;;AAGpG,eAAsB,oBACpB,KACA,QACmC;CACnC,MAAM,gBAAkD;EACtD,GAAG,IAAI;EACP,GAAG,IAAI;EACR;CAED,MAAM,SAAoC,EAAE;CAC5C,MAAM,OAAgC,EAAE;AAExC,MAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,cAAc,EAAE;EACzD,MAAM,SAAS,MAAM,OAAO,aAAa,SAAS,OAAO,KAAK;AAE9D,MAAI,YAAY,UAAU,OAAO,OAC/B,MAAK,MAAM,SAAS,OAAO,OACzB,QAAO,KAAK;GACV,SAAS,MAAM;GACf,MAAM,CAAC,KAAK,GAAI,MAAM,QAAQ,EAAE,CAAE;GACnC,CAAC;MAIJ,MAAK,OAAQ,OAA8B;;AAI/C,KAAI,OAAO,SAAS,EAClB,QAAO;EAAE,SAAS;EAAO,MAAM;EAAM;EAAQ;AAG/C,QAAO;EAAE,SAAS;EAAM;EAAM,QAAQ,EAAE;EAAW"}
@@ -0,0 +1,43 @@
1
+ import { StandardSchemaV1 } from "@standard-schema/spec";
2
+ import { z } from "zod";
3
+
4
+ //#region src/types.d.ts
5
+ interface EnvDefinition {
6
+ server?: z.ZodRawShape;
7
+ client?: z.ZodRawShape;
8
+ }
9
+ interface StandardEnvDefinition {
10
+ server?: Record<string, StandardSchemaV1>;
11
+ client?: Record<string, StandardSchemaV1>;
12
+ /** @internal */
13
+ readonly _standard: true;
14
+ }
15
+ type AnyEnvDefinition = EnvDefinition | StandardEnvDefinition;
16
+ type ValidationResult = {
17
+ success: true;
18
+ data: Record<string, unknown>;
19
+ errors: [];
20
+ } | {
21
+ success: false;
22
+ data: null;
23
+ errors: z.core.$ZodIssue[];
24
+ };
25
+ interface StandardValidationIssue {
26
+ message: string;
27
+ path: ReadonlyArray<PropertyKey | StandardSchemaV1.PathSegment>;
28
+ }
29
+ type StandardValidationResult = {
30
+ success: true;
31
+ data: Record<string, unknown>;
32
+ errors: [];
33
+ } | {
34
+ success: false;
35
+ data: null;
36
+ errors: StandardValidationIssue[];
37
+ };
38
+ type OrEmptyShape<T> = T extends z.ZodRawShape ? T : Record<string, never>;
39
+ type InferClientEnv<T extends EnvDefinition> = z.infer<z.ZodObject<OrEmptyShape<T['client']>>>;
40
+ type InferServerEnv<T extends EnvDefinition> = z.infer<z.ZodObject<OrEmptyShape<T['server']> & OrEmptyShape<T['client']>>>;
41
+ //#endregion
42
+ export { StandardEnvDefinition as a, ValidationResult as c, InferServerEnv as i, EnvDefinition as n, StandardValidationIssue as o, InferClientEnv as r, StandardValidationResult as s, AnyEnvDefinition as t };
43
+ //# sourceMappingURL=types-1okexcwM.d.cts.map
@@ -0,0 +1,43 @@
1
+ import { z } from "zod";
2
+ import { StandardSchemaV1 } from "@standard-schema/spec";
3
+
4
+ //#region src/types.d.ts
5
+ interface EnvDefinition {
6
+ server?: z.ZodRawShape;
7
+ client?: z.ZodRawShape;
8
+ }
9
+ interface StandardEnvDefinition {
10
+ server?: Record<string, StandardSchemaV1>;
11
+ client?: Record<string, StandardSchemaV1>;
12
+ /** @internal */
13
+ readonly _standard: true;
14
+ }
15
+ type AnyEnvDefinition = EnvDefinition | StandardEnvDefinition;
16
+ type ValidationResult = {
17
+ success: true;
18
+ data: Record<string, unknown>;
19
+ errors: [];
20
+ } | {
21
+ success: false;
22
+ data: null;
23
+ errors: z.core.$ZodIssue[];
24
+ };
25
+ interface StandardValidationIssue {
26
+ message: string;
27
+ path: ReadonlyArray<PropertyKey | StandardSchemaV1.PathSegment>;
28
+ }
29
+ type StandardValidationResult = {
30
+ success: true;
31
+ data: Record<string, unknown>;
32
+ errors: [];
33
+ } | {
34
+ success: false;
35
+ data: null;
36
+ errors: StandardValidationIssue[];
37
+ };
38
+ type OrEmptyShape<T> = T extends z.ZodRawShape ? T : Record<string, never>;
39
+ type InferClientEnv<T extends EnvDefinition> = z.infer<z.ZodObject<OrEmptyShape<T['client']>>>;
40
+ type InferServerEnv<T extends EnvDefinition> = z.infer<z.ZodObject<OrEmptyShape<T['server']> & OrEmptyShape<T['client']>>>;
41
+ //#endregion
42
+ export { StandardEnvDefinition as a, ValidationResult as c, InferServerEnv as i, EnvDefinition as n, StandardValidationIssue as o, InferClientEnv as r, StandardValidationResult as s, AnyEnvDefinition as t };
43
+ //# sourceMappingURL=types-DuWT_251.d.mts.map
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://raw.githubusercontent.com/vitejs/vite-plugin-registry/refs/heads/main/data/schema/extended-package-json.schema.json",
3
3
  "name": "@vite-env/core",
4
4
  "type": "module",
5
- "version": "0.2.2",
5
+ "version": "0.3.0",
6
6
  "description": "The env.ts layer for Vite — define once, validate everywhere, import with types",
7
7
  "license": "MIT",
8
8
  "homepage": "https://github.com/pyyupsk/vite-env#readme",
@@ -20,7 +20,8 @@
20
20
  "zod",
21
21
  "validation",
22
22
  "rolldown",
23
- "dotenv"
23
+ "dotenv",
24
+ "standard-schema"
24
25
  ],
25
26
  "exports": {
26
27
  ".": {
@@ -93,6 +94,16 @@
93
94
  "default": "./dist/schema.cjs"
94
95
  }
95
96
  },
97
+ "./standard": {
98
+ "import": {
99
+ "types": "./dist/standard.d.mts",
100
+ "default": "./dist/standard.mjs"
101
+ },
102
+ "require": {
103
+ "types": "./dist/standard.d.cts",
104
+ "default": "./dist/standard.cjs"
105
+ }
106
+ },
96
107
  "./package.json": "./package.json"
97
108
  },
98
109
  "main": "./dist/index.cjs",
@@ -109,7 +120,13 @@
109
120
  "vite": ">=8.0.0",
110
121
  "zod": "^4.0.0"
111
122
  },
123
+ "peerDependenciesMeta": {
124
+ "zod": {
125
+ "optional": true
126
+ }
127
+ },
112
128
  "dependencies": {
129
+ "@standard-schema/spec": "^1.1.0",
113
130
  "jiti": "^2.6.1"
114
131
  },
115
132
  "devDependencies": {
@@ -1,100 +0,0 @@
1
- //#region \0rolldown/runtime.js
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
22
- //#endregion
23
- let zod = require("zod");
24
- let node_path = require("node:path");
25
- node_path = __toESM(node_path);
26
- let node_fs_promises = require("node:fs/promises");
27
- node_fs_promises = __toESM(node_fs_promises);
28
- //#region src/dts.ts
29
- /**
30
- * Writes vite-env.d.ts to project root.
31
- * Declares virtual:env/client and virtual:env/server module types.
32
- * Users never need to manually augment ImportMetaEnv again.
33
- */
34
- async function generateDts(def, root) {
35
- const clientKeys = { ...def.client };
36
- const serverKeys = {
37
- ...def.server,
38
- ...def.client
39
- };
40
- const dts = `// Auto-generated by @vite-env/core
41
- // Do not edit manually — re-generated on every dev server start and build
42
-
43
- declare module 'virtual:env/client' {
44
- const env: {
45
- ${zodShapeToTsFields(clientKeys)}
46
- }
47
- export { env }
48
- export default env
49
- }
50
-
51
- declare module 'virtual:env/server' {
52
- const env: {
53
- ${zodShapeToTsFields(serverKeys)}
54
- }
55
- export { env }
56
- export default env
57
- }
58
- `;
59
- const filePath = node_path.default.join(root, "vite-env.d.ts");
60
- try {
61
- await node_fs_promises.default.writeFile(filePath, dts, "utf-8");
62
- } catch (e) {
63
- throw new Error(`[vite-env] Failed to write vite-env.d.ts to ${root}. Check file permissions.`, { cause: e });
64
- }
65
- }
66
- function zodShapeToTsFields(shape) {
67
- return Object.entries(shape).map(([key, schema]) => {
68
- const s = schema;
69
- const tsType = zodToTs(s);
70
- return ` readonly ${key}${isOptional(s) ? "?" : ""}: ${tsType}`;
71
- }).join("\n");
72
- }
73
- function zodToTs(schema) {
74
- if (schema instanceof zod.z.ZodOptional) return zodToTs(schema.unwrap());
75
- if (schema instanceof zod.z.ZodDefault) return zodToTs(schema.def.innerType);
76
- if (schema instanceof zod.z.ZodString) return "string";
77
- if (schema instanceof zod.z.ZodNumber) return "number";
78
- if (schema instanceof zod.z.ZodBoolean) return "boolean";
79
- if (schema instanceof zod.z.ZodEnum) return schema.options.map((o) => `'${o}'`).join(" | ");
80
- if (schema instanceof zod.z.ZodPipe) return zodToTs(schema.def.out);
81
- return "string";
82
- }
83
- function isOptional(schema) {
84
- return schema instanceof zod.z.ZodOptional;
85
- }
86
- //#endregion
87
- Object.defineProperty(exports, "__toESM", {
88
- enumerable: true,
89
- get: function() {
90
- return __toESM;
91
- }
92
- });
93
- Object.defineProperty(exports, "generateDts", {
94
- enumerable: true,
95
- get: function() {
96
- return generateDts;
97
- }
98
- });
99
-
100
- //# sourceMappingURL=dts-DF71HNdJ.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dts-DF71HNdJ.cjs","names":["path","fs","z"],"sources":["../src/dts.ts"],"sourcesContent":["// @env node\nimport type { EnvDefinition } from './types'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { z } from 'zod'\n\n/**\n * Writes vite-env.d.ts to project root.\n * Declares virtual:env/client and virtual:env/server module types.\n * Users never need to manually augment ImportMetaEnv again.\n */\nexport async function generateDts(\n def: EnvDefinition,\n root: string,\n): Promise<void> {\n const clientKeys = {\n ...def.client,\n }\n const serverKeys = {\n ...def.server,\n ...def.client,\n }\n\n const clientFields = zodShapeToTsFields(clientKeys)\n const serverFields = zodShapeToTsFields(serverKeys)\n\n const dts = `// Auto-generated by @vite-env/core\n// Do not edit manually — re-generated on every dev server start and build\n\ndeclare module 'virtual:env/client' {\n const env: {\n${clientFields}\n }\n export { env }\n export default env\n}\n\ndeclare module 'virtual:env/server' {\n const env: {\n${serverFields}\n }\n export { env }\n export default env\n}\n`\n\n const filePath = path.join(root, 'vite-env.d.ts')\n try {\n await fs.writeFile(filePath, dts, 'utf-8')\n }\n catch (e) {\n throw new Error(\n `[vite-env] Failed to write vite-env.d.ts to ${root}. Check file permissions.`,\n { cause: e },\n )\n }\n}\n\nfunction zodShapeToTsFields(shape: z.ZodRawShape): string {\n return Object.entries(shape)\n .map(([key, schema]) => {\n // Zod v4: ZodRawShape values are $ZodType, cast to ZodTypeAny for instanceof checks\n const s = schema as z.ZodTypeAny\n const tsType = zodToTs(s)\n const optional = isOptional(s)\n return ` readonly ${key}${optional ? '?' : ''}: ${tsType}`\n })\n .join('\\n')\n}\n\nfunction zodToTs(schema: z.ZodTypeAny): string {\n if (schema instanceof z.ZodOptional)\n return zodToTs(schema.unwrap() as unknown as z.ZodTypeAny)\n if (schema instanceof z.ZodDefault)\n return zodToTs(schema.def.innerType as unknown as z.ZodTypeAny)\n if (schema instanceof z.ZodString)\n return 'string'\n if (schema instanceof z.ZodNumber)\n return 'number'\n if (schema instanceof z.ZodBoolean)\n return 'boolean'\n if (schema instanceof z.ZodEnum)\n return (schema.options as string[]).map(o => `'${o}'`).join(' | ')\n if (schema instanceof z.ZodPipe)\n return zodToTs(schema.def.out as unknown as z.ZodTypeAny)\n return 'string'\n}\n\nfunction isOptional(schema: z.ZodTypeAny): boolean {\n return schema instanceof z.ZodOptional\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,eAAsB,YACpB,KACA,MACe;CACf,MAAM,aAAa,EACjB,GAAG,IAAI,QACR;CACD,MAAM,aAAa;EACjB,GAAG,IAAI;EACP,GAAG,IAAI;EACR;CAKD,MAAM,MAAM;;;;;EAHS,mBAAmB,WAAW,CAQtC;;;;;;;;EAPQ,mBAAmB,WAAW,CAetC;;;;;;CAOb,MAAM,WAAWA,UAAAA,QAAK,KAAK,MAAM,gBAAgB;AACjD,KAAI;AACF,QAAMC,iBAAAA,QAAG,UAAU,UAAU,KAAK,QAAQ;UAErC,GAAG;AACR,QAAM,IAAI,MACR,+CAA+C,KAAK,4BACpD,EAAE,OAAO,GAAG,CACb;;;AAIL,SAAS,mBAAmB,OAA8B;AACxD,QAAO,OAAO,QAAQ,MAAM,CACzB,KAAK,CAAC,KAAK,YAAY;EAEtB,MAAM,IAAI;EACV,MAAM,SAAS,QAAQ,EAAE;AAEzB,SAAO,gBAAgB,MADN,WAAW,EAAE,GACU,MAAM,GAAG,IAAI;GACrD,CACD,KAAK,KAAK;;AAGf,SAAS,QAAQ,QAA8B;AAC7C,KAAI,kBAAkBC,IAAAA,EAAE,YACtB,QAAO,QAAQ,OAAO,QAAQ,CAA4B;AAC5D,KAAI,kBAAkBA,IAAAA,EAAE,WACtB,QAAO,QAAQ,OAAO,IAAI,UAAqC;AACjE,KAAI,kBAAkBA,IAAAA,EAAE,UACtB,QAAO;AACT,KAAI,kBAAkBA,IAAAA,EAAE,UACtB,QAAO;AACT,KAAI,kBAAkBA,IAAAA,EAAE,WACtB,QAAO;AACT,KAAI,kBAAkBA,IAAAA,EAAE,QACtB,QAAQ,OAAO,QAAqB,KAAI,MAAK,IAAI,EAAE,GAAG,CAAC,KAAK,MAAM;AACpE,KAAI,kBAAkBA,IAAAA,EAAE,QACtB,QAAO,QAAQ,OAAO,IAAI,IAA+B;AAC3D,QAAO;;AAGT,SAAS,WAAW,QAA+B;AACjD,QAAO,kBAAkBA,IAAAA,EAAE"}
@@ -1,22 +0,0 @@
1
- import { z } from "zod";
2
-
3
- //#region src/types.d.ts
4
- interface EnvDefinition {
5
- server?: z.ZodRawShape;
6
- client?: z.ZodRawShape;
7
- }
8
- type ValidationResult = {
9
- success: true;
10
- data: Record<string, unknown>;
11
- errors: [];
12
- } | {
13
- success: false;
14
- data: null;
15
- errors: z.core.$ZodIssue[];
16
- };
17
- type OrEmptyShape<T> = T extends z.ZodRawShape ? T : Record<string, never>;
18
- type InferClientEnv<T extends EnvDefinition> = z.infer<z.ZodObject<OrEmptyShape<T['client']>>>;
19
- type InferServerEnv<T extends EnvDefinition> = z.infer<z.ZodObject<OrEmptyShape<T['server']> & OrEmptyShape<T['client']>>>;
20
- //#endregion
21
- export { ValidationResult as i, InferClientEnv as n, InferServerEnv as r, EnvDefinition as t };
22
- //# sourceMappingURL=types-CluiDKAQ.d.mts.map
@@ -1,22 +0,0 @@
1
- import { z } from "zod";
2
-
3
- //#region src/types.d.ts
4
- interface EnvDefinition {
5
- server?: z.ZodRawShape;
6
- client?: z.ZodRawShape;
7
- }
8
- type ValidationResult = {
9
- success: true;
10
- data: Record<string, unknown>;
11
- errors: [];
12
- } | {
13
- success: false;
14
- data: null;
15
- errors: z.core.$ZodIssue[];
16
- };
17
- type OrEmptyShape<T> = T extends z.ZodRawShape ? T : Record<string, never>;
18
- type InferClientEnv<T extends EnvDefinition> = z.infer<z.ZodObject<OrEmptyShape<T['client']>>>;
19
- type InferServerEnv<T extends EnvDefinition> = z.infer<z.ZodObject<OrEmptyShape<T['server']> & OrEmptyShape<T['client']>>>;
20
- //#endregion
21
- export { ValidationResult as i, InferClientEnv as n, InferServerEnv as r, EnvDefinition as t };
22
- //# sourceMappingURL=types-DqTMuWwc.d.cts.map