@geekmidas/cli 0.12.0 → 0.13.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.
@@ -2,6 +2,7 @@ import { execSync } from 'node:child_process';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { mkdir, rename, writeFile } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
+ import type { Construct } from '@geekmidas/constructs';
5
6
 
6
7
  export interface BundleOptions {
7
8
  /** Entry point file (e.g., .gkm/server/server.ts) */
@@ -16,6 +17,8 @@ export interface BundleOptions {
16
17
  external: string[];
17
18
  /** Stage for secrets injection (optional) */
18
19
  stage?: string;
20
+ /** Constructs to validate environment variables for */
21
+ constructs?: Construct[];
19
22
  }
20
23
 
21
24
  export interface BundleResult {
@@ -25,6 +28,26 @@ export interface BundleResult {
25
28
  masterKey?: string;
26
29
  }
27
30
 
31
+ /**
32
+ * Collect all required environment variables from constructs.
33
+ * Uses the SnifferEnvironmentParser to detect which env vars each service needs.
34
+ *
35
+ * @param constructs - Array of constructs to analyze
36
+ * @returns Deduplicated array of required environment variable names
37
+ */
38
+ async function collectRequiredEnvVars(
39
+ constructs: Construct[],
40
+ ): Promise<string[]> {
41
+ const allEnvVars = new Set<string>();
42
+
43
+ for (const construct of constructs) {
44
+ const envVars = await construct.getEnvironment();
45
+ envVars.forEach((v) => allEnvVars.add(v));
46
+ }
47
+
48
+ return Array.from(allEnvVars).sort();
49
+ }
50
+
28
51
  /**
29
52
  * Bundle the server application using tsdown
30
53
  *
@@ -34,7 +57,15 @@ export interface BundleResult {
34
57
  export async function bundleServer(
35
58
  options: BundleOptions,
36
59
  ): Promise<BundleResult> {
37
- const { entryPoint, outputDir, minify, sourcemap, external, stage } = options;
60
+ const {
61
+ entryPoint,
62
+ outputDir,
63
+ minify,
64
+ sourcemap,
65
+ external,
66
+ stage,
67
+ constructs,
68
+ } = options;
38
69
 
39
70
  // Ensure output directory exists
40
71
  await mkdir(outputDir, { recursive: true });
@@ -76,9 +107,11 @@ export async function bundleServer(
76
107
  let masterKey: string | undefined;
77
108
 
78
109
  if (stage) {
79
- const { readStageSecrets, toEmbeddableSecrets } = await import(
80
- '../secrets/storage'
81
- );
110
+ const {
111
+ readStageSecrets,
112
+ toEmbeddableSecrets,
113
+ validateEnvironmentVariables,
114
+ } = await import('../secrets/storage');
82
115
  const { encryptSecrets, generateDefineOptions } = await import(
83
116
  '../secrets/encryption'
84
117
  );
@@ -91,6 +124,42 @@ export async function bundleServer(
91
124
  );
92
125
  }
93
126
 
127
+ // Validate environment variables if constructs are provided
128
+ if (constructs && constructs.length > 0) {
129
+ console.log(' Analyzing environment variable requirements...');
130
+ const requiredVars = await collectRequiredEnvVars(constructs);
131
+
132
+ if (requiredVars.length > 0) {
133
+ const validation = validateEnvironmentVariables(requiredVars, secrets);
134
+
135
+ if (!validation.valid) {
136
+ const errorMessage = [
137
+ `Missing environment variables for stage "${stage}":`,
138
+ '',
139
+ ...validation.missing.map((v) => ` ❌ ${v}`),
140
+ '',
141
+ 'To fix this, either:',
142
+ ` 1. Add the missing variables to .gkm/secrets/${stage}.json using:`,
143
+ ` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,
144
+ '',
145
+ ` 2. Or import from a JSON file:`,
146
+ ` gkm secrets:import secrets.json --stage ${stage}`,
147
+ '',
148
+ 'Required variables:',
149
+ ...validation.required.map((v) =>
150
+ validation.missing.includes(v) ? ` ❌ ${v}` : ` ✓ ${v}`,
151
+ ),
152
+ ].join('\n');
153
+
154
+ throw new Error(errorMessage);
155
+ }
156
+
157
+ console.log(
158
+ ` ✓ All ${requiredVars.length} required environment variables found`,
159
+ );
160
+ }
161
+ }
162
+
94
163
  // Convert to embeddable format and encrypt
95
164
  const embeddable = toEmbeddableSecrets(secrets);
96
165
  const encrypted = encryptSecrets(embeddable);
@@ -236,6 +236,15 @@ async function buildForProvider(
236
236
  if (context.production?.bundle && !skipBundle) {
237
237
  logger.log(`\n📦 Bundling production server...`);
238
238
  const { bundleServer } = await import('./bundler');
239
+
240
+ // Collect all constructs for environment variable validation
241
+ const allConstructs = [
242
+ ...endpoints.map((e) => e.construct),
243
+ ...functions.map((f) => f.construct),
244
+ ...crons.map((c) => c.construct),
245
+ ...subscribers.map((s) => s.construct),
246
+ ];
247
+
239
248
  const bundleResult = await bundleServer({
240
249
  entryPoint: join(outputDir, 'server.ts'),
241
250
  outputDir: join(outputDir, 'dist'),
@@ -243,6 +252,7 @@ async function buildForProvider(
243
252
  sourcemap: false,
244
253
  external: context.production.external,
245
254
  stage,
255
+ constructs: allConstructs,
246
256
  });
247
257
  masterKey = bundleResult.masterKey;
248
258
  logger.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
@@ -11,6 +11,7 @@ import {
11
11
  secretsExist,
12
12
  setCustomSecret,
13
13
  toEmbeddableSecrets,
14
+ validateEnvironmentVariables,
14
15
  writeStageSecrets,
15
16
  } from '../storage';
16
17
  import type { StageSecrets } from '../types';
@@ -401,3 +402,210 @@ describe('maskPassword', () => {
401
402
  expect(masked).toBe('1234***89');
402
403
  });
403
404
  });
405
+
406
+ describe('validateEnvironmentVariables', () => {
407
+ const baseSecrets: StageSecrets = {
408
+ stage: 'production',
409
+ createdAt: new Date().toISOString(),
410
+ updatedAt: new Date().toISOString(),
411
+ services: {},
412
+ urls: {},
413
+ custom: {},
414
+ };
415
+
416
+ it('should return valid when no variables are required', () => {
417
+ const result = validateEnvironmentVariables([], baseSecrets);
418
+
419
+ expect(result.valid).toBe(true);
420
+ expect(result.missing).toEqual([]);
421
+ expect(result.provided).toEqual([]);
422
+ expect(result.required).toEqual([]);
423
+ });
424
+
425
+ it('should return valid when all required variables are present', () => {
426
+ const secrets: StageSecrets = {
427
+ ...baseSecrets,
428
+ urls: {
429
+ DATABASE_URL: 'postgresql://...',
430
+ },
431
+ custom: {
432
+ API_KEY: 'sk_test_123',
433
+ },
434
+ };
435
+
436
+ const result = validateEnvironmentVariables(
437
+ ['DATABASE_URL', 'API_KEY'],
438
+ secrets,
439
+ );
440
+
441
+ expect(result.valid).toBe(true);
442
+ expect(result.missing).toEqual([]);
443
+ expect(result.provided).toEqual(['API_KEY', 'DATABASE_URL']);
444
+ expect(result.required).toEqual(['API_KEY', 'DATABASE_URL']);
445
+ });
446
+
447
+ it('should return invalid when some variables are missing', () => {
448
+ const secrets: StageSecrets = {
449
+ ...baseSecrets,
450
+ urls: {
451
+ DATABASE_URL: 'postgresql://...',
452
+ },
453
+ custom: {},
454
+ };
455
+
456
+ const result = validateEnvironmentVariables(
457
+ ['DATABASE_URL', 'API_KEY', 'JWT_SECRET'],
458
+ secrets,
459
+ );
460
+
461
+ expect(result.valid).toBe(false);
462
+ expect(result.missing).toEqual(['API_KEY', 'JWT_SECRET']);
463
+ expect(result.provided).toEqual(['DATABASE_URL']);
464
+ expect(result.required).toEqual(['API_KEY', 'DATABASE_URL', 'JWT_SECRET']);
465
+ });
466
+
467
+ it('should return invalid when all variables are missing', () => {
468
+ const result = validateEnvironmentVariables(
469
+ ['API_KEY', 'JWT_SECRET'],
470
+ baseSecrets,
471
+ );
472
+
473
+ expect(result.valid).toBe(false);
474
+ expect(result.missing).toEqual(['API_KEY', 'JWT_SECRET']);
475
+ expect(result.provided).toEqual([]);
476
+ });
477
+
478
+ it('should recognize service credentials as provided', () => {
479
+ const secrets: StageSecrets = {
480
+ ...baseSecrets,
481
+ services: {
482
+ postgres: {
483
+ host: 'postgres',
484
+ port: 5432,
485
+ username: 'app',
486
+ password: 'secret',
487
+ database: 'app',
488
+ },
489
+ redis: {
490
+ host: 'redis',
491
+ port: 6379,
492
+ username: 'default',
493
+ password: 'redis-pass',
494
+ },
495
+ },
496
+ urls: {},
497
+ custom: {},
498
+ };
499
+
500
+ const result = validateEnvironmentVariables(
501
+ ['POSTGRES_PASSWORD', 'REDIS_HOST', 'POSTGRES_DB'],
502
+ secrets,
503
+ );
504
+
505
+ expect(result.valid).toBe(true);
506
+ expect(result.missing).toEqual([]);
507
+ expect(result.provided).toEqual([
508
+ 'POSTGRES_DB',
509
+ 'POSTGRES_PASSWORD',
510
+ 'REDIS_HOST',
511
+ ]);
512
+ });
513
+
514
+ it('should sort missing and provided arrays alphabetically', () => {
515
+ const secrets: StageSecrets = {
516
+ ...baseSecrets,
517
+ custom: {
518
+ ZEBRA: 'value',
519
+ ALPHA: 'value',
520
+ },
521
+ };
522
+
523
+ const result = validateEnvironmentVariables(
524
+ ['ZEBRA', 'ALPHA', 'YELLOW', 'BETA'],
525
+ secrets,
526
+ );
527
+
528
+ expect(result.missing).toEqual(['BETA', 'YELLOW']);
529
+ expect(result.provided).toEqual(['ALPHA', 'ZEBRA']);
530
+ expect(result.required).toEqual(['ALPHA', 'BETA', 'YELLOW', 'ZEBRA']);
531
+ });
532
+
533
+ it('should handle duplicate required variables', () => {
534
+ const secrets: StageSecrets = {
535
+ ...baseSecrets,
536
+ custom: {
537
+ API_KEY: 'value',
538
+ },
539
+ };
540
+
541
+ const result = validateEnvironmentVariables(
542
+ ['API_KEY', 'API_KEY', 'MISSING'],
543
+ secrets,
544
+ );
545
+
546
+ expect(result.valid).toBe(false);
547
+ expect(result.missing).toEqual(['MISSING']);
548
+ // Note: duplicates in input are preserved in required list
549
+ expect(result.required).toEqual(['API_KEY', 'API_KEY', 'MISSING']);
550
+ });
551
+
552
+ it('should work with complex service configurations', () => {
553
+ const secrets: StageSecrets = {
554
+ ...baseSecrets,
555
+ services: {
556
+ postgres: {
557
+ host: 'postgres',
558
+ port: 5432,
559
+ username: 'app',
560
+ password: 'pg-secret',
561
+ database: 'mydb',
562
+ },
563
+ redis: {
564
+ host: 'redis',
565
+ port: 6379,
566
+ username: 'default',
567
+ password: 'redis-secret',
568
+ },
569
+ rabbitmq: {
570
+ host: 'rabbitmq',
571
+ port: 5672,
572
+ username: 'guest',
573
+ password: 'guest',
574
+ vhost: '/',
575
+ },
576
+ },
577
+ urls: {
578
+ DATABASE_URL: 'postgresql://...',
579
+ REDIS_URL: 'redis://...',
580
+ RABBITMQ_URL: 'amqp://...',
581
+ },
582
+ custom: {
583
+ JWT_SECRET: 'jwt-secret-value',
584
+ },
585
+ };
586
+
587
+ const result = validateEnvironmentVariables(
588
+ [
589
+ 'DATABASE_URL',
590
+ 'REDIS_URL',
591
+ 'RABBITMQ_URL',
592
+ 'JWT_SECRET',
593
+ 'POSTGRES_PASSWORD',
594
+ 'REDIS_PASSWORD',
595
+ 'RABBITMQ_USER',
596
+ 'MISSING_VAR',
597
+ ],
598
+ secrets,
599
+ );
600
+
601
+ expect(result.valid).toBe(false);
602
+ expect(result.missing).toEqual(['MISSING_VAR']);
603
+ expect(result.provided).toContain('DATABASE_URL');
604
+ expect(result.provided).toContain('REDIS_URL');
605
+ expect(result.provided).toContain('RABBITMQ_URL');
606
+ expect(result.provided).toContain('JWT_SECRET');
607
+ expect(result.provided).toContain('POSTGRES_PASSWORD');
608
+ expect(result.provided).toContain('REDIS_PASSWORD');
609
+ expect(result.provided).toContain('RABBITMQ_USER');
610
+ });
611
+ });
@@ -132,3 +132,61 @@ export function maskPassword(password: string): string {
132
132
  }
133
133
  return `${password.slice(0, 4)}${'*'.repeat(password.length - 6)}${password.slice(-2)}`;
134
134
  }
135
+
136
+ /**
137
+ * Result of environment variable validation.
138
+ */
139
+ export interface EnvValidationResult {
140
+ /** Whether all required environment variables are present */
141
+ valid: boolean;
142
+ /** List of missing environment variable names */
143
+ missing: string[];
144
+ /** List of environment variables that are provided */
145
+ provided: string[];
146
+ /** List of environment variables that were required */
147
+ required: string[];
148
+ }
149
+
150
+ /**
151
+ * Validate that all required environment variables are present in secrets.
152
+ *
153
+ * @param requiredVars - Array of environment variable names required by the application
154
+ * @param secrets - Stage secrets to validate against
155
+ * @returns Validation result with missing and provided variables
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * const required = ['DATABASE_URL', 'API_KEY', 'JWT_SECRET'];
160
+ * const secrets = await readStageSecrets('production');
161
+ * const result = validateEnvironmentVariables(required, secrets);
162
+ *
163
+ * if (!result.valid) {
164
+ * console.error(`Missing environment variables: ${result.missing.join(', ')}`);
165
+ * }
166
+ * ```
167
+ */
168
+ export function validateEnvironmentVariables(
169
+ requiredVars: string[],
170
+ secrets: StageSecrets,
171
+ ): EnvValidationResult {
172
+ const embeddable = toEmbeddableSecrets(secrets);
173
+ const availableVars = new Set(Object.keys(embeddable));
174
+
175
+ const missing: string[] = [];
176
+ const provided: string[] = [];
177
+
178
+ for (const varName of requiredVars) {
179
+ if (availableVars.has(varName)) {
180
+ provided.push(varName);
181
+ } else {
182
+ missing.push(varName);
183
+ }
184
+ }
185
+
186
+ return {
187
+ valid: missing.length === 0,
188
+ missing: missing.sort(),
189
+ provided: provided.sort(),
190
+ required: [...requiredVars].sort(),
191
+ };
192
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"bundler-DRXCw_YR.mjs","names":["options: BundleOptions","masterKey: string | undefined"],"sources":["../src/build/bundler.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, rename, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface BundleOptions {\n\t/** Entry point file (e.g., .gkm/server/server.ts) */\n\tentryPoint: string;\n\t/** Output directory for bundled files */\n\toutputDir: string;\n\t/** Minify the output (default: true) */\n\tminify: boolean;\n\t/** Generate sourcemaps (default: false) */\n\tsourcemap: boolean;\n\t/** Packages to exclude from bundling */\n\texternal: string[];\n\t/** Stage for secrets injection (optional) */\n\tstage?: string;\n}\n\nexport interface BundleResult {\n\t/** Path to the bundled output */\n\toutputPath: string;\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n/**\n * Bundle the server application using tsdown\n *\n * @param options - Bundle configuration options\n * @returns Bundle result with output path and optional master key\n */\nexport async function bundleServer(\n\toptions: BundleOptions,\n): Promise<BundleResult> {\n\tconst { entryPoint, outputDir, minify, sourcemap, external, stage } = options;\n\n\t// Ensure output directory exists\n\tawait mkdir(outputDir, { recursive: true });\n\n\t// Build command-line arguments for tsdown\n\tconst args = [\n\t\t'npx',\n\t\t'tsdown',\n\t\tentryPoint,\n\t\t'--no-config', // Don't use any config file from workspace\n\t\t'--out-dir',\n\t\toutputDir,\n\t\t'--format',\n\t\t'esm',\n\t\t'--platform',\n\t\t'node',\n\t\t'--target',\n\t\t'node22',\n\t\t'--clean',\n\t];\n\n\tif (minify) {\n\t\targs.push('--minify');\n\t}\n\n\tif (sourcemap) {\n\t\targs.push('--sourcemap');\n\t}\n\n\t// Add external packages\n\tfor (const ext of external) {\n\t\targs.push('--external', ext);\n\t}\n\n\t// Always exclude node: builtins\n\targs.push('--external', 'node:*');\n\n\t// Handle secrets injection if stage is provided\n\tlet masterKey: string | undefined;\n\n\tif (stage) {\n\t\tconst { readStageSecrets, toEmbeddableSecrets } = await import(\n\t\t\t'../secrets/storage'\n\t\t);\n\t\tconst { encryptSecrets, generateDefineOptions } = await import(\n\t\t\t'../secrets/encryption'\n\t\t);\n\n\t\tconst secrets = await readStageSecrets(stage);\n\n\t\tif (!secrets) {\n\t\t\tthrow new Error(\n\t\t\t\t`No secrets found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t\t);\n\t\t}\n\n\t\t// Convert to embeddable format and encrypt\n\t\tconst embeddable = toEmbeddableSecrets(secrets);\n\t\tconst encrypted = encryptSecrets(embeddable);\n\t\tmasterKey = encrypted.masterKey;\n\n\t\t// Add define options for build-time injection\n\t\tconst defines = generateDefineOptions(encrypted);\n\t\tfor (const [key, value] of Object.entries(defines)) {\n\t\t\targs.push('--define', `${key}=${value}`);\n\t\t}\n\n\t\tconsole.log(` Secrets encrypted for stage \"${stage}\"`);\n\t}\n\n\tconst mjsOutput = join(outputDir, 'server.mjs');\n\n\ttry {\n\t\t// Run tsdown with command-line arguments\n\t\texecSync(args.join(' '), {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t});\n\n\t\t// Rename output to .mjs for explicit ESM\n\t\t// tsdown outputs as server.js for ESM format\n\t\tconst jsOutput = join(outputDir, 'server.js');\n\n\t\tif (existsSync(jsOutput)) {\n\t\t\tawait rename(jsOutput, mjsOutput);\n\t\t}\n\n\t\t// Add shebang to the bundled file\n\t\tconst { readFile } = await import('node:fs/promises');\n\t\tconst content = await readFile(mjsOutput, 'utf-8');\n\t\tif (!content.startsWith('#!')) {\n\t\t\tawait writeFile(mjsOutput, `#!/usr/bin/env node\\n${content}`);\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to bundle server: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n\n\treturn {\n\t\toutputPath: mjsOutput,\n\t\tmasterKey,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;AAiCA,eAAsB,aACrBA,SACwB;CACxB,MAAM,EAAE,YAAY,WAAW,QAAQ,WAAW,UAAU,OAAO,GAAG;AAGtE,OAAM,MAAM,WAAW,EAAE,WAAW,KAAM,EAAC;CAG3C,MAAM,OAAO;EACZ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACA;AAED,KAAI,OACH,MAAK,KAAK,WAAW;AAGtB,KAAI,UACH,MAAK,KAAK,cAAc;AAIzB,MAAK,MAAM,OAAO,SACjB,MAAK,KAAK,cAAc,IAAI;AAI7B,MAAK,KAAK,cAAc,SAAS;CAGjC,IAAIC;AAEJ,KAAI,OAAO;EACV,MAAM,EAAE,kBAAkB,qBAAqB,GAAG,MAAM,OACvD;EAED,MAAM,EAAE,gBAAgB,uBAAuB,GAAG,MAAM,OACvD;EAGD,MAAM,UAAU,MAAM,iBAAiB,MAAM;AAE7C,OAAK,QACJ,OAAM,IAAI,OACR,8BAA8B,MAAM,mCAAmC,MAAM;EAKhF,MAAM,aAAa,oBAAoB,QAAQ;EAC/C,MAAM,YAAY,eAAe,WAAW;AAC5C,cAAY,UAAU;EAGtB,MAAM,UAAU,sBAAsB,UAAU;AAChD,OAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,QAAQ,CACjD,MAAK,KAAK,aAAa,EAAE,IAAI,GAAG,MAAM,EAAE;AAGzC,UAAQ,KAAK,iCAAiC,MAAM,GAAG;CACvD;CAED,MAAM,YAAY,KAAK,WAAW,aAAa;AAE/C,KAAI;AAEH,WAAS,KAAK,KAAK,IAAI,EAAE;GACxB,KAAK,QAAQ,KAAK;GAClB,OAAO;EACP,EAAC;EAIF,MAAM,WAAW,KAAK,WAAW,YAAY;AAE7C,MAAI,WAAW,SAAS,CACvB,OAAM,OAAO,UAAU,UAAU;EAIlC,MAAM,EAAE,sBAAU,GAAG,MAAM,OAAO;EAClC,MAAM,UAAU,MAAM,WAAS,WAAW,QAAQ;AAClD,OAAK,QAAQ,WAAW,KAAK,CAC5B,OAAM,UAAU,YAAY,uBAAuB,QAAQ,EAAE;CAE9D,SAAQ,OAAO;AACf,QAAM,IAAI,OACR,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,gBAAgB;CAEtF;AAED,QAAO;EACN,YAAY;EACZ;CACA;AACD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"bundler-WsEvH_b2.cjs","names":["options: BundleOptions","masterKey: string | undefined"],"sources":["../src/build/bundler.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, rename, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface BundleOptions {\n\t/** Entry point file (e.g., .gkm/server/server.ts) */\n\tentryPoint: string;\n\t/** Output directory for bundled files */\n\toutputDir: string;\n\t/** Minify the output (default: true) */\n\tminify: boolean;\n\t/** Generate sourcemaps (default: false) */\n\tsourcemap: boolean;\n\t/** Packages to exclude from bundling */\n\texternal: string[];\n\t/** Stage for secrets injection (optional) */\n\tstage?: string;\n}\n\nexport interface BundleResult {\n\t/** Path to the bundled output */\n\toutputPath: string;\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n/**\n * Bundle the server application using tsdown\n *\n * @param options - Bundle configuration options\n * @returns Bundle result with output path and optional master key\n */\nexport async function bundleServer(\n\toptions: BundleOptions,\n): Promise<BundleResult> {\n\tconst { entryPoint, outputDir, minify, sourcemap, external, stage } = options;\n\n\t// Ensure output directory exists\n\tawait mkdir(outputDir, { recursive: true });\n\n\t// Build command-line arguments for tsdown\n\tconst args = [\n\t\t'npx',\n\t\t'tsdown',\n\t\tentryPoint,\n\t\t'--no-config', // Don't use any config file from workspace\n\t\t'--out-dir',\n\t\toutputDir,\n\t\t'--format',\n\t\t'esm',\n\t\t'--platform',\n\t\t'node',\n\t\t'--target',\n\t\t'node22',\n\t\t'--clean',\n\t];\n\n\tif (minify) {\n\t\targs.push('--minify');\n\t}\n\n\tif (sourcemap) {\n\t\targs.push('--sourcemap');\n\t}\n\n\t// Add external packages\n\tfor (const ext of external) {\n\t\targs.push('--external', ext);\n\t}\n\n\t// Always exclude node: builtins\n\targs.push('--external', 'node:*');\n\n\t// Handle secrets injection if stage is provided\n\tlet masterKey: string | undefined;\n\n\tif (stage) {\n\t\tconst { readStageSecrets, toEmbeddableSecrets } = await import(\n\t\t\t'../secrets/storage'\n\t\t);\n\t\tconst { encryptSecrets, generateDefineOptions } = await import(\n\t\t\t'../secrets/encryption'\n\t\t);\n\n\t\tconst secrets = await readStageSecrets(stage);\n\n\t\tif (!secrets) {\n\t\t\tthrow new Error(\n\t\t\t\t`No secrets found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t\t);\n\t\t}\n\n\t\t// Convert to embeddable format and encrypt\n\t\tconst embeddable = toEmbeddableSecrets(secrets);\n\t\tconst encrypted = encryptSecrets(embeddable);\n\t\tmasterKey = encrypted.masterKey;\n\n\t\t// Add define options for build-time injection\n\t\tconst defines = generateDefineOptions(encrypted);\n\t\tfor (const [key, value] of Object.entries(defines)) {\n\t\t\targs.push('--define', `${key}=${value}`);\n\t\t}\n\n\t\tconsole.log(` Secrets encrypted for stage \"${stage}\"`);\n\t}\n\n\tconst mjsOutput = join(outputDir, 'server.mjs');\n\n\ttry {\n\t\t// Run tsdown with command-line arguments\n\t\texecSync(args.join(' '), {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t});\n\n\t\t// Rename output to .mjs for explicit ESM\n\t\t// tsdown outputs as server.js for ESM format\n\t\tconst jsOutput = join(outputDir, 'server.js');\n\n\t\tif (existsSync(jsOutput)) {\n\t\t\tawait rename(jsOutput, mjsOutput);\n\t\t}\n\n\t\t// Add shebang to the bundled file\n\t\tconst { readFile } = await import('node:fs/promises');\n\t\tconst content = await readFile(mjsOutput, 'utf-8');\n\t\tif (!content.startsWith('#!')) {\n\t\t\tawait writeFile(mjsOutput, `#!/usr/bin/env node\\n${content}`);\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to bundle server: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n\n\treturn {\n\t\toutputPath: mjsOutput,\n\t\tmasterKey,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;AAiCA,eAAsB,aACrBA,SACwB;CACxB,MAAM,EAAE,YAAY,WAAW,QAAQ,WAAW,UAAU,OAAO,GAAG;AAGtE,OAAM,4BAAM,WAAW,EAAE,WAAW,KAAM,EAAC;CAG3C,MAAM,OAAO;EACZ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACA;AAED,KAAI,OACH,MAAK,KAAK,WAAW;AAGtB,KAAI,UACH,MAAK,KAAK,cAAc;AAIzB,MAAK,MAAM,OAAO,SACjB,MAAK,KAAK,cAAc,IAAI;AAI7B,MAAK,KAAK,cAAc,SAAS;CAGjC,IAAIC;AAEJ,KAAI,OAAO;EACV,MAAM,EAAE,kBAAkB,qBAAqB,GAAG,2CAAM;EAGxD,MAAM,EAAE,gBAAgB,uBAAuB,GAAG,2CAAM;EAIxD,MAAM,UAAU,MAAM,iBAAiB,MAAM;AAE7C,OAAK,QACJ,OAAM,IAAI,OACR,8BAA8B,MAAM,mCAAmC,MAAM;EAKhF,MAAM,aAAa,oBAAoB,QAAQ;EAC/C,MAAM,YAAY,eAAe,WAAW;AAC5C,cAAY,UAAU;EAGtB,MAAM,UAAU,sBAAsB,UAAU;AAChD,OAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,QAAQ,CACjD,MAAK,KAAK,aAAa,EAAE,IAAI,GAAG,MAAM,EAAE;AAGzC,UAAQ,KAAK,iCAAiC,MAAM,GAAG;CACvD;CAED,MAAM,YAAY,oBAAK,WAAW,aAAa;AAE/C,KAAI;AAEH,mCAAS,KAAK,KAAK,IAAI,EAAE;GACxB,KAAK,QAAQ,KAAK;GAClB,OAAO;EACP,EAAC;EAIF,MAAM,WAAW,oBAAK,WAAW,YAAY;AAE7C,MAAI,wBAAW,SAAS,CACvB,OAAM,6BAAO,UAAU,UAAU;EAIlC,MAAM,EAAE,UAAU,GAAG,MAAM,OAAO;EAClC,MAAM,UAAU,MAAM,SAAS,WAAW,QAAQ;AAClD,OAAK,QAAQ,WAAW,KAAK,CAC5B,OAAM,gCAAU,YAAY,uBAAuB,QAAQ,EAAE;CAE9D,SAAQ,OAAO;AACf,QAAM,IAAI,OACR,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,gBAAgB;CAEtF;AAED,QAAO;EACN,YAAY;EACZ;CACA;AACD"}
@@ -1,4 +0,0 @@
1
- const require_storage = require('./storage-BXoJvmv2.cjs');
2
-
3
- exports.readStageSecrets = require_storage.readStageSecrets;
4
- exports.toEmbeddableSecrets = require_storage.toEmbeddableSecrets;
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage-BXoJvmv2.cjs","names":["stage: string","secrets: StageSecrets","key: string","value: string","updated: StageSecrets","password: string"],"sources":["../src/secrets/storage.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { EmbeddableSecrets, StageSecrets } from './types';\n\n/** Default secrets directory relative to project root */\nconst SECRETS_DIR = '.gkm/secrets';\n\n/**\n * Get the secrets directory path.\n */\nexport function getSecretsDir(cwd = process.cwd()): string {\n\treturn join(cwd, SECRETS_DIR);\n}\n\n/**\n * Get the secrets file path for a stage.\n */\nexport function getSecretsPath(stage: string, cwd = process.cwd()): string {\n\treturn join(getSecretsDir(cwd), `${stage}.json`);\n}\n\n/**\n * Check if secrets exist for a stage.\n */\nexport function secretsExist(stage: string, cwd = process.cwd()): boolean {\n\treturn existsSync(getSecretsPath(stage, cwd));\n}\n\n/**\n * Read secrets for a stage.\n * @returns StageSecrets or null if not found\n */\nexport async function readStageSecrets(\n\tstage: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets | null> {\n\tconst path = getSecretsPath(stage, cwd);\n\n\tif (!existsSync(path)) {\n\t\treturn null;\n\t}\n\n\tconst content = await readFile(path, 'utf-8');\n\treturn JSON.parse(content) as StageSecrets;\n}\n\n/**\n * Write secrets for a stage.\n */\nexport async function writeStageSecrets(\n\tsecrets: StageSecrets,\n\tcwd = process.cwd(),\n): Promise<void> {\n\tconst dir = getSecretsDir(cwd);\n\tconst path = getSecretsPath(secrets.stage, cwd);\n\n\t// Ensure directory exists\n\tawait mkdir(dir, { recursive: true });\n\n\t// Write with pretty formatting\n\tawait writeFile(path, JSON.stringify(secrets, null, 2), 'utf-8');\n}\n\n/**\n * Convert StageSecrets to embeddable format (flat key-value pairs).\n * This is what gets encrypted and embedded in the bundle.\n */\nexport function toEmbeddableSecrets(secrets: StageSecrets): EmbeddableSecrets {\n\treturn {\n\t\t...secrets.urls,\n\t\t...secrets.custom,\n\t\t// Also include individual service credentials if needed\n\t\t...(secrets.services.postgres && {\n\t\t\tPOSTGRES_USER: secrets.services.postgres.username,\n\t\t\tPOSTGRES_PASSWORD: secrets.services.postgres.password,\n\t\t\tPOSTGRES_DB: secrets.services.postgres.database ?? 'app',\n\t\t\tPOSTGRES_HOST: secrets.services.postgres.host,\n\t\t\tPOSTGRES_PORT: String(secrets.services.postgres.port),\n\t\t}),\n\t\t...(secrets.services.redis && {\n\t\t\tREDIS_PASSWORD: secrets.services.redis.password,\n\t\t\tREDIS_HOST: secrets.services.redis.host,\n\t\t\tREDIS_PORT: String(secrets.services.redis.port),\n\t\t}),\n\t\t...(secrets.services.rabbitmq && {\n\t\t\tRABBITMQ_USER: secrets.services.rabbitmq.username,\n\t\t\tRABBITMQ_PASSWORD: secrets.services.rabbitmq.password,\n\t\t\tRABBITMQ_HOST: secrets.services.rabbitmq.host,\n\t\t\tRABBITMQ_PORT: String(secrets.services.rabbitmq.port),\n\t\t\tRABBITMQ_VHOST: secrets.services.rabbitmq.vhost ?? '/',\n\t\t}),\n\t};\n}\n\n/**\n * Update a custom secret in the secrets file.\n */\nexport async function setCustomSecret(\n\tstage: string,\n\tkey: string,\n\tvalue: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets> {\n\tconst secrets = await readStageSecrets(stage, cwd);\n\n\tif (!secrets) {\n\t\tthrow new Error(\n\t\t\t`Secrets not found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t}\n\n\tconst updated: StageSecrets = {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tcustom: {\n\t\t\t...secrets.custom,\n\t\t\t[key]: value,\n\t\t},\n\t};\n\n\tawait writeStageSecrets(updated, cwd);\n\treturn updated;\n}\n\n/**\n * Mask a password for display (show first 4 and last 2 chars).\n */\nexport function maskPassword(password: string): string {\n\tif (password.length <= 8) {\n\t\treturn '********';\n\t}\n\treturn `${password.slice(0, 4)}${'*'.repeat(password.length - 6)}${password.slice(-2)}`;\n}\n"],"mappings":";;;;;;;AAMA,MAAM,cAAc;;;;AAKpB,SAAgB,cAAc,MAAM,QAAQ,KAAK,EAAU;AAC1D,QAAO,oBAAK,KAAK,YAAY;AAC7B;;;;AAKD,SAAgB,eAAeA,OAAe,MAAM,QAAQ,KAAK,EAAU;AAC1E,QAAO,oBAAK,cAAc,IAAI,GAAG,EAAE,MAAM,OAAO;AAChD;;;;AAKD,SAAgB,aAAaA,OAAe,MAAM,QAAQ,KAAK,EAAW;AACzE,QAAO,wBAAW,eAAe,OAAO,IAAI,CAAC;AAC7C;;;;;AAMD,eAAsB,iBACrBA,OACA,MAAM,QAAQ,KAAK,EACY;CAC/B,MAAM,OAAO,eAAe,OAAO,IAAI;AAEvC,MAAK,wBAAW,KAAK,CACpB,QAAO;CAGR,MAAM,UAAU,MAAM,+BAAS,MAAM,QAAQ;AAC7C,QAAO,KAAK,MAAM,QAAQ;AAC1B;;;;AAKD,eAAsB,kBACrBC,SACA,MAAM,QAAQ,KAAK,EACH;CAChB,MAAM,MAAM,cAAc,IAAI;CAC9B,MAAM,OAAO,eAAe,QAAQ,OAAO,IAAI;AAG/C,OAAM,4BAAM,KAAK,EAAE,WAAW,KAAM,EAAC;AAGrC,OAAM,gCAAU,MAAM,KAAK,UAAU,SAAS,MAAM,EAAE,EAAE,QAAQ;AAChE;;;;;AAMD,SAAgB,oBAAoBA,SAA0C;AAC7E,QAAO;EACN,GAAG,QAAQ;EACX,GAAG,QAAQ;EAEX,GAAI,QAAQ,SAAS,YAAY;GAChC,eAAe,QAAQ,SAAS,SAAS;GACzC,mBAAmB,QAAQ,SAAS,SAAS;GAC7C,aAAa,QAAQ,SAAS,SAAS,YAAY;GACnD,eAAe,QAAQ,SAAS,SAAS;GACzC,eAAe,OAAO,QAAQ,SAAS,SAAS,KAAK;EACrD;EACD,GAAI,QAAQ,SAAS,SAAS;GAC7B,gBAAgB,QAAQ,SAAS,MAAM;GACvC,YAAY,QAAQ,SAAS,MAAM;GACnC,YAAY,OAAO,QAAQ,SAAS,MAAM,KAAK;EAC/C;EACD,GAAI,QAAQ,SAAS,YAAY;GAChC,eAAe,QAAQ,SAAS,SAAS;GACzC,mBAAmB,QAAQ,SAAS,SAAS;GAC7C,eAAe,QAAQ,SAAS,SAAS;GACzC,eAAe,OAAO,QAAQ,SAAS,SAAS,KAAK;GACrD,gBAAgB,QAAQ,SAAS,SAAS,SAAS;EACnD;CACD;AACD;;;;AAKD,eAAsB,gBACrBD,OACAE,KACAC,OACA,MAAM,QAAQ,KAAK,EACK;CACxB,MAAM,UAAU,MAAM,iBAAiB,OAAO,IAAI;AAElD,MAAK,QACJ,OAAM,IAAI,OACR,+BAA+B,MAAM,mCAAmC,MAAM;CAIjF,MAAMC,UAAwB;EAC7B,GAAG;EACH,WAAW,qBAAI,QAAO,aAAa;EACnC,QAAQ;GACP,GAAG,QAAQ;IACV,MAAM;EACP;CACD;AAED,OAAM,kBAAkB,SAAS,IAAI;AACrC,QAAO;AACP;;;;AAKD,SAAgB,aAAaC,UAA0B;AACtD,KAAI,SAAS,UAAU,EACtB,QAAO;AAER,SAAQ,EAAE,SAAS,MAAM,GAAG,EAAE,CAAC,EAAE,IAAI,OAAO,SAAS,SAAS,EAAE,CAAC,EAAE,SAAS,MAAM,GAAG,CAAC;AACtF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage-C9PU_30f.mjs","names":["stage: string","secrets: StageSecrets","key: string","value: string","updated: StageSecrets","password: string"],"sources":["../src/secrets/storage.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { EmbeddableSecrets, StageSecrets } from './types';\n\n/** Default secrets directory relative to project root */\nconst SECRETS_DIR = '.gkm/secrets';\n\n/**\n * Get the secrets directory path.\n */\nexport function getSecretsDir(cwd = process.cwd()): string {\n\treturn join(cwd, SECRETS_DIR);\n}\n\n/**\n * Get the secrets file path for a stage.\n */\nexport function getSecretsPath(stage: string, cwd = process.cwd()): string {\n\treturn join(getSecretsDir(cwd), `${stage}.json`);\n}\n\n/**\n * Check if secrets exist for a stage.\n */\nexport function secretsExist(stage: string, cwd = process.cwd()): boolean {\n\treturn existsSync(getSecretsPath(stage, cwd));\n}\n\n/**\n * Read secrets for a stage.\n * @returns StageSecrets or null if not found\n */\nexport async function readStageSecrets(\n\tstage: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets | null> {\n\tconst path = getSecretsPath(stage, cwd);\n\n\tif (!existsSync(path)) {\n\t\treturn null;\n\t}\n\n\tconst content = await readFile(path, 'utf-8');\n\treturn JSON.parse(content) as StageSecrets;\n}\n\n/**\n * Write secrets for a stage.\n */\nexport async function writeStageSecrets(\n\tsecrets: StageSecrets,\n\tcwd = process.cwd(),\n): Promise<void> {\n\tconst dir = getSecretsDir(cwd);\n\tconst path = getSecretsPath(secrets.stage, cwd);\n\n\t// Ensure directory exists\n\tawait mkdir(dir, { recursive: true });\n\n\t// Write with pretty formatting\n\tawait writeFile(path, JSON.stringify(secrets, null, 2), 'utf-8');\n}\n\n/**\n * Convert StageSecrets to embeddable format (flat key-value pairs).\n * This is what gets encrypted and embedded in the bundle.\n */\nexport function toEmbeddableSecrets(secrets: StageSecrets): EmbeddableSecrets {\n\treturn {\n\t\t...secrets.urls,\n\t\t...secrets.custom,\n\t\t// Also include individual service credentials if needed\n\t\t...(secrets.services.postgres && {\n\t\t\tPOSTGRES_USER: secrets.services.postgres.username,\n\t\t\tPOSTGRES_PASSWORD: secrets.services.postgres.password,\n\t\t\tPOSTGRES_DB: secrets.services.postgres.database ?? 'app',\n\t\t\tPOSTGRES_HOST: secrets.services.postgres.host,\n\t\t\tPOSTGRES_PORT: String(secrets.services.postgres.port),\n\t\t}),\n\t\t...(secrets.services.redis && {\n\t\t\tREDIS_PASSWORD: secrets.services.redis.password,\n\t\t\tREDIS_HOST: secrets.services.redis.host,\n\t\t\tREDIS_PORT: String(secrets.services.redis.port),\n\t\t}),\n\t\t...(secrets.services.rabbitmq && {\n\t\t\tRABBITMQ_USER: secrets.services.rabbitmq.username,\n\t\t\tRABBITMQ_PASSWORD: secrets.services.rabbitmq.password,\n\t\t\tRABBITMQ_HOST: secrets.services.rabbitmq.host,\n\t\t\tRABBITMQ_PORT: String(secrets.services.rabbitmq.port),\n\t\t\tRABBITMQ_VHOST: secrets.services.rabbitmq.vhost ?? '/',\n\t\t}),\n\t};\n}\n\n/**\n * Update a custom secret in the secrets file.\n */\nexport async function setCustomSecret(\n\tstage: string,\n\tkey: string,\n\tvalue: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets> {\n\tconst secrets = await readStageSecrets(stage, cwd);\n\n\tif (!secrets) {\n\t\tthrow new Error(\n\t\t\t`Secrets not found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t}\n\n\tconst updated: StageSecrets = {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tcustom: {\n\t\t\t...secrets.custom,\n\t\t\t[key]: value,\n\t\t},\n\t};\n\n\tawait writeStageSecrets(updated, cwd);\n\treturn updated;\n}\n\n/**\n * Mask a password for display (show first 4 and last 2 chars).\n */\nexport function maskPassword(password: string): string {\n\tif (password.length <= 8) {\n\t\treturn '********';\n\t}\n\treturn `${password.slice(0, 4)}${'*'.repeat(password.length - 6)}${password.slice(-2)}`;\n}\n"],"mappings":";;;;;;AAMA,MAAM,cAAc;;;;AAKpB,SAAgB,cAAc,MAAM,QAAQ,KAAK,EAAU;AAC1D,QAAO,KAAK,KAAK,YAAY;AAC7B;;;;AAKD,SAAgB,eAAeA,OAAe,MAAM,QAAQ,KAAK,EAAU;AAC1E,QAAO,KAAK,cAAc,IAAI,GAAG,EAAE,MAAM,OAAO;AAChD;;;;AAKD,SAAgB,aAAaA,OAAe,MAAM,QAAQ,KAAK,EAAW;AACzE,QAAO,WAAW,eAAe,OAAO,IAAI,CAAC;AAC7C;;;;;AAMD,eAAsB,iBACrBA,OACA,MAAM,QAAQ,KAAK,EACY;CAC/B,MAAM,OAAO,eAAe,OAAO,IAAI;AAEvC,MAAK,WAAW,KAAK,CACpB,QAAO;CAGR,MAAM,UAAU,MAAM,SAAS,MAAM,QAAQ;AAC7C,QAAO,KAAK,MAAM,QAAQ;AAC1B;;;;AAKD,eAAsB,kBACrBC,SACA,MAAM,QAAQ,KAAK,EACH;CAChB,MAAM,MAAM,cAAc,IAAI;CAC9B,MAAM,OAAO,eAAe,QAAQ,OAAO,IAAI;AAG/C,OAAM,MAAM,KAAK,EAAE,WAAW,KAAM,EAAC;AAGrC,OAAM,UAAU,MAAM,KAAK,UAAU,SAAS,MAAM,EAAE,EAAE,QAAQ;AAChE;;;;;AAMD,SAAgB,oBAAoBA,SAA0C;AAC7E,QAAO;EACN,GAAG,QAAQ;EACX,GAAG,QAAQ;EAEX,GAAI,QAAQ,SAAS,YAAY;GAChC,eAAe,QAAQ,SAAS,SAAS;GACzC,mBAAmB,QAAQ,SAAS,SAAS;GAC7C,aAAa,QAAQ,SAAS,SAAS,YAAY;GACnD,eAAe,QAAQ,SAAS,SAAS;GACzC,eAAe,OAAO,QAAQ,SAAS,SAAS,KAAK;EACrD;EACD,GAAI,QAAQ,SAAS,SAAS;GAC7B,gBAAgB,QAAQ,SAAS,MAAM;GACvC,YAAY,QAAQ,SAAS,MAAM;GACnC,YAAY,OAAO,QAAQ,SAAS,MAAM,KAAK;EAC/C;EACD,GAAI,QAAQ,SAAS,YAAY;GAChC,eAAe,QAAQ,SAAS,SAAS;GACzC,mBAAmB,QAAQ,SAAS,SAAS;GAC7C,eAAe,QAAQ,SAAS,SAAS;GACzC,eAAe,OAAO,QAAQ,SAAS,SAAS,KAAK;GACrD,gBAAgB,QAAQ,SAAS,SAAS,SAAS;EACnD;CACD;AACD;;;;AAKD,eAAsB,gBACrBD,OACAE,KACAC,OACA,MAAM,QAAQ,KAAK,EACK;CACxB,MAAM,UAAU,MAAM,iBAAiB,OAAO,IAAI;AAElD,MAAK,QACJ,OAAM,IAAI,OACR,+BAA+B,MAAM,mCAAmC,MAAM;CAIjF,MAAMC,UAAwB;EAC7B,GAAG;EACH,WAAW,qBAAI,QAAO,aAAa;EACnC,QAAQ;GACP,GAAG,QAAQ;IACV,MAAM;EACP;CACD;AAED,OAAM,kBAAkB,SAAS,IAAI;AACrC,QAAO;AACP;;;;AAKD,SAAgB,aAAaC,UAA0B;AACtD,KAAI,SAAS,UAAU,EACtB,QAAO;AAER,SAAQ,EAAE,SAAS,MAAM,GAAG,EAAE,CAAC,EAAE,IAAI,OAAO,SAAS,SAAS,EAAE,CAAC,EAAE,SAAS,MAAM,GAAG,CAAC;AACtF"}
@@ -1,3 +0,0 @@
1
- import { getSecretsDir, getSecretsPath, maskPassword, readStageSecrets, secretsExist, setCustomSecret, toEmbeddableSecrets, writeStageSecrets } from "./storage-C9PU_30f.mjs";
2
-
3
- export { readStageSecrets, toEmbeddableSecrets };