@eventuras/app-config 0.1.4
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 +423 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +29 -0
- package/dist/cli.js.map +1 -0
- package/dist/clientside.d.ts +97 -0
- package/dist/clientside.d.ts.map +1 -0
- package/dist/clientside.js +94 -0
- package/dist/clientside.js.map +1 -0
- package/dist/generator.d.ts +18 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +48 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +277 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +59 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/next.d.ts +28 -0
- package/dist/next.d.ts.map +1 -0
- package/dist/types.d.ts +98 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/validator.d.ts +18 -0
- package/dist/validator.d.ts.map +1 -0
- package/package.json +64 -0
- package/schema/app-config.schema.json +98 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6BH,MAAM,WAAW,oBAAoB;IACnC,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkChF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
//#region src/generator.ts
|
|
4
|
+
/**
|
|
5
|
+
* Generate TypeScript types from app.config.json
|
|
6
|
+
* This module provides functions to generate type definitions from configuration files
|
|
7
|
+
*/
|
|
8
|
+
function mapTypeToTS(type) {
|
|
9
|
+
return {
|
|
10
|
+
string: "string",
|
|
11
|
+
url: "string",
|
|
12
|
+
int: "number",
|
|
13
|
+
bool: "boolean",
|
|
14
|
+
json: "unknown"
|
|
15
|
+
}[type];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generate TypeScript type definitions from an app.config.json file
|
|
19
|
+
* @param options - Configuration options for type generation
|
|
20
|
+
*/
|
|
21
|
+
async function generateTypes(options) {
|
|
22
|
+
const { configPath, outputPath, interfaceName = "PublicEnv" } = options;
|
|
23
|
+
console.log("📝 Reading config from:", configPath);
|
|
24
|
+
const configContent = await fs.readFile(configPath, "utf-8");
|
|
25
|
+
const config = JSON.parse(configContent);
|
|
26
|
+
const output = `/**
|
|
27
|
+
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
28
|
+
* Generated from app.config.json
|
|
29
|
+
*
|
|
30
|
+
* To regenerate, run: pnpm generate:config-types
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export interface ${interfaceName} {
|
|
34
|
+
${Object.entries(config.env).filter(([, def]) => def.client).map(([name, def]) => {
|
|
35
|
+
const tsType = mapTypeToTS(def.type);
|
|
36
|
+
return ` ${name}${def.required ? "" : "?"}: ${tsType};`;
|
|
37
|
+
}).join("\n")}
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
const outputDir = path.dirname(outputPath);
|
|
41
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
42
|
+
await fs.writeFile(outputPath, output, "utf-8");
|
|
43
|
+
console.log("✅ Generated types at:", outputPath);
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { generateTypes };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.js","names":[],"sources":["../src/generator.ts"],"sourcesContent":["/**\n * Generate TypeScript types from app.config.json\n * This module provides functions to generate type definitions from configuration files\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\ntype EnvVarType = 'string' | 'url' | 'int' | 'bool' | 'json';\n\ninterface EnvVarDef {\n required: boolean;\n client: boolean;\n type: EnvVarType;\n description: string;\n}\n\ninterface AppConfig {\n env: Record<string, EnvVarDef>;\n}\n\nfunction mapTypeToTS(type: EnvVarType): string {\n const map: Record<EnvVarType, string> = {\n string: 'string',\n url: 'string',\n int: 'number',\n bool: 'boolean',\n json: 'unknown',\n };\n return map[type];\n}\n\nexport interface GenerateTypesOptions {\n /** Path to app.config.json file */\n configPath: string;\n /** Path where the generated .d.ts file should be written */\n outputPath: string;\n /** Name of the interface to generate (default: 'PublicEnv') */\n interfaceName?: string;\n}\n\n/**\n * Generate TypeScript type definitions from an app.config.json file\n * @param options - Configuration options for type generation\n */\nexport async function generateTypes(options: GenerateTypesOptions): Promise<void> {\n const { configPath, outputPath, interfaceName = 'PublicEnv' } = options;\n\n console.log('📝 Reading config from:', configPath);\n\n const configContent = await fs.readFile(configPath, 'utf-8');\n const config: AppConfig = JSON.parse(configContent);\n\n const publicVars = Object.entries(config.env)\n .filter(([, def]) => def.client)\n .map(([name, def]) => {\n const tsType = mapTypeToTS(def.type);\n const optional = def.required ? '' : '?';\n return ` ${name}${optional}: ${tsType};`;\n });\n\n const output = `/**\n * AUTO-GENERATED FILE - DO NOT EDIT\n * Generated from app.config.json\n *\n * To regenerate, run: pnpm generate:config-types\n */\n\nexport interface ${interfaceName} {\n${publicVars.join('\\n')}\n}\n`;\n\n // Ensure the output directory exists\n const outputDir = path.dirname(outputPath);\n await fs.mkdir(outputDir, { recursive: true });\n\n await fs.writeFile(outputPath, output, 'utf-8');\n console.log('✅ Generated types at:', outputPath);\n}\n"],"mappings":";;;;;;;AAqBA,SAAS,YAAY,MAA0B;CAQ7C,OAAO;EANL,QAAQ;EACR,KAAK;EACL,KAAK;EACL,MAAM;EACN,MAAM;CAED,EAAI;AACb;;;;;AAeA,eAAsB,cAAc,SAA8C;CAChF,MAAM,EAAE,YAAY,YAAY,gBAAgB,gBAAgB;CAEhE,QAAQ,IAAI,2BAA2B,UAAU;CAEjD,MAAM,gBAAgB,MAAM,GAAG,SAAS,YAAY,OAAO;CAC3D,MAAM,SAAoB,KAAK,MAAM,aAAa;CAUlD,MAAM,SAAS;;;;;;;mBAOE,cAAc;EAfZ,OAAO,QAAQ,OAAO,GAAG,CAAC,CAC1C,QAAQ,GAAG,SAAS,IAAI,MAAM,CAAC,CAC/B,KAAK,CAAC,MAAM,SAAS;EACpB,MAAM,SAAS,YAAY,IAAI,IAAI;EAEnC,OAAO,KAAK,OADK,IAAI,WAAW,KAAK,IACT,IAAI,OAAO;CACzC,CAUF,CAAA,CAAW,KAAK,IAAI,EAAE;;;CAKtB,MAAM,YAAY,KAAK,QAAQ,UAAU;CACzC,MAAM,GAAG,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;CAE7C,MAAM,GAAG,UAAU,YAAY,QAAQ,OAAO;CAC9C,QAAQ,IAAI,yBAAyB,UAAU;AACjD"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ConfigLoader, createConfig, validate } from './loader.js';
|
|
2
|
+
export { EnvValidationError, parseEnvValue, getTypeString } from './validator.js';
|
|
3
|
+
export type { AppConfig, EnvVarDefinition, EnvVarType } from './types.js';
|
|
4
|
+
export { appConfigSchema, envVarDefinitionSchema } from './types.js';
|
|
5
|
+
export { createPublicEnvGetters, createEnvironment, type EnvironmentObject } from './next.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAClF,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/types.ts
|
|
3
|
+
/**
|
|
4
|
+
* Zod schema for environment variable definition
|
|
5
|
+
*/
|
|
6
|
+
var envVarDefinitionSchema = z.object({
|
|
7
|
+
required: z.boolean(),
|
|
8
|
+
client: z.boolean(),
|
|
9
|
+
type: z.enum([
|
|
10
|
+
"string",
|
|
11
|
+
"url",
|
|
12
|
+
"int",
|
|
13
|
+
"bool",
|
|
14
|
+
"json"
|
|
15
|
+
]),
|
|
16
|
+
description: z.string(),
|
|
17
|
+
default: z.union([
|
|
18
|
+
z.string(),
|
|
19
|
+
z.number(),
|
|
20
|
+
z.boolean(),
|
|
21
|
+
z.object({})
|
|
22
|
+
]).optional(),
|
|
23
|
+
pattern: z.string().optional(),
|
|
24
|
+
enum: z.array(z.string()).optional()
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Zod schema for app configuration
|
|
28
|
+
*/
|
|
29
|
+
var appConfigSchema = z.object({
|
|
30
|
+
$schema: z.string().optional(),
|
|
31
|
+
name: z.string(),
|
|
32
|
+
type: z.literal("app"),
|
|
33
|
+
description: z.string().optional(),
|
|
34
|
+
env: z.record(z.string(), envVarDefinitionSchema),
|
|
35
|
+
build: z.object({
|
|
36
|
+
outDir: z.string().optional(),
|
|
37
|
+
target: z.string().optional()
|
|
38
|
+
}).optional(),
|
|
39
|
+
runtime: z.object({ port: z.number().optional() }).optional()
|
|
40
|
+
});
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/validator.ts
|
|
43
|
+
/**
|
|
44
|
+
* Validation error class
|
|
45
|
+
*/
|
|
46
|
+
var EnvValidationError = class extends Error {
|
|
47
|
+
varName;
|
|
48
|
+
definition;
|
|
49
|
+
constructor(message, varName, definition) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.varName = varName;
|
|
52
|
+
this.definition = definition;
|
|
53
|
+
this.name = "EnvValidationError";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Parse and validate an environment variable value based on its type
|
|
58
|
+
*/
|
|
59
|
+
function parseEnvValue(varName, rawValue, definition) {
|
|
60
|
+
if (rawValue === void 0 || rawValue === "") {
|
|
61
|
+
if (definition.required && definition.default === void 0) throw new EnvValidationError(`Required environment variable "${varName}" is not set.\nDescription: ${definition.description}`, varName, definition);
|
|
62
|
+
return definition.default;
|
|
63
|
+
}
|
|
64
|
+
if (definition.enum && !definition.enum.includes(rawValue)) throw new EnvValidationError(`Environment variable "${varName}" must be one of: ${definition.enum.join(", ")}.\nGot: "${rawValue}"`, varName, definition);
|
|
65
|
+
if (definition.pattern) {
|
|
66
|
+
if (!new RegExp(definition.pattern).test(rawValue)) throw new EnvValidationError(`Environment variable "${varName}" does not match pattern: ${definition.pattern}.\nGot: "${rawValue}"`, varName, definition);
|
|
67
|
+
}
|
|
68
|
+
switch (definition.type) {
|
|
69
|
+
case "string": return rawValue;
|
|
70
|
+
case "url": try {
|
|
71
|
+
new URL(rawValue);
|
|
72
|
+
return rawValue;
|
|
73
|
+
} catch {
|
|
74
|
+
throw new EnvValidationError(`Environment variable "${varName}" must be a valid URL.\nGot: "${rawValue}"`, varName, definition);
|
|
75
|
+
}
|
|
76
|
+
case "int": {
|
|
77
|
+
const intValue = Number.parseInt(rawValue, 10);
|
|
78
|
+
if (Number.isNaN(intValue)) throw new EnvValidationError(`Environment variable "${varName}" must be a valid integer.\nGot: "${rawValue}"`, varName, definition);
|
|
79
|
+
return intValue;
|
|
80
|
+
}
|
|
81
|
+
case "bool": {
|
|
82
|
+
const lowerValue = rawValue.toLowerCase();
|
|
83
|
+
if (lowerValue === "true" || lowerValue === "1") return true;
|
|
84
|
+
if (lowerValue === "false" || lowerValue === "0") return false;
|
|
85
|
+
throw new EnvValidationError(`Environment variable "${varName}" must be a boolean (true/false, 1/0).\nGot: "${rawValue}"`, varName, definition);
|
|
86
|
+
}
|
|
87
|
+
case "json": try {
|
|
88
|
+
return JSON.parse(rawValue);
|
|
89
|
+
} catch {
|
|
90
|
+
throw new EnvValidationError(`Environment variable "${varName}" must be valid JSON.\nGot: "${rawValue}"`, varName, definition);
|
|
91
|
+
}
|
|
92
|
+
default: {
|
|
93
|
+
const _exhaustive = definition.type;
|
|
94
|
+
throw new Error(`Unknown type: ${_exhaustive}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get the TypeScript type string for an environment variable type
|
|
100
|
+
*/
|
|
101
|
+
function getTypeString(type) {
|
|
102
|
+
switch (type) {
|
|
103
|
+
case "string":
|
|
104
|
+
case "url": return "string";
|
|
105
|
+
case "int": return "number";
|
|
106
|
+
case "bool": return "boolean";
|
|
107
|
+
case "json": return "unknown";
|
|
108
|
+
default: throw new Error(`Unknown type: ${type}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/loader.ts
|
|
113
|
+
/**
|
|
114
|
+
* Configuration loader class
|
|
115
|
+
*/
|
|
116
|
+
var ConfigLoader = class {
|
|
117
|
+
processEnv;
|
|
118
|
+
config;
|
|
119
|
+
envValues = {};
|
|
120
|
+
/**
|
|
121
|
+
* Direct access to environment variables as properties
|
|
122
|
+
* @example config.env.AUTH0_CLIENT_ID
|
|
123
|
+
*/
|
|
124
|
+
env;
|
|
125
|
+
constructor(configObject, processEnv = process.env) {
|
|
126
|
+
this.processEnv = processEnv;
|
|
127
|
+
const result = appConfigSchema.safeParse(configObject);
|
|
128
|
+
if (!result.success) {
|
|
129
|
+
const issues = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
130
|
+
throw new Error(`Invalid app.config.json:\n${issues}`);
|
|
131
|
+
}
|
|
132
|
+
this.config = result.data;
|
|
133
|
+
const isNextBuild = this.processEnv.NEXT_PHASE === "phase-production-build" || this.processEnv.NEXT_PHASE === "phase-development-server";
|
|
134
|
+
this.validateEnvironment(!isNextBuild);
|
|
135
|
+
this.env = new Proxy(this.envValues, {
|
|
136
|
+
get: (target, prop) => {
|
|
137
|
+
if (!(prop in target)) throw new Error(`Environment variable "${prop}" is not defined in app.config.json.\nAvailable variables: ${Object.keys(target).join(", ")}`);
|
|
138
|
+
return target[prop];
|
|
139
|
+
},
|
|
140
|
+
set: () => {
|
|
141
|
+
throw new Error("Cannot modify environment variables through config.env");
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Validate all environment variables according to config
|
|
147
|
+
* @param strictRequired - Whether to throw errors for missing required vars (false during build)
|
|
148
|
+
*/
|
|
149
|
+
validateEnvironment(strictRequired = true) {
|
|
150
|
+
const errors = [];
|
|
151
|
+
for (const [varName, definition] of Object.entries(this.config.env)) try {
|
|
152
|
+
const value = parseEnvValue(varName, this.processEnv[varName], definition);
|
|
153
|
+
this.envValues[varName] = value;
|
|
154
|
+
if (this.processEnv[varName] === void 0 && value !== void 0) this.processEnv[varName] = String(value);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (error instanceof EnvValidationError) {
|
|
157
|
+
if (!strictRequired && error.message.includes("Required environment variable")) {
|
|
158
|
+
this.envValues[varName] = definition.default ?? "";
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
errors.push(error);
|
|
162
|
+
} else throw error;
|
|
163
|
+
}
|
|
164
|
+
if (errors.length > 0) {
|
|
165
|
+
const errorMessage = errors.map((e) => `\n❌ ${e.message}`).join("\n");
|
|
166
|
+
throw new Error(`Environment validation failed:${errorMessage}\n`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get a typed environment variable value
|
|
171
|
+
*/
|
|
172
|
+
get(varName) {
|
|
173
|
+
if (!(varName in this.envValues)) throw new Error(`Environment variable "${varName}" is not defined in app.config.json.\nAvailable variables: ${Object.keys(this.envValues).join(", ")}`);
|
|
174
|
+
return this.envValues[varName];
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get the raw app configuration
|
|
178
|
+
*/
|
|
179
|
+
getConfig() {
|
|
180
|
+
return this.config;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get all environment variables as a typed object
|
|
184
|
+
*/
|
|
185
|
+
getAllEnv() {
|
|
186
|
+
return { ...this.envValues };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Check if a variable exists
|
|
190
|
+
*/
|
|
191
|
+
has(varName) {
|
|
192
|
+
return varName in this.envValues;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Create a config loader from a config object
|
|
197
|
+
*/
|
|
198
|
+
function createConfig(configObject) {
|
|
199
|
+
return new ConfigLoader(configObject);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Validate app configuration and environment variables.
|
|
203
|
+
* This is useful for running validation explicitly during app initialization.
|
|
204
|
+
*
|
|
205
|
+
* @param configObject - App configuration object (parsed from app.config.json)
|
|
206
|
+
* @throws Error if configuration is invalid or required env vars are missing
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```typescript
|
|
210
|
+
* // In your app initialization (e.g., layout.tsx or main.ts)
|
|
211
|
+
* import { validate } from '@eventuras/app-config';
|
|
212
|
+
* import appConfigJson from './app.config.json';
|
|
213
|
+
*
|
|
214
|
+
* validate(appConfigJson);
|
|
215
|
+
* console.log('✓ Environment validated successfully');
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
function validate(configObject) {
|
|
219
|
+
new ConfigLoader(configObject);
|
|
220
|
+
}
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region src/next.ts
|
|
223
|
+
/**
|
|
224
|
+
* Create explicit getters for NEXT_PUBLIC_* environment variables.
|
|
225
|
+
*
|
|
226
|
+
* This is required for Next.js because it performs build-time replacement of
|
|
227
|
+
* process.env.NEXT_PUBLIC_* variables. We must access process.env directly,
|
|
228
|
+
* not through a runtime lookup.
|
|
229
|
+
*
|
|
230
|
+
* @param config - The app configuration
|
|
231
|
+
* @returns An object with explicit getters for each NEXT_PUBLIC_* variable
|
|
232
|
+
*/
|
|
233
|
+
function createPublicEnvGetters(config) {
|
|
234
|
+
const getters = {};
|
|
235
|
+
for (const [varName, definition] of Object.entries(config.env)) if (definition.client && varName.startsWith("NEXT_PUBLIC_")) Object.defineProperty(getters, varName, {
|
|
236
|
+
get() {
|
|
237
|
+
return process.env[varName];
|
|
238
|
+
},
|
|
239
|
+
enumerable: true
|
|
240
|
+
});
|
|
241
|
+
return getters;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Create a complete Environment object with validation and explicit getters.
|
|
245
|
+
*
|
|
246
|
+
* This provides a drop-in replacement for the old Environment class pattern,
|
|
247
|
+
* with automatic validation and proper Next.js NEXT_PUBLIC_* support.
|
|
248
|
+
*
|
|
249
|
+
* @param configPath - Path to app.config.json
|
|
250
|
+
* @returns An object with validate(), get(), and explicit NEXT_PUBLIC_* getters
|
|
251
|
+
*/
|
|
252
|
+
function createEnvironment(configPath) {
|
|
253
|
+
let configInstance = null;
|
|
254
|
+
function getConfigInstance() {
|
|
255
|
+
if (!configInstance) configInstance = new ConfigLoader(configPath);
|
|
256
|
+
return configInstance;
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
/**
|
|
260
|
+
* Validate environment (happens automatically on first access)
|
|
261
|
+
*/
|
|
262
|
+
validate: () => {
|
|
263
|
+
getConfigInstance();
|
|
264
|
+
},
|
|
265
|
+
/**
|
|
266
|
+
* Get a server-side environment variable
|
|
267
|
+
*/
|
|
268
|
+
get: (varName) => {
|
|
269
|
+
return getConfigInstance().get(varName);
|
|
270
|
+
},
|
|
271
|
+
...createPublicEnvGetters(getConfigInstance().getConfig())
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
//#endregion
|
|
275
|
+
export { ConfigLoader, EnvValidationError, appConfigSchema, createConfig, createEnvironment, createPublicEnvGetters, envVarDefinitionSchema, getTypeString, parseEnvValue, validate };
|
|
276
|
+
|
|
277
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/types.ts","../src/validator.ts","../src/loader.ts","../src/next.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Supported environment variable types\n */\nexport type EnvVarType = 'string' | 'url' | 'int' | 'bool' | 'json';\n\n/**\n * Environment variable definition\n */\nexport interface EnvVarDefinition {\n /** Whether this variable is required */\n required: boolean;\n /** Whether this variable is exposed to the client (e.g., NEXT_PUBLIC_*) */\n client: boolean;\n /** Expected type of the variable */\n type: EnvVarType;\n /** Human-readable description */\n description: string;\n /** Default value if not provided */\n default?: string | number | boolean | object;\n /** Regular expression pattern for validation */\n pattern?: string;\n /** Allowed values */\n enum?: string[];\n}\n\n/**\n * Application configuration schema\n */\nexport interface AppConfig {\n /** JSON Schema reference */\n $schema?: string;\n /** Application package name */\n name: string;\n /** Configuration type */\n type: 'app';\n /** Application description */\n description?: string;\n /** Environment variable definitions */\n env: Record<string, EnvVarDefinition>;\n /** Build configuration */\n build?: {\n outDir?: string;\n target?: string;\n };\n /** Runtime configuration */\n runtime?: {\n port?: number;\n };\n}\n\n/**\n * Zod schema for environment variable definition\n */\nexport const envVarDefinitionSchema = z.object({\n required: z.boolean(),\n client: z.boolean(),\n type: z.enum(['string', 'url', 'int', 'bool', 'json']),\n description: z.string(),\n default: z.union([z.string(), z.number(), z.boolean(), z.object({})]).optional(),\n pattern: z.string().optional(),\n enum: z.array(z.string()).optional(),\n});\n\n/**\n * Zod schema for app configuration\n */\nexport const appConfigSchema = z.object({\n $schema: z.string().optional(),\n name: z.string(),\n type: z.literal('app'),\n description: z.string().optional(),\n env: z.record(z.string(), envVarDefinitionSchema),\n build: z\n .object({\n outDir: z.string().optional(),\n target: z.string().optional(),\n })\n .optional(),\n runtime: z\n .object({\n port: z.number().optional(),\n })\n .optional(),\n});\n","import { EnvVarDefinition, EnvVarType } from './types.js';\n\n/**\n * Validation error class\n */\nexport class EnvValidationError extends Error {\n constructor(\n message: string,\n public varName: string,\n public definition?: EnvVarDefinition\n ) {\n super(message);\n this.name = 'EnvValidationError';\n }\n}\n\n/**\n * Parse and validate an environment variable value based on its type\n */\nexport function parseEnvValue(\n varName: string,\n rawValue: string | undefined,\n definition: EnvVarDefinition\n): unknown {\n // Handle missing values\n if (rawValue === undefined || rawValue === '') {\n if (definition.required && definition.default === undefined) {\n throw new EnvValidationError(\n `Required environment variable \"${varName}\" is not set.\\n` +\n `Description: ${definition.description}`,\n varName,\n definition\n );\n }\n return definition.default;\n }\n\n // Validate enum\n if (definition.enum && !definition.enum.includes(rawValue)) {\n throw new EnvValidationError(\n `Environment variable \"${varName}\" must be one of: ${definition.enum.join(', ')}.\\n` +\n `Got: \"${rawValue}\"`,\n varName,\n definition\n );\n }\n\n // Validate pattern\n if (definition.pattern) {\n const regex = new RegExp(definition.pattern);\n if (!regex.test(rawValue)) {\n throw new EnvValidationError(\n `Environment variable \"${varName}\" does not match pattern: ${definition.pattern}.\\n` +\n `Got: \"${rawValue}\"`,\n varName,\n definition\n );\n }\n }\n\n // Type conversion and validation\n switch (definition.type) {\n case 'string':\n return rawValue;\n\n case 'url':\n try {\n new URL(rawValue);\n return rawValue;\n } catch {\n throw new EnvValidationError(\n `Environment variable \"${varName}\" must be a valid URL.\\n` + `Got: \"${rawValue}\"`,\n varName,\n definition\n );\n }\n\n case 'int': {\n const intValue = Number.parseInt(rawValue, 10);\n if (Number.isNaN(intValue)) {\n throw new EnvValidationError(\n `Environment variable \"${varName}\" must be a valid integer.\\n` + `Got: \"${rawValue}\"`,\n varName,\n definition\n );\n }\n return intValue;\n }\n\n case 'bool': {\n const lowerValue = rawValue.toLowerCase();\n if (lowerValue === 'true' || lowerValue === '1') return true;\n if (lowerValue === 'false' || lowerValue === '0') return false;\n throw new EnvValidationError(\n `Environment variable \"${varName}\" must be a boolean (true/false, 1/0).\\n` +\n `Got: \"${rawValue}\"`,\n varName,\n definition\n );\n }\n\n case 'json':\n try {\n return JSON.parse(rawValue);\n } catch {\n throw new EnvValidationError(\n `Environment variable \"${varName}\" must be valid JSON.\\n` + `Got: \"${rawValue}\"`,\n varName,\n definition\n );\n }\n\n default: {\n const _exhaustive: never = definition.type;\n throw new Error(`Unknown type: ${_exhaustive}`);\n }\n }\n}\n\n/**\n * Get the TypeScript type string for an environment variable type\n */\nexport function getTypeString(type: EnvVarType): string {\n switch (type) {\n case 'string':\n case 'url':\n return 'string';\n case 'int':\n return 'number';\n case 'bool':\n return 'boolean';\n case 'json':\n return 'unknown';\n default: {\n const _exhaustive: never = type;\n throw new Error(`Unknown type: ${_exhaustive}`);\n }\n }\n}\n","import { AppConfig, appConfigSchema } from './types.js';\nimport { parseEnvValue, EnvValidationError } from './validator.js';\n\n/**\n * Configuration loader class\n */\nexport class ConfigLoader {\n private readonly config: AppConfig;\n private envValues: Record<string, unknown> = {};\n\n /**\n * Direct access to environment variables as properties\n * @example config.env.AUTH0_CLIENT_ID\n */\n public readonly env: Record<string, unknown>;\n\n constructor(\n configObject: unknown,\n private processEnv: NodeJS.ProcessEnv = process.env\n ) {\n // Validate schema\n const result = appConfigSchema.safeParse(configObject);\n if (!result.success) {\n const issues = result.error.issues.map(i => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(\n `Invalid app.config.json:\\n${issues}`\n );\n }\n\n this.config = result.data;\n\n // Skip validation during Next.js build/compilation phases\n // During these phases, we still want to read the env vars, but not fail the build if they're missing\n // NEXT_PHASE is set by Next.js during build\n const isNextBuild =\n this.processEnv.NEXT_PHASE === 'phase-production-build' ||\n this.processEnv.NEXT_PHASE === 'phase-development-server';\n\n // Always try to validate/read environment variables\n // During build, we'll be lenient about missing required vars\n this.validateEnvironment(!isNextBuild);\n\n // Create readonly proxy for env access\n this.env = new Proxy(this.envValues, {\n get: (target, prop: string) => {\n if (!(prop in target)) {\n throw new Error(\n `Environment variable \"${prop}\" is not defined in app.config.json.\\n` +\n `Available variables: ${Object.keys(target).join(', ')}`\n );\n }\n return target[prop];\n },\n set: () => {\n throw new Error('Cannot modify environment variables through config.env');\n },\n });\n }\n\n /**\n * Validate all environment variables according to config\n * @param strictRequired - Whether to throw errors for missing required vars (false during build)\n */\n private validateEnvironment(strictRequired = true): void {\n const errors: EnvValidationError[] = [];\n\n for (const [varName, definition] of Object.entries(this.config.env)) {\n try {\n const value = parseEnvValue(varName, this.processEnv[varName], definition);\n this.envValues[varName] = value;\n\n // Set default values back to process.env if they were missing\n if (this.processEnv[varName] === undefined && value !== undefined) {\n this.processEnv[varName] = String(value);\n }\n } catch (error) {\n if (error instanceof EnvValidationError) {\n // During build (strictRequired=false), skip required validation errors but still collect type errors\n if (!strictRequired && error.message.includes('Required environment variable')) {\n // Use default value or empty string if required var is missing during build\n this.envValues[varName] = definition.default ?? '';\n continue;\n }\n errors.push(error);\n } else {\n throw error;\n }\n }\n }\n\n // Throw all errors at once\n if (errors.length > 0) {\n const errorMessage = errors.map(e => `\\n❌ ${e.message}`).join('\\n');\n throw new Error(`Environment validation failed:${errorMessage}\\n`);\n }\n }\n\n /**\n * Get a typed environment variable value\n */\n get<T = unknown>(varName: string): T {\n if (!(varName in this.envValues)) {\n throw new Error(\n `Environment variable \"${varName}\" is not defined in app.config.json.\\n` +\n `Available variables: ${Object.keys(this.envValues).join(', ')}`\n );\n }\n return this.envValues[varName] as T;\n }\n\n /**\n * Get the raw app configuration\n */\n getConfig(): Readonly<AppConfig> {\n return this.config;\n }\n\n /**\n * Get all environment variables as a typed object\n */\n getAllEnv(): Readonly<Record<string, unknown>> {\n return { ...this.envValues };\n }\n\n /**\n * Check if a variable exists\n */\n has(varName: string): boolean {\n return varName in this.envValues;\n }\n}\n\n/**\n * Create a config loader from a config object\n */\nexport function createConfig(configObject: unknown): ConfigLoader {\n return new ConfigLoader(configObject);\n}\n\n/**\n * Validate app configuration and environment variables.\n * This is useful for running validation explicitly during app initialization.\n *\n * @param configObject - App configuration object (parsed from app.config.json)\n * @throws Error if configuration is invalid or required env vars are missing\n *\n * @example\n * ```typescript\n * // In your app initialization (e.g., layout.tsx or main.ts)\n * import { validate } from '@eventuras/app-config';\n * import appConfigJson from './app.config.json';\n *\n * validate(appConfigJson);\n * console.log('✓ Environment validated successfully');\n * ```\n */\nexport function validate(configObject: unknown): void {\n // Simply creating the config will trigger validation\n // If validation fails, an error will be thrown\n new ConfigLoader(configObject);\n}\n","import { AppConfig } from './types.js';\nimport { ConfigLoader } from './loader.js';\n\n/**\n * Create explicit getters for NEXT_PUBLIC_* environment variables.\n *\n * This is required for Next.js because it performs build-time replacement of\n * process.env.NEXT_PUBLIC_* variables. We must access process.env directly,\n * not through a runtime lookup.\n *\n * @param config - The app configuration\n * @returns An object with explicit getters for each NEXT_PUBLIC_* variable\n */\nexport function createPublicEnvGetters(config: AppConfig) {\n const getters: Record<string, unknown> = {};\n\n for (const [varName, definition] of Object.entries(config.env)) {\n if (definition.client && varName.startsWith('NEXT_PUBLIC_')) {\n Object.defineProperty(getters, varName, {\n get() {\n return process.env[varName];\n },\n enumerable: true,\n });\n }\n }\n\n return getters;\n}\n\nexport interface EnvironmentObject {\n validate: () => void;\n get: <T = string>(varName: string) => T;\n [key: string]: unknown;\n}\n\n/**\n * Create a complete Environment object with validation and explicit getters.\n *\n * This provides a drop-in replacement for the old Environment class pattern,\n * with automatic validation and proper Next.js NEXT_PUBLIC_* support.\n *\n * @param configPath - Path to app.config.json\n * @returns An object with validate(), get(), and explicit NEXT_PUBLIC_* getters\n */\nexport function createEnvironment(configPath: string): EnvironmentObject {\n let configInstance: ConfigLoader | null = null;\n\n function getConfigInstance(): ConfigLoader {\n if (!configInstance) {\n configInstance = new ConfigLoader(configPath);\n }\n return configInstance;\n }\n\n const publicGetters = createPublicEnvGetters(getConfigInstance().getConfig());\n\n return {\n /**\n * Validate environment (happens automatically on first access)\n */\n validate: () => {\n getConfigInstance();\n },\n\n /**\n * Get a server-side environment variable\n */\n get: <T = string>(varName: string): T => {\n return getConfigInstance().get(varName) as T;\n },\n\n // Spread in all the NEXT_PUBLIC_* getters\n ...publicGetters,\n };\n}\n"],"mappings":"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"}
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { AppConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration loader class
|
|
4
|
+
*/
|
|
5
|
+
export declare class ConfigLoader {
|
|
6
|
+
private processEnv;
|
|
7
|
+
private readonly config;
|
|
8
|
+
private envValues;
|
|
9
|
+
/**
|
|
10
|
+
* Direct access to environment variables as properties
|
|
11
|
+
* @example config.env.AUTH0_CLIENT_ID
|
|
12
|
+
*/
|
|
13
|
+
readonly env: Record<string, unknown>;
|
|
14
|
+
constructor(configObject: unknown, processEnv?: NodeJS.ProcessEnv);
|
|
15
|
+
/**
|
|
16
|
+
* Validate all environment variables according to config
|
|
17
|
+
* @param strictRequired - Whether to throw errors for missing required vars (false during build)
|
|
18
|
+
*/
|
|
19
|
+
private validateEnvironment;
|
|
20
|
+
/**
|
|
21
|
+
* Get a typed environment variable value
|
|
22
|
+
*/
|
|
23
|
+
get<T = unknown>(varName: string): T;
|
|
24
|
+
/**
|
|
25
|
+
* Get the raw app configuration
|
|
26
|
+
*/
|
|
27
|
+
getConfig(): Readonly<AppConfig>;
|
|
28
|
+
/**
|
|
29
|
+
* Get all environment variables as a typed object
|
|
30
|
+
*/
|
|
31
|
+
getAllEnv(): Readonly<Record<string, unknown>>;
|
|
32
|
+
/**
|
|
33
|
+
* Check if a variable exists
|
|
34
|
+
*/
|
|
35
|
+
has(varName: string): boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a config loader from a config object
|
|
39
|
+
*/
|
|
40
|
+
export declare function createConfig(configObject: unknown): ConfigLoader;
|
|
41
|
+
/**
|
|
42
|
+
* Validate app configuration and environment variables.
|
|
43
|
+
* This is useful for running validation explicitly during app initialization.
|
|
44
|
+
*
|
|
45
|
+
* @param configObject - App configuration object (parsed from app.config.json)
|
|
46
|
+
* @throws Error if configuration is invalid or required env vars are missing
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* // In your app initialization (e.g., layout.tsx or main.ts)
|
|
51
|
+
* import { validate } from '@eventuras/app-config';
|
|
52
|
+
* import appConfigJson from './app.config.json';
|
|
53
|
+
*
|
|
54
|
+
* validate(appConfigJson);
|
|
55
|
+
* console.log('✓ Environment validated successfully');
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function validate(configObject: unknown): void;
|
|
59
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,MAAM,YAAY,CAAC;AAGxD;;GAEG;AACH,qBAAa,YAAY;IAYrB,OAAO,CAAC,UAAU;IAXpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,SAAS,CAA+B;IAEhD;;;OAGG;IACH,SAAgB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAG3C,YAAY,EAAE,OAAO,EACb,UAAU,GAAE,MAAM,CAAC,UAAwB;IAyCrD;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAkC3B;;OAEG;IACH,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC;IAUpC;;OAEG;IACH,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC;IAIhC;;OAEG;IACH,SAAS,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAI9C;;OAEG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;CAG9B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,OAAO,GAAG,YAAY,CAEhE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,YAAY,EAAE,OAAO,GAAG,IAAI,CAIpD"}
|
package/dist/next.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AppConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create explicit getters for NEXT_PUBLIC_* environment variables.
|
|
4
|
+
*
|
|
5
|
+
* This is required for Next.js because it performs build-time replacement of
|
|
6
|
+
* process.env.NEXT_PUBLIC_* variables. We must access process.env directly,
|
|
7
|
+
* not through a runtime lookup.
|
|
8
|
+
*
|
|
9
|
+
* @param config - The app configuration
|
|
10
|
+
* @returns An object with explicit getters for each NEXT_PUBLIC_* variable
|
|
11
|
+
*/
|
|
12
|
+
export declare function createPublicEnvGetters(config: AppConfig): Record<string, unknown>;
|
|
13
|
+
export interface EnvironmentObject {
|
|
14
|
+
validate: () => void;
|
|
15
|
+
get: <T = string>(varName: string) => T;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Create a complete Environment object with validation and explicit getters.
|
|
20
|
+
*
|
|
21
|
+
* This provides a drop-in replacement for the old Environment class pattern,
|
|
22
|
+
* with automatic validation and proper Next.js NEXT_PUBLIC_* support.
|
|
23
|
+
*
|
|
24
|
+
* @param configPath - Path to app.config.json
|
|
25
|
+
* @returns An object with validate(), get(), and explicit NEXT_PUBLIC_* getters
|
|
26
|
+
*/
|
|
27
|
+
export declare function createEnvironment(configPath: string): EnvironmentObject;
|
|
28
|
+
//# sourceMappingURL=next.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../src/next.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,2BAevD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,GAAG,EAAE,CAAC,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC,CAAC;IACxC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CA8BvE"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Supported environment variable types
|
|
4
|
+
*/
|
|
5
|
+
export type EnvVarType = 'string' | 'url' | 'int' | 'bool' | 'json';
|
|
6
|
+
/**
|
|
7
|
+
* Environment variable definition
|
|
8
|
+
*/
|
|
9
|
+
export interface EnvVarDefinition {
|
|
10
|
+
/** Whether this variable is required */
|
|
11
|
+
required: boolean;
|
|
12
|
+
/** Whether this variable is exposed to the client (e.g., NEXT_PUBLIC_*) */
|
|
13
|
+
client: boolean;
|
|
14
|
+
/** Expected type of the variable */
|
|
15
|
+
type: EnvVarType;
|
|
16
|
+
/** Human-readable description */
|
|
17
|
+
description: string;
|
|
18
|
+
/** Default value if not provided */
|
|
19
|
+
default?: string | number | boolean | object;
|
|
20
|
+
/** Regular expression pattern for validation */
|
|
21
|
+
pattern?: string;
|
|
22
|
+
/** Allowed values */
|
|
23
|
+
enum?: string[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Application configuration schema
|
|
27
|
+
*/
|
|
28
|
+
export interface AppConfig {
|
|
29
|
+
/** JSON Schema reference */
|
|
30
|
+
$schema?: string;
|
|
31
|
+
/** Application package name */
|
|
32
|
+
name: string;
|
|
33
|
+
/** Configuration type */
|
|
34
|
+
type: 'app';
|
|
35
|
+
/** Application description */
|
|
36
|
+
description?: string;
|
|
37
|
+
/** Environment variable definitions */
|
|
38
|
+
env: Record<string, EnvVarDefinition>;
|
|
39
|
+
/** Build configuration */
|
|
40
|
+
build?: {
|
|
41
|
+
outDir?: string;
|
|
42
|
+
target?: string;
|
|
43
|
+
};
|
|
44
|
+
/** Runtime configuration */
|
|
45
|
+
runtime?: {
|
|
46
|
+
port?: number;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Zod schema for environment variable definition
|
|
51
|
+
*/
|
|
52
|
+
export declare const envVarDefinitionSchema: z.ZodObject<{
|
|
53
|
+
required: z.ZodBoolean;
|
|
54
|
+
client: z.ZodBoolean;
|
|
55
|
+
type: z.ZodEnum<{
|
|
56
|
+
string: "string";
|
|
57
|
+
url: "url";
|
|
58
|
+
int: "int";
|
|
59
|
+
bool: "bool";
|
|
60
|
+
json: "json";
|
|
61
|
+
}>;
|
|
62
|
+
description: z.ZodString;
|
|
63
|
+
default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodObject<{}, z.core.$strip>]>>;
|
|
64
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
65
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
66
|
+
}, z.core.$strip>;
|
|
67
|
+
/**
|
|
68
|
+
* Zod schema for app configuration
|
|
69
|
+
*/
|
|
70
|
+
export declare const appConfigSchema: z.ZodObject<{
|
|
71
|
+
$schema: z.ZodOptional<z.ZodString>;
|
|
72
|
+
name: z.ZodString;
|
|
73
|
+
type: z.ZodLiteral<"app">;
|
|
74
|
+
description: z.ZodOptional<z.ZodString>;
|
|
75
|
+
env: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
76
|
+
required: z.ZodBoolean;
|
|
77
|
+
client: z.ZodBoolean;
|
|
78
|
+
type: z.ZodEnum<{
|
|
79
|
+
string: "string";
|
|
80
|
+
url: "url";
|
|
81
|
+
int: "int";
|
|
82
|
+
bool: "bool";
|
|
83
|
+
json: "json";
|
|
84
|
+
}>;
|
|
85
|
+
description: z.ZodString;
|
|
86
|
+
default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodObject<{}, z.core.$strip>]>>;
|
|
87
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
88
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
89
|
+
}, z.core.$strip>>;
|
|
90
|
+
build: z.ZodOptional<z.ZodObject<{
|
|
91
|
+
outDir: z.ZodOptional<z.ZodString>;
|
|
92
|
+
target: z.ZodOptional<z.ZodString>;
|
|
93
|
+
}, z.core.$strip>>;
|
|
94
|
+
runtime: z.ZodOptional<z.ZodObject<{
|
|
95
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
96
|
+
}, z.core.$strip>>;
|
|
97
|
+
}, z.core.$strip>;
|
|
98
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wCAAwC;IACxC,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,MAAM,EAAE,OAAO,CAAC;IAChB,oCAAoC;IACpC,IAAI,EAAE,UAAU,CAAC;IACjB,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAC7C,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,EAAE,KAAK,CAAC;IACZ,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACtC,0BAA0B;IAC1B,KAAK,CAAC,EAAE;QACN,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,4BAA4B;IAC5B,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;iBAQjC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiB1B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { EnvVarDefinition, EnvVarType } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validation error class
|
|
4
|
+
*/
|
|
5
|
+
export declare class EnvValidationError extends Error {
|
|
6
|
+
varName: string;
|
|
7
|
+
definition?: EnvVarDefinition | undefined;
|
|
8
|
+
constructor(message: string, varName: string, definition?: EnvVarDefinition | undefined);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse and validate an environment variable value based on its type
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseEnvValue(varName: string, rawValue: string | undefined, definition: EnvVarDefinition): unknown;
|
|
14
|
+
/**
|
|
15
|
+
* Get the TypeScript type string for an environment variable type
|
|
16
|
+
*/
|
|
17
|
+
export declare function getTypeString(type: EnvVarType): string;
|
|
18
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE1D;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAGlC,OAAO,EAAE,MAAM;IACf,UAAU,CAAC,EAAE,gBAAgB;gBAFpC,OAAO,EAAE,MAAM,EACR,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,gBAAgB,YAAA;CAKvC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,UAAU,EAAE,gBAAgB,GAC3B,OAAO,CA8FT;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAgBtD"}
|