@geekmidas/cli 0.3.0 → 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.
Files changed (65) hide show
  1. package/README.md +488 -71
  2. package/dist/{EndpointGenerator-C73wNoih.cjs → EndpointGenerator-BxNCkus4.cjs} +60 -6
  3. package/dist/EndpointGenerator-BxNCkus4.cjs.map +1 -0
  4. package/dist/{EndpointGenerator-CWh18d92.mjs → EndpointGenerator-CzDhG7Or.mjs} +60 -6
  5. package/dist/EndpointGenerator-CzDhG7Or.mjs.map +1 -0
  6. package/dist/OpenApiTsGenerator-NBNEoaeO.cjs +501 -0
  7. package/dist/OpenApiTsGenerator-NBNEoaeO.cjs.map +1 -0
  8. package/dist/OpenApiTsGenerator-q3aWNkuM.mjs +495 -0
  9. package/dist/OpenApiTsGenerator-q3aWNkuM.mjs.map +1 -0
  10. package/dist/build/index.cjs +3 -3
  11. package/dist/build/index.mjs +3 -3
  12. package/dist/{build-CBYBPZpC.cjs → build-CWtHnJMQ.cjs} +3 -3
  13. package/dist/{build-CBYBPZpC.cjs.map → build-CWtHnJMQ.cjs.map} +1 -1
  14. package/dist/{build-C6uEGRj8.mjs → build-DyDgu_D1.mjs} +3 -3
  15. package/dist/{build-C6uEGRj8.mjs.map → build-DyDgu_D1.mjs.map} +1 -1
  16. package/dist/{config-U-mdW-7Y.mjs → config-AFmFKmU0.mjs} +3 -3
  17. package/dist/config-AFmFKmU0.mjs.map +1 -0
  18. package/dist/{config-D1EpSGk6.cjs → config-BVIJpAsa.cjs} +3 -3
  19. package/dist/config-BVIJpAsa.cjs.map +1 -0
  20. package/dist/config.cjs +1 -1
  21. package/dist/config.mjs +1 -1
  22. package/dist/dev/index.cjs +5 -4
  23. package/dist/dev/index.mjs +4 -4
  24. package/dist/{dev-DbtyToc7.cjs → dev-CgDYC4o8.cjs} +95 -31
  25. package/dist/dev-CgDYC4o8.cjs.map +1 -0
  26. package/dist/{dev-DnGYXuMn.mjs → dev-CpA8AQPX.mjs} +90 -32
  27. package/dist/dev-CpA8AQPX.mjs.map +1 -0
  28. package/dist/generators/EndpointGenerator.cjs +1 -1
  29. package/dist/generators/EndpointGenerator.mjs +1 -1
  30. package/dist/generators/OpenApiTsGenerator.cjs +3 -0
  31. package/dist/generators/OpenApiTsGenerator.mjs +3 -0
  32. package/dist/generators/index.cjs +1 -1
  33. package/dist/generators/index.mjs +1 -1
  34. package/dist/index.cjs +15 -9
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.mjs +15 -9
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/openapi-DRTRGhTt.mjs +50 -0
  39. package/dist/openapi-DRTRGhTt.mjs.map +1 -0
  40. package/dist/openapi-DhK4b0lB.cjs +56 -0
  41. package/dist/openapi-DhK4b0lB.cjs.map +1 -0
  42. package/dist/openapi.cjs +4 -3
  43. package/dist/openapi.mjs +4 -3
  44. package/docs/OPENAPI_TYPESCRIPT_DESIGN.md +408 -0
  45. package/package.json +10 -3
  46. package/src/__tests__/openapi.spec.ts +78 -63
  47. package/src/build/types.ts +13 -2
  48. package/src/config.ts +4 -2
  49. package/src/dev/__tests__/index.spec.ts +98 -1
  50. package/src/dev/index.ts +152 -25
  51. package/src/generators/EndpointGenerator.ts +69 -5
  52. package/src/generators/OpenApiTsGenerator.ts +798 -0
  53. package/src/index.ts +6 -3
  54. package/src/openapi.ts +36 -15
  55. package/src/types.ts +23 -0
  56. package/dist/EndpointGenerator-C73wNoih.cjs.map +0 -1
  57. package/dist/EndpointGenerator-CWh18d92.mjs.map +0 -1
  58. package/dist/config-D1EpSGk6.cjs.map +0 -1
  59. package/dist/config-U-mdW-7Y.mjs.map +0 -1
  60. package/dist/dev-DbtyToc7.cjs.map +0 -1
  61. package/dist/dev-DnGYXuMn.mjs.map +0 -1
  62. package/dist/openapi-BTHbPrxS.mjs +0 -36
  63. package/dist/openapi-BTHbPrxS.mjs.map +0 -1
  64. package/dist/openapi-CewcfoRH.cjs +0 -42
  65. package/dist/openapi-CewcfoRH.cjs.map +0 -1
@@ -0,0 +1,56 @@
1
+ const require_chunk = require('./chunk-CUT6urMc.cjs');
2
+ const require_config = require('./config-BVIJpAsa.cjs');
3
+ const require_EndpointGenerator = require('./EndpointGenerator-BxNCkus4.cjs');
4
+ const require_OpenApiTsGenerator = require('./OpenApiTsGenerator-NBNEoaeO.cjs');
5
+ const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
6
+ const node_path = require_chunk.__toESM(require("node:path"));
7
+ const __geekmidas_constructs_endpoints = require_chunk.__toESM(require("@geekmidas/constructs/endpoints"));
8
+
9
+ //#region src/openapi.ts
10
+ async function openapiCommand(options = {}) {
11
+ const logger = console;
12
+ try {
13
+ const config = await require_config.loadConfig(options.cwd);
14
+ const endpointGenerator = new require_EndpointGenerator.EndpointGenerator();
15
+ const loadedEndpoints = await endpointGenerator.load(config.routes);
16
+ if (loadedEndpoints.length === 0) {
17
+ logger.log("No valid endpoints found");
18
+ return;
19
+ }
20
+ const endpoints = loadedEndpoints.map(({ construct }) => construct);
21
+ const isJson = options.json === true;
22
+ const defaultOutput = isJson ? "openapi.json" : "openapi.ts";
23
+ const outputPath = options.output || (0, node_path.join)(process.cwd(), defaultOutput);
24
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(outputPath), { recursive: true });
25
+ if (isJson) {
26
+ const spec = await __geekmidas_constructs_endpoints.Endpoint.buildOpenApiSchema(endpoints, {
27
+ title: "API Documentation",
28
+ version: "1.0.0",
29
+ description: "Auto-generated API documentation from endpoints"
30
+ });
31
+ await (0, node_fs_promises.writeFile)(outputPath, JSON.stringify(spec, null, 2));
32
+ logger.log(`OpenAPI JSON spec generated: ${outputPath}`);
33
+ } else {
34
+ const tsGenerator = new require_OpenApiTsGenerator.OpenApiTsGenerator();
35
+ const tsContent = await tsGenerator.generate(endpoints, {
36
+ title: "API Documentation",
37
+ version: "1.0.0",
38
+ description: "Auto-generated API documentation from endpoints"
39
+ });
40
+ await (0, node_fs_promises.writeFile)(outputPath, tsContent);
41
+ logger.log(`OpenAPI TypeScript module generated: ${outputPath}`);
42
+ }
43
+ logger.log(`Found ${endpoints.length} endpoints`);
44
+ } catch (error) {
45
+ throw new Error(`OpenAPI generation failed: ${error.message}`);
46
+ }
47
+ }
48
+
49
+ //#endregion
50
+ Object.defineProperty(exports, 'openapiCommand', {
51
+ enumerable: true,
52
+ get: function () {
53
+ return openapiCommand;
54
+ }
55
+ });
56
+ //# sourceMappingURL=openapi-DhK4b0lB.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-DhK4b0lB.cjs","names":["options: OpenAPIOptions","EndpointGenerator","OpenApiTsGenerator"],"sources":["../src/openapi.ts"],"sourcesContent":["#!/usr/bin/env -S npx tsx\n\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { Endpoint } from '@geekmidas/constructs/endpoints';\nimport { loadConfig } from './config.js';\nimport { EndpointGenerator } from './generators/EndpointGenerator.js';\nimport { OpenApiTsGenerator } from './generators/OpenApiTsGenerator.js';\n\ninterface OpenAPIOptions {\n output?: string;\n json?: boolean;\n cwd?: string;\n}\n\nexport async function openapiCommand(\n options: OpenAPIOptions = {},\n): Promise<void> {\n const logger = console;\n\n try {\n // Load config using existing function\n const config = await loadConfig(options.cwd);\n const endpointGenerator = new EndpointGenerator();\n\n // Load all endpoints using the refactored function\n const loadedEndpoints = await endpointGenerator.load(config.routes);\n\n if (loadedEndpoints.length === 0) {\n logger.log('No valid endpoints found');\n return;\n }\n\n // Extract just the endpoint instances for OpenAPI generation\n const endpoints = loadedEndpoints.map(({ construct }) => construct);\n\n // Determine output format (TypeScript is default)\n const isJson = options.json === true;\n const defaultOutput = isJson ? 'openapi.json' : 'openapi.ts';\n const outputPath = options.output || join(process.cwd(), defaultOutput);\n\n // Ensure output directory exists\n await mkdir(dirname(outputPath), { recursive: true });\n\n if (isJson) {\n // Generate JSON OpenAPI spec (legacy)\n const spec = await Endpoint.buildOpenApiSchema(endpoints, {\n title: 'API Documentation',\n version: '1.0.0',\n description: 'Auto-generated API documentation from endpoints',\n });\n\n await writeFile(outputPath, JSON.stringify(spec, null, 2));\n logger.log(`OpenAPI JSON spec generated: ${outputPath}`);\n } else {\n // Generate TypeScript OpenAPI module (default)\n const tsGenerator = new OpenApiTsGenerator();\n const tsContent = await tsGenerator.generate(endpoints, {\n title: 'API Documentation',\n version: '1.0.0',\n description: 'Auto-generated API documentation from endpoints',\n });\n\n await writeFile(outputPath, tsContent);\n logger.log(`OpenAPI TypeScript module generated: ${outputPath}`);\n }\n\n logger.log(`Found ${endpoints.length} endpoints`);\n } catch (error) {\n throw new Error(`OpenAPI generation failed: ${(error as Error).message}`);\n }\n}\n"],"mappings":";;;;;;;;;AAeA,eAAsB,eACpBA,UAA0B,CAAE,GACb;CACf,MAAM,SAAS;AAEf,KAAI;EAEF,MAAM,SAAS,MAAM,0BAAW,QAAQ,IAAI;EAC5C,MAAM,oBAAoB,IAAIC;EAG9B,MAAM,kBAAkB,MAAM,kBAAkB,KAAK,OAAO,OAAO;AAEnE,MAAI,gBAAgB,WAAW,GAAG;AAChC,UAAO,IAAI,2BAA2B;AACtC;EACD;EAGD,MAAM,YAAY,gBAAgB,IAAI,CAAC,EAAE,WAAW,KAAK,UAAU;EAGnE,MAAM,SAAS,QAAQ,SAAS;EAChC,MAAM,gBAAgB,SAAS,iBAAiB;EAChD,MAAM,aAAa,QAAQ,UAAU,oBAAK,QAAQ,KAAK,EAAE,cAAc;AAGvE,QAAM,4BAAM,uBAAQ,WAAW,EAAE,EAAE,WAAW,KAAM,EAAC;AAErD,MAAI,QAAQ;GAEV,MAAM,OAAO,MAAM,0CAAS,mBAAmB,WAAW;IACxD,OAAO;IACP,SAAS;IACT,aAAa;GACd,EAAC;AAEF,SAAM,gCAAU,YAAY,KAAK,UAAU,MAAM,MAAM,EAAE,CAAC;AAC1D,UAAO,KAAK,+BAA+B,WAAW,EAAE;EACzD,OAAM;GAEL,MAAM,cAAc,IAAIC;GACxB,MAAM,YAAY,MAAM,YAAY,SAAS,WAAW;IACtD,OAAO;IACP,SAAS;IACT,aAAa;GACd,EAAC;AAEF,SAAM,gCAAU,YAAY,UAAU;AACtC,UAAO,KAAK,uCAAuC,WAAW,EAAE;EACjE;AAED,SAAO,KAAK,QAAQ,UAAU,OAAO,YAAY;CAClD,SAAQ,OAAO;AACd,QAAM,IAAI,OAAO,6BAA8B,MAAgB,QAAQ;CACxE;AACF"}
package/dist/openapi.cjs CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env -S npx tsx
2
- require('./config-D1EpSGk6.cjs');
2
+ require('./config-BVIJpAsa.cjs');
3
3
  require('./Generator-CDoEXCDg.cjs');
4
- require('./EndpointGenerator-C73wNoih.cjs');
5
- const require_openapi = require('./openapi-CewcfoRH.cjs');
4
+ require('./EndpointGenerator-BxNCkus4.cjs');
5
+ require('./OpenApiTsGenerator-NBNEoaeO.cjs');
6
+ const require_openapi = require('./openapi-DhK4b0lB.cjs');
6
7
 
7
8
  exports.openapiCommand = require_openapi.openapiCommand;
package/dist/openapi.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env -S npx tsx
2
- import "./config-U-mdW-7Y.mjs";
2
+ import "./config-AFmFKmU0.mjs";
3
3
  import "./Generator-UanJW0_V.mjs";
4
- import "./EndpointGenerator-CWh18d92.mjs";
5
- import { openapiCommand } from "./openapi-BTHbPrxS.mjs";
4
+ import "./EndpointGenerator-CzDhG7Or.mjs";
5
+ import "./OpenApiTsGenerator-q3aWNkuM.mjs";
6
+ import { openapiCommand } from "./openapi-DRTRGhTt.mjs";
6
7
 
7
8
  export { openapiCommand };
@@ -0,0 +1,408 @@
1
+ # OpenAPI TypeScript Generation - Design Document
2
+
3
+ ## Overview
4
+
5
+ This document describes the design for generating TypeScript OpenAPI specifications with integrated authentication support. The feature extends `gkm openapi` with a `--ts` flag that outputs a TypeScript module instead of JSON, enabling:
6
+
7
+ 1. **Type-safe paths** - Full TypeScript interface for API routes
8
+ 2. **Runtime auth map** - Per-endpoint authentication requirements
9
+ 3. **Reusable schemas** - TypeScript interfaces extracted from Zod/Valibot schemas
10
+ 4. **Security scheme definitions** - OpenAPI security schemes as typed constants
11
+
12
+ ## Motivation
13
+
14
+ ### Current Flow (JSON-based)
15
+
16
+ ```
17
+ Endpoints → gkm openapi → openapi.json → openapi-typescript → types.d.ts
18
+
19
+ (no auth info at runtime)
20
+ ```
21
+
22
+ **Problems:**
23
+ - Auth requirements lost at runtime - OpenAPI security info exists but isn't usable by the fetcher
24
+ - Two-step generation - need external tool (`openapi-typescript`) for types
25
+ - No schema reuse - generated types are isolated, can't reference shared interfaces
26
+
27
+ ### Proposed Flow (TypeScript-native)
28
+
29
+ ```
30
+ Endpoints → gkm openapi --ts → openapi.ts
31
+
32
+ Exports:
33
+ ├── paths (type)
34
+ ├── endpointAuth (runtime map)
35
+ ├── securitySchemes (runtime definitions)
36
+ └── schema interfaces (reusable types)
37
+ ```
38
+
39
+ ## Command Interface
40
+
41
+ ```bash
42
+ # Default: generates TypeScript
43
+ gkm openapi --output ./src/api/openapi.ts
44
+
45
+ # Explicit JSON output (legacy)
46
+ gkm openapi --json --output ./openapi.json
47
+ ```
48
+
49
+ ### Options
50
+
51
+ | Flag | Description | Default |
52
+ |------|-------------|---------|
53
+ | `--json` | Generate JSON instead of TypeScript (legacy) | `false` |
54
+ | `--output` | Output file path | `openapi.ts` |
55
+
56
+ ## Generated Output Structure
57
+
58
+ ### File: `openapi.ts`
59
+
60
+ ```typescript
61
+ // Auto-generated by @geekmidas/cli - DO NOT EDIT
62
+ import type { OpenAPIV3_1 } from 'openapi-types';
63
+
64
+ // ============================================================
65
+ // Security Schemes
66
+ // ============================================================
67
+
68
+ /**
69
+ * Available security schemes for this API.
70
+ * Maps authorizer names to OpenAPI security scheme definitions.
71
+ */
72
+ export const securitySchemes = {
73
+ bearer: {
74
+ type: 'http',
75
+ scheme: 'bearer',
76
+ bearerFormat: 'JWT',
77
+ },
78
+ iam: {
79
+ type: 'apiKey',
80
+ in: 'header',
81
+ name: 'Authorization',
82
+ 'x-amazon-apigateway-authtype': 'awsSigv4',
83
+ },
84
+ apiKey: {
85
+ type: 'apiKey',
86
+ in: 'header',
87
+ name: 'X-API-Key',
88
+ },
89
+ } as const satisfies Record<string, OpenAPIV3_1.SecuritySchemeObject>;
90
+
91
+ export type SecuritySchemeId = keyof typeof securitySchemes;
92
+
93
+ // ============================================================
94
+ // Endpoint Authentication Map
95
+ // ============================================================
96
+
97
+ /**
98
+ * Runtime map of endpoints to their required authentication scheme.
99
+ * `null` indicates a public endpoint (no auth required).
100
+ */
101
+ export const endpointAuth = {
102
+ 'POST /tenants': 'iam',
103
+ 'GET /tenants/{id}': 'bearer',
104
+ 'PUT /tenants/{id}': 'bearer',
105
+ 'DELETE /tenants/{id}': 'bearer',
106
+ 'GET /health': null,
107
+ 'GET /docs': null,
108
+ } as const satisfies Record<string, SecuritySchemeId | null>;
109
+
110
+ export type AuthenticatedEndpoint = {
111
+ [K in keyof typeof endpointAuth]: typeof endpointAuth[K] extends null ? never : K;
112
+ }[keyof typeof endpointAuth];
113
+
114
+ export type PublicEndpoint = {
115
+ [K in keyof typeof endpointAuth]: typeof endpointAuth[K] extends null ? K : never;
116
+ }[keyof typeof endpointAuth];
117
+
118
+ // ============================================================
119
+ // Schema Definitions (Reusable Types)
120
+ // ============================================================
121
+
122
+ export interface Tenant {
123
+ id: string;
124
+ name: string;
125
+ createdAt: string;
126
+ updatedAt: string;
127
+ }
128
+
129
+ export interface CreateTenantInput {
130
+ name: string;
131
+ }
132
+
133
+ export interface UpdateTenantInput {
134
+ name?: string;
135
+ }
136
+
137
+ // ============================================================
138
+ // OpenAPI Paths
139
+ // ============================================================
140
+
141
+ export interface paths {
142
+ '/tenants': {
143
+ post: {
144
+ requestBody: {
145
+ content: {
146
+ 'application/json': CreateTenantInput;
147
+ };
148
+ };
149
+ responses: {
150
+ 201: {
151
+ content: {
152
+ 'application/json': Tenant;
153
+ };
154
+ };
155
+ };
156
+ };
157
+ };
158
+ '/tenants/{id}': {
159
+ parameters: {
160
+ path: {
161
+ id: string;
162
+ };
163
+ };
164
+ get: {
165
+ responses: {
166
+ 200: {
167
+ content: {
168
+ 'application/json': Tenant;
169
+ };
170
+ };
171
+ };
172
+ };
173
+ put: {
174
+ requestBody: {
175
+ content: {
176
+ 'application/json': UpdateTenantInput;
177
+ };
178
+ };
179
+ responses: {
180
+ 200: {
181
+ content: {
182
+ 'application/json': Tenant;
183
+ };
184
+ };
185
+ };
186
+ };
187
+ delete: {
188
+ responses: {
189
+ 204: {
190
+ content: never;
191
+ };
192
+ };
193
+ };
194
+ };
195
+ '/health': {
196
+ get: {
197
+ responses: {
198
+ 200: {
199
+ content: {
200
+ 'application/json': { status: string };
201
+ };
202
+ };
203
+ };
204
+ };
205
+ };
206
+ }
207
+
208
+ // ============================================================
209
+ // Full OpenAPI Specification (Optional)
210
+ // ============================================================
211
+
212
+ export const spec = {
213
+ openapi: '3.1.0',
214
+ info: {
215
+ title: 'Tenant API',
216
+ version: '1.0.0',
217
+ },
218
+ paths: { /* ... */ },
219
+ components: {
220
+ securitySchemes,
221
+ schemas: { /* ... */ },
222
+ },
223
+ } as const satisfies OpenAPIV3_1.Document;
224
+ ```
225
+
226
+ ## Implementation Details
227
+
228
+ ### 1. Authorizer to Security Scheme Mapping
229
+
230
+ The `Authorizer.type` field maps to OpenAPI security schemes:
231
+
232
+ | Authorizer Type | OpenAPI Security Scheme |
233
+ |-----------------|------------------------|
234
+ | `jwt`, `bearer` | `{ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }` |
235
+ | `iam`, `aws-sigv4` | `{ type: 'apiKey', in: 'header', name: 'Authorization', 'x-amazon-apigateway-authtype': 'awsSigv4' }` |
236
+ | `apiKey` | `{ type: 'apiKey', in: metadata.in, name: metadata.name }` |
237
+ | `oauth2` | `{ type: 'oauth2', flows: { ... } }` |
238
+ | `oidc` | `{ type: 'openIdConnect', openIdConnectUrl: metadata.issuer }` |
239
+ | `none` / undefined | `null` (public endpoint) |
240
+
241
+ ### 2. Schema Extraction
242
+
243
+ Schemas are extracted from StandardSchema (Zod/Valibot) definitions:
244
+
245
+ ```typescript
246
+ // From endpoint definition
247
+ const endpoint = e
248
+ .post('/tenants')
249
+ .body(z.object({ name: z.string() }))
250
+ .output(z.object({ id: z.string(), name: z.string() }))
251
+ .handle(async ({ body }) => { ... });
252
+
253
+ // Extracted as
254
+ export interface CreateTenantInput {
255
+ name: string;
256
+ }
257
+
258
+ export interface CreateTenantOutput {
259
+ id: string;
260
+ name: string;
261
+ }
262
+ ```
263
+
264
+ ### 3. Naming Strategy
265
+
266
+ | Schema Location | Generated Name |
267
+ |-----------------|----------------|
268
+ | Body schema | `{OperationId}Input` or `{Method}{Route}Input` |
269
+ | Output schema | `{OperationId}Output` or `{Method}{Route}Output` |
270
+ | Params schema | `{OperationId}Params` |
271
+ | Query schema | `{OperationId}Query` |
272
+
273
+ ### 4. Component Collector Enhancement
274
+
275
+ ```typescript
276
+ // packages/schema/src/openapi.ts
277
+ export interface ComponentCollector {
278
+ schemas: Record<string, OpenAPIV3_1.SchemaObject>;
279
+ securitySchemes: Record<string, OpenAPIV3_1.SecuritySchemeObject>;
280
+ addSchema(id: string, schema: OpenAPIV3_1.SchemaObject): void;
281
+ addSecurityScheme(id: string, scheme: OpenAPIV3_1.SecuritySchemeObject): void;
282
+ getReference(id: string): OpenAPIV3_1.ReferenceObject;
283
+ }
284
+ ```
285
+
286
+ ## Integration with Auth-Aware Fetcher
287
+
288
+ The generated `endpointAuth` map enables automatic auth handling:
289
+
290
+ ```typescript
291
+ // packages/client/src/auth-fetcher.ts
292
+ import { endpointAuth, securitySchemes, type paths } from './openapi';
293
+ import { TokenClient } from '@geekmidas/auth/client';
294
+
295
+ export function createAuthAwareFetcher<Paths>(options: AuthFetcherOptions) {
296
+ const { tokenClient, awsSigner, apiKeyProvider } = options;
297
+
298
+ return async <T extends TypedEndpoint<Paths>>(
299
+ endpoint: T,
300
+ config?: FilteredRequestConfig<Paths, T>,
301
+ ) => {
302
+ const authScheme = endpointAuth[endpoint as keyof typeof endpointAuth];
303
+ let headers: Record<string, string> = {};
304
+
305
+ if (authScheme) {
306
+ const scheme = securitySchemes[authScheme];
307
+
308
+ switch (scheme.type) {
309
+ case 'http':
310
+ if (scheme.scheme === 'bearer') {
311
+ headers = await tokenClient.createValidAuthHeaders();
312
+ }
313
+ break;
314
+ case 'apiKey':
315
+ if (scheme['x-amazon-apigateway-authtype'] === 'awsSigv4') {
316
+ headers = await awsSigner.sign(endpoint, config);
317
+ } else {
318
+ headers[scheme.name] = await apiKeyProvider.getKey();
319
+ }
320
+ break;
321
+ }
322
+ }
323
+
324
+ return baseFetcher.request(endpoint, {
325
+ ...config,
326
+ headers: { ...headers, ...config?.headers },
327
+ });
328
+ };
329
+ }
330
+ ```
331
+
332
+ ## File Structure Changes
333
+
334
+ ```
335
+ packages/
336
+ ├── cli/
337
+ │ └── src/
338
+ │ ├── openapi.ts # Updated: add --ts support
339
+ │ └── generators/
340
+ │ └── OpenApiTsGenerator.ts # New: TypeScript generation
341
+ ├── schema/
342
+ │ └── src/
343
+ │ └── openapi.ts # Updated: security scheme support
344
+ └── client/
345
+ └── src/
346
+ └── auth-fetcher.ts # New: auth-aware fetcher
347
+ ```
348
+
349
+ ## Migration Path
350
+
351
+ ### Breaking Change
352
+
353
+ TypeScript is now the default output format. Existing JSON users must add `--json` flag.
354
+
355
+ ### Existing JSON Users
356
+
357
+ ```bash
358
+ # Before
359
+ gkm openapi --output ./openapi.json
360
+
361
+ # After (explicit JSON)
362
+ gkm openapi --json --output ./openapi.json
363
+ ```
364
+
365
+ ### Adopting TypeScript Output (New Default)
366
+
367
+ 1. Run `gkm openapi --output ./src/api/openapi.ts`
368
+ 2. Update imports from `./openapi-types` to `./openapi`
369
+ 3. Use `createAuthAwareFetcher` instead of `createTypedFetcher`
370
+
371
+ ## Testing Strategy
372
+
373
+ 1. **Unit Tests**
374
+ - Authorizer → security scheme mapping
375
+ - Schema extraction from StandardSchema
376
+ - TypeScript code generation
377
+
378
+ 2. **Integration Tests**
379
+ - Full endpoint → TypeScript output pipeline
380
+ - Generated code compiles without errors
381
+ - Auth map matches endpoint authorizers
382
+
383
+ 3. **E2E Tests**
384
+ - Auth-aware fetcher selects correct auth per endpoint
385
+ - Bearer endpoints get JWT headers
386
+ - IAM endpoints get SigV4 signatures
387
+ - Public endpoints get no auth headers
388
+
389
+ ## Open Questions
390
+
391
+ 1. **Schema naming conflicts** - What if two endpoints have the same operationId?
392
+ - Proposal: Append route hash suffix if conflict detected
393
+
394
+ 2. **Circular schema references** - How to handle `User { friends: User[] }`?
395
+ - Proposal: Use TypeScript `interface` declarations which support self-reference
396
+
397
+ 3. **Optional vs required auth** - Some endpoints allow optional auth (return more data if authenticated)
398
+ - Proposal: Add `optional` auth type: `'bearer' | 'bearer?' | null`
399
+
400
+ ## Timeline
401
+
402
+ | Phase | Description | Estimate |
403
+ |-------|-------------|----------|
404
+ | 1 | Schema package updates (ComponentCollector) | - |
405
+ | 2 | CLI TypeScript generator | - |
406
+ | 3 | Client auth-aware fetcher | - |
407
+ | 4 | Documentation and examples | - |
408
+ | 5 | Testing and refinement | - |
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@geekmidas/cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
+ "description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
4
5
  "private": false,
5
6
  "type": "module",
6
7
  "exports": {
@@ -44,11 +45,17 @@
44
45
  "@geekmidas/testkit": "0.0.17"
45
46
  },
46
47
  "peerDependencies": {
47
- "@geekmidas/constructs": "~0.0.22",
48
- "@geekmidas/envkit": "~0.0.8",
48
+ "@geekmidas/constructs": "~0.1.0",
49
+ "@geekmidas/envkit": "~0.1.0",
49
50
  "@geekmidas/logger": "~0.0.1",
51
+ "@geekmidas/telescope": "~0.0.1",
50
52
  "@geekmidas/schema": "~0.0.2"
51
53
  },
54
+ "peerDependenciesMeta": {
55
+ "@geekmidas/telescope": {
56
+ "optional": true
57
+ }
58
+ },
52
59
  "scripts": {
53
60
  "ts": "tsc --noEmit --skipLibCheck src/**/*.ts",
54
61
  "test": "vitest",