@geekmidas/cli 0.2.4 → 0.4.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 +488 -71
- package/dist/{EndpointGenerator-C73wNoih.cjs → EndpointGenerator-BxNCkus4.cjs} +60 -6
- package/dist/EndpointGenerator-BxNCkus4.cjs.map +1 -0
- package/dist/{EndpointGenerator-CWh18d92.mjs → EndpointGenerator-CzDhG7Or.mjs} +60 -6
- package/dist/EndpointGenerator-CzDhG7Or.mjs.map +1 -0
- package/dist/OpenApiTsGenerator-NBNEoaeO.cjs +501 -0
- package/dist/OpenApiTsGenerator-NBNEoaeO.cjs.map +1 -0
- package/dist/OpenApiTsGenerator-q3aWNkuM.mjs +495 -0
- package/dist/OpenApiTsGenerator-q3aWNkuM.mjs.map +1 -0
- package/dist/build/index.cjs +4 -4
- package/dist/build/index.mjs +4 -4
- package/dist/build/manifests.cjs +3 -2
- package/dist/build/manifests.mjs +2 -2
- package/dist/{build-BVng9MQX.cjs → build-CWtHnJMQ.cjs} +19 -17
- package/dist/build-CWtHnJMQ.cjs.map +1 -0
- package/dist/{build-BqexeI-W.mjs → build-DyDgu_D1.mjs} +20 -18
- package/dist/build-DyDgu_D1.mjs.map +1 -0
- package/dist/{config-U-mdW-7Y.mjs → config-AFmFKmU0.mjs} +3 -3
- package/dist/config-AFmFKmU0.mjs.map +1 -0
- package/dist/{config-D1EpSGk6.cjs → config-BVIJpAsa.cjs} +3 -3
- package/dist/config-BVIJpAsa.cjs.map +1 -0
- package/dist/config.cjs +1 -1
- package/dist/config.mjs +1 -1
- package/dist/dev/index.cjs +5 -4
- package/dist/dev/index.mjs +4 -4
- package/dist/{dev-DbtyToc7.cjs → dev-CgDYC4o8.cjs} +95 -31
- package/dist/dev-CgDYC4o8.cjs.map +1 -0
- package/dist/{dev-DnGYXuMn.mjs → dev-CpA8AQPX.mjs} +90 -32
- package/dist/dev-CpA8AQPX.mjs.map +1 -0
- package/dist/generators/EndpointGenerator.cjs +1 -1
- package/dist/generators/EndpointGenerator.mjs +1 -1
- package/dist/generators/OpenApiTsGenerator.cjs +3 -0
- package/dist/generators/OpenApiTsGenerator.mjs +3 -0
- package/dist/generators/index.cjs +1 -1
- package/dist/generators/index.mjs +1 -1
- package/dist/index.cjs +16 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +16 -10
- package/dist/index.mjs.map +1 -1
- package/dist/manifests-C2eMoMUm.mjs +68 -0
- package/dist/manifests-C2eMoMUm.mjs.map +1 -0
- package/dist/manifests-CK1VV_pM.cjs +80 -0
- package/dist/manifests-CK1VV_pM.cjs.map +1 -0
- package/dist/openapi-DRTRGhTt.mjs +50 -0
- package/dist/openapi-DRTRGhTt.mjs.map +1 -0
- package/dist/openapi-DhK4b0lB.cjs +56 -0
- package/dist/openapi-DhK4b0lB.cjs.map +1 -0
- package/dist/openapi.cjs +4 -3
- package/dist/openapi.mjs +4 -3
- package/docs/OPENAPI_TYPESCRIPT_DESIGN.md +408 -0
- package/docs/manifest-refactor-design.md +287 -0
- package/package.json +10 -3
- package/src/__tests__/openapi.spec.ts +78 -63
- package/src/build/__tests__/index-new.spec.ts +43 -72
- package/src/build/__tests__/manifests.spec.ts +346 -0
- package/src/build/index.ts +59 -62
- package/src/build/manifests.ts +85 -13
- package/src/build/types.ts +13 -2
- package/src/config.ts +4 -2
- package/src/dev/__tests__/index.spec.ts +98 -1
- package/src/dev/index.ts +152 -25
- package/src/generators/EndpointGenerator.ts +69 -5
- package/src/generators/OpenApiTsGenerator.ts +798 -0
- package/src/index.ts +6 -3
- package/src/openapi.ts +36 -15
- package/src/types.ts +23 -7
- package/dist/EndpointGenerator-C73wNoih.cjs.map +0 -1
- package/dist/EndpointGenerator-CWh18d92.mjs.map +0 -1
- package/dist/build-BVng9MQX.cjs.map +0 -1
- package/dist/build-BqexeI-W.mjs.map +0 -1
- package/dist/config-D1EpSGk6.cjs.map +0 -1
- package/dist/config-U-mdW-7Y.mjs.map +0 -1
- package/dist/dev-DbtyToc7.cjs.map +0 -1
- package/dist/dev-DnGYXuMn.mjs.map +0 -1
- package/dist/manifests-BrJXpHrf.mjs +0 -21
- package/dist/manifests-BrJXpHrf.mjs.map +0 -1
- package/dist/manifests-D0saShvH.cjs +0 -27
- package/dist/manifests-D0saShvH.cjs.map +0 -1
- package/dist/openapi-BTHbPrxS.mjs +0 -36
- package/dist/openapi-BTHbPrxS.mjs.map +0 -1
- package/dist/openapi-CewcfoRH.cjs +0 -42
- package/dist/openapi-CewcfoRH.cjs.map +0 -1
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { itWithDir } from '@geekmidas/testkit/os';
|
|
4
|
+
import { describe, expect, vi } from 'vitest';
|
|
5
|
+
import type {
|
|
6
|
+
CronInfo,
|
|
7
|
+
FunctionInfo,
|
|
8
|
+
RouteInfo,
|
|
9
|
+
SubscriberInfo,
|
|
10
|
+
} from '../../types';
|
|
11
|
+
import { generateAwsManifest, generateServerManifest } from '../manifests';
|
|
12
|
+
|
|
13
|
+
describe('generateAwsManifest', () => {
|
|
14
|
+
itWithDir('should generate AWS manifest with routes', async ({ dir }) => {
|
|
15
|
+
const routes: RouteInfo[] = [
|
|
16
|
+
{
|
|
17
|
+
path: '/users',
|
|
18
|
+
method: 'GET',
|
|
19
|
+
handler: '.gkm/aws/getUsers.handler',
|
|
20
|
+
authorizer: 'jwt',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
path: '/users',
|
|
24
|
+
method: 'POST',
|
|
25
|
+
handler: '.gkm/aws/createUser.handler',
|
|
26
|
+
authorizer: 'jwt',
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
await generateAwsManifest(dir, routes, [], [], []);
|
|
31
|
+
|
|
32
|
+
const manifestPath = join(dir, 'manifest', 'aws.ts');
|
|
33
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
34
|
+
|
|
35
|
+
expect(content).toContain('export const manifest = {');
|
|
36
|
+
expect(content).toContain('} as const;');
|
|
37
|
+
expect(content).toContain('/users');
|
|
38
|
+
expect(content).toContain('GET');
|
|
39
|
+
expect(content).toContain('POST');
|
|
40
|
+
expect(content).toContain('jwt');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
itWithDir(
|
|
44
|
+
'should filter out ALL method routes from AWS manifest',
|
|
45
|
+
async ({ dir }) => {
|
|
46
|
+
const routes: RouteInfo[] = [
|
|
47
|
+
{
|
|
48
|
+
path: '/users',
|
|
49
|
+
method: 'GET',
|
|
50
|
+
handler: '.gkm/aws/getUsers.handler',
|
|
51
|
+
authorizer: 'jwt',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
path: '*',
|
|
55
|
+
method: 'ALL',
|
|
56
|
+
handler: '.gkm/server/app.ts',
|
|
57
|
+
authorizer: 'none',
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
await generateAwsManifest(dir, routes, [], [], []);
|
|
62
|
+
|
|
63
|
+
const manifestPath = join(dir, 'manifest', 'aws.ts');
|
|
64
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
65
|
+
|
|
66
|
+
expect(content).toContain('/users');
|
|
67
|
+
expect(content).toContain('GET');
|
|
68
|
+
expect(content).not.toContain('"ALL"');
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
itWithDir('should generate AWS manifest with functions', async ({ dir }) => {
|
|
73
|
+
const functions: FunctionInfo[] = [
|
|
74
|
+
{
|
|
75
|
+
name: 'processData',
|
|
76
|
+
handler: '.gkm/aws/processData.handler',
|
|
77
|
+
timeout: 300,
|
|
78
|
+
memorySize: 512,
|
|
79
|
+
environment: ['DATABASE_URL'],
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
await generateAwsManifest(dir, [], functions, [], []);
|
|
84
|
+
|
|
85
|
+
const manifestPath = join(dir, 'manifest', 'aws.ts');
|
|
86
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
87
|
+
|
|
88
|
+
expect(content).toContain('processData');
|
|
89
|
+
expect(content).toContain('300');
|
|
90
|
+
expect(content).toContain('512');
|
|
91
|
+
expect(content).toContain('DATABASE_URL');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
itWithDir('should generate AWS manifest with crons', async ({ dir }) => {
|
|
95
|
+
const crons: CronInfo[] = [
|
|
96
|
+
{
|
|
97
|
+
name: 'dailyCleanup',
|
|
98
|
+
handler: '.gkm/aws/dailyCleanup.handler',
|
|
99
|
+
schedule: 'rate(1 day)',
|
|
100
|
+
timeout: 300,
|
|
101
|
+
memorySize: 256,
|
|
102
|
+
environment: [],
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
await generateAwsManifest(dir, [], [], crons, []);
|
|
107
|
+
|
|
108
|
+
const manifestPath = join(dir, 'manifest', 'aws.ts');
|
|
109
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
110
|
+
|
|
111
|
+
expect(content).toContain('dailyCleanup');
|
|
112
|
+
expect(content).toContain('rate(1 day)');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
itWithDir(
|
|
116
|
+
'should generate AWS manifest with subscribers',
|
|
117
|
+
async ({ dir }) => {
|
|
118
|
+
const subscribers: SubscriberInfo[] = [
|
|
119
|
+
{
|
|
120
|
+
name: 'orderHandler',
|
|
121
|
+
handler: '.gkm/aws/orderHandler.handler',
|
|
122
|
+
subscribedEvents: ['order.created', 'order.updated'],
|
|
123
|
+
timeout: 30,
|
|
124
|
+
memorySize: 256,
|
|
125
|
+
environment: [],
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
await generateAwsManifest(dir, [], [], [], subscribers);
|
|
130
|
+
|
|
131
|
+
const manifestPath = join(dir, 'manifest', 'aws.ts');
|
|
132
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
133
|
+
|
|
134
|
+
expect(content).toContain('orderHandler');
|
|
135
|
+
expect(content).toContain('order.created');
|
|
136
|
+
expect(content).toContain('order.updated');
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
itWithDir('should export derived types', async ({ dir }) => {
|
|
141
|
+
await generateAwsManifest(dir, [], [], [], []);
|
|
142
|
+
|
|
143
|
+
const manifestPath = join(dir, 'manifest', 'aws.ts');
|
|
144
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
145
|
+
|
|
146
|
+
expect(content).toContain('export type Route =');
|
|
147
|
+
expect(content).toContain('export type Function =');
|
|
148
|
+
expect(content).toContain('export type Cron =');
|
|
149
|
+
expect(content).toContain('export type Subscriber =');
|
|
150
|
+
expect(content).toContain('export type Authorizer =');
|
|
151
|
+
expect(content).toContain('export type HttpMethod =');
|
|
152
|
+
expect(content).toContain('export type RoutePath =');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
itWithDir('should log manifest generation info', async ({ dir }) => {
|
|
156
|
+
const logSpy = vi.spyOn(console, 'log');
|
|
157
|
+
|
|
158
|
+
const routes: RouteInfo[] = [
|
|
159
|
+
{
|
|
160
|
+
path: '/users',
|
|
161
|
+
method: 'GET',
|
|
162
|
+
handler: '.gkm/aws/getUsers.handler',
|
|
163
|
+
authorizer: 'jwt',
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
await generateAwsManifest(dir, routes, [], [], []);
|
|
168
|
+
|
|
169
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
170
|
+
'Generated AWS manifest with 1 routes, 0 functions, 0 crons, 0 subscribers',
|
|
171
|
+
);
|
|
172
|
+
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Manifest:'));
|
|
173
|
+
|
|
174
|
+
logSpy.mockRestore();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe('generateServerManifest', () => {
|
|
179
|
+
itWithDir(
|
|
180
|
+
'should generate server manifest with app info',
|
|
181
|
+
async ({ dir }) => {
|
|
182
|
+
const appInfo = {
|
|
183
|
+
handler: '.gkm/server/app.ts',
|
|
184
|
+
endpoints: '.gkm/server/endpoints.ts',
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
await generateServerManifest(dir, appInfo, [], []);
|
|
188
|
+
|
|
189
|
+
const manifestPath = join(dir, 'manifest', 'server.ts');
|
|
190
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
191
|
+
|
|
192
|
+
expect(content).toContain('export const manifest = {');
|
|
193
|
+
expect(content).toContain('} as const;');
|
|
194
|
+
expect(content).toContain('app:');
|
|
195
|
+
expect(content).toContain('.gkm/server/app.ts');
|
|
196
|
+
expect(content).toContain('.gkm/server/endpoints.ts');
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
itWithDir(
|
|
201
|
+
'should generate server manifest with route metadata',
|
|
202
|
+
async ({ dir }) => {
|
|
203
|
+
const appInfo = {
|
|
204
|
+
handler: '.gkm/server/app.ts',
|
|
205
|
+
endpoints: '.gkm/server/endpoints.ts',
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const routes: RouteInfo[] = [
|
|
209
|
+
{
|
|
210
|
+
path: '/users',
|
|
211
|
+
method: 'GET',
|
|
212
|
+
handler: '', // Not used for server
|
|
213
|
+
authorizer: 'jwt',
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
path: '/posts',
|
|
217
|
+
method: 'POST',
|
|
218
|
+
handler: '',
|
|
219
|
+
authorizer: 'apiKey',
|
|
220
|
+
},
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
await generateServerManifest(dir, appInfo, routes, []);
|
|
224
|
+
|
|
225
|
+
const manifestPath = join(dir, 'manifest', 'server.ts');
|
|
226
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
227
|
+
|
|
228
|
+
expect(content).toContain('/users');
|
|
229
|
+
expect(content).toContain('/posts');
|
|
230
|
+
expect(content).toContain('GET');
|
|
231
|
+
expect(content).toContain('POST');
|
|
232
|
+
expect(content).toContain('jwt');
|
|
233
|
+
expect(content).toContain('apiKey');
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
itWithDir(
|
|
238
|
+
'should filter out ALL method routes from server manifest',
|
|
239
|
+
async ({ dir }) => {
|
|
240
|
+
const appInfo = {
|
|
241
|
+
handler: '.gkm/server/app.ts',
|
|
242
|
+
endpoints: '.gkm/server/endpoints.ts',
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const routes: RouteInfo[] = [
|
|
246
|
+
{
|
|
247
|
+
path: '/users',
|
|
248
|
+
method: 'GET',
|
|
249
|
+
handler: '',
|
|
250
|
+
authorizer: 'jwt',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
path: '*',
|
|
254
|
+
method: 'ALL',
|
|
255
|
+
handler: '',
|
|
256
|
+
authorizer: 'none',
|
|
257
|
+
},
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
await generateServerManifest(dir, appInfo, routes, []);
|
|
261
|
+
|
|
262
|
+
const manifestPath = join(dir, 'manifest', 'server.ts');
|
|
263
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
264
|
+
|
|
265
|
+
expect(content).toContain('/users');
|
|
266
|
+
expect(content).not.toContain('"ALL"');
|
|
267
|
+
},
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
itWithDir(
|
|
271
|
+
'should generate server manifest with subscribers',
|
|
272
|
+
async ({ dir }) => {
|
|
273
|
+
const appInfo = {
|
|
274
|
+
handler: '.gkm/server/app.ts',
|
|
275
|
+
endpoints: '.gkm/server/endpoints.ts',
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const subscribers: SubscriberInfo[] = [
|
|
279
|
+
{
|
|
280
|
+
name: 'orderHandler',
|
|
281
|
+
handler: '.gkm/server/orderHandler.ts',
|
|
282
|
+
subscribedEvents: ['order.created'],
|
|
283
|
+
timeout: 30,
|
|
284
|
+
memorySize: 256,
|
|
285
|
+
environment: [],
|
|
286
|
+
},
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
await generateServerManifest(dir, appInfo, [], subscribers);
|
|
290
|
+
|
|
291
|
+
const manifestPath = join(dir, 'manifest', 'server.ts');
|
|
292
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
293
|
+
|
|
294
|
+
// Server manifest only includes name and events for subscribers
|
|
295
|
+
expect(content).toContain('orderHandler');
|
|
296
|
+
expect(content).toContain('order.created');
|
|
297
|
+
// Should not include handler path in server manifest subscribers
|
|
298
|
+
expect(content).not.toContain('.gkm/server/orderHandler.ts');
|
|
299
|
+
},
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
itWithDir('should export derived types for server', async ({ dir }) => {
|
|
303
|
+
const appInfo = {
|
|
304
|
+
handler: '.gkm/server/app.ts',
|
|
305
|
+
endpoints: '.gkm/server/endpoints.ts',
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
await generateServerManifest(dir, appInfo, [], []);
|
|
309
|
+
|
|
310
|
+
const manifestPath = join(dir, 'manifest', 'server.ts');
|
|
311
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
312
|
+
|
|
313
|
+
expect(content).toContain('export type Route =');
|
|
314
|
+
expect(content).toContain('export type Subscriber =');
|
|
315
|
+
expect(content).toContain('export type Authorizer =');
|
|
316
|
+
expect(content).toContain('export type HttpMethod =');
|
|
317
|
+
expect(content).toContain('export type RoutePath =');
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
itWithDir('should log manifest generation info', async ({ dir }) => {
|
|
321
|
+
const logSpy = vi.spyOn(console, 'log');
|
|
322
|
+
|
|
323
|
+
const appInfo = {
|
|
324
|
+
handler: '.gkm/server/app.ts',
|
|
325
|
+
endpoints: '.gkm/server/endpoints.ts',
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const routes: RouteInfo[] = [
|
|
329
|
+
{
|
|
330
|
+
path: '/users',
|
|
331
|
+
method: 'GET',
|
|
332
|
+
handler: '',
|
|
333
|
+
authorizer: 'jwt',
|
|
334
|
+
},
|
|
335
|
+
];
|
|
336
|
+
|
|
337
|
+
await generateServerManifest(dir, appInfo, routes, []);
|
|
338
|
+
|
|
339
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
340
|
+
'Generated server manifest with 1 routes, 0 subscribers',
|
|
341
|
+
);
|
|
342
|
+
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Manifest:'));
|
|
343
|
+
|
|
344
|
+
logSpy.mockRestore();
|
|
345
|
+
});
|
|
346
|
+
});
|
package/src/build/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdir } from 'node:fs/promises';
|
|
2
|
-
import { join } from 'node:path';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
3
|
import type { Cron } from '@geekmidas/constructs/crons';
|
|
4
4
|
import type { Endpoint } from '@geekmidas/constructs/endpoints';
|
|
5
5
|
import type { Function } from '@geekmidas/constructs/functions';
|
|
@@ -12,15 +12,12 @@ import {
|
|
|
12
12
|
type GeneratedConstruct,
|
|
13
13
|
SubscriberGenerator,
|
|
14
14
|
} from '../generators';
|
|
15
|
-
import type {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
SubscriberInfo,
|
|
22
|
-
} from '../types';
|
|
23
|
-
import { generateManifests } from './manifests';
|
|
15
|
+
import type { BuildOptions, LegacyProvider, RouteInfo } from '../types';
|
|
16
|
+
import {
|
|
17
|
+
type ServerAppInfo,
|
|
18
|
+
generateAwsManifest,
|
|
19
|
+
generateServerManifest,
|
|
20
|
+
} from './manifests';
|
|
24
21
|
import { resolveProviders } from './providerResolver';
|
|
25
22
|
import type { BuildContext } from './types';
|
|
26
23
|
|
|
@@ -104,55 +101,29 @@ export async function buildCommand(options: BuildOptions): Promise<void> {
|
|
|
104
101
|
const rootOutputDir = join(process.cwd(), '.gkm');
|
|
105
102
|
await mkdir(rootOutputDir, { recursive: true });
|
|
106
103
|
|
|
107
|
-
//
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
// Aggregate all routes, functions, crons, and subscribers from all providers
|
|
127
|
-
const aggregatedRoutes = allBuildResults.flatMap((result) => result.routes);
|
|
128
|
-
const aggregatedFunctions = allBuildResults.flatMap(
|
|
129
|
-
(result) => result.functions,
|
|
130
|
-
);
|
|
131
|
-
const aggregatedCrons = allBuildResults.flatMap((result) => result.crons);
|
|
132
|
-
const aggregatedSubscribers = allBuildResults.flatMap(
|
|
133
|
-
(result) => result.subscribers,
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
// Generate single manifest at root .gkm directory
|
|
137
|
-
await generateManifests(
|
|
138
|
-
rootOutputDir,
|
|
139
|
-
aggregatedRoutes,
|
|
140
|
-
aggregatedFunctions,
|
|
141
|
-
aggregatedCrons,
|
|
142
|
-
aggregatedSubscribers,
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
interface BuildResult {
|
|
147
|
-
routes: RouteInfo[];
|
|
148
|
-
functions: FunctionInfo[];
|
|
149
|
-
crons: CronInfo[];
|
|
150
|
-
subscribers: SubscriberInfo[];
|
|
104
|
+
// Build for each provider and generate per-provider manifests
|
|
105
|
+
for (const provider of resolved.providers) {
|
|
106
|
+
await buildForProvider(
|
|
107
|
+
provider,
|
|
108
|
+
buildContext,
|
|
109
|
+
rootOutputDir,
|
|
110
|
+
endpointGenerator,
|
|
111
|
+
functionGenerator,
|
|
112
|
+
cronGenerator,
|
|
113
|
+
subscriberGenerator,
|
|
114
|
+
allEndpoints,
|
|
115
|
+
allFunctions,
|
|
116
|
+
allCrons,
|
|
117
|
+
allSubscribers,
|
|
118
|
+
resolved.enableOpenApi,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
151
121
|
}
|
|
152
122
|
|
|
153
123
|
async function buildForProvider(
|
|
154
124
|
provider: LegacyProvider,
|
|
155
125
|
context: BuildContext,
|
|
126
|
+
rootOutputDir: string,
|
|
156
127
|
endpointGenerator: EndpointGenerator,
|
|
157
128
|
functionGenerator: FunctionGenerator,
|
|
158
129
|
cronGenerator: CronGenerator,
|
|
@@ -162,7 +133,7 @@ async function buildForProvider(
|
|
|
162
133
|
crons: GeneratedConstruct<Cron<any, any, any, any>>[],
|
|
163
134
|
subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],
|
|
164
135
|
enableOpenApi: boolean,
|
|
165
|
-
): Promise<
|
|
136
|
+
): Promise<void> {
|
|
166
137
|
const outputDir = join(process.cwd(), '.gkm', provider);
|
|
167
138
|
|
|
168
139
|
// Ensure output directory exists
|
|
@@ -187,11 +158,37 @@ async function buildForProvider(
|
|
|
187
158
|
`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers for ${provider}`,
|
|
188
159
|
);
|
|
189
160
|
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
161
|
+
// Generate provider-specific manifest
|
|
162
|
+
if (provider === 'server') {
|
|
163
|
+
// For server, collect actual route metadata from endpoint constructs
|
|
164
|
+
const routeMetadata: RouteInfo[] = await Promise.all(
|
|
165
|
+
endpoints.map(async ({ construct }) => ({
|
|
166
|
+
path: construct._path,
|
|
167
|
+
method: construct.method,
|
|
168
|
+
handler: '', // Not needed for server manifest
|
|
169
|
+
authorizer: construct.authorizer?.name ?? 'none',
|
|
170
|
+
})),
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const appInfo: ServerAppInfo = {
|
|
174
|
+
handler: relative(process.cwd(), join(outputDir, 'app.ts')),
|
|
175
|
+
endpoints: relative(process.cwd(), join(outputDir, 'endpoints.ts')),
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
await generateServerManifest(
|
|
179
|
+
rootOutputDir,
|
|
180
|
+
appInfo,
|
|
181
|
+
routeMetadata,
|
|
182
|
+
subscriberInfos,
|
|
183
|
+
);
|
|
184
|
+
} else {
|
|
185
|
+
// For AWS providers, generate AWS manifest
|
|
186
|
+
await generateAwsManifest(
|
|
187
|
+
rootOutputDir,
|
|
188
|
+
routes,
|
|
189
|
+
functionInfos,
|
|
190
|
+
cronInfos,
|
|
191
|
+
subscriberInfos,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
197
194
|
}
|
package/src/build/manifests.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { writeFile } from 'node:fs/promises';
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { join, relative } from 'path';
|
|
3
3
|
import type {
|
|
4
|
-
BuildManifest,
|
|
5
4
|
CronInfo,
|
|
6
5
|
FunctionInfo,
|
|
7
6
|
RouteInfo,
|
|
@@ -10,26 +9,99 @@ import type {
|
|
|
10
9
|
|
|
11
10
|
const logger = console;
|
|
12
11
|
|
|
13
|
-
export
|
|
12
|
+
export type ManifestProvider = 'aws' | 'server';
|
|
13
|
+
|
|
14
|
+
export interface ServerAppInfo {
|
|
15
|
+
handler: string;
|
|
16
|
+
endpoints: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function generateAwsManifest(
|
|
14
20
|
outputDir: string,
|
|
15
21
|
routes: RouteInfo[],
|
|
16
22
|
functions: FunctionInfo[],
|
|
17
23
|
crons: CronInfo[],
|
|
18
24
|
subscribers: SubscriberInfo[],
|
|
19
25
|
): Promise<void> {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
const manifestDir = join(outputDir, 'manifest');
|
|
27
|
+
await mkdir(manifestDir, { recursive: true });
|
|
28
|
+
|
|
29
|
+
// Filter out 'ALL' method routes (server-specific)
|
|
30
|
+
const awsRoutes = routes.filter((r) => r.method !== 'ALL');
|
|
31
|
+
|
|
32
|
+
const content = `export const manifest = {
|
|
33
|
+
routes: ${JSON.stringify(awsRoutes, null, 2)},
|
|
34
|
+
functions: ${JSON.stringify(functions, null, 2)},
|
|
35
|
+
crons: ${JSON.stringify(crons, null, 2)},
|
|
36
|
+
subscribers: ${JSON.stringify(subscribers, null, 2)},
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
// Derived types
|
|
40
|
+
export type Route = (typeof manifest.routes)[number];
|
|
41
|
+
export type Function = (typeof manifest.functions)[number];
|
|
42
|
+
export type Cron = (typeof manifest.crons)[number];
|
|
43
|
+
export type Subscriber = (typeof manifest.subscribers)[number];
|
|
44
|
+
|
|
45
|
+
// Useful union types
|
|
46
|
+
export type Authorizer = Route['authorizer'];
|
|
47
|
+
export type HttpMethod = Route['method'];
|
|
48
|
+
export type RoutePath = Route['path'];
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
const manifestPath = join(manifestDir, 'aws.ts');
|
|
52
|
+
await writeFile(manifestPath, content);
|
|
53
|
+
|
|
54
|
+
logger.log(
|
|
55
|
+
`Generated AWS manifest with ${awsRoutes.length} routes, ${functions.length} functions, ${crons.length} crons, ${subscribers.length} subscribers`,
|
|
56
|
+
);
|
|
57
|
+
logger.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function generateServerManifest(
|
|
61
|
+
outputDir: string,
|
|
62
|
+
appInfo: ServerAppInfo,
|
|
63
|
+
routes: RouteInfo[],
|
|
64
|
+
subscribers: SubscriberInfo[],
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
const manifestDir = join(outputDir, 'manifest');
|
|
67
|
+
await mkdir(manifestDir, { recursive: true });
|
|
68
|
+
|
|
69
|
+
// For server, extract route metadata (path, method, authorizer)
|
|
70
|
+
const serverRoutes = routes
|
|
71
|
+
.filter((r) => r.method !== 'ALL')
|
|
72
|
+
.map((r) => ({
|
|
73
|
+
path: r.path,
|
|
74
|
+
method: r.method,
|
|
75
|
+
authorizer: r.authorizer,
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
// Server subscribers only need name and events
|
|
79
|
+
const serverSubscribers = subscribers.map((s) => ({
|
|
80
|
+
name: s.name,
|
|
81
|
+
subscribedEvents: s.subscribedEvents,
|
|
82
|
+
}));
|
|
83
|
+
|
|
84
|
+
const content = `export const manifest = {
|
|
85
|
+
app: ${JSON.stringify(appInfo, null, 2)},
|
|
86
|
+
routes: ${JSON.stringify(serverRoutes, null, 2)},
|
|
87
|
+
subscribers: ${JSON.stringify(serverSubscribers, null, 2)},
|
|
88
|
+
} as const;
|
|
89
|
+
|
|
90
|
+
// Derived types
|
|
91
|
+
export type Route = (typeof manifest.routes)[number];
|
|
92
|
+
export type Subscriber = (typeof manifest.subscribers)[number];
|
|
93
|
+
|
|
94
|
+
// Useful union types
|
|
95
|
+
export type Authorizer = Route['authorizer'];
|
|
96
|
+
export type HttpMethod = Route['method'];
|
|
97
|
+
export type RoutePath = Route['path'];
|
|
98
|
+
`;
|
|
27
99
|
|
|
28
|
-
const manifestPath = join(
|
|
29
|
-
await writeFile(manifestPath,
|
|
100
|
+
const manifestPath = join(manifestDir, 'server.ts');
|
|
101
|
+
await writeFile(manifestPath, content);
|
|
30
102
|
|
|
31
103
|
logger.log(
|
|
32
|
-
|
|
104
|
+
`Generated server manifest with ${serverRoutes.length} routes, ${serverSubscribers.length} subscribers`,
|
|
33
105
|
);
|
|
34
106
|
logger.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);
|
|
35
107
|
}
|
package/src/build/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { Cron
|
|
2
|
-
import type { Endpoint } from '@geekmidas/constructs';
|
|
1
|
+
import type { Cron } from '@geekmidas/constructs/crons';
|
|
2
|
+
import type { Endpoint } from '@geekmidas/constructs/endpoints';
|
|
3
|
+
import type { Function } from '@geekmidas/constructs/functions';
|
|
3
4
|
|
|
4
5
|
import type { CronInfo, FunctionInfo, RouteInfo } from '../types';
|
|
5
6
|
|
|
@@ -23,11 +24,21 @@ export interface ProcessedCron {
|
|
|
23
24
|
schedule?: string;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
export interface NormalizedTelescopeConfig {
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
path: string;
|
|
30
|
+
ignore: string[];
|
|
31
|
+
recordBody: boolean;
|
|
32
|
+
maxEntries: number;
|
|
33
|
+
websocket: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
26
36
|
export interface BuildContext {
|
|
27
37
|
envParserPath: string;
|
|
28
38
|
envParserImportPattern: string;
|
|
29
39
|
loggerPath: string;
|
|
30
40
|
loggerImportPattern: string;
|
|
41
|
+
telescope?: NormalizedTelescopeConfig;
|
|
31
42
|
}
|
|
32
43
|
|
|
33
44
|
export interface ProviderBuildResult {
|
package/src/config.ts
CHANGED
|
@@ -2,12 +2,14 @@ import { existsSync } from 'fs';
|
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import type { GkmConfig } from './types.ts';
|
|
4
4
|
|
|
5
|
-
export async function loadConfig(
|
|
5
|
+
export async function loadConfig(
|
|
6
|
+
cwd: string = process.cwd(),
|
|
7
|
+
): Promise<GkmConfig> {
|
|
6
8
|
const files = ['gkm.config.json', 'gkm.config.ts', 'gkm.config.js'];
|
|
7
9
|
let configPath = '';
|
|
8
10
|
|
|
9
11
|
for (const file of files) {
|
|
10
|
-
const path = join(
|
|
12
|
+
const path = join(cwd, file);
|
|
11
13
|
if (existsSync(path)) {
|
|
12
14
|
configPath = path;
|
|
13
15
|
break;
|