@geekmidas/cli 0.2.3 → 0.3.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/dist/{CronGenerator-DXRfHQcV.mjs → CronGenerator-Bh26MaNA.mjs} +2 -1
- package/dist/CronGenerator-Bh26MaNA.mjs.map +1 -0
- package/dist/{CronGenerator-1PflEYe2.cjs → CronGenerator-C6MF8rlG.cjs} +2 -1
- package/dist/CronGenerator-C6MF8rlG.cjs.map +1 -0
- package/dist/{EndpointGenerator-BbGrDiCP.cjs → EndpointGenerator-C73wNoih.cjs} +12 -5
- package/dist/EndpointGenerator-C73wNoih.cjs.map +1 -0
- package/dist/{EndpointGenerator-BmZ9BxbO.mjs → EndpointGenerator-CWh18d92.mjs} +12 -5
- package/dist/EndpointGenerator-CWh18d92.mjs.map +1 -0
- package/dist/{FunctionGenerator-DOEB_yPh.mjs → FunctionGenerator-BNE_GC7N.mjs} +2 -1
- package/dist/FunctionGenerator-BNE_GC7N.mjs.map +1 -0
- package/dist/{FunctionGenerator-Clw64SwQ.cjs → FunctionGenerator-FgZUTd8L.cjs} +2 -1
- package/dist/FunctionGenerator-FgZUTd8L.cjs.map +1 -0
- package/dist/{SubscriberGenerator-CB-NHtZW.cjs → SubscriberGenerator-Bd-a7aiw.cjs} +2 -1
- package/dist/SubscriberGenerator-Bd-a7aiw.cjs.map +1 -0
- package/dist/{SubscriberGenerator-Cuu4co3-.mjs → SubscriberGenerator-Dnlj_1FK.mjs} +2 -1
- package/dist/SubscriberGenerator-Dnlj_1FK.mjs.map +1 -0
- package/dist/build/index.cjs +6 -6
- package/dist/build/index.mjs +6 -6
- package/dist/build/manifests.cjs +3 -2
- package/dist/build/manifests.mjs +2 -2
- package/dist/{build-zpABVsc0.mjs → build-C6uEGRj8.mjs} +22 -20
- package/dist/build-C6uEGRj8.mjs.map +1 -0
- package/dist/{build-Ajg356_5.cjs → build-CBYBPZpC.cjs} +21 -19
- package/dist/build-CBYBPZpC.cjs.map +1 -0
- package/dist/dev/index.cjs +13 -0
- package/dist/dev/index.mjs +11 -0
- package/dist/dev-DbtyToc7.cjs +259 -0
- package/dist/dev-DbtyToc7.cjs.map +1 -0
- package/dist/dev-DnGYXuMn.mjs +241 -0
- package/dist/dev-DnGYXuMn.mjs.map +1 -0
- package/dist/generators/CronGenerator.cjs +1 -1
- package/dist/generators/CronGenerator.mjs +1 -1
- package/dist/generators/EndpointGenerator.cjs +1 -1
- package/dist/generators/EndpointGenerator.mjs +1 -1
- package/dist/generators/FunctionGenerator.cjs +1 -1
- package/dist/generators/FunctionGenerator.mjs +1 -1
- package/dist/generators/SubscriberGenerator.cjs +1 -1
- package/dist/generators/SubscriberGenerator.mjs +1 -1
- package/dist/generators/index.cjs +4 -4
- package/dist/generators/index.mjs +4 -4
- package/dist/index.cjs +34 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +34 -13
- 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-BQx3_JbM.mjs → openapi-BTHbPrxS.mjs} +2 -2
- package/dist/{openapi-BQx3_JbM.mjs.map → openapi-BTHbPrxS.mjs.map} +1 -1
- package/dist/{openapi-CMLr04cz.cjs → openapi-CewcfoRH.cjs} +2 -2
- package/dist/{openapi-CMLr04cz.cjs.map → openapi-CewcfoRH.cjs.map} +1 -1
- package/dist/{openapi-react-query-Dvjqx_Eo.cjs → openapi-react-query-D9Z7lh0p.cjs} +1 -1
- package/dist/{openapi-react-query-Dvjqx_Eo.cjs.map → openapi-react-query-D9Z7lh0p.cjs.map} +1 -1
- package/dist/{openapi-react-query-DbrWwQzb.mjs → openapi-react-query-MEBlYIM1.mjs} +1 -1
- package/dist/{openapi-react-query-DbrWwQzb.mjs.map → openapi-react-query-MEBlYIM1.mjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +2 -2
- package/dist/openapi.mjs +2 -2
- package/docs/manifest-refactor-design.md +287 -0
- package/package.json +12 -6
- 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/dev/__tests__/index.spec.ts +208 -0
- package/src/dev/index.ts +377 -0
- package/src/generators/CronGenerator.ts +8 -3
- package/src/generators/EndpointGenerator.ts +94 -6
- package/src/generators/FunctionGenerator.ts +21 -3
- package/src/generators/SubscriberGenerator.ts +1 -0
- package/src/generators/__tests__/SubscriberGenerator.spec.ts +12 -9
- package/src/index.ts +27 -0
- package/src/types.ts +3 -7
- package/dist/CronGenerator-1PflEYe2.cjs.map +0 -1
- package/dist/CronGenerator-DXRfHQcV.mjs.map +0 -1
- package/dist/EndpointGenerator-BbGrDiCP.cjs.map +0 -1
- package/dist/EndpointGenerator-BmZ9BxbO.mjs.map +0 -1
- package/dist/FunctionGenerator-Clw64SwQ.cjs.map +0 -1
- package/dist/FunctionGenerator-DOEB_yPh.mjs.map +0 -1
- package/dist/SubscriberGenerator-CB-NHtZW.cjs.map +0 -1
- package/dist/SubscriberGenerator-Cuu4co3-.mjs.map +0 -1
- package/dist/build-Ajg356_5.cjs.map +0 -1
- package/dist/build-zpABVsc0.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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi-react-query-Dvjqx_Eo.cjs","names":["exec","options: ReactQueryOptions","spec: OpenAPISpec","operations: OperationInfo[]","operation: any","schema: any","apiName: string","op: OperationInfo","paramParts: string[]","exports","str: string"],"sources":["../src/openapi-react-query.ts"],"sourcesContent":["#!/usr/bin/env -S npx tsx\n\nimport { exec } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execAsync = promisify(exec);\n\ninterface ReactQueryOptions {\n input?: string;\n output?: string;\n name?: string;\n}\n\ninterface OpenAPISpec {\n openapi: string;\n info?: {\n title?: string;\n version?: string;\n };\n paths: Record<string, Record<string, any>>;\n}\n\nexport async function generateReactQueryCommand(\n options: ReactQueryOptions = {},\n): Promise<void> {\n const logger = console;\n\n try {\n // Read OpenAPI spec\n const inputPath = options.input || join(process.cwd(), 'openapi.json');\n\n if (!existsSync(inputPath)) {\n throw new Error(\n `OpenAPI spec not found at ${inputPath}. Run 'npx @geekmidas/cli openapi' first.`,\n );\n }\n\n const specContent = await readFile(inputPath, 'utf-8');\n const spec: OpenAPISpec = JSON.parse(specContent);\n\n // Generate TypeScript types from OpenAPI spec\n const outputDir = dirname(\n options.output || join(process.cwd(), 'src', 'api', 'hooks.ts'),\n );\n const typesPath = join(outputDir, 'openapi-types.d.ts');\n\n logger.log('Generating TypeScript types from OpenAPI spec...');\n\n try {\n // Use npx to run openapi-typescript\n await execAsync(\n `npx openapi-typescript \"${inputPath}\" -o \"${typesPath}\"`,\n { cwd: process.cwd() },\n );\n logger.log(`TypeScript types generated: ${typesPath}`);\n } catch (error) {\n logger.warn(\n 'Could not generate types with openapi-typescript. Install it for better type inference.',\n );\n logger.warn('Run: npm install -D openapi-typescript');\n\n // Generate basic types file\n await mkdir(dirname(typesPath), { recursive: true });\n await writeFile(\n typesPath,\n `// Auto-generated placeholder types\nexport interface paths {\n [path: string]: {\n [method: string]: {\n operationId?: string;\n parameters?: any;\n requestBody?: any;\n responses?: any;\n };\n };\n}\n`,\n );\n }\n\n // Extract operation info\n const operations = extractOperations(spec);\n\n // Generate TypeScript code\n const code = generateReactQueryCode(\n spec,\n operations,\n options.name || 'API',\n );\n\n // Write output\n const outputPath =\n options.output || join(process.cwd(), 'src', 'api', 'hooks.ts');\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, code);\n\n logger.log(`React Query hooks generated: ${outputPath}`);\n logger.log(`Generated ${operations.length} hooks`);\n } catch (error) {\n throw new Error(\n `React Query generation failed: ${(error as Error).message}`,\n );\n }\n}\n\ninterface OperationInfo {\n operationId: string;\n path: string;\n method: string;\n endpoint: string; // Full endpoint like 'GET /users/{id}'\n parameters?: Array<{ name: string; in: string; required?: boolean }>;\n requestBody?: boolean;\n responseType?: string;\n}\n\nfunction extractOperations(spec: OpenAPISpec): OperationInfo[] {\n const operations: OperationInfo[] = [];\n\n Object.entries(spec.paths).forEach(([path, methods]) => {\n Object.entries(methods).forEach(([method, operation]) => {\n if (operation.operationId) {\n operations.push({\n operationId: operation.operationId,\n path,\n method: method.toUpperCase(),\n endpoint: `${method.toUpperCase()} ${path}`,\n parameters: operation.parameters,\n requestBody: !!operation.requestBody,\n responseType: extractResponseType(operation),\n });\n }\n });\n });\n\n return operations;\n}\n\nfunction extractResponseType(operation: any): string {\n const responses = operation.responses;\n if (!responses) return 'unknown';\n\n const successResponse = responses['200'] || responses['201'];\n if (!successResponse?.content?.['application/json']?.schema) {\n return 'unknown';\n }\n\n // Basic type inference from schema\n const schema = successResponse.content['application/json'].schema;\n return schemaToTypeString(schema);\n}\n\nfunction schemaToTypeString(schema: any): string {\n if (!schema) return 'unknown';\n\n switch (schema.type) {\n case 'string':\n return 'string';\n case 'number':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'array':\n return `Array<${schemaToTypeString(schema.items)}>`;\n case 'object':\n if (schema.properties) {\n const props = Object.entries(schema.properties)\n .map(\n ([key, value]: [string, any]) =>\n `${key}: ${schemaToTypeString(value)}`,\n )\n .join('; ');\n return `{ ${props} }`;\n }\n return 'Record<string, unknown>';\n default:\n return 'unknown';\n }\n}\n\nfunction generateReactQueryCode(\n spec: OpenAPISpec,\n operations: OperationInfo[],\n apiName: string,\n): string {\n const imports = `import { createTypedQueryClient } from '@geekmidas/client';\nimport type { paths } from './openapi-types';\n\n// Create typed query client\nexport const ${apiName.toLowerCase()} = createTypedQueryClient<paths>({\n baseURL: process.env.NEXT_PUBLIC_API_URL || '/api',\n});\n\n// Export individual hooks for better DX\n`;\n\n const queryHooks = operations\n .filter((op) => op.method === 'GET')\n .map((op) => generateQueryHook(op, apiName))\n .join('\\n\\n');\n\n const mutationHooks = operations\n .filter((op) => op.method !== 'GET')\n .map((op) => generateMutationHook(op, apiName))\n .join('\\n\\n');\n\n const typeExports = generateTypeExports(operations);\n\n return `${imports}\n// Query Hooks\n${queryHooks}\n\n// Mutation Hooks\n${mutationHooks}\n\n// Type exports for convenience\n${typeExports}\n\n// Re-export the api for advanced usage\nexport { ${apiName.toLowerCase()} };\n`;\n}\n\nfunction generateQueryHook(op: OperationInfo, apiName: string): string {\n const hookName = `use${capitalize(op.operationId)}`;\n const endpoint = op.endpoint;\n const hasParams = op.parameters?.some((p) => p.in === 'path');\n const hasQuery = op.parameters?.some((p) => p.in === 'query');\n\n // Generate properly typed hook\n let params = '';\n let args = '';\n\n if (hasParams || hasQuery) {\n const paramParts: string[] = [];\n if (hasParams) {\n const pathParams =\n op.parameters?.filter((p) => p.in === 'path').map((p) => p.name) || [];\n paramParts.push(\n `params: { ${pathParams.map((p) => `${p}: string`).join('; ')} }`,\n );\n }\n if (hasQuery) {\n paramParts.push(`query?: Record<string, any>`);\n }\n params = `config: { ${paramParts.join('; ')} }, `;\n args = ', config';\n }\n\n return `export const ${hookName} = (\n ${params}options?: Parameters<typeof ${apiName.toLowerCase()}.useQuery>[2]\n) => {\n return ${apiName.toLowerCase()}.useQuery('${endpoint}' as any${args}, options);\n};`;\n}\n\nfunction generateMutationHook(op: OperationInfo, apiName: string): string {\n const hookName = `use${capitalize(op.operationId)}`;\n const endpoint = op.endpoint;\n\n return `export const ${hookName} = (\n options?: Parameters<typeof ${apiName.toLowerCase()}.useMutation>[1]\n) => {\n return ${apiName.toLowerCase()}.useMutation('${endpoint}' as any, options);\n};`;\n}\n\nfunction generateTypeExports(operations: OperationInfo[]): string {\n const exports = operations.map((op) => {\n const typeName = capitalize(op.operationId);\n const isQuery = op.method === 'GET';\n\n if (isQuery) {\n return `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['data']>>;`;\n } else {\n return `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['mutateAsync']>>;`;\n }\n });\n\n return exports.join('\\n');\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"openapi-react-query-D9Z7lh0p.cjs","names":["exec","options: ReactQueryOptions","spec: OpenAPISpec","operations: OperationInfo[]","operation: any","schema: any","apiName: string","op: OperationInfo","paramParts: string[]","exports","str: string"],"sources":["../src/openapi-react-query.ts"],"sourcesContent":["#!/usr/bin/env -S npx tsx\n\nimport { exec } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execAsync = promisify(exec);\n\ninterface ReactQueryOptions {\n input?: string;\n output?: string;\n name?: string;\n}\n\ninterface OpenAPISpec {\n openapi: string;\n info?: {\n title?: string;\n version?: string;\n };\n paths: Record<string, Record<string, any>>;\n}\n\nexport async function generateReactQueryCommand(\n options: ReactQueryOptions = {},\n): Promise<void> {\n const logger = console;\n\n try {\n // Read OpenAPI spec\n const inputPath = options.input || join(process.cwd(), 'openapi.json');\n\n if (!existsSync(inputPath)) {\n throw new Error(\n `OpenAPI spec not found at ${inputPath}. Run 'npx @geekmidas/cli openapi' first.`,\n );\n }\n\n const specContent = await readFile(inputPath, 'utf-8');\n const spec: OpenAPISpec = JSON.parse(specContent);\n\n // Generate TypeScript types from OpenAPI spec\n const outputDir = dirname(\n options.output || join(process.cwd(), 'src', 'api', 'hooks.ts'),\n );\n const typesPath = join(outputDir, 'openapi-types.d.ts');\n\n logger.log('Generating TypeScript types from OpenAPI spec...');\n\n try {\n // Use npx to run openapi-typescript\n await execAsync(\n `npx openapi-typescript \"${inputPath}\" -o \"${typesPath}\"`,\n { cwd: process.cwd() },\n );\n logger.log(`TypeScript types generated: ${typesPath}`);\n } catch (error) {\n logger.warn(\n 'Could not generate types with openapi-typescript. Install it for better type inference.',\n );\n logger.warn('Run: npm install -D openapi-typescript');\n\n // Generate basic types file\n await mkdir(dirname(typesPath), { recursive: true });\n await writeFile(\n typesPath,\n `// Auto-generated placeholder types\nexport interface paths {\n [path: string]: {\n [method: string]: {\n operationId?: string;\n parameters?: any;\n requestBody?: any;\n responses?: any;\n };\n };\n}\n`,\n );\n }\n\n // Extract operation info\n const operations = extractOperations(spec);\n\n // Generate TypeScript code\n const code = generateReactQueryCode(\n spec,\n operations,\n options.name || 'API',\n );\n\n // Write output\n const outputPath =\n options.output || join(process.cwd(), 'src', 'api', 'hooks.ts');\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, code);\n\n logger.log(`React Query hooks generated: ${outputPath}`);\n logger.log(`Generated ${operations.length} hooks`);\n } catch (error) {\n throw new Error(\n `React Query generation failed: ${(error as Error).message}`,\n );\n }\n}\n\ninterface OperationInfo {\n operationId: string;\n path: string;\n method: string;\n endpoint: string; // Full endpoint like 'GET /users/{id}'\n parameters?: Array<{ name: string; in: string; required?: boolean }>;\n requestBody?: boolean;\n responseType?: string;\n}\n\nfunction extractOperations(spec: OpenAPISpec): OperationInfo[] {\n const operations: OperationInfo[] = [];\n\n Object.entries(spec.paths).forEach(([path, methods]) => {\n Object.entries(methods).forEach(([method, operation]) => {\n if (operation.operationId) {\n operations.push({\n operationId: operation.operationId,\n path,\n method: method.toUpperCase(),\n endpoint: `${method.toUpperCase()} ${path}`,\n parameters: operation.parameters,\n requestBody: !!operation.requestBody,\n responseType: extractResponseType(operation),\n });\n }\n });\n });\n\n return operations;\n}\n\nfunction extractResponseType(operation: any): string {\n const responses = operation.responses;\n if (!responses) return 'unknown';\n\n const successResponse = responses['200'] || responses['201'];\n if (!successResponse?.content?.['application/json']?.schema) {\n return 'unknown';\n }\n\n // Basic type inference from schema\n const schema = successResponse.content['application/json'].schema;\n return schemaToTypeString(schema);\n}\n\nfunction schemaToTypeString(schema: any): string {\n if (!schema) return 'unknown';\n\n switch (schema.type) {\n case 'string':\n return 'string';\n case 'number':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'array':\n return `Array<${schemaToTypeString(schema.items)}>`;\n case 'object':\n if (schema.properties) {\n const props = Object.entries(schema.properties)\n .map(\n ([key, value]: [string, any]) =>\n `${key}: ${schemaToTypeString(value)}`,\n )\n .join('; ');\n return `{ ${props} }`;\n }\n return 'Record<string, unknown>';\n default:\n return 'unknown';\n }\n}\n\nfunction generateReactQueryCode(\n spec: OpenAPISpec,\n operations: OperationInfo[],\n apiName: string,\n): string {\n const imports = `import { createTypedQueryClient } from '@geekmidas/client';\nimport type { paths } from './openapi-types';\n\n// Create typed query client\nexport const ${apiName.toLowerCase()} = createTypedQueryClient<paths>({\n baseURL: process.env.NEXT_PUBLIC_API_URL || '/api',\n});\n\n// Export individual hooks for better DX\n`;\n\n const queryHooks = operations\n .filter((op) => op.method === 'GET')\n .map((op) => generateQueryHook(op, apiName))\n .join('\\n\\n');\n\n const mutationHooks = operations\n .filter((op) => op.method !== 'GET')\n .map((op) => generateMutationHook(op, apiName))\n .join('\\n\\n');\n\n const typeExports = generateTypeExports(operations);\n\n return `${imports}\n// Query Hooks\n${queryHooks}\n\n// Mutation Hooks\n${mutationHooks}\n\n// Type exports for convenience\n${typeExports}\n\n// Re-export the api for advanced usage\nexport { ${apiName.toLowerCase()} };\n`;\n}\n\nfunction generateQueryHook(op: OperationInfo, apiName: string): string {\n const hookName = `use${capitalize(op.operationId)}`;\n const endpoint = op.endpoint;\n const hasParams = op.parameters?.some((p) => p.in === 'path');\n const hasQuery = op.parameters?.some((p) => p.in === 'query');\n\n // Generate properly typed hook\n let params = '';\n let args = '';\n\n if (hasParams || hasQuery) {\n const paramParts: string[] = [];\n if (hasParams) {\n const pathParams =\n op.parameters?.filter((p) => p.in === 'path').map((p) => p.name) || [];\n paramParts.push(\n `params: { ${pathParams.map((p) => `${p}: string`).join('; ')} }`,\n );\n }\n if (hasQuery) {\n paramParts.push(`query?: Record<string, any>`);\n }\n params = `config: { ${paramParts.join('; ')} }, `;\n args = ', config';\n }\n\n return `export const ${hookName} = (\n ${params}options?: Parameters<typeof ${apiName.toLowerCase()}.useQuery>[2]\n) => {\n return ${apiName.toLowerCase()}.useQuery('${endpoint}' as any${args}, options);\n};`;\n}\n\nfunction generateMutationHook(op: OperationInfo, apiName: string): string {\n const hookName = `use${capitalize(op.operationId)}`;\n const endpoint = op.endpoint;\n\n return `export const ${hookName} = (\n options?: Parameters<typeof ${apiName.toLowerCase()}.useMutation>[1]\n) => {\n return ${apiName.toLowerCase()}.useMutation('${endpoint}' as any, options);\n};`;\n}\n\nfunction generateTypeExports(operations: OperationInfo[]): string {\n const exports = operations.map((op) => {\n const typeName = capitalize(op.operationId);\n const isQuery = op.method === 'GET';\n\n if (isQuery) {\n return `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['data']>>;`;\n } else {\n return `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['mutateAsync']>>;`;\n }\n });\n\n return exports.join('\\n');\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi-react-query-DbrWwQzb.mjs","names":["options: ReactQueryOptions","spec: OpenAPISpec","operations: OperationInfo[]","operation: any","schema: any","apiName: string","op: OperationInfo","paramParts: string[]","str: string"],"sources":["../src/openapi-react-query.ts"],"sourcesContent":["#!/usr/bin/env -S npx tsx\n\nimport { exec } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execAsync = promisify(exec);\n\ninterface ReactQueryOptions {\n input?: string;\n output?: string;\n name?: string;\n}\n\ninterface OpenAPISpec {\n openapi: string;\n info?: {\n title?: string;\n version?: string;\n };\n paths: Record<string, Record<string, any>>;\n}\n\nexport async function generateReactQueryCommand(\n options: ReactQueryOptions = {},\n): Promise<void> {\n const logger = console;\n\n try {\n // Read OpenAPI spec\n const inputPath = options.input || join(process.cwd(), 'openapi.json');\n\n if (!existsSync(inputPath)) {\n throw new Error(\n `OpenAPI spec not found at ${inputPath}. Run 'npx @geekmidas/cli openapi' first.`,\n );\n }\n\n const specContent = await readFile(inputPath, 'utf-8');\n const spec: OpenAPISpec = JSON.parse(specContent);\n\n // Generate TypeScript types from OpenAPI spec\n const outputDir = dirname(\n options.output || join(process.cwd(), 'src', 'api', 'hooks.ts'),\n );\n const typesPath = join(outputDir, 'openapi-types.d.ts');\n\n logger.log('Generating TypeScript types from OpenAPI spec...');\n\n try {\n // Use npx to run openapi-typescript\n await execAsync(\n `npx openapi-typescript \"${inputPath}\" -o \"${typesPath}\"`,\n { cwd: process.cwd() },\n );\n logger.log(`TypeScript types generated: ${typesPath}`);\n } catch (error) {\n logger.warn(\n 'Could not generate types with openapi-typescript. Install it for better type inference.',\n );\n logger.warn('Run: npm install -D openapi-typescript');\n\n // Generate basic types file\n await mkdir(dirname(typesPath), { recursive: true });\n await writeFile(\n typesPath,\n `// Auto-generated placeholder types\nexport interface paths {\n [path: string]: {\n [method: string]: {\n operationId?: string;\n parameters?: any;\n requestBody?: any;\n responses?: any;\n };\n };\n}\n`,\n );\n }\n\n // Extract operation info\n const operations = extractOperations(spec);\n\n // Generate TypeScript code\n const code = generateReactQueryCode(\n spec,\n operations,\n options.name || 'API',\n );\n\n // Write output\n const outputPath =\n options.output || join(process.cwd(), 'src', 'api', 'hooks.ts');\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, code);\n\n logger.log(`React Query hooks generated: ${outputPath}`);\n logger.log(`Generated ${operations.length} hooks`);\n } catch (error) {\n throw new Error(\n `React Query generation failed: ${(error as Error).message}`,\n );\n }\n}\n\ninterface OperationInfo {\n operationId: string;\n path: string;\n method: string;\n endpoint: string; // Full endpoint like 'GET /users/{id}'\n parameters?: Array<{ name: string; in: string; required?: boolean }>;\n requestBody?: boolean;\n responseType?: string;\n}\n\nfunction extractOperations(spec: OpenAPISpec): OperationInfo[] {\n const operations: OperationInfo[] = [];\n\n Object.entries(spec.paths).forEach(([path, methods]) => {\n Object.entries(methods).forEach(([method, operation]) => {\n if (operation.operationId) {\n operations.push({\n operationId: operation.operationId,\n path,\n method: method.toUpperCase(),\n endpoint: `${method.toUpperCase()} ${path}`,\n parameters: operation.parameters,\n requestBody: !!operation.requestBody,\n responseType: extractResponseType(operation),\n });\n }\n });\n });\n\n return operations;\n}\n\nfunction extractResponseType(operation: any): string {\n const responses = operation.responses;\n if (!responses) return 'unknown';\n\n const successResponse = responses['200'] || responses['201'];\n if (!successResponse?.content?.['application/json']?.schema) {\n return 'unknown';\n }\n\n // Basic type inference from schema\n const schema = successResponse.content['application/json'].schema;\n return schemaToTypeString(schema);\n}\n\nfunction schemaToTypeString(schema: any): string {\n if (!schema) return 'unknown';\n\n switch (schema.type) {\n case 'string':\n return 'string';\n case 'number':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'array':\n return `Array<${schemaToTypeString(schema.items)}>`;\n case 'object':\n if (schema.properties) {\n const props = Object.entries(schema.properties)\n .map(\n ([key, value]: [string, any]) =>\n `${key}: ${schemaToTypeString(value)}`,\n )\n .join('; ');\n return `{ ${props} }`;\n }\n return 'Record<string, unknown>';\n default:\n return 'unknown';\n }\n}\n\nfunction generateReactQueryCode(\n spec: OpenAPISpec,\n operations: OperationInfo[],\n apiName: string,\n): string {\n const imports = `import { createTypedQueryClient } from '@geekmidas/client';\nimport type { paths } from './openapi-types';\n\n// Create typed query client\nexport const ${apiName.toLowerCase()} = createTypedQueryClient<paths>({\n baseURL: process.env.NEXT_PUBLIC_API_URL || '/api',\n});\n\n// Export individual hooks for better DX\n`;\n\n const queryHooks = operations\n .filter((op) => op.method === 'GET')\n .map((op) => generateQueryHook(op, apiName))\n .join('\\n\\n');\n\n const mutationHooks = operations\n .filter((op) => op.method !== 'GET')\n .map((op) => generateMutationHook(op, apiName))\n .join('\\n\\n');\n\n const typeExports = generateTypeExports(operations);\n\n return `${imports}\n// Query Hooks\n${queryHooks}\n\n// Mutation Hooks\n${mutationHooks}\n\n// Type exports for convenience\n${typeExports}\n\n// Re-export the api for advanced usage\nexport { ${apiName.toLowerCase()} };\n`;\n}\n\nfunction generateQueryHook(op: OperationInfo, apiName: string): string {\n const hookName = `use${capitalize(op.operationId)}`;\n const endpoint = op.endpoint;\n const hasParams = op.parameters?.some((p) => p.in === 'path');\n const hasQuery = op.parameters?.some((p) => p.in === 'query');\n\n // Generate properly typed hook\n let params = '';\n let args = '';\n\n if (hasParams || hasQuery) {\n const paramParts: string[] = [];\n if (hasParams) {\n const pathParams =\n op.parameters?.filter((p) => p.in === 'path').map((p) => p.name) || [];\n paramParts.push(\n `params: { ${pathParams.map((p) => `${p}: string`).join('; ')} }`,\n );\n }\n if (hasQuery) {\n paramParts.push(`query?: Record<string, any>`);\n }\n params = `config: { ${paramParts.join('; ')} }, `;\n args = ', config';\n }\n\n return `export const ${hookName} = (\n ${params}options?: Parameters<typeof ${apiName.toLowerCase()}.useQuery>[2]\n) => {\n return ${apiName.toLowerCase()}.useQuery('${endpoint}' as any${args}, options);\n};`;\n}\n\nfunction generateMutationHook(op: OperationInfo, apiName: string): string {\n const hookName = `use${capitalize(op.operationId)}`;\n const endpoint = op.endpoint;\n\n return `export const ${hookName} = (\n options?: Parameters<typeof ${apiName.toLowerCase()}.useMutation>[1]\n) => {\n return ${apiName.toLowerCase()}.useMutation('${endpoint}' as any, options);\n};`;\n}\n\nfunction generateTypeExports(operations: OperationInfo[]): string {\n const exports = operations.map((op) => {\n const typeName = capitalize(op.operationId);\n const isQuery = op.method === 'GET';\n\n if (isQuery) {\n return `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['data']>>;`;\n } else {\n return `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['mutateAsync']>>;`;\n }\n });\n\n return exports.join('\\n');\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"openapi-react-query-MEBlYIM1.mjs","names":["options: ReactQueryOptions","spec: OpenAPISpec","operations: OperationInfo[]","operation: any","schema: any","apiName: string","op: OperationInfo","paramParts: string[]","str: string"],"sources":["../src/openapi-react-query.ts"],"sourcesContent":["#!/usr/bin/env -S npx tsx\n\nimport { exec } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execAsync = promisify(exec);\n\ninterface ReactQueryOptions {\n input?: string;\n output?: string;\n name?: string;\n}\n\ninterface OpenAPISpec {\n openapi: string;\n info?: {\n title?: string;\n version?: string;\n };\n paths: Record<string, Record<string, any>>;\n}\n\nexport async function generateReactQueryCommand(\n options: ReactQueryOptions = {},\n): Promise<void> {\n const logger = console;\n\n try {\n // Read OpenAPI spec\n const inputPath = options.input || join(process.cwd(), 'openapi.json');\n\n if (!existsSync(inputPath)) {\n throw new Error(\n `OpenAPI spec not found at ${inputPath}. Run 'npx @geekmidas/cli openapi' first.`,\n );\n }\n\n const specContent = await readFile(inputPath, 'utf-8');\n const spec: OpenAPISpec = JSON.parse(specContent);\n\n // Generate TypeScript types from OpenAPI spec\n const outputDir = dirname(\n options.output || join(process.cwd(), 'src', 'api', 'hooks.ts'),\n );\n const typesPath = join(outputDir, 'openapi-types.d.ts');\n\n logger.log('Generating TypeScript types from OpenAPI spec...');\n\n try {\n // Use npx to run openapi-typescript\n await execAsync(\n `npx openapi-typescript \"${inputPath}\" -o \"${typesPath}\"`,\n { cwd: process.cwd() },\n );\n logger.log(`TypeScript types generated: ${typesPath}`);\n } catch (error) {\n logger.warn(\n 'Could not generate types with openapi-typescript. Install it for better type inference.',\n );\n logger.warn('Run: npm install -D openapi-typescript');\n\n // Generate basic types file\n await mkdir(dirname(typesPath), { recursive: true });\n await writeFile(\n typesPath,\n `// Auto-generated placeholder types\nexport interface paths {\n [path: string]: {\n [method: string]: {\n operationId?: string;\n parameters?: any;\n requestBody?: any;\n responses?: any;\n };\n };\n}\n`,\n );\n }\n\n // Extract operation info\n const operations = extractOperations(spec);\n\n // Generate TypeScript code\n const code = generateReactQueryCode(\n spec,\n operations,\n options.name || 'API',\n );\n\n // Write output\n const outputPath =\n options.output || join(process.cwd(), 'src', 'api', 'hooks.ts');\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, code);\n\n logger.log(`React Query hooks generated: ${outputPath}`);\n logger.log(`Generated ${operations.length} hooks`);\n } catch (error) {\n throw new Error(\n `React Query generation failed: ${(error as Error).message}`,\n );\n }\n}\n\ninterface OperationInfo {\n operationId: string;\n path: string;\n method: string;\n endpoint: string; // Full endpoint like 'GET /users/{id}'\n parameters?: Array<{ name: string; in: string; required?: boolean }>;\n requestBody?: boolean;\n responseType?: string;\n}\n\nfunction extractOperations(spec: OpenAPISpec): OperationInfo[] {\n const operations: OperationInfo[] = [];\n\n Object.entries(spec.paths).forEach(([path, methods]) => {\n Object.entries(methods).forEach(([method, operation]) => {\n if (operation.operationId) {\n operations.push({\n operationId: operation.operationId,\n path,\n method: method.toUpperCase(),\n endpoint: `${method.toUpperCase()} ${path}`,\n parameters: operation.parameters,\n requestBody: !!operation.requestBody,\n responseType: extractResponseType(operation),\n });\n }\n });\n });\n\n return operations;\n}\n\nfunction extractResponseType(operation: any): string {\n const responses = operation.responses;\n if (!responses) return 'unknown';\n\n const successResponse = responses['200'] || responses['201'];\n if (!successResponse?.content?.['application/json']?.schema) {\n return 'unknown';\n }\n\n // Basic type inference from schema\n const schema = successResponse.content['application/json'].schema;\n return schemaToTypeString(schema);\n}\n\nfunction schemaToTypeString(schema: any): string {\n if (!schema) return 'unknown';\n\n switch (schema.type) {\n case 'string':\n return 'string';\n case 'number':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'array':\n return `Array<${schemaToTypeString(schema.items)}>`;\n case 'object':\n if (schema.properties) {\n const props = Object.entries(schema.properties)\n .map(\n ([key, value]: [string, any]) =>\n `${key}: ${schemaToTypeString(value)}`,\n )\n .join('; ');\n return `{ ${props} }`;\n }\n return 'Record<string, unknown>';\n default:\n return 'unknown';\n }\n}\n\nfunction generateReactQueryCode(\n spec: OpenAPISpec,\n operations: OperationInfo[],\n apiName: string,\n): string {\n const imports = `import { createTypedQueryClient } from '@geekmidas/client';\nimport type { paths } from './openapi-types';\n\n// Create typed query client\nexport const ${apiName.toLowerCase()} = createTypedQueryClient<paths>({\n baseURL: process.env.NEXT_PUBLIC_API_URL || '/api',\n});\n\n// Export individual hooks for better DX\n`;\n\n const queryHooks = operations\n .filter((op) => op.method === 'GET')\n .map((op) => generateQueryHook(op, apiName))\n .join('\\n\\n');\n\n const mutationHooks = operations\n .filter((op) => op.method !== 'GET')\n .map((op) => generateMutationHook(op, apiName))\n .join('\\n\\n');\n\n const typeExports = generateTypeExports(operations);\n\n return `${imports}\n// Query Hooks\n${queryHooks}\n\n// Mutation Hooks\n${mutationHooks}\n\n// Type exports for convenience\n${typeExports}\n\n// Re-export the api for advanced usage\nexport { ${apiName.toLowerCase()} };\n`;\n}\n\nfunction generateQueryHook(op: OperationInfo, apiName: string): string {\n const hookName = `use${capitalize(op.operationId)}`;\n const endpoint = op.endpoint;\n const hasParams = op.parameters?.some((p) => p.in === 'path');\n const hasQuery = op.parameters?.some((p) => p.in === 'query');\n\n // Generate properly typed hook\n let params = '';\n let args = '';\n\n if (hasParams || hasQuery) {\n const paramParts: string[] = [];\n if (hasParams) {\n const pathParams =\n op.parameters?.filter((p) => p.in === 'path').map((p) => p.name) || [];\n paramParts.push(\n `params: { ${pathParams.map((p) => `${p}: string`).join('; ')} }`,\n );\n }\n if (hasQuery) {\n paramParts.push(`query?: Record<string, any>`);\n }\n params = `config: { ${paramParts.join('; ')} }, `;\n args = ', config';\n }\n\n return `export const ${hookName} = (\n ${params}options?: Parameters<typeof ${apiName.toLowerCase()}.useQuery>[2]\n) => {\n return ${apiName.toLowerCase()}.useQuery('${endpoint}' as any${args}, options);\n};`;\n}\n\nfunction generateMutationHook(op: OperationInfo, apiName: string): string {\n const hookName = `use${capitalize(op.operationId)}`;\n const endpoint = op.endpoint;\n\n return `export const ${hookName} = (\n options?: Parameters<typeof ${apiName.toLowerCase()}.useMutation>[1]\n) => {\n return ${apiName.toLowerCase()}.useMutation('${endpoint}' as any, options);\n};`;\n}\n\nfunction generateTypeExports(operations: OperationInfo[]): string {\n const exports = operations.map((op) => {\n const typeName = capitalize(op.operationId);\n const isQuery = op.method === 'GET';\n\n if (isQuery) {\n return `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['data']>>;`;\n } else {\n return `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['mutateAsync']>>;`;\n }\n });\n\n return exports.join('\\n');\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
|
-
const require_openapi_react_query = require('./openapi-react-query-
|
|
2
|
+
const require_openapi_react_query = require('./openapi-react-query-D9Z7lh0p.cjs');
|
|
3
3
|
|
|
4
4
|
exports.generateReactQueryCommand = require_openapi_react_query.generateReactQueryCommand;
|
package/dist/openapi.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
require('./config-D1EpSGk6.cjs');
|
|
3
3
|
require('./Generator-CDoEXCDg.cjs');
|
|
4
|
-
require('./EndpointGenerator-
|
|
5
|
-
const require_openapi = require('./openapi-
|
|
4
|
+
require('./EndpointGenerator-C73wNoih.cjs');
|
|
5
|
+
const require_openapi = require('./openapi-CewcfoRH.cjs');
|
|
6
6
|
|
|
7
7
|
exports.openapiCommand = require_openapi.openapiCommand;
|
package/dist/openapi.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import "./config-U-mdW-7Y.mjs";
|
|
3
3
|
import "./Generator-UanJW0_V.mjs";
|
|
4
|
-
import "./EndpointGenerator-
|
|
5
|
-
import { openapiCommand } from "./openapi-
|
|
4
|
+
import "./EndpointGenerator-CWh18d92.mjs";
|
|
5
|
+
import { openapiCommand } from "./openapi-BTHbPrxS.mjs";
|
|
6
6
|
|
|
7
7
|
export { openapiCommand };
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Manifest Refactoring Design
|
|
2
|
+
|
|
3
|
+
## Current State
|
|
4
|
+
|
|
5
|
+
Currently, the CLI generates a single `manifest.json` file at `.gkm/manifest.json` that aggregates all routes, functions, crons, and subscribers across all providers.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
.gkm/
|
|
9
|
+
├── manifest.json # Single JSON manifest
|
|
10
|
+
├── aws-apigatewayv2/ # AWS handlers
|
|
11
|
+
│ ├── getUsers.ts
|
|
12
|
+
│ └── createUser.ts
|
|
13
|
+
└── server/ # Server handlers
|
|
14
|
+
├── app.ts
|
|
15
|
+
└── endpoints.ts
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Problems
|
|
19
|
+
|
|
20
|
+
1. **Mixed provider data**: AWS and server routes are combined, causing `method: 'ALL'` to appear in manifests used for AWS infrastructure
|
|
21
|
+
2. **JSON format**: No TypeScript types available, consumers must define their own types
|
|
22
|
+
3. **No derived types**: Consumers can't easily extract types like `Authorizer` from the manifest
|
|
23
|
+
|
|
24
|
+
## Proposed Changes
|
|
25
|
+
|
|
26
|
+
### 1. Folder Structure
|
|
27
|
+
|
|
28
|
+
Change from single `manifest.json` to a `manifest/` folder with TypeScript files per provider:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
.gkm/
|
|
32
|
+
├── manifest/
|
|
33
|
+
│ ├── aws.ts # AWS-specific manifest
|
|
34
|
+
│ └── server.ts # Server-specific manifest
|
|
35
|
+
├── aws-apigatewayv2/
|
|
36
|
+
│ ├── getUsers.ts
|
|
37
|
+
│ └── createUser.ts
|
|
38
|
+
└── server/
|
|
39
|
+
├── app.ts
|
|
40
|
+
└── endpoints.ts
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. AWS Manifest (`manifest/aws.ts`)
|
|
44
|
+
|
|
45
|
+
Contains only AWS routes with actual HTTP methods (no `method: 'ALL'`):
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
export const manifest = {
|
|
49
|
+
routes: [
|
|
50
|
+
{
|
|
51
|
+
path: '/users',
|
|
52
|
+
method: 'GET',
|
|
53
|
+
handler: '.gkm/aws-apigatewayv2/getUsers.handler',
|
|
54
|
+
timeout: 30,
|
|
55
|
+
memorySize: 256,
|
|
56
|
+
environment: ['DATABASE_URL', 'API_KEY'],
|
|
57
|
+
authorizer: 'jwt',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
path: '/users',
|
|
61
|
+
method: 'POST',
|
|
62
|
+
handler: '.gkm/aws-apigatewayv2/createUser.handler',
|
|
63
|
+
timeout: 30,
|
|
64
|
+
memorySize: 256,
|
|
65
|
+
environment: ['DATABASE_URL'],
|
|
66
|
+
authorizer: 'jwt',
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
functions: [
|
|
70
|
+
{
|
|
71
|
+
name: 'processPayment',
|
|
72
|
+
handler: '.gkm/aws-lambda/processPayment.handler',
|
|
73
|
+
timeout: 60,
|
|
74
|
+
memorySize: 512,
|
|
75
|
+
environment: ['STRIPE_KEY'],
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
crons: [
|
|
79
|
+
{
|
|
80
|
+
name: 'dailyReport',
|
|
81
|
+
handler: '.gkm/aws-lambda/dailyReport.handler',
|
|
82
|
+
schedule: 'rate(1 day)',
|
|
83
|
+
timeout: 300,
|
|
84
|
+
memorySize: 256,
|
|
85
|
+
environment: [],
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
subscribers: [
|
|
89
|
+
{
|
|
90
|
+
name: 'orderCreated',
|
|
91
|
+
handler: '.gkm/aws-lambda/orderCreated.handler',
|
|
92
|
+
subscribedEvents: ['order.created'],
|
|
93
|
+
timeout: 30,
|
|
94
|
+
memorySize: 256,
|
|
95
|
+
environment: [],
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
} as const;
|
|
99
|
+
|
|
100
|
+
// Derived types
|
|
101
|
+
export type Route = (typeof manifest.routes)[number];
|
|
102
|
+
export type Function = (typeof manifest.functions)[number];
|
|
103
|
+
export type Cron = (typeof manifest.crons)[number];
|
|
104
|
+
export type Subscriber = (typeof manifest.subscribers)[number];
|
|
105
|
+
|
|
106
|
+
// Useful union types
|
|
107
|
+
export type Authorizer = Route['authorizer'];
|
|
108
|
+
export type HttpMethod = Route['method'];
|
|
109
|
+
export type RoutePath = Route['path'];
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 3. Server Manifest (`manifest/server.ts`)
|
|
113
|
+
|
|
114
|
+
Contains server app info and route metadata for documentation/type derivation:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
export const manifest = {
|
|
118
|
+
app: {
|
|
119
|
+
handler: '.gkm/server/app.ts',
|
|
120
|
+
endpoints: '.gkm/server/endpoints.ts',
|
|
121
|
+
},
|
|
122
|
+
routes: [
|
|
123
|
+
{
|
|
124
|
+
path: '/users',
|
|
125
|
+
method: 'GET',
|
|
126
|
+
authorizer: 'jwt',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
path: '/users',
|
|
130
|
+
method: 'POST',
|
|
131
|
+
authorizer: 'jwt',
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
subscribers: [
|
|
135
|
+
{
|
|
136
|
+
name: 'orderCreated',
|
|
137
|
+
subscribedEvents: ['order.created'],
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
} as const;
|
|
141
|
+
|
|
142
|
+
// Derived types
|
|
143
|
+
export type Route = (typeof manifest.routes)[number];
|
|
144
|
+
export type Subscriber = (typeof manifest.subscribers)[number];
|
|
145
|
+
|
|
146
|
+
// Useful union types
|
|
147
|
+
export type Authorizer = Route['authorizer'];
|
|
148
|
+
export type HttpMethod = Route['method'];
|
|
149
|
+
export type RoutePath = Route['path'];
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Implementation Details
|
|
153
|
+
|
|
154
|
+
### Changes to `manifests.ts`
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
export async function generateManifests(
|
|
158
|
+
outputDir: string,
|
|
159
|
+
provider: 'aws' | 'server',
|
|
160
|
+
routes: RouteInfo[],
|
|
161
|
+
functions: FunctionInfo[],
|
|
162
|
+
crons: CronInfo[],
|
|
163
|
+
subscribers: SubscriberInfo[],
|
|
164
|
+
): Promise<void> {
|
|
165
|
+
const manifestDir = join(outputDir, 'manifest');
|
|
166
|
+
await mkdir(manifestDir, { recursive: true });
|
|
167
|
+
|
|
168
|
+
if (provider === 'aws') {
|
|
169
|
+
await generateAwsManifest(manifestDir, routes, functions, crons, subscribers);
|
|
170
|
+
} else {
|
|
171
|
+
await generateServerManifest(manifestDir, routes, subscribers);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function generateAwsManifest(
|
|
176
|
+
manifestDir: string,
|
|
177
|
+
routes: RouteInfo[],
|
|
178
|
+
functions: FunctionInfo[],
|
|
179
|
+
crons: CronInfo[],
|
|
180
|
+
subscribers: SubscriberInfo[],
|
|
181
|
+
): Promise<void> {
|
|
182
|
+
// Filter out 'ALL' method routes (server-specific)
|
|
183
|
+
const awsRoutes = routes.filter(r => r.method !== 'ALL');
|
|
184
|
+
|
|
185
|
+
const content = `export const manifest = {
|
|
186
|
+
routes: ${JSON.stringify(awsRoutes, null, 2)},
|
|
187
|
+
functions: ${JSON.stringify(functions, null, 2)},
|
|
188
|
+
crons: ${JSON.stringify(crons, null, 2)},
|
|
189
|
+
subscribers: ${JSON.stringify(subscribers, null, 2)},
|
|
190
|
+
} as const;
|
|
191
|
+
|
|
192
|
+
// Derived types
|
|
193
|
+
export type Route = (typeof manifest.routes)[number];
|
|
194
|
+
export type Function = (typeof manifest.functions)[number];
|
|
195
|
+
export type Cron = (typeof manifest.crons)[number];
|
|
196
|
+
export type Subscriber = (typeof manifest.subscribers)[number];
|
|
197
|
+
|
|
198
|
+
// Useful union types
|
|
199
|
+
export type Authorizer = Route['authorizer'];
|
|
200
|
+
export type HttpMethod = Route['method'];
|
|
201
|
+
export type RoutePath = Route['path'];
|
|
202
|
+
`;
|
|
203
|
+
|
|
204
|
+
await writeFile(join(manifestDir, 'aws.ts'), content);
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Changes to Build Flow
|
|
209
|
+
|
|
210
|
+
Currently `buildCommand` aggregates all providers into one manifest. Change to:
|
|
211
|
+
|
|
212
|
+
1. Generate per-provider manifests during each provider's build
|
|
213
|
+
2. Remove aggregation step
|
|
214
|
+
3. Each provider generates its own TypeScript manifest file
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
async function buildForProvider(
|
|
218
|
+
provider: LegacyProvider,
|
|
219
|
+
// ...
|
|
220
|
+
): Promise<BuildResult> {
|
|
221
|
+
// ... existing build logic ...
|
|
222
|
+
|
|
223
|
+
// Generate provider-specific manifest
|
|
224
|
+
const manifestProvider = provider.startsWith('aws') ? 'aws' : 'server';
|
|
225
|
+
await generateManifests(
|
|
226
|
+
rootOutputDir,
|
|
227
|
+
manifestProvider,
|
|
228
|
+
routes,
|
|
229
|
+
functionInfos,
|
|
230
|
+
cronInfos,
|
|
231
|
+
subscriberInfos,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
return { routes, functions: functionInfos, crons: cronInfos, subscribers: subscriberInfos };
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Usage Examples
|
|
239
|
+
|
|
240
|
+
### AWS CDK / SST Integration
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import { manifest, type Route, type Authorizer } from './.gkm/manifest/aws';
|
|
244
|
+
|
|
245
|
+
// Type-safe iteration over routes
|
|
246
|
+
for (const route of manifest.routes) {
|
|
247
|
+
new ApiGatewayRoute(this, route.path, {
|
|
248
|
+
method: route.method,
|
|
249
|
+
handler: route.handler,
|
|
250
|
+
authorizer: getAuthorizer(route.authorizer),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Use derived types
|
|
255
|
+
function getAuthorizer(name: Authorizer): IAuthorizer {
|
|
256
|
+
switch (name) {
|
|
257
|
+
case 'jwt':
|
|
258
|
+
return jwtAuthorizer;
|
|
259
|
+
case 'apiKey':
|
|
260
|
+
return apiKeyAuthorizer;
|
|
261
|
+
case 'none':
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Type Checking Authorizer Values
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
import type { Authorizer } from './.gkm/manifest/aws';
|
|
271
|
+
|
|
272
|
+
// Authorizer is a union type of all authorizer values
|
|
273
|
+
// e.g., 'jwt' | 'apiKey' | 'none'
|
|
274
|
+
const authorizer: Authorizer = 'jwt'; // ✓
|
|
275
|
+
const invalid: Authorizer = 'invalid'; // ✗ Type error
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Migration Notes
|
|
279
|
+
|
|
280
|
+
1. **Breaking change**: Consumers using `manifest.json` need to switch to TypeScript imports
|
|
281
|
+
2. **Benefit**: Full TypeScript support with derived types
|
|
282
|
+
|
|
283
|
+
## Design Decisions
|
|
284
|
+
|
|
285
|
+
1. **No backward compatibility**: `manifest.json` will not be generated
|
|
286
|
+
2. **Server manifest includes route metadata**: For documentation and type derivation
|
|
287
|
+
3. **No re-export index**: Each manifest is imported directly
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geekmidas/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -23,8 +23,13 @@
|
|
|
23
23
|
"bin": {
|
|
24
24
|
"gkm": "./dist/index.cjs"
|
|
25
25
|
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/geekmidas/toolbox"
|
|
29
|
+
},
|
|
26
30
|
"dependencies": {
|
|
27
31
|
"@apidevtools/swagger-parser": "^10.1.0",
|
|
32
|
+
"chokidar": "~4.0.3",
|
|
28
33
|
"commander": "^12.1.0",
|
|
29
34
|
"fast-glob": "^3.3.2",
|
|
30
35
|
"lodash.kebabcase": "^4.1.1",
|
|
@@ -32,16 +37,17 @@
|
|
|
32
37
|
},
|
|
33
38
|
"devDependencies": {
|
|
34
39
|
"@types/lodash.kebabcase": "^4.1.9",
|
|
40
|
+
"@types/node": "~24.9.1",
|
|
35
41
|
"typescript": "^5.8.2",
|
|
36
42
|
"vitest": "^3.2.4",
|
|
37
|
-
"zod": "
|
|
38
|
-
"@geekmidas/testkit": "0.0.
|
|
43
|
+
"zod": "~4.1.13",
|
|
44
|
+
"@geekmidas/testkit": "0.0.17"
|
|
39
45
|
},
|
|
40
46
|
"peerDependencies": {
|
|
47
|
+
"@geekmidas/constructs": "~0.0.22",
|
|
48
|
+
"@geekmidas/envkit": "~0.0.8",
|
|
41
49
|
"@geekmidas/logger": "~0.0.1",
|
|
42
|
-
"@geekmidas/schema": "~0.0.2"
|
|
43
|
-
"@geekmidas/envkit": "~0.0.7",
|
|
44
|
-
"@geekmidas/constructs": "~0.0.6"
|
|
50
|
+
"@geekmidas/schema": "~0.0.2"
|
|
45
51
|
},
|
|
46
52
|
"scripts": {
|
|
47
53
|
"ts": "tsc --noEmit --skipLibCheck src/**/*.ts",
|
|
@@ -82,6 +82,20 @@ export default {
|
|
|
82
82
|
'utf-8',
|
|
83
83
|
);
|
|
84
84
|
expect(endpointsContent).toContain('HonoEndpoint');
|
|
85
|
+
|
|
86
|
+
// Verify server manifest was created at .gkm/manifest/server.ts
|
|
87
|
+
const manifestPath = join(dir, '.gkm', 'manifest', 'server.ts');
|
|
88
|
+
const manifestContent = await readFile(manifestPath, 'utf-8');
|
|
89
|
+
|
|
90
|
+
// Verify manifest structure
|
|
91
|
+
expect(manifestContent).toContain('export const manifest = {');
|
|
92
|
+
expect(manifestContent).toContain('} as const;');
|
|
93
|
+
expect(manifestContent).toContain('app:');
|
|
94
|
+
expect(manifestContent).toContain('routes:');
|
|
95
|
+
|
|
96
|
+
// Verify derived types are exported
|
|
97
|
+
expect(manifestContent).toContain('export type Route =');
|
|
98
|
+
expect(manifestContent).toContain('export type Authorizer =');
|
|
85
99
|
} finally {
|
|
86
100
|
process.chdir(originalCwd);
|
|
87
101
|
}
|
|
@@ -202,78 +216,35 @@ export default {
|
|
|
202
216
|
),
|
|
203
217
|
).toContain('AmazonApiGatewayV2Endpoint');
|
|
204
218
|
|
|
205
|
-
// Verify
|
|
206
|
-
const manifestPath = join(dir, '.gkm', 'manifest.
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
// Verify manifest
|
|
210
|
-
expect(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
path: '/posts',
|
|
235
|
-
method: 'POST',
|
|
236
|
-
handler: expect.stringContaining('getPostsEndpoint.handler'),
|
|
237
|
-
}),
|
|
238
|
-
]),
|
|
239
|
-
functions: expect.arrayContaining([
|
|
240
|
-
expect.objectContaining({
|
|
241
|
-
name: 'processDataFunction',
|
|
242
|
-
handler: expect.stringContaining(
|
|
243
|
-
'functions/processDataFunction.handler',
|
|
244
|
-
),
|
|
245
|
-
timeout: 300,
|
|
246
|
-
}),
|
|
247
|
-
expect.objectContaining({
|
|
248
|
-
name: 'sendEmailFunction',
|
|
249
|
-
handler: expect.stringContaining(
|
|
250
|
-
'functions/sendEmailFunction.handler',
|
|
251
|
-
),
|
|
252
|
-
timeout: 30,
|
|
253
|
-
}),
|
|
254
|
-
]),
|
|
255
|
-
crons: expect.arrayContaining([
|
|
256
|
-
expect.objectContaining({
|
|
257
|
-
name: 'dailyCleanupCron',
|
|
258
|
-
handler: expect.stringContaining(
|
|
259
|
-
'crons/dailyCleanupCron.handler',
|
|
260
|
-
),
|
|
261
|
-
schedule: 'rate(1 day)',
|
|
262
|
-
}),
|
|
263
|
-
expect.objectContaining({
|
|
264
|
-
name: 'hourlyReportCron',
|
|
265
|
-
handler: expect.stringContaining(
|
|
266
|
-
'crons/hourlyReportCron.handler',
|
|
267
|
-
),
|
|
268
|
-
schedule: 'cron(0 * * * ? *)',
|
|
269
|
-
}),
|
|
270
|
-
]),
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
// Verify counts - should have duplicated routes (one for each provider)
|
|
274
|
-
expect(manifest.routes).toHaveLength(4); // 2 routes x 2 providers
|
|
275
|
-
expect(manifest.functions).toHaveLength(2);
|
|
276
|
-
expect(manifest.crons).toHaveLength(2);
|
|
219
|
+
// Verify AWS manifest was created at .gkm/manifest/aws.ts
|
|
220
|
+
const manifestPath = join(dir, '.gkm', 'manifest', 'aws.ts');
|
|
221
|
+
const manifestContent = await readFile(manifestPath, 'utf-8');
|
|
222
|
+
|
|
223
|
+
// Verify manifest is TypeScript with as const
|
|
224
|
+
expect(manifestContent).toContain('export const manifest = {');
|
|
225
|
+
expect(manifestContent).toContain('} as const;');
|
|
226
|
+
|
|
227
|
+
// Verify derived types are exported
|
|
228
|
+
expect(manifestContent).toContain('export type Route =');
|
|
229
|
+
expect(manifestContent).toContain('export type Function =');
|
|
230
|
+
expect(manifestContent).toContain('export type Cron =');
|
|
231
|
+
expect(manifestContent).toContain('export type Authorizer =');
|
|
232
|
+
|
|
233
|
+
// Verify routes are included (JSON.stringify output uses quoted keys/values)
|
|
234
|
+
expect(manifestContent).toContain('/users');
|
|
235
|
+
expect(manifestContent).toContain('/posts');
|
|
236
|
+
expect(manifestContent).toContain('GET');
|
|
237
|
+
expect(manifestContent).toContain('POST');
|
|
238
|
+
|
|
239
|
+
// Verify functions are included
|
|
240
|
+
expect(manifestContent).toContain('processDataFunction');
|
|
241
|
+
expect(manifestContent).toContain('sendEmailFunction');
|
|
242
|
+
|
|
243
|
+
// Verify crons are included
|
|
244
|
+
expect(manifestContent).toContain('dailyCleanupCron');
|
|
245
|
+
expect(manifestContent).toContain('hourlyReportCron');
|
|
246
|
+
expect(manifestContent).toContain('rate(1 day)');
|
|
247
|
+
expect(manifestContent).toContain('cron(0 * * * ? *)');
|
|
277
248
|
} finally {
|
|
278
249
|
process.chdir(originalCwd);
|
|
279
250
|
}
|