@vite-env/core 0.1.0 → 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 +62 -0
- package/dist/config.cjs +4 -2
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/config.mjs +3 -1
- package/dist/config.mjs.map +1 -1
- package/dist/{dts-DT7HjKrz.cjs → dts-DF71HNdJ.cjs} +10 -9
- package/dist/dts-DF71HNdJ.cjs.map +1 -0
- package/dist/dts.cjs +1 -1
- package/dist/dts.d.cts +1 -1
- package/dist/dts.d.mts +1 -1
- package/dist/dts.mjs +9 -8
- package/dist/dts.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/leak.cjs +3 -1
- package/dist/leak.cjs.map +1 -1
- package/dist/leak.d.cts +2 -2
- package/dist/leak.d.mts +2 -2
- package/dist/leak.mjs +3 -1
- package/dist/leak.mjs.map +1 -1
- package/dist/plugin.cjs +26 -24
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.mjs +25 -23
- package/dist/plugin.mjs.map +1 -1
- package/dist/schema.cjs +2 -3
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +2 -2
- package/dist/schema.d.mts +2 -2
- package/dist/schema.mjs +1 -2
- package/dist/schema.mjs.map +1 -1
- package/dist/{types-BmEBJZdy.d.cts → types-CluiDKAQ.d.mts} +12 -8
- package/dist/{types-CLfG9amO.d.mts → types-DqTMuWwc.d.cts} +12 -8
- package/package.json +19 -1
- package/dist/dts-DT7HjKrz.cjs.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @vite-env/core
|
|
2
|
+
|
|
3
|
+
The `env.ts` layer for Vite — define once, validate everywhere, import with types.
|
|
4
|
+
|
|
5
|
+
- Typed virtual modules (`virtual:env/client`, `virtual:env/server`)
|
|
6
|
+
- Server/client split with build-time leak detection
|
|
7
|
+
- Auto-coercion via Zod v4 (`z.stringbool()`, `z.coerce.number()`)
|
|
8
|
+
- Auto `.d.ts` generation
|
|
9
|
+
- Vite 8 / Rolldown native
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @vite-env/core zod
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
**1. Define your schema** — `env.ts`
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { defineEnv, z } from '@vite-env/core'
|
|
23
|
+
|
|
24
|
+
export default defineEnv({
|
|
25
|
+
server: {
|
|
26
|
+
DATABASE_URL: z.url(),
|
|
27
|
+
JWT_SECRET: z.string().min(32),
|
|
28
|
+
},
|
|
29
|
+
client: {
|
|
30
|
+
VITE_API_URL: z.url(),
|
|
31
|
+
VITE_DARK_MODE: z.stringbool().default(false),
|
|
32
|
+
VITE_NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**2. Add the plugin** — `vite.config.ts`
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import ViteEnv from '@vite-env/core/plugin'
|
|
41
|
+
import { defineConfig } from 'vite'
|
|
42
|
+
|
|
43
|
+
export default defineConfig({
|
|
44
|
+
plugins: [ViteEnv()],
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**3. Import typed env**
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { env } from 'virtual:env/client'
|
|
52
|
+
|
|
53
|
+
env.VITE_API_URL // string
|
|
54
|
+
env.VITE_DARK_MODE // boolean
|
|
55
|
+
env.VITE_NODE_ENV // 'development' | 'test' | 'production'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
See the [full documentation](https://github.com/pyyupsk/vite-env) for server/client split details, CLI tools, and more.
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
[MIT](https://github.com/pyyupsk/vite-env/blob/main/LICENSE)
|
package/dist/config.cjs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("./dts-
|
|
2
|
+
require("./dts-DF71HNdJ.cjs");
|
|
3
3
|
let jiti = require("jiti");
|
|
4
4
|
//#region src/config.ts
|
|
5
5
|
async function loadEnvConfig(configPath) {
|
|
6
6
|
const mod = await (0, jiti.createJiti)(configPath).import(configPath);
|
|
7
|
-
|
|
7
|
+
const def = mod.default ?? mod;
|
|
8
|
+
if (!def || typeof def !== "object") throw new Error(`[vite-env] env config at ${configPath} must export an object (got ${typeof def}).\n Use: export default defineEnv({ ... })`);
|
|
9
|
+
return def;
|
|
8
10
|
}
|
|
9
11
|
//#endregion
|
|
10
12
|
exports.loadEnvConfig = loadEnvConfig;
|
package/dist/config.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.cjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import type { EnvDefinition } from './types'\nimport { createJiti } from 'jiti'\n\nexport async function loadEnvConfig(configPath: string): Promise<EnvDefinition> {\n const jiti = createJiti(configPath)\n const mod = await jiti.import(configPath) as any\n
|
|
1
|
+
{"version":3,"file":"config.cjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import type { EnvDefinition } from './types'\nimport { createJiti } from 'jiti'\n\nexport async function loadEnvConfig(configPath: string): Promise<EnvDefinition> {\n const jiti = createJiti(configPath)\n const mod: Record<string, unknown> = await jiti.import(configPath) as any\n const def: unknown = mod.default ?? mod\n\n if (!def || typeof def !== 'object') {\n throw new Error(\n `[vite-env] env config at ${configPath} must export an object (got ${typeof def}).\\n`\n + ` Use: export default defineEnv({ ... })`,\n )\n }\n\n return def as EnvDefinition\n}\n"],"mappings":";;;;AAGA,eAAsB,cAAc,YAA4C;CAE9E,MAAM,MAA+B,OAAA,GAAA,KAAA,YADb,WAAW,CACa,OAAO,WAAW;CAClE,MAAM,MAAe,IAAI,WAAW;AAEpC,KAAI,CAAC,OAAO,OAAO,QAAQ,SACzB,OAAM,IAAI,MACR,4BAA4B,WAAW,8BAA8B,OAAO,IAAI,8CAEjF;AAGH,QAAO"}
|
package/dist/config.d.cts
CHANGED
package/dist/config.d.mts
CHANGED
package/dist/config.mjs
CHANGED
|
@@ -2,7 +2,9 @@ import { createJiti } from "jiti";
|
|
|
2
2
|
//#region src/config.ts
|
|
3
3
|
async function loadEnvConfig(configPath) {
|
|
4
4
|
const mod = await createJiti(configPath).import(configPath);
|
|
5
|
-
|
|
5
|
+
const def = mod.default ?? mod;
|
|
6
|
+
if (!def || typeof def !== "object") throw new Error(`[vite-env] env config at ${configPath} must export an object (got ${typeof def}).\n Use: export default defineEnv({ ... })`);
|
|
7
|
+
return def;
|
|
6
8
|
}
|
|
7
9
|
//#endregion
|
|
8
10
|
export { loadEnvConfig };
|
package/dist/config.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import type { EnvDefinition } from './types'\nimport { createJiti } from 'jiti'\n\nexport async function loadEnvConfig(configPath: string): Promise<EnvDefinition> {\n const jiti = createJiti(configPath)\n const mod = await jiti.import(configPath) as any\n
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import type { EnvDefinition } from './types'\nimport { createJiti } from 'jiti'\n\nexport async function loadEnvConfig(configPath: string): Promise<EnvDefinition> {\n const jiti = createJiti(configPath)\n const mod: Record<string, unknown> = await jiti.import(configPath) as any\n const def: unknown = mod.default ?? mod\n\n if (!def || typeof def !== 'object') {\n throw new Error(\n `[vite-env] env config at ${configPath} must export an object (got ${typeof def}).\\n`\n + ` Use: export default defineEnv({ ... })`,\n )\n }\n\n return def as EnvDefinition\n}\n"],"mappings":";;AAGA,eAAsB,cAAc,YAA4C;CAE9E,MAAM,MAA+B,MADxB,WAAW,WAAW,CACa,OAAO,WAAW;CAClE,MAAM,MAAe,IAAI,WAAW;AAEpC,KAAI,CAAC,OAAO,OAAO,QAAQ,SACzB,OAAM,IAAI,MACR,4BAA4B,WAAW,8BAA8B,OAAO,IAAI,8CAEjF;AAGH,QAAO"}
|
|
@@ -32,14 +32,10 @@ node_fs_promises = __toESM(node_fs_promises);
|
|
|
32
32
|
* Users never need to manually augment ImportMetaEnv again.
|
|
33
33
|
*/
|
|
34
34
|
async function generateDts(def, root) {
|
|
35
|
-
const clientKeys = {
|
|
36
|
-
...def.client,
|
|
37
|
-
...def.shared
|
|
38
|
-
};
|
|
35
|
+
const clientKeys = { ...def.client };
|
|
39
36
|
const serverKeys = {
|
|
40
37
|
...def.server,
|
|
41
|
-
...def.client
|
|
42
|
-
...def.shared
|
|
38
|
+
...def.client
|
|
43
39
|
};
|
|
44
40
|
const dts = `// Auto-generated by @vite-env/core
|
|
45
41
|
// Do not edit manually — re-generated on every dev server start and build
|
|
@@ -60,7 +56,12 @@ ${zodShapeToTsFields(serverKeys)}
|
|
|
60
56
|
export default env
|
|
61
57
|
}
|
|
62
58
|
`;
|
|
63
|
-
|
|
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
|
+
}
|
|
64
65
|
}
|
|
65
66
|
function zodShapeToTsFields(shape) {
|
|
66
67
|
return Object.entries(shape).map(([key, schema]) => {
|
|
@@ -80,7 +81,7 @@ function zodToTs(schema) {
|
|
|
80
81
|
return "string";
|
|
81
82
|
}
|
|
82
83
|
function isOptional(schema) {
|
|
83
|
-
return schema instanceof zod.z.ZodOptional
|
|
84
|
+
return schema instanceof zod.z.ZodOptional;
|
|
84
85
|
}
|
|
85
86
|
//#endregion
|
|
86
87
|
Object.defineProperty(exports, "__toESM", {
|
|
@@ -96,4 +97,4 @@ Object.defineProperty(exports, "generateDts", {
|
|
|
96
97
|
}
|
|
97
98
|
});
|
|
98
99
|
|
|
99
|
-
//# sourceMappingURL=dts-
|
|
100
|
+
//# sourceMappingURL=dts-DF71HNdJ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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 unknown 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"}
|
package/dist/dts.cjs
CHANGED
package/dist/dts.d.cts
CHANGED
package/dist/dts.d.mts
CHANGED
package/dist/dts.mjs
CHANGED
|
@@ -8,14 +8,10 @@ import fs from "node:fs/promises";
|
|
|
8
8
|
* Users never need to manually augment ImportMetaEnv again.
|
|
9
9
|
*/
|
|
10
10
|
async function generateDts(def, root) {
|
|
11
|
-
const clientKeys = {
|
|
12
|
-
...def.client,
|
|
13
|
-
...def.shared
|
|
14
|
-
};
|
|
11
|
+
const clientKeys = { ...def.client };
|
|
15
12
|
const serverKeys = {
|
|
16
13
|
...def.server,
|
|
17
|
-
...def.client
|
|
18
|
-
...def.shared
|
|
14
|
+
...def.client
|
|
19
15
|
};
|
|
20
16
|
const dts = `// Auto-generated by @vite-env/core
|
|
21
17
|
// Do not edit manually — re-generated on every dev server start and build
|
|
@@ -36,7 +32,12 @@ ${zodShapeToTsFields(serverKeys)}
|
|
|
36
32
|
export default env
|
|
37
33
|
}
|
|
38
34
|
`;
|
|
39
|
-
|
|
35
|
+
const filePath = path.join(root, "vite-env.d.ts");
|
|
36
|
+
try {
|
|
37
|
+
await fs.writeFile(filePath, dts, "utf-8");
|
|
38
|
+
} catch (e) {
|
|
39
|
+
throw new Error(`[vite-env] Failed to write vite-env.d.ts to ${root}. Check file permissions.`, { cause: e });
|
|
40
|
+
}
|
|
40
41
|
}
|
|
41
42
|
function zodShapeToTsFields(shape) {
|
|
42
43
|
return Object.entries(shape).map(([key, schema]) => {
|
|
@@ -56,7 +57,7 @@ function zodToTs(schema) {
|
|
|
56
57
|
return "string";
|
|
57
58
|
}
|
|
58
59
|
function isOptional(schema) {
|
|
59
|
-
return schema instanceof z.ZodOptional
|
|
60
|
+
return schema instanceof z.ZodOptional;
|
|
60
61
|
}
|
|
61
62
|
//#endregion
|
|
62
63
|
export { generateDts };
|
package/dist/dts.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dts.mjs","names":[],"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
|
|
1
|
+
{"version":3,"file":"dts.mjs","names":[],"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 unknown 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,WAAW,KAAK,KAAK,MAAM,gBAAgB;AACjD,KAAI;AACF,QAAM,GAAG,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,kBAAkB,EAAE,YACtB,QAAO,QAAQ,OAAO,QAAQ,CAA4B;AAC5D,KAAI,kBAAkB,EAAE,WACtB,QAAO,QAAQ,OAAO,IAAI,UAAqC;AACjE,KAAI,kBAAkB,EAAE,UACtB,QAAO;AACT,KAAI,kBAAkB,EAAE,UACtB,QAAO;AACT,KAAI,kBAAkB,EAAE,WACtB,QAAO;AACT,KAAI,kBAAkB,EAAE,QACtB,QAAQ,OAAO,QAAqB,KAAI,MAAK,IAAI,EAAE,GAAG,CAAC,KAAK,MAAM;AACpE,KAAI,kBAAkB,EAAE,QACtB,QAAO,QAAQ,OAAO,IAAI,IAA+B;AAC3D,QAAO;;AAGT,SAAS,WAAW,QAA+B;AACjD,QAAO,kBAAkB,EAAE"}
|
package/dist/index.cjs
CHANGED
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as InferClientEnv, r as InferServerEnv, t as EnvDefinition } from "./types-
|
|
1
|
+
import { n as InferClientEnv, r as InferServerEnv, t as EnvDefinition } from "./types-DqTMuWwc.cjs";
|
|
2
2
|
import { defineEnv } from "./schema.cjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
export { type EnvDefinition, type InferClientEnv, type InferServerEnv, defineEnv, z };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as InferClientEnv, r as InferServerEnv, t as EnvDefinition } from "./types-
|
|
1
|
+
import { n as InferClientEnv, r as InferServerEnv, t as EnvDefinition } from "./types-CluiDKAQ.mjs";
|
|
2
2
|
import { defineEnv } from "./schema.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
export { type EnvDefinition, type InferClientEnv, type InferServerEnv, defineEnv, z };
|
package/dist/leak.cjs
CHANGED
|
@@ -8,8 +8,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
8
8
|
* appears as a literal string in any output chunk's source code.
|
|
9
9
|
* Short/common values (< 8 chars) are skipped to avoid false positives.
|
|
10
10
|
*/
|
|
11
|
-
function detectServerLeak(def, data, bundle) {
|
|
11
|
+
function detectServerLeak(def, data, bundle, onSkipped) {
|
|
12
12
|
const serverKeys = new Set(Object.keys(def.server ?? {}));
|
|
13
|
+
const shortSecrets = Object.entries(data).filter((entry) => serverKeys.has(entry[0]) && typeof entry[1] === "string" && entry[1].length < 8);
|
|
14
|
+
if (shortSecrets.length > 0 && onSkipped) onSkipped(shortSecrets.map(([k]) => k));
|
|
13
15
|
const serverSecrets = Object.entries(data).filter((entry) => serverKeys.has(entry[0]) && typeof entry[1] === "string" && entry[1].length >= 8);
|
|
14
16
|
const chunks = Object.entries(bundle).filter(([, chunk]) => chunk.type === "chunk" && !!chunk.code);
|
|
15
17
|
const leaks = [];
|
package/dist/leak.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"leak.cjs","names":[],"sources":["../src/leak.ts"],"sourcesContent":["import type { EnvDefinition } from './types'\n\ninterface LeakReport {\n key: string\n chunk: string\n}\n\n/**\n * Scans all client-destined chunks for literal values of server-only vars.\n * Fires in generateBundle() — Rolldown sequential hook, safe.\n *\n * Strategy: for each server-only key, check if its actual runtime value\n * appears as a literal string in any output chunk's source code.\n * Short/common values (< 8 chars) are skipped to avoid false positives.\n */\nexport function detectServerLeak(\n def: EnvDefinition,\n data: Record<string, unknown>,\n bundle: Record<string, { type: string, code?: string }>,\n): LeakReport[] {\n const serverKeys = new Set(Object.keys(def.server ?? {}))\n\n const serverSecrets = Object.entries(data).filter(\n (entry): entry is [string, string] =>\n serverKeys.has(entry[0])\n && typeof entry[1] === 'string'\n && entry[1].length >= 8,\n )\n\n const chunks = Object.entries(bundle).filter(\n ([, chunk]) => chunk.type === 'chunk' && !!chunk.code,\n )\n\n const leaks: LeakReport[] = []\n for (const [key, value] of serverSecrets) {\n for (const [chunkName, chunk] of chunks) {\n if (chunk.code!.includes(value)) {\n leaks.push({ key, chunk: chunkName })\n }\n }\n }\n\n return leaks\n}\n"],"mappings":";;;;;;;;;;AAeA,SAAgB,iBACd,KACA,MACA,
|
|
1
|
+
{"version":3,"file":"leak.cjs","names":[],"sources":["../src/leak.ts"],"sourcesContent":["import type { EnvDefinition } from './types'\n\ninterface LeakReport {\n key: string\n chunk: string\n}\n\n/**\n * Scans all client-destined chunks for literal values of server-only vars.\n * Fires in generateBundle() — Rolldown sequential hook, safe.\n *\n * Strategy: for each server-only key, check if its actual runtime value\n * appears as a literal string in any output chunk's source code.\n * Short/common values (< 8 chars) are skipped to avoid false positives.\n */\nexport function detectServerLeak(\n def: EnvDefinition,\n data: Record<string, unknown>,\n bundle: Record<string, { type: string, code?: string }>,\n onSkipped?: (keys: string[]) => void,\n): LeakReport[] {\n const serverKeys = new Set(Object.keys(def.server ?? {}))\n\n const shortSecrets = Object.entries(data).filter(\n (entry): entry is [string, string] =>\n serverKeys.has(entry[0])\n && typeof entry[1] === 'string'\n && entry[1].length < 8,\n )\n\n if (shortSecrets.length > 0 && onSkipped) {\n onSkipped(shortSecrets.map(([k]) => k))\n }\n\n const serverSecrets = Object.entries(data).filter(\n (entry): entry is [string, string] =>\n serverKeys.has(entry[0])\n && typeof entry[1] === 'string'\n && entry[1].length >= 8,\n )\n\n const chunks = Object.entries(bundle).filter(\n ([, chunk]) => chunk.type === 'chunk' && !!chunk.code,\n )\n\n const leaks: LeakReport[] = []\n for (const [key, value] of serverSecrets) {\n for (const [chunkName, chunk] of chunks) {\n if (chunk.code!.includes(value)) {\n leaks.push({ key, chunk: chunkName })\n }\n }\n }\n\n return leaks\n}\n"],"mappings":";;;;;;;;;;AAeA,SAAgB,iBACd,KACA,MACA,QACA,WACc;CACd,MAAM,aAAa,IAAI,IAAI,OAAO,KAAK,IAAI,UAAU,EAAE,CAAC,CAAC;CAEzD,MAAM,eAAe,OAAO,QAAQ,KAAK,CAAC,QACvC,UACC,WAAW,IAAI,MAAM,GAAG,IACrB,OAAO,MAAM,OAAO,YACpB,MAAM,GAAG,SAAS,EACxB;AAED,KAAI,aAAa,SAAS,KAAK,UAC7B,WAAU,aAAa,KAAK,CAAC,OAAO,EAAE,CAAC;CAGzC,MAAM,gBAAgB,OAAO,QAAQ,KAAK,CAAC,QACxC,UACC,WAAW,IAAI,MAAM,GAAG,IACrB,OAAO,MAAM,OAAO,YACpB,MAAM,GAAG,UAAU,EACzB;CAED,MAAM,SAAS,OAAO,QAAQ,OAAO,CAAC,QACnC,GAAG,WAAW,MAAM,SAAS,WAAW,CAAC,CAAC,MAAM,KAClD;CAED,MAAM,QAAsB,EAAE;AAC9B,MAAK,MAAM,CAAC,KAAK,UAAU,cACzB,MAAK,MAAM,CAAC,WAAW,UAAU,OAC/B,KAAI,MAAM,KAAM,SAAS,MAAM,CAC7B,OAAM,KAAK;EAAE;EAAK,OAAO;EAAW,CAAC;AAK3C,QAAO"}
|
package/dist/leak.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as EnvDefinition } from "./types-
|
|
1
|
+
import { t as EnvDefinition } from "./types-DqTMuWwc.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/leak.d.ts
|
|
4
4
|
interface LeakReport {
|
|
@@ -16,7 +16,7 @@ interface LeakReport {
|
|
|
16
16
|
declare function detectServerLeak(def: EnvDefinition, data: Record<string, unknown>, bundle: Record<string, {
|
|
17
17
|
type: string;
|
|
18
18
|
code?: string;
|
|
19
|
-
}
|
|
19
|
+
}>, onSkipped?: (keys: string[]) => void): LeakReport[];
|
|
20
20
|
//#endregion
|
|
21
21
|
export { detectServerLeak };
|
|
22
22
|
//# sourceMappingURL=leak.d.cts.map
|
package/dist/leak.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as EnvDefinition } from "./types-
|
|
1
|
+
import { t as EnvDefinition } from "./types-CluiDKAQ.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/leak.d.ts
|
|
4
4
|
interface LeakReport {
|
|
@@ -16,7 +16,7 @@ interface LeakReport {
|
|
|
16
16
|
declare function detectServerLeak(def: EnvDefinition, data: Record<string, unknown>, bundle: Record<string, {
|
|
17
17
|
type: string;
|
|
18
18
|
code?: string;
|
|
19
|
-
}
|
|
19
|
+
}>, onSkipped?: (keys: string[]) => void): LeakReport[];
|
|
20
20
|
//#endregion
|
|
21
21
|
export { detectServerLeak };
|
|
22
22
|
//# sourceMappingURL=leak.d.mts.map
|
package/dist/leak.mjs
CHANGED
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* appears as a literal string in any output chunk's source code.
|
|
8
8
|
* Short/common values (< 8 chars) are skipped to avoid false positives.
|
|
9
9
|
*/
|
|
10
|
-
function detectServerLeak(def, data, bundle) {
|
|
10
|
+
function detectServerLeak(def, data, bundle, onSkipped) {
|
|
11
11
|
const serverKeys = new Set(Object.keys(def.server ?? {}));
|
|
12
|
+
const shortSecrets = Object.entries(data).filter((entry) => serverKeys.has(entry[0]) && typeof entry[1] === "string" && entry[1].length < 8);
|
|
13
|
+
if (shortSecrets.length > 0 && onSkipped) onSkipped(shortSecrets.map(([k]) => k));
|
|
12
14
|
const serverSecrets = Object.entries(data).filter((entry) => serverKeys.has(entry[0]) && typeof entry[1] === "string" && entry[1].length >= 8);
|
|
13
15
|
const chunks = Object.entries(bundle).filter(([, chunk]) => chunk.type === "chunk" && !!chunk.code);
|
|
14
16
|
const leaks = [];
|
package/dist/leak.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"leak.mjs","names":[],"sources":["../src/leak.ts"],"sourcesContent":["import type { EnvDefinition } from './types'\n\ninterface LeakReport {\n key: string\n chunk: string\n}\n\n/**\n * Scans all client-destined chunks for literal values of server-only vars.\n * Fires in generateBundle() — Rolldown sequential hook, safe.\n *\n * Strategy: for each server-only key, check if its actual runtime value\n * appears as a literal string in any output chunk's source code.\n * Short/common values (< 8 chars) are skipped to avoid false positives.\n */\nexport function detectServerLeak(\n def: EnvDefinition,\n data: Record<string, unknown>,\n bundle: Record<string, { type: string, code?: string }>,\n): LeakReport[] {\n const serverKeys = new Set(Object.keys(def.server ?? {}))\n\n const serverSecrets = Object.entries(data).filter(\n (entry): entry is [string, string] =>\n serverKeys.has(entry[0])\n && typeof entry[1] === 'string'\n && entry[1].length >= 8,\n )\n\n const chunks = Object.entries(bundle).filter(\n ([, chunk]) => chunk.type === 'chunk' && !!chunk.code,\n )\n\n const leaks: LeakReport[] = []\n for (const [key, value] of serverSecrets) {\n for (const [chunkName, chunk] of chunks) {\n if (chunk.code!.includes(value)) {\n leaks.push({ key, chunk: chunkName })\n }\n }\n }\n\n return leaks\n}\n"],"mappings":";;;;;;;;;AAeA,SAAgB,iBACd,KACA,MACA,
|
|
1
|
+
{"version":3,"file":"leak.mjs","names":[],"sources":["../src/leak.ts"],"sourcesContent":["import type { EnvDefinition } from './types'\n\ninterface LeakReport {\n key: string\n chunk: string\n}\n\n/**\n * Scans all client-destined chunks for literal values of server-only vars.\n * Fires in generateBundle() — Rolldown sequential hook, safe.\n *\n * Strategy: for each server-only key, check if its actual runtime value\n * appears as a literal string in any output chunk's source code.\n * Short/common values (< 8 chars) are skipped to avoid false positives.\n */\nexport function detectServerLeak(\n def: EnvDefinition,\n data: Record<string, unknown>,\n bundle: Record<string, { type: string, code?: string }>,\n onSkipped?: (keys: string[]) => void,\n): LeakReport[] {\n const serverKeys = new Set(Object.keys(def.server ?? {}))\n\n const shortSecrets = Object.entries(data).filter(\n (entry): entry is [string, string] =>\n serverKeys.has(entry[0])\n && typeof entry[1] === 'string'\n && entry[1].length < 8,\n )\n\n if (shortSecrets.length > 0 && onSkipped) {\n onSkipped(shortSecrets.map(([k]) => k))\n }\n\n const serverSecrets = Object.entries(data).filter(\n (entry): entry is [string, string] =>\n serverKeys.has(entry[0])\n && typeof entry[1] === 'string'\n && entry[1].length >= 8,\n )\n\n const chunks = Object.entries(bundle).filter(\n ([, chunk]) => chunk.type === 'chunk' && !!chunk.code,\n )\n\n const leaks: LeakReport[] = []\n for (const [key, value] of serverSecrets) {\n for (const [chunkName, chunk] of chunks) {\n if (chunk.code!.includes(value)) {\n leaks.push({ key, chunk: chunkName })\n }\n }\n }\n\n return leaks\n}\n"],"mappings":";;;;;;;;;AAeA,SAAgB,iBACd,KACA,MACA,QACA,WACc;CACd,MAAM,aAAa,IAAI,IAAI,OAAO,KAAK,IAAI,UAAU,EAAE,CAAC,CAAC;CAEzD,MAAM,eAAe,OAAO,QAAQ,KAAK,CAAC,QACvC,UACC,WAAW,IAAI,MAAM,GAAG,IACrB,OAAO,MAAM,OAAO,YACpB,MAAM,GAAG,SAAS,EACxB;AAED,KAAI,aAAa,SAAS,KAAK,UAC7B,WAAU,aAAa,KAAK,CAAC,OAAO,EAAE,CAAC;CAGzC,MAAM,gBAAgB,OAAO,QAAQ,KAAK,CAAC,QACxC,UACC,WAAW,IAAI,MAAM,GAAG,IACrB,OAAO,MAAM,OAAO,YACpB,MAAM,GAAG,UAAU,EACzB;CAED,MAAM,SAAS,OAAO,QAAQ,OAAO,CAAC,QACnC,GAAG,WAAW,MAAM,SAAS,WAAW,CAAC,CAAC,MAAM,KAClD;CAED,MAAM,QAAsB,EAAE;AAC9B,MAAK,MAAM,CAAC,KAAK,UAAU,cACzB,MAAK,MAAM,CAAC,WAAW,UAAU,OAC/B,KAAI,MAAM,KAAM,SAAS,MAAM,CAC7B,OAAM,KAAK;EAAE;EAAK,OAAO;EAAW,CAAC;AAK3C,QAAO"}
|
package/dist/plugin.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_dts = require("./dts-
|
|
1
|
+
const require_dts = require("./dts-DF71HNdJ.cjs");
|
|
2
2
|
const require_schema = require("./schema.cjs");
|
|
3
3
|
const require_config = require("./config.cjs");
|
|
4
4
|
const require_format = require("./format.cjs");
|
|
@@ -31,7 +31,7 @@ function filterStrings(env) {
|
|
|
31
31
|
//#endregion
|
|
32
32
|
//#region src/virtual.ts
|
|
33
33
|
function buildClientModule(def, data) {
|
|
34
|
-
const clientKeys = new Set(
|
|
34
|
+
const clientKeys = new Set(Object.keys(def.client ?? {}));
|
|
35
35
|
const clientData = Object.fromEntries(Object.entries(data).filter(([k]) => clientKeys.has(k)));
|
|
36
36
|
return {
|
|
37
37
|
moduleType: "js",
|
|
@@ -75,7 +75,7 @@ function ViteEnv(options = {}) {
|
|
|
75
75
|
}
|
|
76
76
|
lastValidated = result.data;
|
|
77
77
|
await require_dts.generateDts(envDefinition, resolvedConfig.root);
|
|
78
|
-
const count = Object.keys(result.data
|
|
78
|
+
const count = Object.keys(result.data).length;
|
|
79
79
|
resolvedConfig.logger.info(` \x1B[32m✓\x1B[0m \x1B[36m[vite-env]\x1B[0m ${count} variables validated`);
|
|
80
80
|
},
|
|
81
81
|
resolveId(id) {
|
|
@@ -86,15 +86,11 @@ function ViteEnv(options = {}) {
|
|
|
86
86
|
if (id === "\0virtual:env/client") return buildClientModule(envDefinition, lastValidated);
|
|
87
87
|
if (id === "\0virtual:env/server") return buildServerModule(envDefinition, lastValidated);
|
|
88
88
|
},
|
|
89
|
-
|
|
89
|
+
generateBundle(_options, bundle) {
|
|
90
90
|
if (resolvedConfig.build.ssr) return;
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const formatted = require_format.formatZodError(result.errors);
|
|
95
|
-
throw new Error(`[vite-env] Env validation failed at bundle emit:\n\n${formatted}`);
|
|
96
|
-
}
|
|
97
|
-
const leaks = require_leak.detectServerLeak(envDefinition, result.data || {}, bundle);
|
|
91
|
+
const leaks = require_leak.detectServerLeak(envDefinition, lastValidated, bundle, (keys) => {
|
|
92
|
+
resolvedConfig.logger.warn(` \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(", ")}`);
|
|
93
|
+
});
|
|
98
94
|
if (leaks.length > 0) throw new Error(`[vite-env] Server environment variables detected in client bundle!\n\n${leaks.map((l) => ` ✗ ${l.key} found in ${l.chunk}`).join("\n")}\n\n These variables are marked as server-only and must never reach the browser.`);
|
|
99
95
|
},
|
|
100
96
|
configureServer(server) {
|
|
@@ -105,19 +101,25 @@ function ViteEnv(options = {}) {
|
|
|
105
101
|
if (!node_path.default.basename(file).startsWith(".env")) return;
|
|
106
102
|
clearTimeout(debounceTimer);
|
|
107
103
|
debounceTimer = setTimeout(async () => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
server.moduleGraph.
|
|
119
|
-
server.
|
|
120
|
-
|
|
104
|
+
try {
|
|
105
|
+
const rawEnv = await loadEnvSources(resolvedConfig);
|
|
106
|
+
const result = require_schema.validateEnv(envDefinition, rawEnv);
|
|
107
|
+
if (!result.success) {
|
|
108
|
+
const formatted = require_format.formatZodError(result.errors);
|
|
109
|
+
resolvedConfig.logger.warn(`\n \x1B[33m⚠\x1B[0m \x1B[36m[vite-env]\x1B[0m Env revalidation failed:\n${formatted}`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
lastValidated = result.data;
|
|
113
|
+
const clientMod = server.moduleGraph.getModuleById("\0virtual:env/client");
|
|
114
|
+
const serverMod = server.moduleGraph.getModuleById("\0virtual:env/server");
|
|
115
|
+
if (clientMod) server.moduleGraph.invalidateModule(clientMod);
|
|
116
|
+
if (serverMod) server.moduleGraph.invalidateModule(serverMod);
|
|
117
|
+
if (clientMod || serverMod) {
|
|
118
|
+
server.hot.send({ type: "full-reload" });
|
|
119
|
+
resolvedConfig.logger.info(` \x1B[32m✓\x1B[0m \x1B[36m[vite-env]\x1B[0m Env revalidated`);
|
|
120
|
+
}
|
|
121
|
+
} catch (e) {
|
|
122
|
+
resolvedConfig.logger.error(`\n \x1B[31m✗\x1B[0m \x1B[36m[vite-env]\x1B[0m Failed to reload env files: ${e instanceof Error ? e.message : String(e)}`);
|
|
121
123
|
}
|
|
122
124
|
}, 150);
|
|
123
125
|
});
|
package/dist/plugin.cjs.map
CHANGED
|
@@ -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(
|
|
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 throw new Error(\n `[vite-env] Server environment variables detected in client bundle!\\n\\n${leaks.map(l => ` ✗ ${l.key} found in ${l.chunk}`).join('\\n')\n }\\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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"}
|
package/dist/plugin.mjs
CHANGED
|
@@ -29,7 +29,7 @@ function filterStrings(env) {
|
|
|
29
29
|
//#endregion
|
|
30
30
|
//#region src/virtual.ts
|
|
31
31
|
function buildClientModule(def, data) {
|
|
32
|
-
const clientKeys = new Set(
|
|
32
|
+
const clientKeys = new Set(Object.keys(def.client ?? {}));
|
|
33
33
|
const clientData = Object.fromEntries(Object.entries(data).filter(([k]) => clientKeys.has(k)));
|
|
34
34
|
return {
|
|
35
35
|
moduleType: "js",
|
|
@@ -73,7 +73,7 @@ function ViteEnv(options = {}) {
|
|
|
73
73
|
}
|
|
74
74
|
lastValidated = result.data;
|
|
75
75
|
await generateDts(envDefinition, resolvedConfig.root);
|
|
76
|
-
const count = Object.keys(result.data
|
|
76
|
+
const count = Object.keys(result.data).length;
|
|
77
77
|
resolvedConfig.logger.info(` \x1B[32m✓\x1B[0m \x1B[36m[vite-env]\x1B[0m ${count} variables validated`);
|
|
78
78
|
},
|
|
79
79
|
resolveId(id) {
|
|
@@ -84,15 +84,11 @@ function ViteEnv(options = {}) {
|
|
|
84
84
|
if (id === "\0virtual:env/client") return buildClientModule(envDefinition, lastValidated);
|
|
85
85
|
if (id === "\0virtual:env/server") return buildServerModule(envDefinition, lastValidated);
|
|
86
86
|
},
|
|
87
|
-
|
|
87
|
+
generateBundle(_options, bundle) {
|
|
88
88
|
if (resolvedConfig.build.ssr) return;
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const formatted = formatZodError(result.errors);
|
|
93
|
-
throw new Error(`[vite-env] Env validation failed at bundle emit:\n\n${formatted}`);
|
|
94
|
-
}
|
|
95
|
-
const leaks = detectServerLeak(envDefinition, result.data || {}, bundle);
|
|
89
|
+
const leaks = detectServerLeak(envDefinition, lastValidated, bundle, (keys) => {
|
|
90
|
+
resolvedConfig.logger.warn(` \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(", ")}`);
|
|
91
|
+
});
|
|
96
92
|
if (leaks.length > 0) throw new Error(`[vite-env] Server environment variables detected in client bundle!\n\n${leaks.map((l) => ` ✗ ${l.key} found in ${l.chunk}`).join("\n")}\n\n These variables are marked as server-only and must never reach the browser.`);
|
|
97
93
|
},
|
|
98
94
|
configureServer(server) {
|
|
@@ -103,19 +99,25 @@ function ViteEnv(options = {}) {
|
|
|
103
99
|
if (!path.basename(file).startsWith(".env")) return;
|
|
104
100
|
clearTimeout(debounceTimer);
|
|
105
101
|
debounceTimer = setTimeout(async () => {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
server.moduleGraph.
|
|
117
|
-
server.
|
|
118
|
-
|
|
102
|
+
try {
|
|
103
|
+
const rawEnv = await loadEnvSources(resolvedConfig);
|
|
104
|
+
const result = validateEnv(envDefinition, rawEnv);
|
|
105
|
+
if (!result.success) {
|
|
106
|
+
const formatted = formatZodError(result.errors);
|
|
107
|
+
resolvedConfig.logger.warn(`\n \x1B[33m⚠\x1B[0m \x1B[36m[vite-env]\x1B[0m Env revalidation failed:\n${formatted}`);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
lastValidated = result.data;
|
|
111
|
+
const clientMod = server.moduleGraph.getModuleById("\0virtual:env/client");
|
|
112
|
+
const serverMod = server.moduleGraph.getModuleById("\0virtual:env/server");
|
|
113
|
+
if (clientMod) server.moduleGraph.invalidateModule(clientMod);
|
|
114
|
+
if (serverMod) server.moduleGraph.invalidateModule(serverMod);
|
|
115
|
+
if (clientMod || serverMod) {
|
|
116
|
+
server.hot.send({ type: "full-reload" });
|
|
117
|
+
resolvedConfig.logger.info(` \x1B[32m✓\x1B[0m \x1B[36m[vite-env]\x1B[0m Env revalidated`);
|
|
118
|
+
}
|
|
119
|
+
} catch (e) {
|
|
120
|
+
resolvedConfig.logger.error(`\n \x1B[31m✗\x1B[0m \x1B[36m[vite-env]\x1B[0m Failed to reload env files: ${e instanceof Error ? e.message : String(e)}`);
|
|
119
121
|
}
|
|
120
122
|
}, 150);
|
|
121
123
|
});
|
package/dist/plugin.mjs.map
CHANGED
|
@@ -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(
|
|
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 throw new Error(\n `[vite-env] Server environment variables detected in client bundle!\\n\\n${leaks.map(l => ` ✗ ${l.key} found in ${l.chunk}`).join('\\n')\n }\\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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"}
|
package/dist/schema.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("./dts-
|
|
2
|
+
require("./dts-DF71HNdJ.cjs");
|
|
3
3
|
let zod = require("zod");
|
|
4
4
|
//#region src/schema.ts
|
|
5
5
|
function defineEnv(definition) {
|
|
@@ -11,8 +11,7 @@ function defineEnv(definition) {
|
|
|
11
11
|
function validateEnv(def, rawEnv) {
|
|
12
12
|
const combinedShape = {
|
|
13
13
|
...def.server,
|
|
14
|
-
...def.client
|
|
15
|
-
...def.shared
|
|
14
|
+
...def.client
|
|
16
15
|
};
|
|
17
16
|
const result = zod.z.object(combinedShape).safeParse(rawEnv);
|
|
18
17
|
if (result.success) return {
|
package/dist/schema.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.cjs","names":["z"],"sources":["../src/schema.ts"],"sourcesContent":["import type { EnvDefinition, ValidationResult } from './types'\nimport { z } from 'zod'\n\nexport function defineEnv(definition:
|
|
1
|
+
{"version":3,"file":"schema.cjs","names":["z"],"sources":["../src/schema.ts"],"sourcesContent":["import type { EnvDefinition, ValidationResult } from './types'\nimport { z } from 'zod'\n\nexport function defineEnv<T extends EnvDefinition>(definition: T): T {\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\n}\n\nexport function validateEnv(\n def: EnvDefinition,\n rawEnv: Record<string, string>,\n): ValidationResult {\n const combinedShape = {\n ...def.server,\n ...def.client,\n }\n\n const schema = z.object(combinedShape)\n const result = schema.safeParse(rawEnv)\n\n if (result.success) {\n return { success: true, data: result.data, errors: [] as const }\n }\n\n return {\n success: false,\n data: null,\n errors: result.error.issues,\n }\n}\n"],"mappings":";;;;AAGA,SAAgB,UAAmC,YAAkB;AACnE,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;;AAGT,SAAgB,YACd,KACA,QACkB;CAClB,MAAM,gBAAgB;EACpB,GAAG,IAAI;EACP,GAAG,IAAI;EACR;CAGD,MAAM,SADSA,IAAAA,EAAE,OAAO,cAAc,CAChB,UAAU,OAAO;AAEvC,KAAI,OAAO,QACT,QAAO;EAAE,SAAS;EAAM,MAAM,OAAO;EAAM,QAAQ,EAAE;EAAW;AAGlE,QAAO;EACL,SAAS;EACT,MAAM;EACN,QAAQ,OAAO,MAAM;EACtB"}
|
package/dist/schema.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { i as ValidationResult, t as EnvDefinition } from "./types-
|
|
1
|
+
import { i as ValidationResult, t as EnvDefinition } from "./types-DqTMuWwc.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/schema.d.ts
|
|
4
|
-
declare function defineEnv(definition:
|
|
4
|
+
declare function defineEnv<T extends EnvDefinition>(definition: T): T;
|
|
5
5
|
declare function validateEnv(def: EnvDefinition, rawEnv: Record<string, string>): ValidationResult;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { defineEnv, validateEnv };
|
package/dist/schema.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { i as ValidationResult, t as EnvDefinition } from "./types-
|
|
1
|
+
import { i as ValidationResult, t as EnvDefinition } from "./types-CluiDKAQ.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/schema.d.ts
|
|
4
|
-
declare function defineEnv(definition:
|
|
4
|
+
declare function defineEnv<T extends EnvDefinition>(definition: T): T;
|
|
5
5
|
declare function validateEnv(def: EnvDefinition, rawEnv: Record<string, string>): ValidationResult;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { defineEnv, validateEnv };
|
package/dist/schema.mjs
CHANGED
|
@@ -9,8 +9,7 @@ function defineEnv(definition) {
|
|
|
9
9
|
function validateEnv(def, rawEnv) {
|
|
10
10
|
const combinedShape = {
|
|
11
11
|
...def.server,
|
|
12
|
-
...def.client
|
|
13
|
-
...def.shared
|
|
12
|
+
...def.client
|
|
14
13
|
};
|
|
15
14
|
const result = z.object(combinedShape).safeParse(rawEnv);
|
|
16
15
|
if (result.success) return {
|
package/dist/schema.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.mjs","names":[],"sources":["../src/schema.ts"],"sourcesContent":["import type { EnvDefinition, ValidationResult } from './types'\nimport { z } from 'zod'\n\nexport function defineEnv(definition:
|
|
1
|
+
{"version":3,"file":"schema.mjs","names":[],"sources":["../src/schema.ts"],"sourcesContent":["import type { EnvDefinition, ValidationResult } from './types'\nimport { z } from 'zod'\n\nexport function defineEnv<T extends EnvDefinition>(definition: T): T {\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\n}\n\nexport function validateEnv(\n def: EnvDefinition,\n rawEnv: Record<string, string>,\n): ValidationResult {\n const combinedShape = {\n ...def.server,\n ...def.client,\n }\n\n const schema = z.object(combinedShape)\n const result = schema.safeParse(rawEnv)\n\n if (result.success) {\n return { success: true, data: result.data, errors: [] as const }\n }\n\n return {\n success: false,\n data: null,\n errors: result.error.issues,\n }\n}\n"],"mappings":";;AAGA,SAAgB,UAAmC,YAAkB;AACnE,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;;AAGT,SAAgB,YACd,KACA,QACkB;CAClB,MAAM,gBAAgB;EACpB,GAAG,IAAI;EACP,GAAG,IAAI;EACR;CAGD,MAAM,SADS,EAAE,OAAO,cAAc,CAChB,UAAU,OAAO;AAEvC,KAAI,OAAO,QACT,QAAO;EAAE,SAAS;EAAM,MAAM,OAAO;EAAM,QAAQ,EAAE;EAAW;AAGlE,QAAO;EACL,SAAS;EACT,MAAM;EACN,QAAQ,OAAO,MAAM;EACtB"}
|
|
@@ -4,15 +4,19 @@ import { z } from "zod";
|
|
|
4
4
|
interface EnvDefinition {
|
|
5
5
|
server?: z.ZodRawShape;
|
|
6
6
|
client?: z.ZodRawShape;
|
|
7
|
-
shared?: z.ZodRawShape;
|
|
8
7
|
}
|
|
9
|
-
|
|
10
|
-
success:
|
|
11
|
-
data: Record<string, unknown
|
|
8
|
+
type ValidationResult = {
|
|
9
|
+
success: true;
|
|
10
|
+
data: Record<string, unknown>;
|
|
11
|
+
errors: [];
|
|
12
|
+
} | {
|
|
13
|
+
success: false;
|
|
14
|
+
data: null;
|
|
12
15
|
errors: z.core.$ZodIssue[];
|
|
13
|
-
}
|
|
14
|
-
type
|
|
15
|
-
type
|
|
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']>>>;
|
|
16
20
|
//#endregion
|
|
17
21
|
export { ValidationResult as i, InferClientEnv as n, InferServerEnv as r, EnvDefinition as t };
|
|
18
|
-
//# sourceMappingURL=types-
|
|
22
|
+
//# sourceMappingURL=types-CluiDKAQ.d.mts.map
|
|
@@ -4,15 +4,19 @@ import { z } from "zod";
|
|
|
4
4
|
interface EnvDefinition {
|
|
5
5
|
server?: z.ZodRawShape;
|
|
6
6
|
client?: z.ZodRawShape;
|
|
7
|
-
shared?: z.ZodRawShape;
|
|
8
7
|
}
|
|
9
|
-
|
|
10
|
-
success:
|
|
11
|
-
data: Record<string, unknown
|
|
8
|
+
type ValidationResult = {
|
|
9
|
+
success: true;
|
|
10
|
+
data: Record<string, unknown>;
|
|
11
|
+
errors: [];
|
|
12
|
+
} | {
|
|
13
|
+
success: false;
|
|
14
|
+
data: null;
|
|
12
15
|
errors: z.core.$ZodIssue[];
|
|
13
|
-
}
|
|
14
|
-
type
|
|
15
|
-
type
|
|
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']>>>;
|
|
16
20
|
//#endregion
|
|
17
21
|
export { ValidationResult as i, InferClientEnv as n, InferServerEnv as r, EnvDefinition as t };
|
|
18
|
-
//# sourceMappingURL=types-
|
|
22
|
+
//# sourceMappingURL=types-DqTMuWwc.d.cts.map
|
package/package.json
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vite-env/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "The env.ts layer for Vite — define once, validate everywhere, import with types",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/pyyupsk/vite-env#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/pyyupsk/vite-env.git",
|
|
11
|
+
"directory": "packages/core"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"vite",
|
|
15
|
+
"vite-plugin",
|
|
16
|
+
"environment-variables",
|
|
17
|
+
"env",
|
|
18
|
+
"typescript",
|
|
19
|
+
"zod",
|
|
20
|
+
"validation",
|
|
21
|
+
"rolldown",
|
|
22
|
+
"dotenv"
|
|
23
|
+
],
|
|
7
24
|
"exports": {
|
|
8
25
|
".": {
|
|
9
26
|
"import": {
|
|
@@ -81,6 +98,7 @@
|
|
|
81
98
|
"module": "./dist/index.mjs",
|
|
82
99
|
"types": "./dist/index.d.cts",
|
|
83
100
|
"files": [
|
|
101
|
+
"README.md",
|
|
84
102
|
"dist"
|
|
85
103
|
],
|
|
86
104
|
"engines": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dts-DT7HjKrz.cjs","names":["fs","path","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 ...def.shared,\n }\n const serverKeys = {\n ...def.server,\n ...def.client,\n ...def.shared,\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 await fs.writeFile(path.join(root, 'vite-env.d.ts'), dts, 'utf-8')\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 unknown 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 || schema instanceof z.ZodDefault\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,eAAsB,YACpB,KACA,MACe;CACf,MAAM,aAAa;EACjB,GAAG,IAAI;EACP,GAAG,IAAI;EACR;CACD,MAAM,aAAa;EACjB,GAAG,IAAI;EACP,GAAG,IAAI;EACP,GAAG,IAAI;EACR;CAKD,MAAM,MAAM;;;;;EAHS,mBAAmB,WAAW,CAQtC;;;;;;;;EAPQ,mBAAmB,WAAW,CAetC;;;;;;AAOb,OAAMA,iBAAAA,QAAG,UAAUC,UAAAA,QAAK,KAAK,MAAM,gBAAgB,EAAE,KAAK,QAAQ;;AAGpE,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,eAAe,kBAAkBA,IAAAA,EAAE"}
|