@geekmidas/cli 1.10.9 → 1.10.10

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.
@@ -1,4 +1,4 @@
1
- const require_fullstack_secrets = require('./fullstack-secrets-CtWIYuI0.cjs');
1
+ const require_fullstack_secrets = require('./fullstack-secrets-BC9t9wWB.cjs');
2
2
 
3
3
  //#region src/secrets/reconcile.ts
4
4
  /**
@@ -33,4 +33,4 @@ function reconcileMissingSecrets(secrets, workspace) {
33
33
 
34
34
  //#endregion
35
35
  exports.reconcileMissingSecrets = reconcileMissingSecrets;
36
- //# sourceMappingURL=reconcile-D6u4HSg8.cjs.map
36
+ //# sourceMappingURL=reconcile-BZ8j_-0z.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile-D6u4HSg8.cjs","names":["secrets: StageSecrets","workspace: NormalizedWorkspace","addedKeys: string[]"],"sources":["../src/secrets/reconcile.ts"],"sourcesContent":["import { generateFullstackCustomSecrets } from '../setup/fullstack-secrets.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface ReconcileResult {\n\t/** The updated secrets with missing keys backfilled */\n\tsecrets: StageSecrets;\n\t/** Keys that were added */\n\taddedKeys: string[];\n}\n\n/**\n * Reconcile missing custom secrets for a workspace.\n *\n * Compares current secrets against what generateFullstackCustomSecrets()\n * would produce and backfills any missing keys without overwriting\n * existing values.\n *\n * @returns ReconcileResult if keys were added, null if secrets are up-to-date\n */\nexport function reconcileMissingSecrets(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n): ReconcileResult | null {\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (!isMultiApp) {\n\t\treturn null;\n\t}\n\n\tconst expectedCustom = generateFullstackCustomSecrets(workspace);\n\tconst addedKeys: string[] = [];\n\tconst mergedCustom = { ...secrets.custom };\n\n\tfor (const [key, value] of Object.entries(expectedCustom)) {\n\t\tif (!(key in mergedCustom)) {\n\t\t\tmergedCustom[key] = value;\n\t\t\taddedKeys.push(key);\n\t\t}\n\t}\n\n\tif (addedKeys.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsecrets: {\n\t\t\t...secrets,\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\tcustom: mergedCustom,\n\t\t},\n\t\taddedKeys,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,SAAgB,wBACfA,SACAC,WACyB;CACzB,MAAM,aAAa,OAAO,KAAK,UAAU,KAAK,CAAC,SAAS;AACxD,MAAK,WACJ,QAAO;CAGR,MAAM,iBAAiB,yDAA+B,UAAU;CAChE,MAAMC,YAAsB,CAAE;CAC9B,MAAM,eAAe,EAAE,GAAG,QAAQ,OAAQ;AAE1C,MAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,eAAe,CACxD,OAAM,OAAO,eAAe;AAC3B,eAAa,OAAO;AACpB,YAAU,KAAK,IAAI;CACnB;AAGF,KAAI,UAAU,WAAW,EACxB,QAAO;AAGR,QAAO;EACN,SAAS;GACR,GAAG;GACH,WAAW,qBAAI,QAAO,aAAa;GACnC,QAAQ;EACR;EACD;CACA;AACD"}
1
+ {"version":3,"file":"reconcile-BZ8j_-0z.cjs","names":["secrets: StageSecrets","workspace: NormalizedWorkspace","addedKeys: string[]"],"sources":["../src/secrets/reconcile.ts"],"sourcesContent":["import { generateFullstackCustomSecrets } from '../setup/fullstack-secrets.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface ReconcileResult {\n\t/** The updated secrets with missing keys backfilled */\n\tsecrets: StageSecrets;\n\t/** Keys that were added */\n\taddedKeys: string[];\n}\n\n/**\n * Reconcile missing custom secrets for a workspace.\n *\n * Compares current secrets against what generateFullstackCustomSecrets()\n * would produce and backfills any missing keys without overwriting\n * existing values.\n *\n * @returns ReconcileResult if keys were added, null if secrets are up-to-date\n */\nexport function reconcileMissingSecrets(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n): ReconcileResult | null {\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (!isMultiApp) {\n\t\treturn null;\n\t}\n\n\tconst expectedCustom = generateFullstackCustomSecrets(workspace);\n\tconst addedKeys: string[] = [];\n\tconst mergedCustom = { ...secrets.custom };\n\n\tfor (const [key, value] of Object.entries(expectedCustom)) {\n\t\tif (!(key in mergedCustom)) {\n\t\t\tmergedCustom[key] = value;\n\t\t\taddedKeys.push(key);\n\t\t}\n\t}\n\n\tif (addedKeys.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsecrets: {\n\t\t\t...secrets,\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\tcustom: mergedCustom,\n\t\t},\n\t\taddedKeys,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,SAAgB,wBACfA,SACAC,WACyB;CACzB,MAAM,aAAa,OAAO,KAAK,UAAU,KAAK,CAAC,SAAS;AACxD,MAAK,WACJ,QAAO;CAGR,MAAM,iBAAiB,yDAA+B,UAAU;CAChE,MAAMC,YAAsB,CAAE;CAC9B,MAAM,eAAe,EAAE,GAAG,QAAQ,OAAQ;AAE1C,MAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,eAAe,CACxD,OAAM,OAAO,eAAe;AAC3B,eAAa,OAAO;AACpB,YAAU,KAAK,IAAI;CACnB;AAGF,KAAI,UAAU,WAAW,EACxB,QAAO;AAGR,QAAO;EACN,SAAS;GACR,GAAG;GACH,WAAW,qBAAI,QAAO,aAAa;GACnC,QAAQ;EACR;EACD;CACA;AACD"}
@@ -1,4 +1,4 @@
1
- import { generateFullstackCustomSecrets } from "./fullstack-secrets-C2lbdbLZ.mjs";
1
+ import { generateFullstackCustomSecrets } from "./fullstack-secrets-DmUOfLeX.mjs";
2
2
 
3
3
  //#region src/secrets/reconcile.ts
4
4
  /**
@@ -33,4 +33,4 @@ function reconcileMissingSecrets(secrets, workspace) {
33
33
 
34
34
  //#endregion
35
35
  export { reconcileMissingSecrets };
36
- //# sourceMappingURL=reconcile-BnM6FA6g.mjs.map
36
+ //# sourceMappingURL=reconcile-C0dsg-Gq.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile-BnM6FA6g.mjs","names":["secrets: StageSecrets","workspace: NormalizedWorkspace","addedKeys: string[]"],"sources":["../src/secrets/reconcile.ts"],"sourcesContent":["import { generateFullstackCustomSecrets } from '../setup/fullstack-secrets.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface ReconcileResult {\n\t/** The updated secrets with missing keys backfilled */\n\tsecrets: StageSecrets;\n\t/** Keys that were added */\n\taddedKeys: string[];\n}\n\n/**\n * Reconcile missing custom secrets for a workspace.\n *\n * Compares current secrets against what generateFullstackCustomSecrets()\n * would produce and backfills any missing keys without overwriting\n * existing values.\n *\n * @returns ReconcileResult if keys were added, null if secrets are up-to-date\n */\nexport function reconcileMissingSecrets(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n): ReconcileResult | null {\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (!isMultiApp) {\n\t\treturn null;\n\t}\n\n\tconst expectedCustom = generateFullstackCustomSecrets(workspace);\n\tconst addedKeys: string[] = [];\n\tconst mergedCustom = { ...secrets.custom };\n\n\tfor (const [key, value] of Object.entries(expectedCustom)) {\n\t\tif (!(key in mergedCustom)) {\n\t\t\tmergedCustom[key] = value;\n\t\t\taddedKeys.push(key);\n\t\t}\n\t}\n\n\tif (addedKeys.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsecrets: {\n\t\t\t...secrets,\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\tcustom: mergedCustom,\n\t\t},\n\t\taddedKeys,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,SAAgB,wBACfA,SACAC,WACyB;CACzB,MAAM,aAAa,OAAO,KAAK,UAAU,KAAK,CAAC,SAAS;AACxD,MAAK,WACJ,QAAO;CAGR,MAAM,iBAAiB,+BAA+B,UAAU;CAChE,MAAMC,YAAsB,CAAE;CAC9B,MAAM,eAAe,EAAE,GAAG,QAAQ,OAAQ;AAE1C,MAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,eAAe,CACxD,OAAM,OAAO,eAAe;AAC3B,eAAa,OAAO;AACpB,YAAU,KAAK,IAAI;CACnB;AAGF,KAAI,UAAU,WAAW,EACxB,QAAO;AAGR,QAAO;EACN,SAAS;GACR,GAAG;GACH,WAAW,qBAAI,QAAO,aAAa;GACnC,QAAQ;EACR;EACD;CACA;AACD"}
1
+ {"version":3,"file":"reconcile-C0dsg-Gq.mjs","names":["secrets: StageSecrets","workspace: NormalizedWorkspace","addedKeys: string[]"],"sources":["../src/secrets/reconcile.ts"],"sourcesContent":["import { generateFullstackCustomSecrets } from '../setup/fullstack-secrets.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface ReconcileResult {\n\t/** The updated secrets with missing keys backfilled */\n\tsecrets: StageSecrets;\n\t/** Keys that were added */\n\taddedKeys: string[];\n}\n\n/**\n * Reconcile missing custom secrets for a workspace.\n *\n * Compares current secrets against what generateFullstackCustomSecrets()\n * would produce and backfills any missing keys without overwriting\n * existing values.\n *\n * @returns ReconcileResult if keys were added, null if secrets are up-to-date\n */\nexport function reconcileMissingSecrets(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n): ReconcileResult | null {\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (!isMultiApp) {\n\t\treturn null;\n\t}\n\n\tconst expectedCustom = generateFullstackCustomSecrets(workspace);\n\tconst addedKeys: string[] = [];\n\tconst mergedCustom = { ...secrets.custom };\n\n\tfor (const [key, value] of Object.entries(expectedCustom)) {\n\t\tif (!(key in mergedCustom)) {\n\t\t\tmergedCustom[key] = value;\n\t\t\taddedKeys.push(key);\n\t\t}\n\t}\n\n\tif (addedKeys.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsecrets: {\n\t\t\t...secrets,\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\tcustom: mergedCustom,\n\t\t},\n\t\taddedKeys,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,SAAgB,wBACfA,SACAC,WACyB;CACzB,MAAM,aAAa,OAAO,KAAK,UAAU,KAAK,CAAC,SAAS;AACxD,MAAK,WACJ,QAAO;CAGR,MAAM,iBAAiB,+BAA+B,UAAU;CAChE,MAAMC,YAAsB,CAAE;CAC9B,MAAM,eAAe,EAAE,GAAG,QAAQ,OAAQ;AAE1C,MAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,eAAe,CACxD,OAAM,OAAO,eAAe;AAC3B,eAAa,OAAO;AACpB,YAAU,KAAK,IAAI;CACnB;AAGF,KAAI,UAAU,WAAW,EACxB,QAAO;AAGR,QAAO;EACN,SAAS;GACR,GAAG;GACH,WAAW,qBAAI,QAAO,aAAa;GACnC,QAAQ;EACR;EACD;CACA;AACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/cli",
3
- "version": "1.10.9",
3
+ "version": "1.10.10",
4
4
  "description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
5
5
  "private": false,
6
6
  "type": "module",
@@ -56,11 +56,11 @@
56
56
  "prompts": "~2.4.2",
57
57
  "tsx": "~4.20.3",
58
58
  "yaml": "~2.8.2",
59
- "@geekmidas/logger": "~1.0.0",
60
- "@geekmidas/envkit": "~1.0.3",
59
+ "@geekmidas/errors": "~1.0.0",
61
60
  "@geekmidas/constructs": "~3.0.2",
62
- "@geekmidas/schema": "~1.0.0",
63
- "@geekmidas/errors": "~1.0.0"
61
+ "@geekmidas/envkit": "~1.0.3",
62
+ "@geekmidas/logger": "~1.0.0",
63
+ "@geekmidas/schema": "~1.0.0"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@types/lodash.kebabcase": "^4.1.9",
@@ -124,6 +124,65 @@ describe('reconcileSecrets', () => {
124
124
  expect(result).toBeNull();
125
125
  });
126
126
 
127
+ it('should add missing service credentials when workspace config adds a new service', () => {
128
+ const workspace = createWorkspace({
129
+ services: { db: true, storage: true },
130
+ });
131
+ // Secrets only have postgres, not minio
132
+ const secrets = createSecrets({
133
+ NODE_ENV: 'development',
134
+ PORT: '3000',
135
+ API_DATABASE_URL: 'postgresql://api:pass@localhost:5432/test_dev',
136
+ API_DB_PASSWORD: 'pass',
137
+ AUTH_DATABASE_URL: 'postgresql://auth:pass@localhost:5432/test_dev',
138
+ AUTH_DB_PASSWORD: 'pass',
139
+ WEB_URL: 'http://localhost:3002',
140
+ BETTER_AUTH_SECRET: 'existing',
141
+ BETTER_AUTH_URL: 'http://localhost:3001',
142
+ BETTER_AUTH_TRUSTED_ORIGINS:
143
+ 'http://localhost:3000,http://localhost:3001,http://localhost:3002',
144
+ AUTH_PORT: '3001',
145
+ AUTH_URL: 'http://localhost:3001',
146
+ });
147
+
148
+ const result = reconcileSecrets(secrets, workspace);
149
+
150
+ expect(result).not.toBeNull();
151
+ expect(result!.services.minio).toBeDefined();
152
+ expect(result!.services.minio!.host).toBe('localhost');
153
+ expect(result!.services.minio!.port).toBe(9000);
154
+ expect(result!.services.minio!.bucket).toBe('app');
155
+ expect(result!.services.minio!.password).toHaveLength(32);
156
+ expect(result!.urls.S3_ENDPOINT).toBe('http://localhost:9000');
157
+ // Existing postgres should be preserved
158
+ expect(result!.services.postgres).toEqual(secrets.services.postgres);
159
+ });
160
+
161
+ it('should not regenerate credentials for existing services', () => {
162
+ const workspace = createWorkspace({
163
+ services: { db: true, storage: true },
164
+ });
165
+ // Include ALL expected custom keys so reconcile has nothing to add
166
+ const expected = generateFullstackCustomSecrets(
167
+ createWorkspace({ services: { db: true, storage: true } }),
168
+ );
169
+ const secrets = createSecrets(expected);
170
+ // Add existing minio creds
171
+ secrets.services.minio = {
172
+ host: 'localhost',
173
+ port: 9000,
174
+ username: 'mykey',
175
+ password: 'mysecret',
176
+ bucket: 'my-bucket',
177
+ };
178
+ secrets.urls.S3_ENDPOINT = 'http://localhost:9000';
179
+
180
+ const result = reconcileSecrets(secrets, workspace);
181
+
182
+ // No changes needed — all services and custom keys present
183
+ expect(result).toBeNull();
184
+ });
185
+
127
186
  it('should return null for single-app workspaces', () => {
128
187
  const workspace = createWorkspace({
129
188
  apps: {
@@ -3,7 +3,11 @@ import { join } from 'node:path';
3
3
  import prompts from 'prompts';
4
4
  import { loadWorkspaceConfig } from '../config.js';
5
5
  import { resolveServicePorts, startWorkspaceServices } from '../dev/index.js';
6
- import { createStageSecrets } from '../secrets/generator.js';
6
+ import {
7
+ createStageSecrets,
8
+ generateConnectionUrls,
9
+ generateServiceCredentials,
10
+ } from '../secrets/generator.js';
7
11
  import {
8
12
  readStageSecrets,
9
13
  secretsExist,
@@ -153,32 +157,63 @@ export function reconcileSecrets(
153
157
  secrets: StageSecrets,
154
158
  workspace: NormalizedWorkspace,
155
159
  ): StageSecrets | null {
156
- const isMultiApp = Object.keys(workspace.apps).length > 1;
157
- if (!isMultiApp) {
158
- return null;
160
+ let changed = false;
161
+ let result = { ...secrets };
162
+
163
+ // Reconcile service credentials: add missing services
164
+ const serviceMap: {
165
+ key: keyof typeof workspace.services;
166
+ name: ComposeServiceName;
167
+ }[] = [
168
+ { key: 'db', name: 'postgres' },
169
+ { key: 'cache', name: 'redis' },
170
+ { key: 'storage', name: 'minio' },
171
+ ];
172
+
173
+ for (const { key, name } of serviceMap) {
174
+ if (workspace.services[key] && !result.services[name]) {
175
+ const creds = generateServiceCredentials(name);
176
+ result = {
177
+ ...result,
178
+ services: { ...result.services, [name]: creds },
179
+ };
180
+ result.urls = generateConnectionUrls(result.services);
181
+ logger.log(` 🔄 Adding missing service credentials: ${name}`);
182
+ changed = true;
183
+ }
159
184
  }
160
185
 
161
- const expected = generateFullstackCustomSecrets(workspace);
162
- const missing: Record<string, string> = {};
186
+ // Reconcile custom secrets for multi-app workspaces
187
+ const isMultiApp = Object.keys(workspace.apps).length > 1;
188
+ if (isMultiApp) {
189
+ const expected = generateFullstackCustomSecrets(workspace);
190
+ const missing: Record<string, string> = {};
191
+
192
+ for (const [key, value] of Object.entries(expected)) {
193
+ if (!(key in result.custom)) {
194
+ missing[key] = value;
195
+ }
196
+ }
163
197
 
164
- for (const [key, value] of Object.entries(expected)) {
165
- if (!(key in secrets.custom)) {
166
- missing[key] = value;
198
+ if (Object.keys(missing).length > 0) {
199
+ logger.log(
200
+ ` 🔄 Adding missing secrets: ${Object.keys(missing).join(', ')}`,
201
+ );
202
+ result = {
203
+ ...result,
204
+ custom: { ...result.custom, ...missing },
205
+ };
206
+ changed = true;
167
207
  }
168
208
  }
169
209
 
170
- if (Object.keys(missing).length === 0) {
210
+ if (!changed) {
171
211
  return null;
172
212
  }
173
213
 
174
- logger.log(
175
- ` 🔄 Adding missing secrets: ${Object.keys(missing).join(', ')}`,
176
- );
177
-
178
214
  return {
179
- ...secrets,
215
+ ...result,
180
216
  updatedAt: new Date().toISOString(),
181
- custom: { ...secrets.custom, ...missing },
182
217
  };
183
218
  }
184
219