composable.env 0.1.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 +222 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +111 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/src/builder.d.ts +52 -0
- package/dist/src/builder.d.ts.map +1 -0
- package/dist/src/builder.js +564 -0
- package/dist/src/builder.js.map +1 -0
- package/dist/src/contracts.d.ts +43 -0
- package/dist/src/contracts.d.ts.map +1 -0
- package/dist/src/contracts.js +154 -0
- package/dist/src/contracts.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/types.d.ts +624 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +57 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# composable.env
|
|
2
|
+
|
|
3
|
+
> Build `.env` files for every service from reusable **components**, **profiles**, and **contracts**.
|
|
4
|
+
|
|
5
|
+
Like CSS for environment variables. Define once, compose everywhere, validate against contracts.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g composable.env
|
|
9
|
+
cenv build --profile production
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The problem
|
|
15
|
+
|
|
16
|
+
Managing `.env` files across multiple services and environments leads to:
|
|
17
|
+
- Copy-pasted values that drift out of sync
|
|
18
|
+
- No validation — missing variables fail at runtime, not build time
|
|
19
|
+
- No inheritance — `staging` and `production` have 90% overlap but no shared base
|
|
20
|
+
- Secrets scattered across files with no clear team vs personal boundary
|
|
21
|
+
|
|
22
|
+
## The solution: three building blocks
|
|
23
|
+
|
|
24
|
+
### Components
|
|
25
|
+
Reusable `.env` variable definitions, organized by sections:
|
|
26
|
+
|
|
27
|
+
```ini
|
|
28
|
+
# env/components/database.env
|
|
29
|
+
NAMESPACE=DATABASE
|
|
30
|
+
|
|
31
|
+
[default]
|
|
32
|
+
HOST=localhost
|
|
33
|
+
PORT=5432
|
|
34
|
+
|
|
35
|
+
[production]
|
|
36
|
+
HOST=${DATABASE_PROD_HOST}
|
|
37
|
+
|
|
38
|
+
[staging]
|
|
39
|
+
HOST=${DATABASE_STAGING_HOST}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Profiles
|
|
43
|
+
Named compositions of components. Profiles can extend each other.
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
// env/profiles/staging.json
|
|
47
|
+
{
|
|
48
|
+
"name": "Staging",
|
|
49
|
+
"description": "Staging environment",
|
|
50
|
+
"extends": "production",
|
|
51
|
+
"components": {
|
|
52
|
+
"database": "staging",
|
|
53
|
+
"redis": "staging"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Contracts
|
|
59
|
+
TypeScript files that declare what a service requires. The build fails if any required variable is missing.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// env/contracts/api.contract.ts
|
|
63
|
+
import type { ServiceContract } from 'composable.env';
|
|
64
|
+
|
|
65
|
+
export const ApiContract: ServiceContract = {
|
|
66
|
+
name: 'api',
|
|
67
|
+
location: 'apps/api', // where to write .env.production
|
|
68
|
+
required: {
|
|
69
|
+
DATABASE_URL: '${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME}',
|
|
70
|
+
REDIS_URL: 'REDIS_URL',
|
|
71
|
+
},
|
|
72
|
+
optional: {
|
|
73
|
+
LOG_LEVEL: 'LOG_LEVEL',
|
|
74
|
+
},
|
|
75
|
+
defaults: {
|
|
76
|
+
LOG_LEVEL: 'info',
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Directory structure
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
env/
|
|
87
|
+
components/ # Reusable variable definitions
|
|
88
|
+
database.env
|
|
89
|
+
redis.env
|
|
90
|
+
auth.env
|
|
91
|
+
profiles/ # Named environment compositions
|
|
92
|
+
default.json # Lists all component names (required)
|
|
93
|
+
production.json
|
|
94
|
+
staging.json
|
|
95
|
+
contracts/ # Per-service variable requirements (optional)
|
|
96
|
+
api.contract.ts
|
|
97
|
+
worker.contract.ts
|
|
98
|
+
.env.shared # Team-wide raw values (can be committed)
|
|
99
|
+
.env.local # Personal overrides (always gitignored)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Getting started
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Scaffold the directory structure
|
|
108
|
+
cenv init
|
|
109
|
+
|
|
110
|
+
# List available profiles
|
|
111
|
+
cenv list
|
|
112
|
+
|
|
113
|
+
# Build all service .env files from a profile
|
|
114
|
+
cenv build --profile production
|
|
115
|
+
|
|
116
|
+
# Build from a profile, single output file
|
|
117
|
+
cenv build --profile staging --output .env.staging
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## How it works
|
|
123
|
+
|
|
124
|
+
1. **Load** `default.json` to get all component names
|
|
125
|
+
2. **Resolve** profile inheritance chain (e.g., `staging → production → default`)
|
|
126
|
+
3. **Compose** each component's sections in order: `[default]` → `[production]` → `[staging]`
|
|
127
|
+
4. **Layer** `.env.shared` (team values) then `.env.local` (personal overrides)
|
|
128
|
+
5. **Resolve** `${VAR}` substitutions with multi-pass chaining
|
|
129
|
+
6. **Validate** against all contracts — fail atomically if any required variable is missing
|
|
130
|
+
7. **Write** one `.env.{profile}` file per contract at the service's `location`
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Component files
|
|
135
|
+
|
|
136
|
+
Components are INI files with named sections. The section name matches the profile name by convention — no explicit mapping needed.
|
|
137
|
+
|
|
138
|
+
```ini
|
|
139
|
+
NAMESPACE=REDIS # Optional: prefix all vars with REDIS_
|
|
140
|
+
|
|
141
|
+
[default]
|
|
142
|
+
URL=redis://localhost:6379
|
|
143
|
+
|
|
144
|
+
[production]
|
|
145
|
+
URL=${REDIS_PROD_URL} # Resolved from .env.shared or .env.local
|
|
146
|
+
|
|
147
|
+
[staging]
|
|
148
|
+
URL=${REDIS_STAGING_URL}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Variables are prefixed with `NAMESPACE_` when set:
|
|
152
|
+
- `URL` in `[default]` → `REDIS_URL=redis://localhost:6379`
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Profile inheritance
|
|
157
|
+
|
|
158
|
+
Child profiles inherit all parent components and can selectively override:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"extends": "production",
|
|
163
|
+
"components": {
|
|
164
|
+
"database": "staging" // only override database
|
|
165
|
+
// everything else inherits from production
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Shared vs local values
|
|
173
|
+
|
|
174
|
+
| File | Purpose | Committed? |
|
|
175
|
+
|------|---------|-----------|
|
|
176
|
+
| `env/.env.shared` | Team-wide raw values | Your choice |
|
|
177
|
+
| `env/.env.local` | Personal overrides | Never (gitignored) |
|
|
178
|
+
|
|
179
|
+
`.env.local` always takes precedence over `.env.shared`.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Contracts
|
|
184
|
+
|
|
185
|
+
Contracts are optional but powerful. They:
|
|
186
|
+
- Declare exactly what a service needs
|
|
187
|
+
- Map system variable names to app variable names
|
|
188
|
+
- Support template values: `"${HOST}:${PORT}"`
|
|
189
|
+
- Support fallback chains: `"TUNNEL_URL : LOCAL_URL"`
|
|
190
|
+
- Fail the entire build if any required variable is missing (atomic)
|
|
191
|
+
|
|
192
|
+
Export name convention: `PascalCaseContract`
|
|
193
|
+
```typescript
|
|
194
|
+
// env/contracts/my-worker.contract.ts
|
|
195
|
+
export const MyWorkerContract: ServiceContract = { ... }
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Programmatic API
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { EnvironmentBuilder } from 'composable.env';
|
|
204
|
+
|
|
205
|
+
const builder = new EnvironmentBuilder(
|
|
206
|
+
process.cwd(), // configDir (where env/ lives)
|
|
207
|
+
'.env', // outputPath (for single-file builds)
|
|
208
|
+
'production' // envName (suffix for .env.{profile} files)
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const result = await builder.buildFromProfile('production');
|
|
212
|
+
if (!result.success) {
|
|
213
|
+
console.error(result.errors);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { EnvironmentBuilder } from '../src/index.js';
|
|
5
|
+
const program = new Command();
|
|
6
|
+
program
|
|
7
|
+
.name('cenv')
|
|
8
|
+
.description('composable.env — build .env files from components, profiles, and contracts')
|
|
9
|
+
.version('0.1.0');
|
|
10
|
+
program
|
|
11
|
+
.command('build')
|
|
12
|
+
.description('Build .env files from a profile')
|
|
13
|
+
.requiredOption('-p, --profile <name>', 'Profile name (e.g., production, staging)')
|
|
14
|
+
.option('-o, --output <path>', 'Output path for single-file builds', '.env')
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
const configDir = process.cwd();
|
|
17
|
+
const builder = new EnvironmentBuilder(configDir, options.output, options.profile);
|
|
18
|
+
try {
|
|
19
|
+
console.log(chalk.blue(`Building from profile: ${options.profile}`));
|
|
20
|
+
const result = await builder.buildFromProfile(options.profile);
|
|
21
|
+
if (result.success) {
|
|
22
|
+
console.log(chalk.green(`✅ Environment built successfully`));
|
|
23
|
+
if (result.warnings?.length) {
|
|
24
|
+
result.warnings.forEach(w => console.log(chalk.yellow(` ${w}`)));
|
|
25
|
+
}
|
|
26
|
+
console.log(chalk.gray(` Files: ${result.envPath}`));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
console.error(chalk.red('❌ Build failed:'));
|
|
30
|
+
result.errors?.forEach(e => console.error(chalk.red(` ${e}`)));
|
|
31
|
+
if (result.warnings?.length) {
|
|
32
|
+
result.warnings.forEach(w => console.log(chalk.yellow(` ${w}`)));
|
|
33
|
+
}
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error(chalk.red(`❌ Unexpected error: ${error}`));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
program
|
|
43
|
+
.command('list')
|
|
44
|
+
.description('List available profiles')
|
|
45
|
+
.action(() => {
|
|
46
|
+
const builder = new EnvironmentBuilder(process.cwd(), '');
|
|
47
|
+
const profiles = builder.listProfiles();
|
|
48
|
+
if (profiles.length === 0) {
|
|
49
|
+
console.log(chalk.yellow('No profiles found in env/profiles/'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
console.log(chalk.blue('Available profiles:'));
|
|
53
|
+
profiles.forEach(({ name, description }) => {
|
|
54
|
+
console.log(` ${chalk.green(name)}`);
|
|
55
|
+
console.log(chalk.gray(` ${description}`));
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
program
|
|
59
|
+
.command('init')
|
|
60
|
+
.description('Scaffold a new composable.env directory structure')
|
|
61
|
+
.action(async () => {
|
|
62
|
+
const fs = await import('fs');
|
|
63
|
+
const path = await import('path');
|
|
64
|
+
const cwd = process.cwd();
|
|
65
|
+
const dirs = [
|
|
66
|
+
'env/components',
|
|
67
|
+
'env/profiles',
|
|
68
|
+
'env/contracts',
|
|
69
|
+
];
|
|
70
|
+
for (const dir of dirs) {
|
|
71
|
+
const fullPath = path.join(cwd, dir);
|
|
72
|
+
if (!fs.existsSync(fullPath)) {
|
|
73
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
74
|
+
console.log(chalk.green(` created ${dir}/`));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Scaffold default.json
|
|
78
|
+
const defaultPath = path.join(cwd, 'env/profiles/default.json');
|
|
79
|
+
if (!fs.existsSync(defaultPath)) {
|
|
80
|
+
fs.writeFileSync(defaultPath, JSON.stringify({
|
|
81
|
+
name: 'Default',
|
|
82
|
+
description: 'All available components',
|
|
83
|
+
components: [],
|
|
84
|
+
}, null, 2) + '\n');
|
|
85
|
+
console.log(chalk.green(' created env/profiles/default.json'));
|
|
86
|
+
}
|
|
87
|
+
// Scaffold .gitignore entries
|
|
88
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
89
|
+
const entries = 'env/.env.local\n';
|
|
90
|
+
if (fs.existsSync(gitignorePath)) {
|
|
91
|
+
const existing = fs.readFileSync(gitignorePath, 'utf8');
|
|
92
|
+
if (!existing.includes('env/.env.local')) {
|
|
93
|
+
fs.appendFileSync(gitignorePath, '\n# composable.env\n' + entries);
|
|
94
|
+
console.log(chalk.green(' updated .gitignore'));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
fs.writeFileSync(gitignorePath, '# composable.env\n' + entries);
|
|
99
|
+
console.log(chalk.green(' created .gitignore'));
|
|
100
|
+
}
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log(chalk.blue('Next steps:'));
|
|
103
|
+
console.log(' 1. Add component files to env/components/');
|
|
104
|
+
console.log(' 2. Define profiles in env/profiles/');
|
|
105
|
+
console.log(' 3. Add contract files to env/contracts/ (optional)');
|
|
106
|
+
console.log(' 4. Add shared values to env/.env.shared (commit this)');
|
|
107
|
+
console.log(' 5. Add local overrides to env/.env.local (gitignored)');
|
|
108
|
+
console.log(' 6. Run: cenv build --profile <name>');
|
|
109
|
+
});
|
|
110
|
+
program.parse();
|
|
111
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Profile, Components, BuildResult } from './types.js';
|
|
2
|
+
export declare class EnvironmentBuilder {
|
|
3
|
+
private envName?;
|
|
4
|
+
private configDir;
|
|
5
|
+
private outputPath;
|
|
6
|
+
private contracts;
|
|
7
|
+
constructor(configDir: string, outputPath: string, envName?: string | undefined);
|
|
8
|
+
initialize(): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Build environment from a named profile.
|
|
11
|
+
*
|
|
12
|
+
* Convention:
|
|
13
|
+
* - default.json lists ALL component names
|
|
14
|
+
* - Profile JSON files are optional (only needed for explicit overrides)
|
|
15
|
+
* - Profiles can extend other profiles via "extends"
|
|
16
|
+
* - For each component: [default] section + profile-named section(s) layer on top
|
|
17
|
+
*/
|
|
18
|
+
buildFromProfile(profileName: string): Promise<BuildResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Build environment from an explicit component map.
|
|
21
|
+
* Writes a single .env file to outputPath.
|
|
22
|
+
*/
|
|
23
|
+
buildFromComponents(components: Components, profile?: Profile): Promise<BuildResult>;
|
|
24
|
+
/**
|
|
25
|
+
* Generate a service's .env file from the resolved variable pool.
|
|
26
|
+
*/
|
|
27
|
+
generateServiceEnvFile(serviceName: string, systemVars: Record<string, string>, outputPath?: string, currentEnv?: string): Promise<void>;
|
|
28
|
+
listProfiles(): {
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
}[];
|
|
32
|
+
private buildServiceEnvironments;
|
|
33
|
+
/**
|
|
34
|
+
* Load .env.shared (team) and .env.local (personal) from env/ root.
|
|
35
|
+
* .env.local always takes precedence.
|
|
36
|
+
*/
|
|
37
|
+
private loadSharedFiles;
|
|
38
|
+
private loadComponentPool;
|
|
39
|
+
private loadProfileWithInheritance;
|
|
40
|
+
private loadComponentConfig;
|
|
41
|
+
private extractQuotedValues;
|
|
42
|
+
private loadEnvFile;
|
|
43
|
+
private resolveVariables;
|
|
44
|
+
private validateAllContracts;
|
|
45
|
+
private formatEnvValue;
|
|
46
|
+
private generateDockerCompose;
|
|
47
|
+
private shouldIncludeService;
|
|
48
|
+
private processServiceConfig;
|
|
49
|
+
private evaluateCondition;
|
|
50
|
+
private substituteVariables;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/builder.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,OAAO,EACP,UAAU,EAEV,WAAW,EAEZ,MAAM,YAAY,CAAC;AAGpB,qBAAa,kBAAkB;IAQ3B,OAAO,CAAC,OAAO,CAAC;IAPlB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAkB;gBAGjC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,MAAM,YAAA;IAOpB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC;;;;;;;;OAQG;IACG,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAgFjE;;;OAGG;IACG,mBAAmB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC;IA0E1F;;OAEG;IACG,sBAAsB,CAC1B,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAiDhB,YAAY,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE;YAuBzC,wBAAwB;IA+FtC;;;OAGG;IACH,OAAO,CAAC,eAAe;YAcT,iBAAiB;IAiC/B,OAAO,CAAC,0BAA0B;IAkDlC,OAAO,CAAC,mBAAmB;IA0B3B,OAAO,CAAC,mBAAmB;IAyB3B,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,gBAAgB;IAyCxB,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,cAAc;YA6BR,qBAAqB;IA2BnC,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,iBAAiB;IA2BzB,OAAO,CAAC,mBAAmB;CAgB5B"}
|