@geekmidas/cli 1.0.2 → 1.2.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/{SSMStateProvider-C4wp4AZe.mjs → SSMStateProvider-BjCi_58g.mjs} +16 -7
  3. package/dist/SSMStateProvider-BjCi_58g.mjs.map +1 -0
  4. package/dist/{SSMStateProvider-BxAPU99a.cjs → SSMStateProvider-D79o_JjM.cjs} +16 -7
  5. package/dist/SSMStateProvider-D79o_JjM.cjs.map +1 -0
  6. package/dist/{config-C6awcFBx.mjs → config-BQ4a36Rq.mjs} +2 -2
  7. package/dist/{config-C6awcFBx.mjs.map → config-BQ4a36Rq.mjs.map} +1 -1
  8. package/dist/{config-BGeJsW1r.cjs → config-Bayob8pB.cjs} +2 -2
  9. package/dist/{config-BGeJsW1r.cjs.map → config-Bayob8pB.cjs.map} +1 -1
  10. package/dist/config.cjs +2 -2
  11. package/dist/config.d.cts +1 -1
  12. package/dist/config.d.mts +1 -1
  13. package/dist/config.mjs +2 -2
  14. package/dist/{index-KFEbMIRa.d.mts → index-Bi9vGQJy.d.mts} +61 -13
  15. package/dist/index-Bi9vGQJy.d.mts.map +1 -0
  16. package/dist/{index-B5rGIc4g.d.cts → index-CufAAnge.d.cts} +61 -13
  17. package/dist/index-CufAAnge.d.cts.map +1 -0
  18. package/dist/index.cjs +14 -9
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.mjs +14 -9
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{openapi-D1KXv2Ml.cjs → openapi-BZP8jkI4.cjs} +2 -2
  23. package/dist/{openapi-D1KXv2Ml.cjs.map → openapi-BZP8jkI4.cjs.map} +1 -1
  24. package/dist/{openapi-BMFmLnX6.mjs → openapi-DrbBWq0s.mjs} +2 -2
  25. package/dist/{openapi-BMFmLnX6.mjs.map → openapi-DrbBWq0s.mjs.map} +1 -1
  26. package/dist/openapi.cjs +3 -3
  27. package/dist/openapi.mjs +3 -3
  28. package/dist/workspace/index.cjs +2 -1
  29. package/dist/workspace/index.d.cts +2 -2
  30. package/dist/workspace/index.d.mts +2 -2
  31. package/dist/workspace/index.mjs +2 -2
  32. package/dist/{workspace-BFRUOOrh.cjs → workspace-BMJE18LV.cjs} +46 -6
  33. package/dist/workspace-BMJE18LV.cjs.map +1 -0
  34. package/dist/{workspace-DAxG3_H2.mjs → workspace-CASoZOjs.mjs} +41 -7
  35. package/dist/workspace-CASoZOjs.mjs.map +1 -0
  36. package/package.json +4 -4
  37. package/src/deploy/SSMStateProvider.ts +20 -7
  38. package/src/deploy/StateProvider.ts +1 -1
  39. package/src/deploy/__tests__/CachedStateProvider.spec.ts +7 -0
  40. package/src/deploy/__tests__/LocalStateProvider.spec.ts +4 -0
  41. package/src/deploy/__tests__/SSMStateProvider.spec.ts +20 -8
  42. package/src/deploy/__tests__/dns-verification.spec.ts +1 -1
  43. package/src/deploy/__tests__/env-resolver.spec.ts +9 -9
  44. package/src/deploy/__tests__/state-e2e.spec.ts +387 -0
  45. package/src/deploy/__tests__/state.spec.ts +53 -29
  46. package/src/deploy/index.ts +6 -1
  47. package/src/deploy/state.ts +4 -0
  48. package/src/init/__tests__/init.spec.ts +10 -1
  49. package/src/init/versions.ts +1 -1
  50. package/src/secrets/__tests__/storage.spec.ts +6 -2
  51. package/src/workspace/__tests__/index.spec.ts +129 -0
  52. package/src/workspace/index.ts +44 -0
  53. package/src/workspace/schema.ts +17 -6
  54. package/src/workspace/types.ts +26 -9
  55. package/dist/SSMStateProvider-BxAPU99a.cjs.map +0 -1
  56. package/dist/SSMStateProvider-C4wp4AZe.mjs.map +0 -1
  57. package/dist/index-B5rGIc4g.d.cts.map +0 -1
  58. package/dist/index-KFEbMIRa.d.mts.map +0 -1
  59. package/dist/workspace-BFRUOOrh.cjs.map +0 -1
  60. package/dist/workspace-DAxG3_H2.mjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/cli",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
5
5
  "private": false,
6
6
  "type": "module",
@@ -56,8 +56,8 @@
56
56
  "@geekmidas/constructs": "~1.0.0",
57
57
  "@geekmidas/envkit": "~1.0.0",
58
58
  "@geekmidas/errors": "~1.0.0",
59
- "@geekmidas/logger": "~1.0.0",
60
- "@geekmidas/schema": "~1.0.0"
59
+ "@geekmidas/schema": "~1.0.0",
60
+ "@geekmidas/logger": "~1.0.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/lodash.kebabcase": "^4.1.9",
@@ -67,7 +67,7 @@
67
67
  "typescript": "^5.8.2",
68
68
  "vitest": "^3.2.4",
69
69
  "zod": "~4.1.13",
70
- "@geekmidas/testkit": "1.0.0"
70
+ "@geekmidas/testkit": "1.0.1"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "@geekmidas/telescope": "~1.0.0"
@@ -12,6 +12,7 @@ import {
12
12
  ParameterNotFound,
13
13
  PutParameterCommand,
14
14
  SSMClient,
15
+ type SSMClientConfig,
15
16
  } from '@aws-sdk/client-ssm';
16
17
  import type { AwsRegion, StateProvider } from './StateProvider';
17
18
  import type { DokployStageState } from './state';
@@ -19,8 +20,12 @@ import type { DokployStageState } from './state';
19
20
  export interface SSMStateProviderOptions {
20
21
  /** Workspace name (used in parameter path) */
21
22
  workspaceName: string;
22
- /** AWS region (required) */
23
- region: AwsRegion;
23
+ /** AWS region */
24
+ region?: AwsRegion;
25
+ /** AWS credentials (optional - uses default credential chain if not provided) */
26
+ credentials?: SSMClientConfig['credentials'];
27
+ /** Custom endpoint (for LocalStack or other S3-compatible services) */
28
+ endpoint?: string;
24
29
  }
25
30
 
26
31
  /**
@@ -30,14 +35,22 @@ export interface SSMStateProviderOptions {
30
35
  * Parameter path: /gkm/{workspaceName}/{stage}/state
31
36
  */
32
37
  export class SSMStateProvider implements StateProvider {
33
- private readonly client: SSMClient;
34
- private readonly workspaceName: string;
38
+ constructor(
39
+ readonly workspaceName: string,
40
+ private readonly client: SSMClient,
41
+ ) {}
35
42
 
36
- constructor(options: SSMStateProviderOptions) {
37
- this.workspaceName = options.workspaceName;
38
- this.client = new SSMClient({
43
+ /**
44
+ * Create an SSMStateProvider with a new SSMClient.
45
+ */
46
+ static create(options: SSMStateProviderOptions): SSMStateProvider {
47
+ const client = new SSMClient({
39
48
  region: options.region,
49
+ credentials: options.credentials,
50
+ endpoint: options.endpoint,
40
51
  });
52
+
53
+ return new SSMStateProvider(options.workspaceName, client);
41
54
  }
42
55
 
43
56
  /**
@@ -158,7 +158,7 @@ export async function createStateProvider(
158
158
  const { CachedStateProvider } = await import('./CachedStateProvider');
159
159
 
160
160
  const local = new LocalStateProvider(workspaceRoot);
161
- const ssm = new SSMStateProvider({
161
+ const ssm = SSMStateProvider.create({
162
162
  workspaceName,
163
163
  region: (config as SSMStateConfig).region,
164
164
  });
@@ -36,6 +36,7 @@ describe('CachedStateProvider', () => {
36
36
  const state: DokployStageState = {
37
37
  provider: 'dokploy',
38
38
  stage: 'production',
39
+ projectId: 'proj_test',
39
40
  environmentId: 'env_123',
40
41
  applications: { api: 'local_app' },
41
42
  services: {},
@@ -58,6 +59,7 @@ describe('CachedStateProvider', () => {
58
59
  const state: DokployStageState = {
59
60
  provider: 'dokploy',
60
61
  stage: 'production',
62
+ projectId: 'proj_test',
61
63
  environmentId: 'env_123',
62
64
  applications: { api: 'remote_app' },
63
65
  services: {},
@@ -96,6 +98,7 @@ describe('CachedStateProvider', () => {
96
98
  const state: DokployStageState = {
97
99
  provider: 'dokploy',
98
100
  stage: 'production',
101
+ projectId: 'proj_test',
99
102
  environmentId: 'env_123',
100
103
  applications: { api: 'app_123' },
101
104
  services: {},
@@ -120,6 +123,7 @@ describe('CachedStateProvider', () => {
120
123
  const remoteState: DokployStageState = {
121
124
  provider: 'dokploy',
122
125
  stage: 'production',
126
+ projectId: 'proj_test',
123
127
  environmentId: 'env_123',
124
128
  applications: { api: 'remote_app' },
125
129
  services: {},
@@ -155,6 +159,7 @@ describe('CachedStateProvider', () => {
155
159
  const localState: DokployStageState = {
156
160
  provider: 'dokploy',
157
161
  stage: 'production',
162
+ projectId: 'proj_test',
158
163
  environmentId: 'env_123',
159
164
  applications: { api: 'local_app' },
160
165
  services: {},
@@ -190,6 +195,7 @@ describe('CachedStateProvider', () => {
190
195
  const localState: DokployStageState = {
191
196
  provider: 'dokploy',
192
197
  stage: 'production',
198
+ projectId: 'proj_test',
193
199
  environmentId: 'env_123',
194
200
  applications: { api: 'local_app' },
195
201
  services: {},
@@ -198,6 +204,7 @@ describe('CachedStateProvider', () => {
198
204
  const remoteState: DokployStageState = {
199
205
  provider: 'dokploy',
200
206
  stage: 'production',
207
+ projectId: 'proj_test',
201
208
  environmentId: 'env_123',
202
209
  applications: { api: 'remote_app' },
203
210
  services: {},
@@ -29,6 +29,7 @@ describe('LocalStateProvider', () => {
29
29
  const stateData: DokployStageState = {
30
30
  provider: 'dokploy',
31
31
  stage: 'production',
32
+ projectId: 'proj_test',
32
33
  environmentId: 'env_123',
33
34
  applications: { api: 'app_123' },
34
35
  services: { postgresId: 'pg_123' },
@@ -62,6 +63,7 @@ describe('LocalStateProvider', () => {
62
63
  const state: DokployStageState = {
63
64
  provider: 'dokploy',
64
65
  stage: 'staging',
66
+ projectId: 'proj_test',
65
67
  environmentId: 'env_456',
66
68
  applications: {},
67
69
  services: {},
@@ -81,6 +83,7 @@ describe('LocalStateProvider', () => {
81
83
  const state: DokployStageState = {
82
84
  provider: 'dokploy',
83
85
  stage: 'staging',
86
+ projectId: 'proj_test',
84
87
  environmentId: 'env_456',
85
88
  applications: {},
86
89
  services: {},
@@ -98,6 +101,7 @@ describe('LocalStateProvider', () => {
98
101
  const state: DokployStageState = {
99
102
  provider: 'dokploy',
100
103
  stage: 'production',
104
+ projectId: 'proj_test',
101
105
  environmentId: 'env_123',
102
106
  applications: { api: 'app_123', web: 'app_456' },
103
107
  services: { postgresId: 'pg_123', redisId: 'redis_123' },
@@ -44,14 +44,8 @@ describe('SSMStateProvider', () => {
44
44
  },
45
45
  });
46
46
 
47
- provider = new SSMStateProvider({
48
- workspaceName,
49
- region: 'us-east-1',
50
- });
51
-
52
- // Override the client's endpoint for localstack
53
- // @ts-expect-error - accessing private property for testing
54
- provider.client = client;
47
+ // Create provider with injected client
48
+ provider = new SSMStateProvider(workspaceName, client);
55
49
  });
56
50
 
57
51
  afterEach(async () => {
@@ -70,6 +64,19 @@ describe('SSMStateProvider', () => {
70
64
  client.destroy();
71
65
  });
72
66
 
67
+ describe('static create', () => {
68
+ it('should create provider with options', () => {
69
+ const provider = SSMStateProvider.create({
70
+ workspaceName: 'my-workspace',
71
+ region: 'us-west-2',
72
+ endpoint: LOCALSTACK_ENDPOINT,
73
+ });
74
+
75
+ expect(provider).toBeInstanceOf(SSMStateProvider);
76
+ expect(provider.workspaceName).toBe('my-workspace');
77
+ });
78
+ });
79
+
73
80
  describe('read', () => {
74
81
  it('should return null when parameter does not exist', async () => {
75
82
  const state = await provider.read('nonexistent-stage');
@@ -80,6 +87,7 @@ describe('SSMStateProvider', () => {
80
87
  const stateData: DokployStageState = {
81
88
  provider: 'dokploy',
82
89
  stage: testStage,
90
+ projectId: 'proj_test',
83
91
  environmentId: 'env_123',
84
92
  applications: { api: 'app_123' },
85
93
  services: { postgresId: 'pg_123' },
@@ -105,6 +113,7 @@ describe('SSMStateProvider', () => {
105
113
  const state: DokployStageState = {
106
114
  provider: 'dokploy',
107
115
  stage: testStage,
116
+ projectId: 'proj_test',
108
117
  environmentId: 'env_123',
109
118
  applications: { api: 'app_123' },
110
119
  services: {},
@@ -128,6 +137,7 @@ describe('SSMStateProvider', () => {
128
137
  const state1: DokployStageState = {
129
138
  provider: 'dokploy',
130
139
  stage: testStage,
140
+ projectId: 'proj_test',
131
141
  environmentId: 'env_123',
132
142
  applications: { api: 'app_old' },
133
143
  services: {},
@@ -137,6 +147,7 @@ describe('SSMStateProvider', () => {
137
147
  const state2: DokployStageState = {
138
148
  provider: 'dokploy',
139
149
  stage: testStage,
150
+ projectId: 'proj_test',
140
151
  environmentId: 'env_123',
141
152
  applications: { api: 'app_new' },
142
153
  services: {},
@@ -161,6 +172,7 @@ describe('SSMStateProvider', () => {
161
172
  const state: DokployStageState = {
162
173
  provider: 'dokploy',
163
174
  stage: testStage,
175
+ projectId: 'proj_test',
164
176
  environmentId: 'env_123',
165
177
  applications: {},
166
178
  services: {},
@@ -44,7 +44,7 @@ describe('verifyDnsRecords', () => {
44
44
 
45
45
  beforeEach(() => {
46
46
  vi.clearAllMocks();
47
- state = createEmptyState('production', 'env-123');
47
+ state = createEmptyState('production', 'proj_test', 'env-123');
48
48
  console.log = vi.fn();
49
49
  });
50
50
 
@@ -53,7 +53,7 @@ describe('generateSecret', () => {
53
53
  describe('getOrGenerateSecret', () => {
54
54
  it('should return existing secret if already stored', () => {
55
55
  const state: DokployStageState = {
56
- ...createEmptyState('production', 'env-123'),
56
+ ...createEmptyState('production', 'proj_test', 'env-123'),
57
57
  generatedSecrets: {
58
58
  api: { BETTER_AUTH_SECRET: 'existing-secret-123' },
59
59
  },
@@ -64,7 +64,7 @@ describe('getOrGenerateSecret', () => {
64
64
  });
65
65
 
66
66
  it('should generate and store new secret if not exists', () => {
67
- const state = createEmptyState('production', 'env-123');
67
+ const state = createEmptyState('production', 'proj_test', 'env-123');
68
68
 
69
69
  const result = getOrGenerateSecret(state, 'api', 'BETTER_AUTH_SECRET');
70
70
 
@@ -73,7 +73,7 @@ describe('getOrGenerateSecret', () => {
73
73
  });
74
74
 
75
75
  it('should generate different secrets for different apps', () => {
76
- const state = createEmptyState('production', 'env-123');
76
+ const state = createEmptyState('production', 'proj_test', 'env-123');
77
77
 
78
78
  const apiSecret = getOrGenerateSecret(state, 'api', 'BETTER_AUTH_SECRET');
79
79
  const authSecret = getOrGenerateSecret(state, 'auth', 'BETTER_AUTH_SECRET');
@@ -84,7 +84,7 @@ describe('getOrGenerateSecret', () => {
84
84
  });
85
85
 
86
86
  it('should generate different secrets for different secret names', () => {
87
- const state = createEmptyState('production', 'env-123');
87
+ const state = createEmptyState('production', 'proj_test', 'env-123');
88
88
 
89
89
  const secret1 = getOrGenerateSecret(state, 'api', 'SECRET_ONE');
90
90
  const secret2 = getOrGenerateSecret(state, 'api', 'SECRET_TWO');
@@ -93,7 +93,7 @@ describe('getOrGenerateSecret', () => {
93
93
  });
94
94
 
95
95
  it('should return same secret on subsequent calls', () => {
96
- const state = createEmptyState('production', 'env-123');
96
+ const state = createEmptyState('production', 'proj_test', 'env-123');
97
97
 
98
98
  const first = getOrGenerateSecret(state, 'api', 'BETTER_AUTH_SECRET');
99
99
  const second = getOrGenerateSecret(state, 'api', 'BETTER_AUTH_SECRET');
@@ -181,7 +181,7 @@ describe('resolveEnvVar', () => {
181
181
  app: createApp(),
182
182
  appName: 'api',
183
183
  stage: 'production',
184
- state: createEmptyState('production', 'env-123'),
184
+ state: createEmptyState('production', 'proj_test', 'env-123'),
185
185
  appHostname: 'api.example.com',
186
186
  frontendUrls: [],
187
187
  ...overrides,
@@ -253,7 +253,7 @@ describe('resolveEnvVar', () => {
253
253
  });
254
254
 
255
255
  it('should resolve BETTER_AUTH_SECRET by generating and storing secret', () => {
256
- const state = createEmptyState('production', 'env-123');
256
+ const state = createEmptyState('production', 'proj_test', 'env-123');
257
257
  const context = createContext({ state, appName: 'auth' });
258
258
 
259
259
  const secret = resolveEnvVar('BETTER_AUTH_SECRET', context);
@@ -362,7 +362,7 @@ describe('resolveEnvVars', () => {
362
362
  },
363
363
  appName: 'api',
364
364
  stage: 'production',
365
- state: createEmptyState('production', 'env-123'),
365
+ state: createEmptyState('production', 'proj_test', 'env-123'),
366
366
  appHostname: 'api.example.com',
367
367
  frontendUrls: ['https://web.example.com'],
368
368
  ...overrides,
@@ -449,7 +449,7 @@ describe('validateEnvVars', () => {
449
449
  },
450
450
  appName: 'api',
451
451
  stage: 'production',
452
- state: createEmptyState('production', 'env-123'),
452
+ state: createEmptyState('production', 'proj_test', 'env-123'),
453
453
  appHostname: 'api.example.com',
454
454
  frontendUrls: [],
455
455
  ...overrides,