@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
package/README.md
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# @eventuras/app-config
|
|
2
|
+
|
|
3
|
+
Declarative environment configuration for Eventuras applications.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`@eventuras/app-config` provides a centralized, type-safe way to manage environment variables across all Eventuras applications. Instead of manually validating env vars in code, you declare them in an `app.config.json` file.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Development
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Build the library
|
|
14
|
+
pnpm build
|
|
15
|
+
|
|
16
|
+
# Type check
|
|
17
|
+
pnpm typecheck
|
|
18
|
+
|
|
19
|
+
# Lint
|
|
20
|
+
pnpm lint
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## TypeScript Type Generation
|
|
24
|
+
|
|
25
|
+
The library includes a tool to generate TypeScript type definitions from your `app.config.json` file. This ensures your client-side environment variables are properly typed.
|
|
26
|
+
|
|
27
|
+
### Using the generator programmatically
|
|
28
|
+
|
|
29
|
+
Create a script in your app's `scripts/` directory:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
#!/usr/bin/env tsx
|
|
33
|
+
import path from 'path';
|
|
34
|
+
import { fileURLToPath } from 'url';
|
|
35
|
+
import { generateTypes } from '@eventuras/app-config/generator';
|
|
36
|
+
|
|
37
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
38
|
+
const __dirname = path.dirname(__filename);
|
|
39
|
+
|
|
40
|
+
generateTypes({
|
|
41
|
+
configPath: path.join(__dirname, '..', 'app.config.json'),
|
|
42
|
+
outputPath: path.join(__dirname, '..', 'src', 'config.client.generated.d.ts'),
|
|
43
|
+
interfaceName: 'WebPublicEnv', // Optional, defaults to 'PublicEnv'
|
|
44
|
+
}).catch((error: unknown) => {
|
|
45
|
+
console.error('❌ Failed to generate types:', error);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Add a script to your `package.json`:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"scripts": {
|
|
55
|
+
"generate:config-types": "tsx scripts/generate-config-types.ts"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Generated output
|
|
61
|
+
|
|
62
|
+
For example, given this config:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"env": {
|
|
67
|
+
"NEXT_PUBLIC_API_BASE_URL": {
|
|
68
|
+
"required": true,
|
|
69
|
+
"client": true,
|
|
70
|
+
"type": "url",
|
|
71
|
+
"description": "API base URL"
|
|
72
|
+
},
|
|
73
|
+
"NEXT_PUBLIC_ORGANIZATION_ID": {
|
|
74
|
+
"required": false,
|
|
75
|
+
"client": true,
|
|
76
|
+
"type": "int",
|
|
77
|
+
"description": "Organization ID"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The generated `config.client.generated.d.ts` will be:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
/**
|
|
87
|
+
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
88
|
+
* Generated from app.config.json
|
|
89
|
+
*
|
|
90
|
+
* To regenerate, run: pnpm generate:config-types
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
export interface WebPublicEnv {
|
|
94
|
+
NEXT_PUBLIC_API_BASE_URL: string;
|
|
95
|
+
NEXT_PUBLIC_ORGANIZATION_ID?: number;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
You can then use this interface to type your client-side environment:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import type { WebPublicEnv } from './config.client.generated';
|
|
103
|
+
|
|
104
|
+
declare global {
|
|
105
|
+
namespace NodeJS {
|
|
106
|
+
interface ProcessEnv extends WebPublicEnv {}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## Installation
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pnpm add @eventuras/app-config
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Usage
|
|
119
|
+
|
|
120
|
+
### 1. Create `app.config.json`
|
|
121
|
+
|
|
122
|
+
Create an `app.config.json` file in your app's root directory:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"$schema": "https://eventuras.dev/schema/app-config.schema.json",
|
|
127
|
+
"name": "@eventuras/web",
|
|
128
|
+
"type": "app",
|
|
129
|
+
"description": "Main web frontend for Eventuras",
|
|
130
|
+
"env": {
|
|
131
|
+
"NEXT_PUBLIC_API_BASE_URL": {
|
|
132
|
+
"required": true,
|
|
133
|
+
"client": true,
|
|
134
|
+
"type": "url",
|
|
135
|
+
"description": "Base URL for the Eventuras API"
|
|
136
|
+
},
|
|
137
|
+
"NEXT_PUBLIC_ORGANIZATION_ID": {
|
|
138
|
+
"required": false,
|
|
139
|
+
"client": true,
|
|
140
|
+
"type": "string",
|
|
141
|
+
"description": "Organization ID for scoping configuration",
|
|
142
|
+
"default": "1"
|
|
143
|
+
},
|
|
144
|
+
"AUTH0_CLIENT_SECRET": {
|
|
145
|
+
"required": true,
|
|
146
|
+
"client": false,
|
|
147
|
+
"type": "string",
|
|
148
|
+
"description": "Auth0 client secret (server-side only)"
|
|
149
|
+
},
|
|
150
|
+
"PORT": {
|
|
151
|
+
"required": false,
|
|
152
|
+
"client": false,
|
|
153
|
+
"type": "int",
|
|
154
|
+
"description": "Port to run the server on",
|
|
155
|
+
"default": 3000
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 2. Load and validate configuration
|
|
162
|
+
|
|
163
|
+
#### Server-Side Usage (Recommended)
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { createConfig } from '@eventuras/app-config';
|
|
167
|
+
|
|
168
|
+
const config = createConfig('./app.config.json');
|
|
169
|
+
|
|
170
|
+
// Method 1: Direct property access (cleanest for server-side)
|
|
171
|
+
const clientId = config.env.AUTH0_CLIENT_ID;
|
|
172
|
+
const clientSecret = config.env.AUTH0_CLIENT_SECRET;
|
|
173
|
+
const sessionSecret = config.env.SESSION_SECRET;
|
|
174
|
+
|
|
175
|
+
// Method 2: Using get() with type parameter
|
|
176
|
+
const apiUrl = config.get<string>('NEXT_PUBLIC_API_BASE_URL');
|
|
177
|
+
const orgId = config.get<string>('NEXT_PUBLIC_ORGANIZATION_ID');
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### Next.js Usage (Client + Server)
|
|
181
|
+
|
|
182
|
+
For Next.js apps, use the `createEnvironment()` helper for automatic compatibility:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// config.ts
|
|
186
|
+
import { createConfig, createEnvironment } from '@eventuras/app-config';
|
|
187
|
+
import { resolve } from 'path';
|
|
188
|
+
|
|
189
|
+
// Server-side configuration
|
|
190
|
+
export const appConfig = createConfig(
|
|
191
|
+
resolve(process.cwd(), 'app.config.json')
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// Client-side public environment variables
|
|
195
|
+
export const publicEnv = createEnvironment(
|
|
196
|
+
resolve(process.cwd(), 'app.config.json')
|
|
197
|
+
);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// In client components - use publicEnv
|
|
202
|
+
'use client';
|
|
203
|
+
|
|
204
|
+
export default function MyComponent() {
|
|
205
|
+
const apiUrl = publicEnv.NEXT_PUBLIC_API_BASE_URL; // ✅ Works in client
|
|
206
|
+
const domain = publicEnv.NEXT_PUBLIC_AUTH0_DOMAIN; // ✅ Works in client
|
|
207
|
+
|
|
208
|
+
return <div>API: {apiUrl}</div>;
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// In server components / API routes - use appConfig.env
|
|
214
|
+
export async function getServerSideProps() {
|
|
215
|
+
// Recommended: Use appConfig.env for server-side variables
|
|
216
|
+
const clientSecret = appConfig.env.AUTH0_CLIENT_SECRET;
|
|
217
|
+
const sessionSecret = appConfig.env.SESSION_SECRET;
|
|
218
|
+
|
|
219
|
+
// Alternative: Use publicEnv.get() (also works server-side)
|
|
220
|
+
const altSecret = publicEnv.get('AUTH0_CLIENT_SECRET');
|
|
221
|
+
|
|
222
|
+
return { props: {} };
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Why explicit getters for NEXT_PUBLIC_*?**
|
|
227
|
+
Next.js performs build-time replacement of `process.env.NEXT_PUBLIC_*`. The `createEnvironment()` helper automatically generates getters that access `process.env` directly, ensuring client-side compatibility while still validating all variables server-side.
|
|
228
|
+
|
|
229
|
+
### 3. Validation happens automatically
|
|
230
|
+
|
|
231
|
+
When you create a config, all environment variables are validated:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// This will throw if any required env vars are missing or invalid
|
|
235
|
+
const config = createConfig('./app.config.json');
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
You can also validate explicitly during app initialization:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { validate } from '@eventuras/app-config';
|
|
242
|
+
|
|
243
|
+
// In your app initialization (e.g., layout.tsx or main.ts)
|
|
244
|
+
validate('./app.config.json');
|
|
245
|
+
console.log('✓ Environment validated successfully');
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Error messages are helpful:
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
Environment validation failed:
|
|
252
|
+
|
|
253
|
+
❌ Required environment variable "NEXT_PUBLIC_API_BASE_URL" is not set.
|
|
254
|
+
Description: Base URL for the Eventuras API
|
|
255
|
+
|
|
256
|
+
❌ Environment variable "PORT" must be a valid integer.
|
|
257
|
+
Got: "not-a-number"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Environment Variable Types
|
|
261
|
+
|
|
262
|
+
| Type | Description | Example |
|
|
263
|
+
|------|-------------|---------|
|
|
264
|
+
| `string` | Any string value | `"hello"` |
|
|
265
|
+
| `url` | Valid URL | `"https://api.example.com"` |
|
|
266
|
+
| `int` | Integer number | `3000` |
|
|
267
|
+
| `bool` | Boolean (true/false, 1/0) | `true`, `"1"` |
|
|
268
|
+
| `json` | Valid JSON | `{"key": "value"}` |
|
|
269
|
+
|
|
270
|
+
## Configuration Schema
|
|
271
|
+
|
|
272
|
+
### `app.config.json` Structure
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
interface AppConfig {
|
|
276
|
+
$schema?: string; // JSON Schema reference
|
|
277
|
+
name: string; // Package name (e.g., "@eventuras/web")
|
|
278
|
+
type: 'app'; // Always "app"
|
|
279
|
+
description?: string; // Human-readable description
|
|
280
|
+
env: { // Environment variable definitions
|
|
281
|
+
[varName: string]: {
|
|
282
|
+
required: boolean; // Is this var required?
|
|
283
|
+
client: boolean; // Exposed to client? (NEXT_PUBLIC_*)
|
|
284
|
+
type: EnvVarType; // 'string' | 'url' | 'int' | 'bool' | 'json'
|
|
285
|
+
description: string; // What is this var for?
|
|
286
|
+
default?: any; // Default value
|
|
287
|
+
pattern?: string; // Regex pattern
|
|
288
|
+
enum?: string[]; // Allowed values
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
build?: { // Build configuration (optional)
|
|
292
|
+
outDir?: string;
|
|
293
|
+
target?: string;
|
|
294
|
+
};
|
|
295
|
+
runtime?: { // Runtime configuration (optional)
|
|
296
|
+
port?: number;
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Environment Variable Definition
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
interface EnvVarDefinition {
|
|
305
|
+
required: boolean; // Throw error if missing?
|
|
306
|
+
client: boolean; // Client-side accessible?
|
|
307
|
+
type: EnvVarType; // Expected type
|
|
308
|
+
description: string; // Documentation
|
|
309
|
+
default?: any; // Fallback value
|
|
310
|
+
pattern?: string; // Validation regex
|
|
311
|
+
enum?: string[]; // Allowed values
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Migration from `Environment.ts`
|
|
316
|
+
|
|
317
|
+
### Before (apps/web/src/utils/Environment.ts)
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
export enum EnvironmentVariables {
|
|
321
|
+
NEXT_PUBLIC_API_BASE_URL = 'NEXT_PUBLIC_API_BASE_URL',
|
|
322
|
+
AUTH0_CLIENT_SECRET = 'AUTH0_CLIENT_SECRET',
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
class Environment {
|
|
326
|
+
static validate() { /* ... */ }
|
|
327
|
+
|
|
328
|
+
static get NEXT_PUBLIC_API_BASE_URL() {
|
|
329
|
+
return process.env.NEXT_PUBLIC_API_BASE_URL!;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
static get(identifier: string): string {
|
|
333
|
+
return process.env[identifier]!;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### After (apps/web/app.config.json + config.ts)
|
|
339
|
+
|
|
340
|
+
**app.config.json:**
|
|
341
|
+
```json
|
|
342
|
+
{
|
|
343
|
+
"name": "@eventuras/web",
|
|
344
|
+
"type": "app",
|
|
345
|
+
"env": {
|
|
346
|
+
"NEXT_PUBLIC_API_BASE_URL": {
|
|
347
|
+
"required": true,
|
|
348
|
+
"client": true,
|
|
349
|
+
"type": "url",
|
|
350
|
+
"description": "API base URL"
|
|
351
|
+
},
|
|
352
|
+
"AUTH0_CLIENT_SECRET": {
|
|
353
|
+
"required": true,
|
|
354
|
+
"client": false,
|
|
355
|
+
"type": "string",
|
|
356
|
+
"description": "Auth0 secret"
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**src/config.ts:**
|
|
363
|
+
```typescript
|
|
364
|
+
import { createConfig } from '@eventuras/app-config';
|
|
365
|
+
|
|
366
|
+
export const config = createConfig('./app.config.json');
|
|
367
|
+
|
|
368
|
+
// Usage:
|
|
369
|
+
const apiUrl = config.get<string>('NEXT_PUBLIC_API_BASE_URL');
|
|
370
|
+
const secret = config.get<string>('AUTH0_CLIENT_SECRET');
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Next.js Integration
|
|
374
|
+
|
|
375
|
+
For Next.js apps, you can validate the config in your root layout:
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
// app/layout.tsx
|
|
379
|
+
import { validate } from '@eventuras/app-config';
|
|
380
|
+
|
|
381
|
+
// Validate on server startup
|
|
382
|
+
validate('./app.config.json');
|
|
383
|
+
|
|
384
|
+
export default function RootLayout({ children }) {
|
|
385
|
+
return <html><body>{children}</body></html>;
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Or use the config instance directly:
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
// app/layout.tsx
|
|
393
|
+
import { createConfig } from '@eventuras/app-config';
|
|
394
|
+
|
|
395
|
+
// Validate on startup
|
|
396
|
+
const config = createConfig('./app.config.json');
|
|
397
|
+
|
|
398
|
+
export default function RootLayout({ children }) {
|
|
399
|
+
return <html><body>{children}</body></html>;
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
Access client-side variables (NEXT_PUBLIC_*):
|
|
404
|
+
```typescript
|
|
405
|
+
// Client component
|
|
406
|
+
'use client';
|
|
407
|
+
|
|
408
|
+
import { config } from '@/config';
|
|
409
|
+
|
|
410
|
+
export function MyComponent() {
|
|
411
|
+
// ⚠️ IMPORTANT: For Next.js, NEXT_PUBLIC_* vars MUST access process.env directly
|
|
412
|
+
// Next.js replaces process.env.NEXT_PUBLIC_* at build time
|
|
413
|
+
const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL;
|
|
414
|
+
|
|
415
|
+
// Or use the Environment wrapper which does this for you:
|
|
416
|
+
import { Environment } from '@/config';
|
|
417
|
+
const apiUrl = Environment.NEXT_PUBLIC_API_BASE_URL;
|
|
418
|
+
|
|
419
|
+
return <div>API: {apiUrl}</div>;
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Why the limitation?** Next.js performs build-time replacement of `process.env.NEXT_PUBLIC_*` variables. The `appConfig.get()` method reads from a runtime object, so it won't work for client-side code. Server-side code can use either approach.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;GAGG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { generateTypes } from "./generator.js";
|
|
3
|
+
//#region src/cli.ts
|
|
4
|
+
/**
|
|
5
|
+
* CLI tool to generate TypeScript types from app.config.json
|
|
6
|
+
* Usage: node cli.js <configPath> <outputPath> [interfaceName]
|
|
7
|
+
*/
|
|
8
|
+
async function main() {
|
|
9
|
+
const [, , configPath, outputPath, interfaceName] = process.argv;
|
|
10
|
+
if (!configPath || !outputPath) {
|
|
11
|
+
console.error("Usage: generate-config-types <configPath> <outputPath> [interfaceName]");
|
|
12
|
+
console.error("Example: generate-config-types ./app.config.json ./src/config.generated.d.ts PublicEnv");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
await generateTypes({
|
|
17
|
+
configPath,
|
|
18
|
+
outputPath,
|
|
19
|
+
interfaceName
|
|
20
|
+
});
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error("❌ Failed to generate types:", error);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
main();
|
|
27
|
+
//#endregion
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI tool to generate TypeScript types from app.config.json\n * Usage: node cli.js <configPath> <outputPath> [interfaceName]\n */\n\nimport { generateTypes } from './generator.js';\n\nasync function main() {\n const [, , configPath, outputPath, interfaceName] = process.argv;\n\n if (!configPath || !outputPath) {\n console.error('Usage: generate-config-types <configPath> <outputPath> [interfaceName]');\n console.error('Example: generate-config-types ./app.config.json ./src/config.generated.d.ts PublicEnv');\n process.exit(1);\n }\n\n try {\n await generateTypes({\n configPath,\n outputPath,\n interfaceName,\n });\n } catch (error) {\n console.error('❌ Failed to generate types:', error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":";;;;;;;AAQA,eAAe,OAAO;CACpB,MAAM,KAAK,YAAY,YAAY,iBAAiB,QAAQ;CAE5D,IAAI,CAAC,cAAc,CAAC,YAAY;EAC9B,QAAQ,MAAM,wEAAwE;EACtF,QAAQ,MAAM,wFAAwF;EACtG,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI;EACF,MAAM,cAAc;GAClB;GACA;GACA;EACF,CAAC;CACH,SAAS,OAAO;EACd,QAAQ,MAAM,+BAA+B,KAAK;EAClD,QAAQ,KAAK,CAAC;CAChB;AACF;AAEA,KAAK"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { AppConfig } from './types.js';
|
|
2
|
+
export type { AppConfig } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Map environment variable type string to TypeScript type
|
|
5
|
+
*/
|
|
6
|
+
type EnvVarTypeMap = {
|
|
7
|
+
string: string;
|
|
8
|
+
url: string;
|
|
9
|
+
int: number;
|
|
10
|
+
bool: boolean;
|
|
11
|
+
json: unknown;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Infer the TypeScript type from an environment variable definition with literal type support
|
|
15
|
+
*/
|
|
16
|
+
type InferEnvVarType<T> = T extends {
|
|
17
|
+
type: infer Type;
|
|
18
|
+
required: infer Req;
|
|
19
|
+
} ? Type extends keyof EnvVarTypeMap ? Req extends true ? EnvVarTypeMap[Type] : EnvVarTypeMap[Type] | undefined : unknown : unknown;
|
|
20
|
+
/**
|
|
21
|
+
* Extract public environment variable types from AppConfig
|
|
22
|
+
*/
|
|
23
|
+
export type PublicEnvObject<T> = T extends {
|
|
24
|
+
env: infer Env;
|
|
25
|
+
} ? {
|
|
26
|
+
[K in keyof Env as Env[K] extends {
|
|
27
|
+
client: true;
|
|
28
|
+
} ? K : never]: InferEnvVarType<Env[K]>;
|
|
29
|
+
} : never;
|
|
30
|
+
/**
|
|
31
|
+
* Helper to parse integer value
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseIntValue(value: string | undefined): number | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Helper to parse boolean value
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseBoolValue(value: string | undefined): boolean | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Helper to parse JSON value
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseJsonValue(value: string | undefined): unknown;
|
|
42
|
+
/**
|
|
43
|
+
* Helper to define a getter property on an object.
|
|
44
|
+
*
|
|
45
|
+
* This is useful for creating explicit getters that Next.js can statically analyze.
|
|
46
|
+
* Instead of dynamic property access like process.env[varName], you should use
|
|
47
|
+
* direct property access like process.env.NEXT_PUBLIC_MY_VAR in the getter function.
|
|
48
|
+
*
|
|
49
|
+
* @param target - The target object to add the getter to
|
|
50
|
+
* @param propName - The property name
|
|
51
|
+
* @param getter - The getter function that returns the value
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const env = {};
|
|
56
|
+
* defineGetter(env, 'NEXT_PUBLIC_API_URL', () => process.env.NEXT_PUBLIC_API_URL);
|
|
57
|
+
* defineGetter(env, 'NEXT_PUBLIC_ORG_ID', () => parseIntValue(process.env.NEXT_PUBLIC_ORG_ID));
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare function defineGetter<T>(target: Record<string, unknown>, propName: string, getter: () => T): void;
|
|
61
|
+
/**
|
|
62
|
+
* Create an empty object for public environment variables.
|
|
63
|
+
*
|
|
64
|
+
* Use this with defineGetter to create explicit getters for NEXT_PUBLIC_* variables.
|
|
65
|
+
* This approach allows Next.js to perform static analysis and replace environment
|
|
66
|
+
* variables at build time.
|
|
67
|
+
*
|
|
68
|
+
* @returns An empty object to add getters to
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* const publicEnv = createPublicEnvGetters();
|
|
73
|
+
* defineGetter(publicEnv, 'NEXT_PUBLIC_API_URL', () => process.env.NEXT_PUBLIC_API_URL);
|
|
74
|
+
* defineGetter(publicEnv, 'NEXT_PUBLIC_ORG_ID', () => parseIntValue(process.env.NEXT_PUBLIC_ORG_ID));
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function createPublicEnvGetters(): Record<string, unknown>;
|
|
78
|
+
/**
|
|
79
|
+
* Create a public environment object with the correct TypeScript type.
|
|
80
|
+
*
|
|
81
|
+
* This is a type-safe wrapper that returns an empty object you can add getters to.
|
|
82
|
+
* Use this with defineGetter to create explicit NEXT_PUBLIC_* getters.
|
|
83
|
+
*
|
|
84
|
+
* @param _config - Plain app configuration object (used only for type inference)
|
|
85
|
+
* @returns An empty object with the correct TypeScript type for your config
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* import appConfig from '../app.config.json';
|
|
90
|
+
*
|
|
91
|
+
* export const publicEnv = createPublicEnv(appConfig as AppConfig);
|
|
92
|
+
* defineGetter(publicEnv, 'NEXT_PUBLIC_API_URL', () => process.env.NEXT_PUBLIC_API_URL);
|
|
93
|
+
* defineGetter(publicEnv, 'NEXT_PUBLIC_ORG_ID', () => parseIntValue(process.env.NEXT_PUBLIC_ORG_ID));
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export declare function createPublicEnv<const T extends AppConfig>(_config: T): PublicEnvObject<T>;
|
|
97
|
+
//# sourceMappingURL=clientside.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clientside.d.ts","sourceRoot":"","sources":["../src/clientside.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;GAEG;AACH,KAAK,aAAa,GAAG;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF;;GAEG;AACH,KAAK,eAAe,CAAC,CAAC,IACpB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,CAAA;CAAE,GAC/C,IAAI,SAAS,MAAM,aAAa,GAC9B,GAAG,SAAS,IAAI,GACd,aAAa,CAAC,IAAI,CAAC,GACnB,aAAa,CAAC,IAAI,CAAC,GAAG,SAAS,GACjC,OAAO,GACT,OAAO,CAAC;AAEd;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,GAAG,EAAE,MAAM,GAAG,CAAA;CAAE,GACzD;KACG,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS;QAAE,MAAM,EAAE,IAAI,CAAA;KAAE,GAAG,CAAC,GAAG,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACzF,GACD,KAAK,CAAC;AAEV;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG3E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAG7E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAOjE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,GACd,IAAI,CAKN;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEhE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAEzF"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
//#region src/clientside.ts
|
|
2
|
+
/**
|
|
3
|
+
* Helper to parse integer value
|
|
4
|
+
*/
|
|
5
|
+
function parseIntValue(value) {
|
|
6
|
+
if (value === void 0) return void 0;
|
|
7
|
+
return Number.parseInt(value, 10);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Helper to parse boolean value
|
|
11
|
+
*/
|
|
12
|
+
function parseBoolValue(value) {
|
|
13
|
+
if (value === void 0) return void 0;
|
|
14
|
+
return value === "true" || value === "1";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Helper to parse JSON value
|
|
18
|
+
*/
|
|
19
|
+
function parseJsonValue(value) {
|
|
20
|
+
if (value === void 0) return void 0;
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(value);
|
|
23
|
+
} catch {
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Helper to define a getter property on an object.
|
|
29
|
+
*
|
|
30
|
+
* This is useful for creating explicit getters that Next.js can statically analyze.
|
|
31
|
+
* Instead of dynamic property access like process.env[varName], you should use
|
|
32
|
+
* direct property access like process.env.NEXT_PUBLIC_MY_VAR in the getter function.
|
|
33
|
+
*
|
|
34
|
+
* @param target - The target object to add the getter to
|
|
35
|
+
* @param propName - The property name
|
|
36
|
+
* @param getter - The getter function that returns the value
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const env = {};
|
|
41
|
+
* defineGetter(env, 'NEXT_PUBLIC_API_URL', () => process.env.NEXT_PUBLIC_API_URL);
|
|
42
|
+
* defineGetter(env, 'NEXT_PUBLIC_ORG_ID', () => parseIntValue(process.env.NEXT_PUBLIC_ORG_ID));
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
function defineGetter(target, propName, getter) {
|
|
46
|
+
Object.defineProperty(target, propName, {
|
|
47
|
+
get: getter,
|
|
48
|
+
enumerable: true
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create an empty object for public environment variables.
|
|
53
|
+
*
|
|
54
|
+
* Use this with defineGetter to create explicit getters for NEXT_PUBLIC_* variables.
|
|
55
|
+
* This approach allows Next.js to perform static analysis and replace environment
|
|
56
|
+
* variables at build time.
|
|
57
|
+
*
|
|
58
|
+
* @returns An empty object to add getters to
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const publicEnv = createPublicEnvGetters();
|
|
63
|
+
* defineGetter(publicEnv, 'NEXT_PUBLIC_API_URL', () => process.env.NEXT_PUBLIC_API_URL);
|
|
64
|
+
* defineGetter(publicEnv, 'NEXT_PUBLIC_ORG_ID', () => parseIntValue(process.env.NEXT_PUBLIC_ORG_ID));
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
function createPublicEnvGetters() {
|
|
68
|
+
return {};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create a public environment object with the correct TypeScript type.
|
|
72
|
+
*
|
|
73
|
+
* This is a type-safe wrapper that returns an empty object you can add getters to.
|
|
74
|
+
* Use this with defineGetter to create explicit NEXT_PUBLIC_* getters.
|
|
75
|
+
*
|
|
76
|
+
* @param _config - Plain app configuration object (used only for type inference)
|
|
77
|
+
* @returns An empty object with the correct TypeScript type for your config
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* import appConfig from '../app.config.json';
|
|
82
|
+
*
|
|
83
|
+
* export const publicEnv = createPublicEnv(appConfig as AppConfig);
|
|
84
|
+
* defineGetter(publicEnv, 'NEXT_PUBLIC_API_URL', () => process.env.NEXT_PUBLIC_API_URL);
|
|
85
|
+
* defineGetter(publicEnv, 'NEXT_PUBLIC_ORG_ID', () => parseIntValue(process.env.NEXT_PUBLIC_ORG_ID));
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
function createPublicEnv(_config) {
|
|
89
|
+
return createPublicEnvGetters();
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { createPublicEnv, createPublicEnvGetters, defineGetter, parseBoolValue, parseIntValue, parseJsonValue };
|
|
93
|
+
|
|
94
|
+
//# sourceMappingURL=clientside.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clientside.js","names":[],"sources":["../src/clientside.ts"],"sourcesContent":["import { AppConfig } from './types.js';\n\n// Re-export types for client usage\nexport type { AppConfig } from './types.js';\n\n/**\n * Map environment variable type string to TypeScript type\n */\ntype EnvVarTypeMap = {\n string: string;\n url: string;\n int: number;\n bool: boolean;\n json: unknown;\n};\n\n/**\n * Infer the TypeScript type from an environment variable definition with literal type support\n */\ntype InferEnvVarType<T> =\n T extends { type: infer Type; required: infer Req }\n ? Type extends keyof EnvVarTypeMap\n ? Req extends true\n ? EnvVarTypeMap[Type]\n : EnvVarTypeMap[Type] | undefined\n : unknown\n : unknown;\n\n/**\n * Extract public environment variable types from AppConfig\n */\nexport type PublicEnvObject<T> = T extends { env: infer Env }\n ? {\n [K in keyof Env as Env[K] extends { client: true } ? K : never]: InferEnvVarType<Env[K]>;\n }\n : never;\n\n/**\n * Helper to parse integer value\n */\nexport function parseIntValue(value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n return Number.parseInt(value, 10);\n}\n\n/**\n * Helper to parse boolean value\n */\nexport function parseBoolValue(value: string | undefined): boolean | undefined {\n if (value === undefined) return undefined;\n return value === 'true' || value === '1';\n}\n\n/**\n * Helper to parse JSON value\n */\nexport function parseJsonValue(value: string | undefined): unknown {\n if (value === undefined) return undefined;\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n}\n\n/**\n * Helper to define a getter property on an object.\n *\n * This is useful for creating explicit getters that Next.js can statically analyze.\n * Instead of dynamic property access like process.env[varName], you should use\n * direct property access like process.env.NEXT_PUBLIC_MY_VAR in the getter function.\n *\n * @param target - The target object to add the getter to\n * @param propName - The property name\n * @param getter - The getter function that returns the value\n *\n * @example\n * ```ts\n * const env = {};\n * defineGetter(env, 'NEXT_PUBLIC_API_URL', () => process.env.NEXT_PUBLIC_API_URL);\n * defineGetter(env, 'NEXT_PUBLIC_ORG_ID', () => parseIntValue(process.env.NEXT_PUBLIC_ORG_ID));\n * ```\n */\nexport function defineGetter<T>(\n target: Record<string, unknown>,\n propName: string,\n getter: () => T\n): void {\n Object.defineProperty(target, propName, {\n get: getter,\n enumerable: true,\n });\n}\n\n/**\n * Create an empty object for public environment variables.\n *\n * Use this with defineGetter to create explicit getters for NEXT_PUBLIC_* variables.\n * This approach allows Next.js to perform static analysis and replace environment\n * variables at build time.\n *\n * @returns An empty object to add getters to\n *\n * @example\n * ```ts\n * const publicEnv = createPublicEnvGetters();\n * defineGetter(publicEnv, 'NEXT_PUBLIC_API_URL', () => process.env.NEXT_PUBLIC_API_URL);\n * defineGetter(publicEnv, 'NEXT_PUBLIC_ORG_ID', () => parseIntValue(process.env.NEXT_PUBLIC_ORG_ID));\n * ```\n */\nexport function createPublicEnvGetters(): Record<string, unknown> {\n return {};\n}\n\n/**\n * Create a public environment object with the correct TypeScript type.\n *\n * This is a type-safe wrapper that returns an empty object you can add getters to.\n * Use this with defineGetter to create explicit NEXT_PUBLIC_* getters.\n *\n * @param _config - Plain app configuration object (used only for type inference)\n * @returns An empty object with the correct TypeScript type for your config\n *\n * @example\n * ```ts\n * import appConfig from '../app.config.json';\n *\n * export const publicEnv = createPublicEnv(appConfig as AppConfig);\n * defineGetter(publicEnv, 'NEXT_PUBLIC_API_URL', () => process.env.NEXT_PUBLIC_API_URL);\n * defineGetter(publicEnv, 'NEXT_PUBLIC_ORG_ID', () => parseIntValue(process.env.NEXT_PUBLIC_ORG_ID));\n * ```\n */\nexport function createPublicEnv<const T extends AppConfig>(_config: T): PublicEnvObject<T> {\n return createPublicEnvGetters() as PublicEnvObject<T>;\n}\n"],"mappings":";;;;AAwCA,SAAgB,cAAc,OAA+C;CAC3E,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,OAAO,OAAO,SAAS,OAAO,EAAE;AAClC;;;;AAKA,SAAgB,eAAe,OAAgD;CAC7E,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,OAAO,UAAU,UAAU,UAAU;AACvC;;;;AAKA,SAAgB,eAAe,OAAoC;CACjE,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,IAAI;EACF,OAAO,KAAK,MAAM,KAAK;CACzB,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,aACd,QACA,UACA,QACM;CACN,OAAO,eAAe,QAAQ,UAAU;EACtC,KAAK;EACL,YAAY;CACd,CAAC;AACH;;;;;;;;;;;;;;;;;AAkBA,SAAgB,yBAAkD;CAChE,OAAO,CAAC;AACV;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,gBAA2C,SAAgC;CACzF,OAAO,uBAAuB;AAChC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate TypeScript types from app.config.json
|
|
3
|
+
* This module provides functions to generate type definitions from configuration files
|
|
4
|
+
*/
|
|
5
|
+
export interface GenerateTypesOptions {
|
|
6
|
+
/** Path to app.config.json file */
|
|
7
|
+
configPath: string;
|
|
8
|
+
/** Path where the generated .d.ts file should be written */
|
|
9
|
+
outputPath: string;
|
|
10
|
+
/** Name of the interface to generate (default: 'PublicEnv') */
|
|
11
|
+
interfaceName?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generate TypeScript type definitions from an app.config.json file
|
|
15
|
+
* @param options - Configuration options for type generation
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateTypes(options: GenerateTypesOptions): Promise<void>;
|
|
18
|
+
//# sourceMappingURL=generator.d.ts.map
|