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.
@@ -0,0 +1,154 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ export class ContractManager {
4
+ constructor(configDir) {
5
+ this.contracts = new Map();
6
+ this.contractsDir = path.join(configDir, 'env', 'contracts');
7
+ }
8
+ async initialize() {
9
+ await this.loadContracts();
10
+ }
11
+ async loadContracts() {
12
+ if (!fs.existsSync(this.contractsDir)) {
13
+ // No contracts directory — contracts are optional
14
+ return;
15
+ }
16
+ const files = fs
17
+ .readdirSync(this.contractsDir)
18
+ .filter(f => f.endsWith('.contract.ts') || f.endsWith('.contract.js'));
19
+ for (const file of files) {
20
+ try {
21
+ const filePath = path.join(this.contractsDir, file);
22
+ const serviceName = file.replace(/\.contract\.(ts|js)$/, '');
23
+ const module = await import(filePath);
24
+ // Expects export named: PascalCaseContract (e.g., ApiContract, DatabaseContract)
25
+ const pascalCaseName = serviceName
26
+ .split('-')
27
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
28
+ .join('');
29
+ const exportName = `${pascalCaseName}Contract`;
30
+ const contract = module[exportName];
31
+ if (contract) {
32
+ this.contracts.set(serviceName, contract);
33
+ }
34
+ else {
35
+ console.warn(`Contract file ${file} does not export '${exportName}'. Skipping.`);
36
+ }
37
+ }
38
+ catch (err) {
39
+ console.warn(`Failed to load contract ${file}:`, err instanceof Error ? err.message : String(err));
40
+ }
41
+ }
42
+ }
43
+ /**
44
+ * Validate that all required variables for a service are present in the pool.
45
+ */
46
+ validateContract(serviceName, systemVars) {
47
+ const contract = this.contracts.get(serviceName);
48
+ if (!contract) {
49
+ return {
50
+ valid: true,
51
+ missing: [],
52
+ warnings: [`No contract found for service: ${serviceName}`],
53
+ };
54
+ }
55
+ const missing = [];
56
+ const warnings = [];
57
+ for (const [appVar, systemVar] of Object.entries(contract.required)) {
58
+ const resolved = this.resolveMapping(systemVar, systemVars);
59
+ if (resolved === undefined) {
60
+ missing.push(`${systemVar} (needed for ${appVar})`);
61
+ }
62
+ }
63
+ if (contract.secret) {
64
+ for (const [appVar, systemVar] of Object.entries(contract.secret)) {
65
+ const resolved = this.resolveMapping(systemVar, systemVars);
66
+ if (resolved === undefined) {
67
+ missing.push(`${systemVar} (needed for secret ${appVar})`);
68
+ }
69
+ }
70
+ }
71
+ if (contract.optional) {
72
+ for (const [appVar, systemVar] of Object.entries(contract.optional)) {
73
+ if (!(systemVar in systemVars) && contract.defaults?.[appVar]) {
74
+ warnings.push(`${systemVar} not set, using default for ${appVar}: ${contract.defaults[appVar]}`);
75
+ }
76
+ }
77
+ }
78
+ return { valid: missing.length === 0, missing, warnings };
79
+ }
80
+ /**
81
+ * Map the resolved variable pool to a service's own variable names.
82
+ * Supports direct mapping and template strings: "${HOST}:${PORT}"
83
+ */
84
+ mapContractVariables(serviceName, systemVars) {
85
+ const contract = this.contracts.get(serviceName);
86
+ if (!contract)
87
+ return {};
88
+ const serviceVars = {};
89
+ for (const [appVar, systemVar] of Object.entries(contract.required)) {
90
+ const value = this.resolveMapping(systemVar, systemVars);
91
+ if (value !== undefined)
92
+ serviceVars[appVar] = value;
93
+ }
94
+ if (contract.optional) {
95
+ for (const [appVar, systemVar] of Object.entries(contract.optional)) {
96
+ const value = this.resolveMapping(systemVar, systemVars);
97
+ if (value !== undefined) {
98
+ serviceVars[appVar] = value;
99
+ }
100
+ else if (contract.defaults?.[appVar]) {
101
+ serviceVars[appVar] = contract.defaults[appVar];
102
+ }
103
+ }
104
+ }
105
+ if (contract.secret) {
106
+ for (const [appVar, systemVar] of Object.entries(contract.secret)) {
107
+ const value = this.resolveMapping(systemVar, systemVars);
108
+ if (value !== undefined)
109
+ serviceVars[appVar] = value;
110
+ }
111
+ }
112
+ if (contract.defaults) {
113
+ for (const [appVar, defaultValue] of Object.entries(contract.defaults)) {
114
+ if (serviceVars[appVar] === undefined) {
115
+ serviceVars[appVar] = defaultValue;
116
+ }
117
+ }
118
+ }
119
+ return serviceVars;
120
+ }
121
+ /**
122
+ * Resolve a single var name, supporting fallback chains: "VAR1 : VAR2 : VAR3"
123
+ */
124
+ resolveVariable(varName, systemVars) {
125
+ if (varName.includes(':')) {
126
+ const candidates = varName.split(':').map(v => v.trim());
127
+ for (const candidate of candidates) {
128
+ const value = candidate in systemVars ? systemVars[candidate] : process.env[candidate];
129
+ if (value !== undefined && value !== '')
130
+ return value;
131
+ }
132
+ return undefined;
133
+ }
134
+ return varName in systemVars ? systemVars[varName] : process.env[varName];
135
+ }
136
+ /**
137
+ * Resolve a mapping — direct variable name or template "${HOST}:${PORT}"
138
+ */
139
+ resolveMapping(mapping, systemVars) {
140
+ if (/\$\{([^}]+)\}/.test(mapping)) {
141
+ const resolved = mapping.replace(/\$\{([^}]+)\}/g, (match, varName) => {
142
+ const value = this.resolveVariable(varName, systemVars);
143
+ return value !== undefined ? value : match;
144
+ });
145
+ // Only return if fully resolved
146
+ return /\$\{([^}]+)\}/.test(resolved) ? undefined : resolved;
147
+ }
148
+ return systemVars[mapping];
149
+ }
150
+ getContracts() {
151
+ return this.contracts;
152
+ }
153
+ }
154
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../src/contracts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { EnvironmentBuilder } from './builder.js';
2
+ export { ContractManager } from './contracts.js';
3
+ export type { ServiceContract } from './contracts.js';
4
+ export type { Components, Profile, EnvironmentConfig, BuildResult } from './types.js';
5
+ //# 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,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { EnvironmentBuilder } from './builder.js';
2
+ export { ContractManager } from './contracts.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}