@crossdelta/infrastructure 0.7.5 → 0.8.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 CHANGED
@@ -28,6 +28,7 @@ const config: K8sServiceConfig = {
28
28
  },
29
29
  ingress: { path: '/api', host: 'example.com' },
30
30
  env: { DATABASE_URL: dbUrl },
31
+ containerEnv: { NODE_OPTIONS: '--max-old-space-size=384' },
31
32
  secrets: { API_KEY: apiKey },
32
33
  resources: {
33
34
  requests: { cpu: '50m', memory: '64Mi' },
@@ -95,6 +96,7 @@ interface K8sServiceConfig {
95
96
  host?: string
96
97
  }
97
98
  env?: Record<string, pulumi.Input<string>>
99
+ containerEnv?: Record<string, pulumi.Input<string>>
98
100
  secrets?: Record<string, pulumi.Input<string>>
99
101
  image?: string
100
102
  skip?: boolean
@@ -4,6 +4,7 @@
4
4
  import { execSync } from "node:child_process";
5
5
  import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
6
6
  import { join } from "node:path";
7
+ var LOCK_FILES = ["bun.lock", "bun.lockb", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"];
7
8
  var toEnvKey = (name) => name.toUpperCase().replace(/-/g, "_");
8
9
  var resolveNumber = (value, fileContent) => {
9
10
  const literal = Number.parseInt(value, 10);
@@ -22,28 +23,84 @@ var extract = {
22
23
  const match = content.match(pattern)?.[1];
23
24
  if (!match)
24
25
  return;
25
- const numbers = match.split(",").map((v) => resolveNumber(v.trim(), content)).filter((n) => n !== undefined);
26
+ const numbers = match.split(",").map((value) => resolveNumber(value.trim(), content)).filter((num) => num !== undefined);
26
27
  return numbers.length > 0 ? numbers : undefined;
27
28
  }
28
29
  };
29
- var LOCK_FILES = ["bun.lock", "bun.lockb", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"];
30
- var hasLockFile = (dir) => LOCK_FILES.some((file) => existsSync(join(dir, file)));
30
+ var extractEnvLiterals = (content) => {
31
+ const envBlockMatch = content.match(/(?<!\w)env:\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/);
32
+ if (!envBlockMatch?.[1])
33
+ return;
34
+ const literals = {};
35
+ const literalPattern = /^\s*(\w+):\s*['"]([^'"]+)['"]/gm;
36
+ for (const [, key, value] of envBlockMatch[1].matchAll(literalPattern)) {
37
+ if (key && value)
38
+ literals[key] = value;
39
+ }
40
+ return Object.keys(literals).length > 0 ? literals : undefined;
41
+ };
42
+ var getServicePort = (config) => {
43
+ if (config.primaryPort)
44
+ return config.primaryPort;
45
+ if (config.containerPort)
46
+ return config.containerPort;
47
+ if (config.httpPort)
48
+ return config.httpPort;
49
+ if (config.internalPorts?.[0])
50
+ return config.internalPorts[0];
51
+ return;
52
+ };
53
+ var getLocalUrl = (config) => {
54
+ const port = getServicePort(config);
55
+ if (!port)
56
+ return;
57
+ if (config.internalUrl) {
58
+ const protocolMatch = config.internalUrl.match(/^(\w+):\/\//);
59
+ if (protocolMatch) {
60
+ return `${protocolMatch[1]}://localhost:${port}`;
61
+ }
62
+ }
63
+ return `http://localhost:${port}`;
64
+ };
65
+ var buildServiceEnvLines = (serviceConfigs) => {
66
+ const lines = [];
67
+ lines.push("", "# Service URLs");
68
+ for (const config of serviceConfigs) {
69
+ const url = getLocalUrl(config);
70
+ if (url)
71
+ lines.push(`${toEnvKey(config.name)}_URL=${url}`);
72
+ }
73
+ lines.push("", "# Service Ports");
74
+ for (const config of serviceConfigs) {
75
+ const port = getServicePort(config);
76
+ if (port)
77
+ lines.push(`${toEnvKey(config.name)}_PORT=${port}`);
78
+ }
79
+ const envLiteralConfigs = serviceConfigs.filter((config) => config.envLiterals !== undefined);
80
+ if (envLiteralConfigs.length > 0) {
81
+ lines.push("", "# Service Environment");
82
+ for (const config of envLiteralConfigs) {
83
+ for (const [key, value] of Object.entries(config.envLiterals)) {
84
+ lines.push(`${key}=${value}`);
85
+ }
86
+ }
87
+ }
88
+ return lines;
89
+ };
90
+ var hasLockFile = (directory) => LOCK_FILES.some((file) => existsSync(join(directory, file)));
31
91
  var findWorkspaceRoot = () => {
32
- let dir = process.cwd();
33
- while (!hasLockFile(dir)) {
34
- const parent = join(dir, "..");
35
- if (parent === dir)
92
+ let directory = process.cwd();
93
+ while (!hasLockFile(directory)) {
94
+ const parent = join(directory, "..");
95
+ if (parent === directory)
36
96
  throw new Error("Could not locate workspace root");
37
- dir = parent;
97
+ directory = parent;
38
98
  }
39
- return dir;
99
+ return directory;
40
100
  };
41
- var loadPulumiConfig = async (infraDir, stack) => {
101
+ var loadPulumiConfig = async (infraDirectory, stack) => {
42
102
  try {
43
- const stdout = execSync(`pulumi config --show-secrets --json --stack ${stack} --cwd ${infraDir}`, {
44
- encoding: "utf-8",
45
- stdio: ["pipe", "pipe", "pipe"]
46
- });
103
+ const stdout = execSync(`pulumi config --show-secrets --json --stack ${stack} --cwd ${infraDirectory}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
47
104
  const config = JSON.parse(stdout);
48
105
  return Object.entries(config).filter(([key]) => key.includes(":")).filter(([, entry]) => entry.value !== undefined && entry.value !== null && entry.value !== "undefined").map(([fullKey, entry]) => {
49
106
  const [, rawKey] = fullKey.split(":");
@@ -53,12 +110,12 @@ var loadPulumiConfig = async (infraDir, stack) => {
53
110
  return [];
54
111
  }
55
112
  };
56
- var discoverServices = (servicesDir) => {
57
- if (!existsSync(servicesDir))
113
+ var discoverServices = (servicesDirectory) => {
114
+ if (!existsSync(servicesDirectory))
58
115
  return [];
59
- const files = readdirSync(servicesDir).filter((file) => file.endsWith(".ts") && file !== "index.ts");
116
+ const files = readdirSync(servicesDirectory).filter((file) => file.endsWith(".ts") && file !== "index.ts");
60
117
  return files.map((file) => {
61
- const content = readFileSync(join(servicesDir, file), "utf-8");
118
+ const content = readFileSync(join(servicesDirectory, file), "utf-8");
62
119
  const portsApiMatch = content.match(/ports\(\)\.(?:http|https|grpc|primary)\((\d+)\)/) || content.match(/ports\(\)\.add\((\d+)/);
63
120
  return {
64
121
  name: extract.string(content, /name:\s*['"]([^'"]+)['"]/) ?? file.replace(".ts", ""),
@@ -66,68 +123,33 @@ var discoverServices = (servicesDir) => {
66
123
  containerPort: extract.number(content, /containerPort:\s*(\w+)/),
67
124
  httpPort: extract.number(content, /httpPort:\s*(\w+)/),
68
125
  internalPorts: extract.numberArray(content, /internalPorts:\s*\[([^\]]+)\]/),
69
- internalUrl: extract.string(content, /internalUrl:\s*[`'"]([^`'"]+)[`'"]/)
126
+ internalUrl: extract.string(content, /internalUrl:\s*[`'"]([^`'"]+)[`'"]/),
127
+ envLiterals: extractEnvLiterals(content)
70
128
  };
71
129
  });
72
130
  };
73
- var getServicePort = (config) => {
74
- if (config.primaryPort)
75
- return config.primaryPort;
76
- if (config.containerPort)
77
- return config.containerPort;
78
- if (config.httpPort)
79
- return config.httpPort;
80
- if (config.internalPorts?.[0])
81
- return config.internalPorts[0];
82
- return;
83
- };
84
- var getLocalUrl = (config) => {
85
- const port = getServicePort(config);
86
- if (!port)
87
- return;
88
- if (config.internalUrl) {
89
- const protocolMatch = config.internalUrl.match(/^(\w+):\/\//);
90
- if (protocolMatch) {
91
- return `${protocolMatch[1]}://localhost:${port}`;
92
- }
93
- }
94
- return `http://localhost:${port}`;
95
- };
96
131
  var main = async () => {
97
132
  const noPulumi = process.argv.includes("--no-pulumi");
98
- const workspaceRootDir = findWorkspaceRoot();
99
- const infraDir = join(workspaceRootDir, "infra");
100
- const servicesDir = join(infraDir, "services");
133
+ const workspaceRootDirectory = findWorkspaceRoot();
134
+ const infraDirectory = join(workspaceRootDirectory, "infra");
135
+ const servicesDirectory = join(infraDirectory, "services");
101
136
  const envLines = ["# Generated .env.local"];
102
- if (!noPulumi && existsSync(join(infraDir, "Pulumi.yaml"))) {
103
- const pulumiEnvs = await loadPulumiConfig(infraDir, "dev");
104
- if (pulumiEnvs.length > 0) {
137
+ if (!noPulumi && existsSync(join(infraDirectory, "Pulumi.yaml"))) {
138
+ const pulumiEntries = await loadPulumiConfig(infraDirectory, "dev");
139
+ if (pulumiEntries.length > 0) {
105
140
  envLines.push("", "# Pulumi Secrets");
106
- envLines.push(...pulumiEnvs);
141
+ envLines.push(...pulumiEntries);
107
142
  }
108
143
  }
109
- const serviceConfigs = discoverServices(servicesDir);
144
+ const serviceConfigs = discoverServices(servicesDirectory);
110
145
  if (serviceConfigs.length > 0) {
111
- envLines.push("", "# Service URLs");
112
- for (const config of serviceConfigs) {
113
- const url = getLocalUrl(config);
114
- if (url) {
115
- envLines.push(`${toEnvKey(config.name)}_URL=${url}`);
116
- }
117
- }
118
- envLines.push("", "# Service Ports");
119
- for (const config of serviceConfigs) {
120
- const port = getServicePort(config);
121
- if (port) {
122
- envLines.push(`${toEnvKey(config.name)}_PORT=${port}`);
123
- }
124
- }
146
+ envLines.push(...buildServiceEnvLines(serviceConfigs));
125
147
  console.log(`✅ Discovered ${serviceConfigs.length} services`);
126
148
  }
127
- writeFileSync(join(workspaceRootDir, ".env.local"), `${envLines.join(`
149
+ writeFileSync(join(workspaceRootDirectory, ".env.local"), `${envLines.join(`
128
150
  `)}
129
151
  `);
130
- console.log(`✅ .env.local generated at ${workspaceRootDir}`);
152
+ console.log(`✅ .env.local generated at ${workspaceRootDirectory}`);
131
153
  };
132
154
  main().catch((err) => {
133
155
  console.error("❌", err.message);
@@ -26,8 +26,11 @@ interface MinimalServiceConfig {
26
26
  httpPort?: number
27
27
  internalPorts?: number[]
28
28
  internalUrl?: string
29
+ envLiterals?: Record<string, string>
29
30
  }
30
31
 
32
+ const LOCK_FILES = ['bun.lock', 'bun.lockb', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml']
33
+
31
34
  const toEnvKey = (name: string): string => name.toUpperCase().replace(/-/g, '_')
32
35
 
33
36
  const resolveNumber = (value: string, fileContent: string): number | undefined => {
@@ -52,33 +55,97 @@ const extract = {
52
55
 
53
56
  const numbers = match
54
57
  .split(',')
55
- .map((v) => resolveNumber(v.trim(), content))
56
- .filter((n): n is number => n !== undefined)
58
+ .map((value) => resolveNumber(value.trim(), content))
59
+ .filter((num): num is number => num !== undefined)
57
60
 
58
61
  return numbers.length > 0 ? numbers : undefined
59
62
  },
60
63
  }
61
64
 
62
- const LOCK_FILES = ['bun.lock', 'bun.lockb', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml']
65
+ const extractEnvLiterals = (content: string): Record<string, string> | undefined => {
66
+ const envBlockMatch = content.match(/(?<!\w)env:\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/)
67
+ if (!envBlockMatch?.[1]) return undefined
68
+
69
+ const literals: Record<string, string> = {}
70
+ const literalPattern = /^\s*(\w+):\s*['"]([^'"]+)['"]/gm
71
+ for (const [, key, value] of envBlockMatch[1].matchAll(literalPattern)) {
72
+ if (key && value) literals[key] = value
73
+ }
74
+ return Object.keys(literals).length > 0 ? literals : undefined
75
+ }
76
+
77
+ const getServicePort = (config: MinimalServiceConfig): number | undefined => {
78
+ if (config.primaryPort) return config.primaryPort
79
+ if (config.containerPort) return config.containerPort
80
+ if (config.httpPort) return config.httpPort
81
+ if (config.internalPorts?.[0]) return config.internalPorts[0]
82
+ return undefined
83
+ }
84
+
85
+ const getLocalUrl = (config: MinimalServiceConfig): string | undefined => {
86
+ const port = getServicePort(config)
87
+ if (!port) return undefined
63
88
 
64
- const hasLockFile = (dir: string): boolean => LOCK_FILES.some((file) => existsSync(join(dir, file)))
89
+ if (config.internalUrl) {
90
+ const protocolMatch = config.internalUrl.match(/^(\w+):\/\//)
91
+ if (protocolMatch) {
92
+ return `${protocolMatch[1]}://localhost:${port}`
93
+ }
94
+ }
95
+
96
+ return `http://localhost:${port}`
97
+ }
98
+
99
+ const buildServiceEnvLines = (serviceConfigs: MinimalServiceConfig[]): string[] => {
100
+ const lines: string[] = []
101
+
102
+ lines.push('', '# Service URLs')
103
+ for (const config of serviceConfigs) {
104
+ const url = getLocalUrl(config)
105
+ if (url) lines.push(`${toEnvKey(config.name)}_URL=${url}`)
106
+ }
107
+
108
+ lines.push('', '# Service Ports')
109
+ for (const config of serviceConfigs) {
110
+ const port = getServicePort(config)
111
+ if (port) lines.push(`${toEnvKey(config.name)}_PORT=${port}`)
112
+ }
113
+
114
+ const envLiteralConfigs = serviceConfigs.filter(
115
+ (config): config is MinimalServiceConfig & { envLiterals: Record<string, string> } =>
116
+ config.envLiterals !== undefined,
117
+ )
118
+ if (envLiteralConfigs.length > 0) {
119
+ lines.push('', '# Service Environment')
120
+ for (const config of envLiteralConfigs) {
121
+ for (const [key, value] of Object.entries(config.envLiterals)) {
122
+ lines.push(`${key}=${value}`)
123
+ }
124
+ }
125
+ }
126
+
127
+ return lines
128
+ }
129
+
130
+ const hasLockFile = (directory: string): boolean =>
131
+ LOCK_FILES.some((file) => existsSync(join(directory, file)))
65
132
 
66
133
  const findWorkspaceRoot = (): string => {
67
- let dir = process.cwd()
68
- while (!hasLockFile(dir)) {
69
- const parent = join(dir, '..')
70
- if (parent === dir) throw new Error('Could not locate workspace root')
71
- dir = parent
134
+ let directory = process.cwd()
135
+ while (!hasLockFile(directory)) {
136
+ const parent = join(directory, '..')
137
+ if (parent === directory) throw new Error('Could not locate workspace root')
138
+ directory = parent
72
139
  }
73
- return dir
140
+ return directory
74
141
  }
75
142
 
76
- const loadPulumiConfig = async (infraDir: string, stack: string): Promise<string[]> => {
143
+ const loadPulumiConfig = async (infraDirectory: string, stack: string): Promise<string[]> => {
77
144
  try {
78
- const stdout = execSync(`pulumi config --show-secrets --json --stack ${stack} --cwd ${infraDir}`, {
79
- encoding: 'utf-8',
80
- stdio: ['pipe', 'pipe', 'pipe'],
81
- })
145
+ const stdout = execSync(
146
+ `pulumi config --show-secrets --json --stack ${stack} --cwd ${infraDirectory}`,
147
+ { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] },
148
+ )
82
149
 
83
150
  const config = JSON.parse(stdout) as Record<string, PulumiConfigEntry>
84
151
 
@@ -94,15 +161,15 @@ const loadPulumiConfig = async (infraDir: string, stack: string): Promise<string
94
161
  }
95
162
  }
96
163
 
97
- const discoverServices = (servicesDir: string): MinimalServiceConfig[] => {
98
- if (!existsSync(servicesDir)) return []
164
+ const discoverServices = (servicesDirectory: string): MinimalServiceConfig[] => {
165
+ if (!existsSync(servicesDirectory)) return []
99
166
 
100
- const files = readdirSync(servicesDir).filter((file) => file.endsWith('.ts') && file !== 'index.ts')
167
+ const files = readdirSync(servicesDirectory).filter((file) => file.endsWith('.ts') && file !== 'index.ts')
101
168
 
102
169
  return files.map((file) => {
103
- const content = readFileSync(join(servicesDir, file), 'utf-8')
170
+ const content = readFileSync(join(servicesDirectory, file), 'utf-8')
104
171
 
105
- // Extract port from new ports() API: ports().http(4001) or ports().primary(4222)
172
+ // Extract port from ports() API: ports().http(4001) or ports().primary(4222)
106
173
  const portsApiMatch =
107
174
  content.match(/ports\(\)\.(?:http|https|grpc|primary)\((\d+)\)/) ||
108
175
  content.match(/ports\(\)\.add\((\d+)/)
@@ -114,74 +181,35 @@ const discoverServices = (servicesDir: string): MinimalServiceConfig[] => {
114
181
  httpPort: extract.number(content, /httpPort:\s*(\w+)/),
115
182
  internalPorts: extract.numberArray(content, /internalPorts:\s*\[([^\]]+)\]/),
116
183
  internalUrl: extract.string(content, /internalUrl:\s*[`'"]([^`'"]+)[`'"]/),
184
+ envLiterals: extractEnvLiterals(content),
117
185
  }
118
186
  })
119
187
  }
120
188
 
121
- const getServicePort = (config: MinimalServiceConfig): number | undefined => {
122
- if (config.primaryPort) return config.primaryPort // New ports() API
123
- if (config.containerPort) return config.containerPort
124
- if (config.httpPort) return config.httpPort
125
- if (config.internalPorts?.[0]) return config.internalPorts[0]
126
- return undefined
127
- }
128
-
129
- const getLocalUrl = (config: MinimalServiceConfig): string | undefined => {
130
- const port = getServicePort(config)
131
- if (!port) return undefined
132
-
133
- if (config.internalUrl) {
134
- const protocolMatch = config.internalUrl.match(/^(\w+):\/\//)
135
- if (protocolMatch) {
136
- return `${protocolMatch[1]}://localhost:${port}`
137
- }
138
- }
139
-
140
- return `http://localhost:${port}`
141
- }
142
-
143
189
  const main = async () => {
144
190
  const noPulumi = process.argv.includes('--no-pulumi')
145
- const workspaceRootDir = findWorkspaceRoot()
146
- const infraDir = join(workspaceRootDir, 'infra')
147
- const servicesDir = join(infraDir, 'services')
148
-
191
+ const workspaceRootDirectory = findWorkspaceRoot()
192
+ const infraDirectory = join(workspaceRootDirectory, 'infra')
193
+ const servicesDirectory = join(infraDirectory, 'services')
149
194
  const envLines: string[] = ['# Generated .env.local']
150
195
 
151
- // Load Pulumi secrets if available and not disabled
152
- if (!noPulumi && existsSync(join(infraDir, 'Pulumi.yaml'))) {
153
- const pulumiEnvs = await loadPulumiConfig(infraDir, 'dev')
154
- if (pulumiEnvs.length > 0) {
196
+ if (!noPulumi && existsSync(join(infraDirectory, 'Pulumi.yaml'))) {
197
+ const pulumiEntries = await loadPulumiConfig(infraDirectory, 'dev')
198
+ if (pulumiEntries.length > 0) {
155
199
  envLines.push('', '# Pulumi Secrets')
156
- envLines.push(...pulumiEnvs)
200
+ envLines.push(...pulumiEntries)
157
201
  }
158
202
  }
159
203
 
160
- // Discover services
161
- const serviceConfigs = discoverServices(servicesDir)
162
-
204
+ const serviceConfigs = discoverServices(servicesDirectory)
205
+
163
206
  if (serviceConfigs.length > 0) {
164
- envLines.push('', '# Service URLs')
165
- for (const config of serviceConfigs) {
166
- const url = getLocalUrl(config)
167
- if (url) {
168
- envLines.push(`${toEnvKey(config.name)}_URL=${url}`)
169
- }
170
- }
171
-
172
- envLines.push('', '# Service Ports')
173
- for (const config of serviceConfigs) {
174
- const port = getServicePort(config)
175
- if (port) {
176
- envLines.push(`${toEnvKey(config.name)}_PORT=${port}`)
177
- }
178
- }
179
-
207
+ envLines.push(...buildServiceEnvLines(serviceConfigs))
180
208
  console.log(`✅ Discovered ${serviceConfigs.length} services`)
181
209
  }
182
210
 
183
- writeFileSync(join(workspaceRootDir, '.env.local'), `${envLines.join('\n')}\n`)
184
- console.log(`✅ .env.local generated at ${workspaceRootDir}`)
211
+ writeFileSync(join(workspaceRootDirectory, '.env.local'), `${envLines.join('\n')}\n`)
212
+ console.log(`✅ .env.local generated at ${workspaceRootDirectory}`)
185
213
  }
186
214
 
187
215
  main().catch((err) => {
package/dist/index.cjs CHANGED
@@ -1266,10 +1266,10 @@ var createImagePullSecret = (provider, namespace, name, config) => {
1266
1266
  var getImagePullPolicy = (image, explicit) => explicit ?? (image.endsWith(":latest") ? "Always" : "IfNotPresent");
1267
1267
  var buildEnvVars = (config) => {
1268
1268
  const portEnv = { name: "PORT", value: String(config.ports.primary.port) };
1269
- const plainEnvVars = config.env ? Object.entries(config.env).map(([key, value]) => ({
1269
+ const plainEnvVars = [...Object.entries(config.env ?? {}), ...Object.entries(config.containerEnv ?? {})].map(([key, value]) => ({
1270
1270
  name: key,
1271
1271
  value: pulumi6.output(value)
1272
- })) : [];
1272
+ }));
1273
1273
  const secretEnvVars = config.secrets ? Object.keys(config.secrets).map((key) => ({
1274
1274
  name: key,
1275
1275
  valueFrom: {
package/dist/index.js CHANGED
@@ -1172,10 +1172,10 @@ var createImagePullSecret = (provider, namespace, name, config) => {
1172
1172
  var getImagePullPolicy = (image, explicit) => explicit ?? (image.endsWith(":latest") ? "Always" : "IfNotPresent");
1173
1173
  var buildEnvVars = (config) => {
1174
1174
  const portEnv = { name: "PORT", value: String(config.ports.primary.port) };
1175
- const plainEnvVars = config.env ? Object.entries(config.env).map(([key, value]) => ({
1175
+ const plainEnvVars = [...Object.entries(config.env ?? {}), ...Object.entries(config.containerEnv ?? {})].map(([key, value]) => ({
1176
1176
  name: key,
1177
1177
  value: pulumi6.output(value)
1178
- })) : [];
1178
+ }));
1179
1179
  const secretEnvVars = config.secrets ? Object.keys(config.secrets).map((key) => ({
1180
1180
  name: key,
1181
1181
  valueFrom: {
@@ -244,8 +244,10 @@ export interface K8sServiceConfig {
244
244
  containerPort?: number;
245
245
  /** Number of replicas (defaults to 1) */
246
246
  replicas?: number;
247
- /** Environment variables (plain values) */
247
+ /** Environment variables (plain values, available locally and in containers) */
248
248
  env?: Record<string, pulumi.Input<string>>;
249
+ /** Environment variables applied only inside the container (excluded from generate-env) */
250
+ containerEnv?: Record<string, pulumi.Input<string>>;
249
251
  /** Secret environment variables (stored in K8s Secret) */
250
252
  secrets?: Record<string, pulumi.Input<string>>;
251
253
  /** Ingress configuration (set to enable public access) */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/infrastructure",
3
- "version": "0.7.5",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -35,7 +35,7 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@crossdelta/cloudevents": "0.7.3"
38
+ "@crossdelta/cloudevents": "^0.7.13"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@pulumi/digitalocean": "^4.0.0",