@crossdelta/infrastructure 0.2.26 → 0.3.0-beta.1

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,419 @@
1
+ /**
2
+ * Provider-agnostic core types for infrastructure configuration.
3
+ *
4
+ * These types serve as the foundation for all cloud providers (DOKS, EKS, AKS, GKE).
5
+ * Provider-specific implementations extend these core types with their own features.
6
+ */
7
+ import type * as pulumi from '@pulumi/pulumi';
8
+ /**
9
+ * Supported cloud providers.
10
+ */
11
+ export declare const Provider: {
12
+ /** DigitalOcean Kubernetes Service */
13
+ readonly DOKS: "doks";
14
+ /** Amazon Elastic Kubernetes Service */
15
+ readonly EKS: "eks";
16
+ /** Azure Kubernetes Service */
17
+ readonly AKS: "aks";
18
+ /** Google Kubernetes Engine */
19
+ readonly GKE: "gke";
20
+ /** Local Kubernetes (minikube, kind, k3s) */
21
+ readonly LOCAL: "local";
22
+ };
23
+ export type Provider = (typeof Provider)[keyof typeof Provider];
24
+ /**
25
+ * CPU and memory resource specifications.
26
+ */
27
+ export interface Resources {
28
+ /** CPU (e.g., '100m', '0.5', '2') */
29
+ cpu?: string;
30
+ /** Memory (e.g., '128Mi', '1Gi') */
31
+ memory?: string;
32
+ }
33
+ /**
34
+ * Resource requests and limits for containers.
35
+ */
36
+ export interface ResourceConfig {
37
+ /** Guaranteed resources */
38
+ requests?: Resources;
39
+ /** Maximum resources */
40
+ limits?: Resources;
41
+ }
42
+ /**
43
+ * Port protocol type.
44
+ */
45
+ export type PortProtocol = 'TCP' | 'UDP' | 'HTTP' | 'HTTPS' | 'GRPC';
46
+ /**
47
+ * Unified port configuration.
48
+ *
49
+ * Replaces the inconsistent httpPort/internalPorts/containerPort model
50
+ * with a single, flexible port definition.
51
+ */
52
+ export interface PortConfig {
53
+ /** Port name (e.g., 'http', 'grpc', 'metrics') */
54
+ name?: string;
55
+ /** Port number the container listens on */
56
+ port: number;
57
+ /** External port (if different from container port) */
58
+ targetPort?: number;
59
+ /** Protocol (defaults to 'TCP') */
60
+ protocol?: PortProtocol;
61
+ /** Whether this port is exposed publicly via ingress (defaults to false) */
62
+ public?: boolean;
63
+ }
64
+ /**
65
+ * Service port configuration.
66
+ *
67
+ * Supports both simple (single port) and complex (multiple ports) configurations.
68
+ */
69
+ export interface ServicePorts {
70
+ /**
71
+ * Primary port (required).
72
+ * Used for health checks and as default port for service URLs.
73
+ */
74
+ primary: PortConfig;
75
+ /**
76
+ * Additional ports (optional).
77
+ * For services that expose multiple ports (e.g., HTTP + metrics).
78
+ */
79
+ additional?: PortConfig[];
80
+ }
81
+ /**
82
+ * Helper to create a simple single-port configuration.
83
+ */
84
+ export declare function createPort(port: number, options?: Partial<Omit<PortConfig, 'port'>>): ServicePorts;
85
+ /**
86
+ * Helper to create a multi-port configuration.
87
+ *
88
+ * @deprecated Use PortsBuilder for better API. Example: ports().primary(4222).add(8222).build()
89
+ */
90
+ export declare function createPorts(primary: PortConfig, additional: PortConfig[]): ServicePorts;
91
+ /**
92
+ * Fluent builder for service ports configuration.
93
+ *
94
+ * Provides a cleaner, more intuitive API for defining ports.
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * // Simple single port
99
+ * const ports = ports().http(8080).build()
100
+ *
101
+ * // Public HTTP port
102
+ * const ports = ports().http(3000).public().build()
103
+ *
104
+ * // Multiple ports
105
+ * const ports = ports()
106
+ * .primary(4222, 'client')
107
+ * .add(8222, 'monitoring').public()
108
+ * .add(6222, 'routing')
109
+ * .build()
110
+ *
111
+ * // With protocols
112
+ * const ports = ports()
113
+ * .http(8080).public()
114
+ * .grpc(9090)
115
+ * .add(9091, 'metrics')
116
+ * .build()
117
+ * ```
118
+ */
119
+ export declare class PortsBuilder {
120
+ private _primary?;
121
+ private _additional;
122
+ private _lastPort?;
123
+ /**
124
+ * Set primary port with optional name.
125
+ */
126
+ primary(port: number, name?: string): this;
127
+ /**
128
+ * Set primary HTTP port.
129
+ */
130
+ http(port: number, name?: string): this;
131
+ /**
132
+ * Set primary HTTPS port.
133
+ */
134
+ https(port: number, name?: string): this;
135
+ /**
136
+ * Set primary gRPC port.
137
+ */
138
+ grpc(port: number, name?: string): this;
139
+ /**
140
+ * Add an additional port.
141
+ */
142
+ add(port: number, name?: string, protocol?: PortProtocol): this;
143
+ /**
144
+ * Add an additional HTTP port.
145
+ */
146
+ addHttp(port: number, name?: string): this;
147
+ /**
148
+ * Add an additional gRPC port.
149
+ */
150
+ addGrpc(port: number, name?: string): this;
151
+ /**
152
+ * Mark the last added port as public (exposed via ingress).
153
+ */
154
+ public(): this;
155
+ /**
156
+ * Set protocol for the last added port.
157
+ */
158
+ protocol(protocol: PortProtocol): this;
159
+ /**
160
+ * Set target port (external port) for the last added port.
161
+ */
162
+ targetPort(targetPort: number): this;
163
+ /**
164
+ * Build the final ServicePorts configuration.
165
+ */
166
+ build(): ServicePorts;
167
+ }
168
+ /**
169
+ * Create a new ports builder (fluent API).
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * // Simple HTTP service
174
+ * const config = {
175
+ * ports: ports().http(8080).public().build()
176
+ * }
177
+ *
178
+ * // NATS with multiple ports
179
+ * const config = {
180
+ * ports: ports()
181
+ * .primary(4222, 'client')
182
+ * .add(8222, 'monitoring').public()
183
+ * .add(6222, 'routing')
184
+ * .build()
185
+ * }
186
+ * ```
187
+ */
188
+ export declare function ports(): PortsBuilder;
189
+ /**
190
+ * Health check type.
191
+ */
192
+ export type HealthCheckType = 'http' | 'tcp' | 'exec' | 'grpc';
193
+ /**
194
+ * Health check configuration.
195
+ */
196
+ export interface HealthCheck {
197
+ /** Type of health check */
198
+ type?: HealthCheckType;
199
+ /** HTTP path (for http checks) */
200
+ path?: string;
201
+ /** Port to check (defaults to primary port) */
202
+ port?: number;
203
+ /** Initial delay before starting checks (seconds) */
204
+ initialDelaySeconds?: number;
205
+ /** Period between checks (seconds) */
206
+ periodSeconds?: number;
207
+ /** Number of consecutive failures before unhealthy */
208
+ failureThreshold?: number;
209
+ /** Number of consecutive successes to be healthy */
210
+ successThreshold?: number;
211
+ /** Timeout for each check (seconds) */
212
+ timeoutSeconds?: number;
213
+ }
214
+ /**
215
+ * Path matching type for ingress rules.
216
+ */
217
+ export type PathType = 'Prefix' | 'Exact' | 'Regex';
218
+ /**
219
+ * TLS configuration for ingress.
220
+ */
221
+ export interface TLSConfig {
222
+ /** Enable TLS */
223
+ enabled: boolean;
224
+ /** Certificate issuer (e.g., 'letsencrypt-production', 'letsencrypt-staging') */
225
+ issuer?: string;
226
+ /** Secret name for certificate (auto-generated if not provided) */
227
+ secretName?: string;
228
+ }
229
+ /**
230
+ * Ingress rule configuration.
231
+ */
232
+ export interface IngressConfig {
233
+ /** Path for routing (e.g., '/', '/api') */
234
+ path: string;
235
+ /** Path matching type (defaults to 'Prefix') */
236
+ pathType?: PathType;
237
+ /** Strip path prefix before forwarding (defaults to true) */
238
+ stripPrefix?: boolean;
239
+ /** Hostname (required for TLS) */
240
+ host?: string;
241
+ /** Additional hostnames (e.g., www subdomain) */
242
+ additionalHosts?: string[];
243
+ /** TLS configuration */
244
+ tls?: TLSConfig;
245
+ /** Custom annotations */
246
+ annotations?: Record<string, string>;
247
+ }
248
+ /**
249
+ * Volume access mode.
250
+ */
251
+ export type VolumeAccessMode = 'ReadWriteOnce' | 'ReadOnlyMany' | 'ReadWriteMany';
252
+ /**
253
+ * Volume mount configuration.
254
+ */
255
+ export interface VolumeMount {
256
+ /** Volume name (unique identifier) */
257
+ name: string;
258
+ /** Mount path in container */
259
+ mountPath: string;
260
+ /** Volume size (e.g., '10Gi') */
261
+ size?: string;
262
+ /** Storage class (provider-specific) */
263
+ storageClass?: string;
264
+ /** Access mode */
265
+ accessMode?: VolumeAccessMode;
266
+ /** Read-only mount */
267
+ readOnly?: boolean;
268
+ }
269
+ /**
270
+ * Service type.
271
+ */
272
+ export type ServiceType = 'ClusterIP' | 'NodePort' | 'LoadBalancer';
273
+ /**
274
+ * Core service configuration (provider-agnostic).
275
+ *
276
+ * This is the base configuration that all providers implement.
277
+ * Provider-specific features are added via extensions.
278
+ *
279
+ * @example
280
+ * ```typescript
281
+ * // Simple HTTP service
282
+ * const config: CoreServiceConfig = {
283
+ * name: 'api',
284
+ * ports: createPort(8080, { public: true }),
285
+ * replicas: 2,
286
+ * }
287
+ *
288
+ * // Complex service with multiple ports
289
+ * const config: CoreServiceConfig = {
290
+ * name: 'nats',
291
+ * ports: createPorts(
292
+ * { port: 4222, name: 'client', protocol: 'TCP' },
293
+ * [{ port: 8222, name: 'monitoring', protocol: 'HTTP' }]
294
+ * ),
295
+ * volumes: [{ name: 'data', mountPath: '/data', size: '10Gi' }],
296
+ * }
297
+ * ```
298
+ */
299
+ export interface CoreServiceConfig {
300
+ /** Unique service name */
301
+ name: string;
302
+ /**
303
+ * Container image (e.g., 'ghcr.io/org/app:latest').
304
+ * If not specified, auto-generated from provider config + service name.
305
+ */
306
+ image?: string;
307
+ /** Port configuration */
308
+ ports: ServicePorts;
309
+ /** Number of replicas (defaults to 1) */
310
+ replicas?: number;
311
+ /** Environment variables (plain values) */
312
+ env?: Record<string, pulumi.Input<string>>;
313
+ /** Secret environment variables */
314
+ secrets?: Record<string, pulumi.Input<string>>;
315
+ /** Ingress configuration (for public services) */
316
+ ingress?: IngressConfig;
317
+ /** Health check configuration */
318
+ healthCheck?: HealthCheck;
319
+ /** Resource requests and limits */
320
+ resources?: ResourceConfig;
321
+ /** Volume mounts for persistent storage */
322
+ volumes?: VolumeMount[];
323
+ /** Command to run (overrides container entrypoint) */
324
+ command?: string[];
325
+ /** Arguments to pass to command */
326
+ args?: string[];
327
+ /** Additional labels */
328
+ labels?: Record<string, string>;
329
+ /** Additional annotations */
330
+ annotations?: Record<string, string>;
331
+ /** Service type (defaults to 'ClusterIP') */
332
+ serviceType?: ServiceType;
333
+ /** Skip deployment (useful for temporarily disabling) */
334
+ skip?: boolean;
335
+ /** Image pull policy */
336
+ imagePullPolicy?: 'Always' | 'Never' | 'IfNotPresent';
337
+ /** Image pull secret name for private registries */
338
+ imagePullSecretName?: string;
339
+ }
340
+ /**
341
+ * Node pool configuration.
342
+ */
343
+ export interface NodePoolConfig {
344
+ /** Node pool name */
345
+ name: string;
346
+ /** Instance/droplet size (provider-specific) */
347
+ size: string;
348
+ /** Number of nodes (ignored if autoScale is set) */
349
+ nodeCount?: number;
350
+ /** Auto-scaling configuration */
351
+ autoScale?: {
352
+ minNodes: number;
353
+ maxNodes: number;
354
+ };
355
+ /** Node labels */
356
+ labels?: Record<string, string>;
357
+ /** Node taints */
358
+ taints?: Array<{
359
+ key: string;
360
+ value: string;
361
+ effect: 'NoSchedule' | 'PreferNoSchedule' | 'NoExecute';
362
+ }>;
363
+ }
364
+ /**
365
+ * Core cluster configuration (provider-agnostic).
366
+ */
367
+ export interface CoreClusterConfig {
368
+ /** Cluster name */
369
+ name: string;
370
+ /** Region/zone */
371
+ region: string;
372
+ /** Kubernetes version */
373
+ version?: string;
374
+ /** Node pools */
375
+ nodePools: NodePoolConfig[];
376
+ /** Enable high availability control plane */
377
+ ha?: boolean;
378
+ /** Enable auto-upgrade */
379
+ autoUpgrade?: boolean;
380
+ /** Tags */
381
+ tags?: string[];
382
+ }
383
+ /**
384
+ * Core VPC/network configuration (provider-agnostic).
385
+ */
386
+ export interface CoreNetworkConfig {
387
+ /** Network name */
388
+ name: string;
389
+ /** Region/zone */
390
+ region: string;
391
+ /** IP range (CIDR notation) */
392
+ cidrBlock?: string;
393
+ /** Description */
394
+ description?: string;
395
+ }
396
+ /**
397
+ * Service deployment result.
398
+ */
399
+ export interface ServiceDeploymentResult {
400
+ /** Service name */
401
+ name: string;
402
+ /** Internal service URL (for service-to-service communication) */
403
+ internalUrl: pulumi.Output<string>;
404
+ /** Service DNS name within cluster */
405
+ serviceDns: string;
406
+ /** Public URL (if ingress is configured) */
407
+ publicUrl?: pulumi.Output<string>;
408
+ }
409
+ /**
410
+ * Cluster deployment result.
411
+ */
412
+ export interface ClusterDeploymentResult {
413
+ /** Cluster name */
414
+ name: string;
415
+ /** Kubernetes endpoint */
416
+ endpoint: pulumi.Output<string>;
417
+ /** Kubeconfig for cluster access */
418
+ kubeconfig: pulumi.Output<string>;
419
+ }
package/dist/env.cjs CHANGED
@@ -33,21 +33,15 @@ __export(exports_env, {
33
33
  getServicePort: () => getServicePort
34
34
  });
35
35
  module.exports = __toCommonJS(exports_env);
36
-
37
- // lib/helpers/service-runtime.ts
38
- var toEnvKey = (name) => name.toUpperCase().replace(/-/g, "_");
39
- function getServicePort(serviceName, defaultPort = 8080) {
40
- const envKey = `${toEnvKey(serviceName)}_PORT`;
41
- const envValue = process.env[envKey];
42
- if (envValue) {
43
- const parsed = Number(envValue);
44
- if (!Number.isNaN(parsed)) {
45
- return parsed;
46
- }
36
+ var getServicePort = (serviceName) => {
37
+ const envVar = `${serviceName.toUpperCase().replace(/-/g, "_")}_PORT`;
38
+ const port = Number(process.env.PORT || process.env[envVar]);
39
+ if (!port || Number.isNaN(port)) {
40
+ throw new Error(`Port not found for service '${serviceName}'. ` + `Expected environment variable: ${envVar}. ` + `Run 'bun run generate-env' to create .env.local`);
47
41
  }
48
- return defaultPort;
49
- }
50
- function getServiceUrl(serviceName) {
51
- const envKey = `${toEnvKey(serviceName)}_URL`;
52
- return process.env[envKey];
53
- }
42
+ return port;
43
+ };
44
+ var getServiceUrl = (serviceName) => {
45
+ const envVar = `${serviceName.toUpperCase().replace(/-/g, "_")}_URL`;
46
+ return process.env[envVar];
47
+ };
package/dist/env.d.ts CHANGED
@@ -2,7 +2,21 @@
2
2
  * Environment helpers for services to read configuration.
3
3
  * Use this entry point in production services (Node.js, NestJS, etc.)
4
4
  *
5
+ * @deprecated This module is being phased out. Use environment variables directly.
6
+ *
5
7
  * @example
6
- * import { getServicePort, getServiceUrl } from '@crossdelta/infrastructure/env'
8
+ * // Instead of importing from /env, use process.env directly:
9
+ * const port = Number(process.env.PORT || process.env.MY_SERVICE_PORT) || 4001
10
+ */
11
+ /**
12
+ * Get the port for a service from environment variables.
13
+ * @throws Error if port is not found in environment
14
+ * @deprecated Use process.env directly: Number(process.env.MY_SERVICE_PORT)
15
+ */
16
+ export declare const getServicePort: (serviceName: string) => number;
17
+ /**
18
+ * Get the URL for a service.
19
+ * @throws Error if service URL is not found in environment
20
+ * @deprecated Use process.env or construct URLs manually
7
21
  */
8
- export * from './helpers/service-runtime';
22
+ export declare const getServiceUrl: (serviceName: string) => string | undefined;
package/dist/env.js CHANGED
@@ -1,20 +1,16 @@
1
- // lib/helpers/service-runtime.ts
2
- var toEnvKey = (name) => name.toUpperCase().replace(/-/g, "_");
3
- function getServicePort(serviceName, defaultPort = 8080) {
4
- const envKey = `${toEnvKey(serviceName)}_PORT`;
5
- const envValue = process.env[envKey];
6
- if (envValue) {
7
- const parsed = Number(envValue);
8
- if (!Number.isNaN(parsed)) {
9
- return parsed;
10
- }
1
+ // lib/env.ts
2
+ var getServicePort = (serviceName) => {
3
+ const envVar = `${serviceName.toUpperCase().replace(/-/g, "_")}_PORT`;
4
+ const port = Number(process.env.PORT || process.env[envVar]);
5
+ if (!port || Number.isNaN(port)) {
6
+ throw new Error(`Port not found for service '${serviceName}'. ` + `Expected environment variable: ${envVar}. ` + `Run 'bun run generate-env' to create .env.local`);
11
7
  }
12
- return defaultPort;
13
- }
14
- function getServiceUrl(serviceName) {
15
- const envKey = `${toEnvKey(serviceName)}_URL`;
16
- return process.env[envKey];
17
- }
8
+ return port;
9
+ };
10
+ var getServiceUrl = (serviceName) => {
11
+ const envVar = `${serviceName.toUpperCase().replace(/-/g, "_")}_URL`;
12
+ return process.env[envVar];
13
+ };
18
14
  export {
19
15
  getServiceUrl,
20
16
  getServicePort
@@ -1,5 +1,66 @@
1
1
  import type { K8sServiceConfig } from '../runtimes/doks/types';
2
2
  /**
3
+ * Options for discovering service configurations.
4
+ */
5
+ export interface DiscoverOptions {
6
+ /** Path to services directory (relative to cwd or absolute) */
7
+ servicesDir?: string;
8
+ /** Filter services by name (regex) */
9
+ filter?: RegExp | string;
10
+ /** Include only these services (by name) */
11
+ include?: string[];
12
+ /** Exclude these services (by name) */
13
+ exclude?: string[];
14
+ /** Sort services by name (defaults to true) */
15
+ sort?: boolean;
16
+ /** Validate configurations (check for duplicate ports, missing required fields) */
17
+ validate?: boolean;
18
+ /** Auto-generate images for services without image specified */
19
+ autoImage?: boolean;
20
+ /** Registry for auto-generated images (defaults to pf.registry from package.json) */
21
+ registry?: string;
22
+ /** Inject environment variables into all services */
23
+ env?: Record<string, string>;
24
+ /** Tag filter: only include services with these tags */
25
+ tags?: string[];
26
+ }
27
+ /**
28
+ * Service discovery result with metadata.
29
+ */
30
+ export interface DiscoveryResult {
31
+ /** Discovered service configurations */
32
+ configs: K8sServiceConfig[];
33
+ /** Total number of service files found */
34
+ totalFiles: number;
35
+ /** Number of services after filtering */
36
+ filteredCount: number;
37
+ /** Services that were skipped (skip: true) */
38
+ skipped: string[];
39
+ /** Validation warnings */
40
+ warnings: string[];
41
+ }
42
+ /**
43
+ * Smart service configuration discovery with filtering, validation, and auto-generation.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * // Simple discovery (backward compatible)
48
+ * const configs = discoverServiceConfigs('services')
49
+ *
50
+ * // Advanced discovery with options
51
+ * const result = discoverServiceConfigsWithOptions({
52
+ * servicesDir: 'services',
53
+ * filter: /^api-/, // Only services starting with 'api-'
54
+ * exclude: ['api-test'],
55
+ * validate: true,
56
+ * env: { NODE_ENV: 'production' }
57
+ * })
58
+ * ```
59
+ */
60
+ export declare function discoverServiceConfigsWithOptions(options?: DiscoverOptions): DiscoveryResult;
61
+ /**
62
+ * Simple service discovery (backward compatible).
63
+ *
3
64
  * Auto-discovers all K8s service configurations from infra/services directory.
4
65
  * Each .ts file (except index.ts) should export a K8sServiceConfig as default.
5
66
  *
@@ -11,11 +72,7 @@ import type { K8sServiceConfig } from '../runtimes/doks/types';
11
72
  *
12
73
  * @example
13
74
  * ```typescript
14
- * // Config without image - will use pf.registry automatically
15
- * const config: K8sServiceConfig = {
16
- * name: 'my-service',
17
- * containerPort: 4001,
18
- * }
75
+ * const configs = discoverServiceConfigs('services')
19
76
  * ```
20
77
  */
21
78
  export declare function discoverServiceConfigs(servicesDir?: string): K8sServiceConfig[];
@@ -1,6 +1,5 @@
1
- import { type ImageConfig } from '../types';
2
1
  /**
3
- * Helper to create a Docker Hub image config.
2
+ * Helper to create a Docker Hub image string.
4
3
  * For official images, use 'library' as the registry.
5
4
  *
6
5
  * @example
@@ -11,4 +10,4 @@ import { type ImageConfig } from '../types';
11
10
  * // User image: bitnami/redis:7.0
12
11
  * dockerHubImage('redis', '7.0', 'bitnami')
13
12
  */
14
- export declare const dockerHubImage: (repository: string, tag: string, registry?: string) => ImageConfig;
13
+ export declare const dockerHubImage: (repository: string, tag: string, registry?: string) => string;
@@ -1,9 +1,2 @@
1
- export * from './config';
2
1
  export * from './discover-services';
3
2
  export * from './docker-hub-image';
4
- export * from './droplet-builder';
5
- export * from './image';
6
- export * from './otel';
7
- export * from './service-builder';
8
- export * from './service-runtime';
9
- export * from './service-urls';