@geekmidas/cli 1.9.0 → 1.9.1
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/CHANGELOG.md +6 -0
- package/dist/{HostingerProvider-CEsQbmpY.cjs → HostingerProvider-5KYmwoK2.cjs} +1 -1
- package/dist/{HostingerProvider-CEsQbmpY.cjs.map → HostingerProvider-5KYmwoK2.cjs.map} +1 -1
- package/dist/{HostingerProvider-DkahM5AP.mjs → HostingerProvider-ANWchdiK.mjs} +1 -1
- package/dist/{HostingerProvider-DkahM5AP.mjs.map → HostingerProvider-ANWchdiK.mjs.map} +1 -1
- package/dist/{LocalStateProvider-Roi202l7.cjs → LocalStateProvider-CLifRC0Y.cjs} +1 -1
- package/dist/{LocalStateProvider-Roi202l7.cjs.map → LocalStateProvider-CLifRC0Y.cjs.map} +1 -1
- package/dist/{LocalStateProvider-DXIwWb7k.mjs → LocalStateProvider-Dp0KkRcw.mjs} +1 -1
- package/dist/{LocalStateProvider-DXIwWb7k.mjs.map → LocalStateProvider-Dp0KkRcw.mjs.map} +1 -1
- package/dist/{Route53Provider-Ckq_n5Be.mjs → Route53Provider-QoPgcXxn.mjs} +1 -1
- package/dist/{Route53Provider-Ckq_n5Be.mjs.map → Route53Provider-QoPgcXxn.mjs.map} +1 -1
- package/dist/{Route53Provider-BqXeHzuc.cjs → Route53Provider-owQQ4pn6.cjs} +1 -1
- package/dist/{Route53Provider-BqXeHzuc.cjs.map → Route53Provider-owQQ4pn6.cjs.map} +1 -1
- package/dist/{SSMStateProvider-BReQA5re.cjs → SSMStateProvider-CT8tjl9o.cjs} +1 -1
- package/dist/{SSMStateProvider-BReQA5re.cjs.map → SSMStateProvider-CT8tjl9o.cjs.map} +1 -1
- package/dist/{SSMStateProvider-wddd0_-d.mjs → SSMStateProvider-CksOTB8M.mjs} +1 -1
- package/dist/{SSMStateProvider-wddd0_-d.mjs.map → SSMStateProvider-CksOTB8M.mjs.map} +1 -1
- package/dist/{backup-provisioner-BAExdDtc.mjs → backup-provisioner-BEXoHTuC.mjs} +1 -1
- package/dist/{backup-provisioner-BAExdDtc.mjs.map → backup-provisioner-BEXoHTuC.mjs.map} +1 -1
- package/dist/{backup-provisioner-C8VK63I-.cjs → backup-provisioner-C4noe75O.cjs} +1 -1
- package/dist/{backup-provisioner-C8VK63I-.cjs.map → backup-provisioner-C4noe75O.cjs.map} +1 -1
- package/dist/{bundler-BxHyDhdt.mjs → bundler-DQYjKFPm.mjs} +1 -1
- package/dist/{bundler-BxHyDhdt.mjs.map → bundler-DQYjKFPm.mjs.map} +1 -1
- package/dist/{bundler-CuMIfXw5.cjs → bundler-NpfYPBUo.cjs} +1 -1
- package/dist/{bundler-CuMIfXw5.cjs.map → bundler-NpfYPBUo.cjs.map} +1 -1
- package/dist/config.d.mts +2 -2
- package/dist/fullstack-secrets-COWz084x.cjs +238 -0
- package/dist/fullstack-secrets-COWz084x.cjs.map +1 -0
- package/dist/fullstack-secrets-UZAFWuH4.mjs +202 -0
- package/dist/fullstack-secrets-UZAFWuH4.mjs.map +1 -0
- package/dist/{index-BVNXOydm.d.mts → index-Bt2kX0-R.d.mts} +2 -2
- package/dist/{index-BVNXOydm.d.mts.map → index-Bt2kX0-R.d.mts.map} +1 -1
- package/dist/index.cjs +141 -276
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +128 -263
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-react-query-DaTMSPD5.mjs → openapi-react-query-C4UdILaI.mjs} +1 -1
- package/dist/{openapi-react-query-DaTMSPD5.mjs.map → openapi-react-query-C4UdILaI.mjs.map} +1 -1
- package/dist/{openapi-react-query-BeXvk-wa.cjs → openapi-react-query-DYbBq-WJ.cjs} +1 -1
- package/dist/{openapi-react-query-BeXvk-wa.cjs.map → openapi-react-query-DYbBq-WJ.cjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/reconcile-7yarEvmK.cjs +36 -0
- package/dist/reconcile-7yarEvmK.cjs.map +1 -0
- package/dist/reconcile-D2WCDQue.mjs +36 -0
- package/dist/reconcile-D2WCDQue.mjs.map +1 -0
- package/dist/{sync-BnqNNc6O.mjs → sync-6FoT41G3.mjs} +1 -1
- package/dist/{sync-CHfhmXF3.mjs → sync-CbeKrnQV.mjs} +1 -1
- package/dist/{sync-CHfhmXF3.mjs.map → sync-CbeKrnQV.mjs.map} +1 -1
- package/dist/{sync-BOS0jKLn.cjs → sync-DdkKaHqP.cjs} +1 -1
- package/dist/{sync-BOS0jKLn.cjs.map → sync-DdkKaHqP.cjs.map} +1 -1
- package/dist/sync-RsnjXYwG.cjs +4 -0
- package/dist/{types-eTlj5f2M.d.mts → types-wXMIMOyK.d.mts} +1 -1
- package/dist/{types-eTlj5f2M.d.mts.map → types-wXMIMOyK.d.mts.map} +1 -1
- package/dist/workspace/index.d.mts +2 -2
- package/package.json +3 -3
- package/src/dev/__tests__/index.spec.ts +49 -0
- package/src/dev/index.ts +84 -63
- package/src/index.ts +79 -1
- package/src/init/versions.ts +4 -4
- package/src/secrets/__tests__/reconcile.spec.ts +123 -0
- package/src/secrets/reconcile.ts +53 -0
- package/src/setup/fullstack-secrets.ts +2 -0
- package/dist/sync-BxFB34zW.cjs +0 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi-react-query-DaTMSPD5.mjs","names":["options: ReactQueryOptions","spec: OpenAPISpec","operations: OperationInfo[]","operation: any","schema: any","_spec: OpenAPISpec","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\tinput?: string;\n\toutput?: string;\n\tname?: string;\n}\n\ninterface OpenAPISpec {\n\topenapi: string;\n\tinfo?: {\n\t\ttitle?: string;\n\t\tversion?: string;\n\t};\n\tpaths: Record<string, Record<string, any>>;\n}\n\nexport async function generateReactQueryCommand(\n\toptions: ReactQueryOptions = {},\n): Promise<void> {\n\tconst logger = console;\n\n\ttry {\n\t\t// Read OpenAPI spec\n\t\tconst inputPath = options.input || join(process.cwd(), 'openapi.json');\n\n\t\tif (!existsSync(inputPath)) {\n\t\t\tthrow new Error(\n\t\t\t\t`OpenAPI spec not found at ${inputPath}. Run 'npx @geekmidas/cli openapi' first.`,\n\t\t\t);\n\t\t}\n\n\t\tconst specContent = await readFile(inputPath, 'utf-8');\n\t\tconst spec: OpenAPISpec = JSON.parse(specContent);\n\n\t\t// Generate TypeScript types from OpenAPI spec\n\t\tconst outputDir = dirname(\n\t\t\toptions.output || join(process.cwd(), 'src', 'api', 'hooks.ts'),\n\t\t);\n\t\tconst typesPath = join(outputDir, 'openapi-types.d.ts');\n\n\t\tlogger.log('Generating TypeScript types from OpenAPI spec...');\n\n\t\ttry {\n\t\t\t// Use npx to run openapi-typescript\n\t\t\tawait execAsync(\n\t\t\t\t`npx openapi-typescript \"${inputPath}\" -o \"${typesPath}\"`,\n\t\t\t\t{ cwd: process.cwd() },\n\t\t\t);\n\t\t\tlogger.log(`TypeScript types generated: ${typesPath}`);\n\t\t} catch (_error) {\n\t\t\tlogger.warn(\n\t\t\t\t'Could not generate types with openapi-typescript. Install it for better type inference.',\n\t\t\t);\n\t\t\tlogger.warn('Run: npm install -D openapi-typescript');\n\n\t\t\t// Generate basic types file\n\t\t\tawait mkdir(dirname(typesPath), { recursive: true });\n\t\t\tawait writeFile(\n\t\t\t\ttypesPath,\n\t\t\t\t`// 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\t\t\t);\n\t\t}\n\n\t\t// Extract operation info\n\t\tconst operations = extractOperations(spec);\n\n\t\t// Generate TypeScript code\n\t\tconst code = generateReactQueryCode(\n\t\t\tspec,\n\t\t\toperations,\n\t\t\toptions.name || 'API',\n\t\t);\n\n\t\t// Write output\n\t\tconst outputPath =\n\t\t\toptions.output || join(process.cwd(), 'src', 'api', 'hooks.ts');\n\t\tawait mkdir(dirname(outputPath), { recursive: true });\n\t\tawait writeFile(outputPath, code);\n\n\t\tlogger.log(`React Query hooks generated: ${outputPath}`);\n\t\tlogger.log(`Generated ${operations.length} hooks`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`React Query generation failed: ${(error as Error).message}`,\n\t\t);\n\t}\n}\n\ninterface OperationInfo {\n\toperationId: string;\n\tpath: string;\n\tmethod: string;\n\tendpoint: string; // Full endpoint like 'GET /users/{id}'\n\tparameters?: Array<{ name: string; in: string; required?: boolean }>;\n\trequestBody?: boolean;\n\tresponseType?: string;\n}\n\nfunction extractOperations(spec: OpenAPISpec): OperationInfo[] {\n\tconst operations: OperationInfo[] = [];\n\n\tObject.entries(spec.paths).forEach(([path, methods]) => {\n\t\tObject.entries(methods).forEach(([method, operation]) => {\n\t\t\tif (operation.operationId) {\n\t\t\t\toperations.push({\n\t\t\t\t\toperationId: operation.operationId,\n\t\t\t\t\tpath,\n\t\t\t\t\tmethod: method.toUpperCase(),\n\t\t\t\t\tendpoint: `${method.toUpperCase()} ${path}`,\n\t\t\t\t\tparameters: operation.parameters,\n\t\t\t\t\trequestBody: !!operation.requestBody,\n\t\t\t\t\tresponseType: extractResponseType(operation),\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t});\n\n\treturn operations;\n}\n\nfunction extractResponseType(operation: any): string {\n\tconst responses = operation.responses;\n\tif (!responses) return 'unknown';\n\n\tconst successResponse = responses['200'] || responses['201'];\n\tif (!successResponse?.content?.['application/json']?.schema) {\n\t\treturn 'unknown';\n\t}\n\n\t// Basic type inference from schema\n\tconst schema = successResponse.content['application/json'].schema;\n\treturn schemaToTypeString(schema);\n}\n\nfunction schemaToTypeString(schema: any): string {\n\tif (!schema) return 'unknown';\n\n\tswitch (schema.type) {\n\t\tcase 'string':\n\t\t\treturn 'string';\n\t\tcase 'number':\n\t\tcase 'integer':\n\t\t\treturn 'number';\n\t\tcase 'boolean':\n\t\t\treturn 'boolean';\n\t\tcase 'array':\n\t\t\treturn `Array<${schemaToTypeString(schema.items)}>`;\n\t\tcase 'object':\n\t\t\tif (schema.properties) {\n\t\t\t\tconst props = Object.entries(schema.properties)\n\t\t\t\t\t.map(\n\t\t\t\t\t\t([key, value]: [string, any]) =>\n\t\t\t\t\t\t\t`${key}: ${schemaToTypeString(value)}`,\n\t\t\t\t\t)\n\t\t\t\t\t.join('; ');\n\t\t\t\treturn `{ ${props} }`;\n\t\t\t}\n\t\t\treturn 'Record<string, unknown>';\n\t\tdefault:\n\t\t\treturn 'unknown';\n\t}\n}\n\nfunction generateReactQueryCode(\n\t_spec: OpenAPISpec,\n\toperations: OperationInfo[],\n\tapiName: string,\n): string {\n\tconst 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\tconst queryHooks = operations\n\t\t.filter((op) => op.method === 'GET')\n\t\t.map((op) => generateQueryHook(op, apiName))\n\t\t.join('\\n\\n');\n\n\tconst mutationHooks = operations\n\t\t.filter((op) => op.method !== 'GET')\n\t\t.map((op) => generateMutationHook(op, apiName))\n\t\t.join('\\n\\n');\n\n\tconst typeExports = generateTypeExports(operations);\n\n\treturn `${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\tconst hookName = `use${capitalize(op.operationId)}`;\n\tconst endpoint = op.endpoint;\n\tconst hasParams = op.parameters?.some((p) => p.in === 'path');\n\tconst hasQuery = op.parameters?.some((p) => p.in === 'query');\n\n\t// Generate properly typed hook\n\tlet params = '';\n\tlet args = '';\n\n\tif (hasParams || hasQuery) {\n\t\tconst paramParts: string[] = [];\n\t\tif (hasParams) {\n\t\t\tconst pathParams =\n\t\t\t\top.parameters?.filter((p) => p.in === 'path').map((p) => p.name) || [];\n\t\t\tparamParts.push(\n\t\t\t\t`params: { ${pathParams.map((p) => `${p}: string`).join('; ')} }`,\n\t\t\t);\n\t\t}\n\t\tif (hasQuery) {\n\t\t\tparamParts.push(`query?: Record<string, any>`);\n\t\t}\n\t\tparams = `config: { ${paramParts.join('; ')} }, `;\n\t\targs = ', config';\n\t}\n\n\treturn `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\tconst hookName = `use${capitalize(op.operationId)}`;\n\tconst endpoint = op.endpoint;\n\n\treturn `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\tconst exports = operations.map((op) => {\n\t\tconst typeName = capitalize(op.operationId);\n\t\tconst isQuery = op.method === 'GET';\n\n\t\tif (isQuery) {\n\t\t\treturn `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['data']>>;`;\n\t\t} else {\n\t\t\treturn `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['mutateAsync']>>;`;\n\t\t}\n\t});\n\n\treturn exports.join('\\n');\n}\n\nfunction capitalize(str: string): string {\n\treturn str.charAt(0).toUpperCase() + str.slice(1);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"openapi-react-query-C4UdILaI.mjs","names":["options: ReactQueryOptions","spec: OpenAPISpec","operations: OperationInfo[]","operation: any","schema: any","_spec: OpenAPISpec","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\tinput?: string;\n\toutput?: string;\n\tname?: string;\n}\n\ninterface OpenAPISpec {\n\topenapi: string;\n\tinfo?: {\n\t\ttitle?: string;\n\t\tversion?: string;\n\t};\n\tpaths: Record<string, Record<string, any>>;\n}\n\nexport async function generateReactQueryCommand(\n\toptions: ReactQueryOptions = {},\n): Promise<void> {\n\tconst logger = console;\n\n\ttry {\n\t\t// Read OpenAPI spec\n\t\tconst inputPath = options.input || join(process.cwd(), 'openapi.json');\n\n\t\tif (!existsSync(inputPath)) {\n\t\t\tthrow new Error(\n\t\t\t\t`OpenAPI spec not found at ${inputPath}. Run 'npx @geekmidas/cli openapi' first.`,\n\t\t\t);\n\t\t}\n\n\t\tconst specContent = await readFile(inputPath, 'utf-8');\n\t\tconst spec: OpenAPISpec = JSON.parse(specContent);\n\n\t\t// Generate TypeScript types from OpenAPI spec\n\t\tconst outputDir = dirname(\n\t\t\toptions.output || join(process.cwd(), 'src', 'api', 'hooks.ts'),\n\t\t);\n\t\tconst typesPath = join(outputDir, 'openapi-types.d.ts');\n\n\t\tlogger.log('Generating TypeScript types from OpenAPI spec...');\n\n\t\ttry {\n\t\t\t// Use npx to run openapi-typescript\n\t\t\tawait execAsync(\n\t\t\t\t`npx openapi-typescript \"${inputPath}\" -o \"${typesPath}\"`,\n\t\t\t\t{ cwd: process.cwd() },\n\t\t\t);\n\t\t\tlogger.log(`TypeScript types generated: ${typesPath}`);\n\t\t} catch (_error) {\n\t\t\tlogger.warn(\n\t\t\t\t'Could not generate types with openapi-typescript. Install it for better type inference.',\n\t\t\t);\n\t\t\tlogger.warn('Run: npm install -D openapi-typescript');\n\n\t\t\t// Generate basic types file\n\t\t\tawait mkdir(dirname(typesPath), { recursive: true });\n\t\t\tawait writeFile(\n\t\t\t\ttypesPath,\n\t\t\t\t`// 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\t\t\t);\n\t\t}\n\n\t\t// Extract operation info\n\t\tconst operations = extractOperations(spec);\n\n\t\t// Generate TypeScript code\n\t\tconst code = generateReactQueryCode(\n\t\t\tspec,\n\t\t\toperations,\n\t\t\toptions.name || 'API',\n\t\t);\n\n\t\t// Write output\n\t\tconst outputPath =\n\t\t\toptions.output || join(process.cwd(), 'src', 'api', 'hooks.ts');\n\t\tawait mkdir(dirname(outputPath), { recursive: true });\n\t\tawait writeFile(outputPath, code);\n\n\t\tlogger.log(`React Query hooks generated: ${outputPath}`);\n\t\tlogger.log(`Generated ${operations.length} hooks`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`React Query generation failed: ${(error as Error).message}`,\n\t\t);\n\t}\n}\n\ninterface OperationInfo {\n\toperationId: string;\n\tpath: string;\n\tmethod: string;\n\tendpoint: string; // Full endpoint like 'GET /users/{id}'\n\tparameters?: Array<{ name: string; in: string; required?: boolean }>;\n\trequestBody?: boolean;\n\tresponseType?: string;\n}\n\nfunction extractOperations(spec: OpenAPISpec): OperationInfo[] {\n\tconst operations: OperationInfo[] = [];\n\n\tObject.entries(spec.paths).forEach(([path, methods]) => {\n\t\tObject.entries(methods).forEach(([method, operation]) => {\n\t\t\tif (operation.operationId) {\n\t\t\t\toperations.push({\n\t\t\t\t\toperationId: operation.operationId,\n\t\t\t\t\tpath,\n\t\t\t\t\tmethod: method.toUpperCase(),\n\t\t\t\t\tendpoint: `${method.toUpperCase()} ${path}`,\n\t\t\t\t\tparameters: operation.parameters,\n\t\t\t\t\trequestBody: !!operation.requestBody,\n\t\t\t\t\tresponseType: extractResponseType(operation),\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t});\n\n\treturn operations;\n}\n\nfunction extractResponseType(operation: any): string {\n\tconst responses = operation.responses;\n\tif (!responses) return 'unknown';\n\n\tconst successResponse = responses['200'] || responses['201'];\n\tif (!successResponse?.content?.['application/json']?.schema) {\n\t\treturn 'unknown';\n\t}\n\n\t// Basic type inference from schema\n\tconst schema = successResponse.content['application/json'].schema;\n\treturn schemaToTypeString(schema);\n}\n\nfunction schemaToTypeString(schema: any): string {\n\tif (!schema) return 'unknown';\n\n\tswitch (schema.type) {\n\t\tcase 'string':\n\t\t\treturn 'string';\n\t\tcase 'number':\n\t\tcase 'integer':\n\t\t\treturn 'number';\n\t\tcase 'boolean':\n\t\t\treturn 'boolean';\n\t\tcase 'array':\n\t\t\treturn `Array<${schemaToTypeString(schema.items)}>`;\n\t\tcase 'object':\n\t\t\tif (schema.properties) {\n\t\t\t\tconst props = Object.entries(schema.properties)\n\t\t\t\t\t.map(\n\t\t\t\t\t\t([key, value]: [string, any]) =>\n\t\t\t\t\t\t\t`${key}: ${schemaToTypeString(value)}`,\n\t\t\t\t\t)\n\t\t\t\t\t.join('; ');\n\t\t\t\treturn `{ ${props} }`;\n\t\t\t}\n\t\t\treturn 'Record<string, unknown>';\n\t\tdefault:\n\t\t\treturn 'unknown';\n\t}\n}\n\nfunction generateReactQueryCode(\n\t_spec: OpenAPISpec,\n\toperations: OperationInfo[],\n\tapiName: string,\n): string {\n\tconst 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\tconst queryHooks = operations\n\t\t.filter((op) => op.method === 'GET')\n\t\t.map((op) => generateQueryHook(op, apiName))\n\t\t.join('\\n\\n');\n\n\tconst mutationHooks = operations\n\t\t.filter((op) => op.method !== 'GET')\n\t\t.map((op) => generateMutationHook(op, apiName))\n\t\t.join('\\n\\n');\n\n\tconst typeExports = generateTypeExports(operations);\n\n\treturn `${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\tconst hookName = `use${capitalize(op.operationId)}`;\n\tconst endpoint = op.endpoint;\n\tconst hasParams = op.parameters?.some((p) => p.in === 'path');\n\tconst hasQuery = op.parameters?.some((p) => p.in === 'query');\n\n\t// Generate properly typed hook\n\tlet params = '';\n\tlet args = '';\n\n\tif (hasParams || hasQuery) {\n\t\tconst paramParts: string[] = [];\n\t\tif (hasParams) {\n\t\t\tconst pathParams =\n\t\t\t\top.parameters?.filter((p) => p.in === 'path').map((p) => p.name) || [];\n\t\t\tparamParts.push(\n\t\t\t\t`params: { ${pathParams.map((p) => `${p}: string`).join('; ')} }`,\n\t\t\t);\n\t\t}\n\t\tif (hasQuery) {\n\t\t\tparamParts.push(`query?: Record<string, any>`);\n\t\t}\n\t\tparams = `config: { ${paramParts.join('; ')} }, `;\n\t\targs = ', config';\n\t}\n\n\treturn `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\tconst hookName = `use${capitalize(op.operationId)}`;\n\tconst endpoint = op.endpoint;\n\n\treturn `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\tconst exports = operations.map((op) => {\n\t\tconst typeName = capitalize(op.operationId);\n\t\tconst isQuery = op.method === 'GET';\n\n\t\tif (isQuery) {\n\t\t\treturn `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['data']>>;`;\n\t\t} else {\n\t\t\treturn `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['mutateAsync']>>;`;\n\t\t}\n\t});\n\n\treturn exports.join('\\n');\n}\n\nfunction capitalize(str: string): string {\n\treturn str.charAt(0).toUpperCase() + str.slice(1);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi-react-query-BeXvk-wa.cjs","names":["exec","options: ReactQueryOptions","spec: OpenAPISpec","operations: OperationInfo[]","operation: any","schema: any","_spec: OpenAPISpec","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\tinput?: string;\n\toutput?: string;\n\tname?: string;\n}\n\ninterface OpenAPISpec {\n\topenapi: string;\n\tinfo?: {\n\t\ttitle?: string;\n\t\tversion?: string;\n\t};\n\tpaths: Record<string, Record<string, any>>;\n}\n\nexport async function generateReactQueryCommand(\n\toptions: ReactQueryOptions = {},\n): Promise<void> {\n\tconst logger = console;\n\n\ttry {\n\t\t// Read OpenAPI spec\n\t\tconst inputPath = options.input || join(process.cwd(), 'openapi.json');\n\n\t\tif (!existsSync(inputPath)) {\n\t\t\tthrow new Error(\n\t\t\t\t`OpenAPI spec not found at ${inputPath}. Run 'npx @geekmidas/cli openapi' first.`,\n\t\t\t);\n\t\t}\n\n\t\tconst specContent = await readFile(inputPath, 'utf-8');\n\t\tconst spec: OpenAPISpec = JSON.parse(specContent);\n\n\t\t// Generate TypeScript types from OpenAPI spec\n\t\tconst outputDir = dirname(\n\t\t\toptions.output || join(process.cwd(), 'src', 'api', 'hooks.ts'),\n\t\t);\n\t\tconst typesPath = join(outputDir, 'openapi-types.d.ts');\n\n\t\tlogger.log('Generating TypeScript types from OpenAPI spec...');\n\n\t\ttry {\n\t\t\t// Use npx to run openapi-typescript\n\t\t\tawait execAsync(\n\t\t\t\t`npx openapi-typescript \"${inputPath}\" -o \"${typesPath}\"`,\n\t\t\t\t{ cwd: process.cwd() },\n\t\t\t);\n\t\t\tlogger.log(`TypeScript types generated: ${typesPath}`);\n\t\t} catch (_error) {\n\t\t\tlogger.warn(\n\t\t\t\t'Could not generate types with openapi-typescript. Install it for better type inference.',\n\t\t\t);\n\t\t\tlogger.warn('Run: npm install -D openapi-typescript');\n\n\t\t\t// Generate basic types file\n\t\t\tawait mkdir(dirname(typesPath), { recursive: true });\n\t\t\tawait writeFile(\n\t\t\t\ttypesPath,\n\t\t\t\t`// 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\t\t\t);\n\t\t}\n\n\t\t// Extract operation info\n\t\tconst operations = extractOperations(spec);\n\n\t\t// Generate TypeScript code\n\t\tconst code = generateReactQueryCode(\n\t\t\tspec,\n\t\t\toperations,\n\t\t\toptions.name || 'API',\n\t\t);\n\n\t\t// Write output\n\t\tconst outputPath =\n\t\t\toptions.output || join(process.cwd(), 'src', 'api', 'hooks.ts');\n\t\tawait mkdir(dirname(outputPath), { recursive: true });\n\t\tawait writeFile(outputPath, code);\n\n\t\tlogger.log(`React Query hooks generated: ${outputPath}`);\n\t\tlogger.log(`Generated ${operations.length} hooks`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`React Query generation failed: ${(error as Error).message}`,\n\t\t);\n\t}\n}\n\ninterface OperationInfo {\n\toperationId: string;\n\tpath: string;\n\tmethod: string;\n\tendpoint: string; // Full endpoint like 'GET /users/{id}'\n\tparameters?: Array<{ name: string; in: string; required?: boolean }>;\n\trequestBody?: boolean;\n\tresponseType?: string;\n}\n\nfunction extractOperations(spec: OpenAPISpec): OperationInfo[] {\n\tconst operations: OperationInfo[] = [];\n\n\tObject.entries(spec.paths).forEach(([path, methods]) => {\n\t\tObject.entries(methods).forEach(([method, operation]) => {\n\t\t\tif (operation.operationId) {\n\t\t\t\toperations.push({\n\t\t\t\t\toperationId: operation.operationId,\n\t\t\t\t\tpath,\n\t\t\t\t\tmethod: method.toUpperCase(),\n\t\t\t\t\tendpoint: `${method.toUpperCase()} ${path}`,\n\t\t\t\t\tparameters: operation.parameters,\n\t\t\t\t\trequestBody: !!operation.requestBody,\n\t\t\t\t\tresponseType: extractResponseType(operation),\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t});\n\n\treturn operations;\n}\n\nfunction extractResponseType(operation: any): string {\n\tconst responses = operation.responses;\n\tif (!responses) return 'unknown';\n\n\tconst successResponse = responses['200'] || responses['201'];\n\tif (!successResponse?.content?.['application/json']?.schema) {\n\t\treturn 'unknown';\n\t}\n\n\t// Basic type inference from schema\n\tconst schema = successResponse.content['application/json'].schema;\n\treturn schemaToTypeString(schema);\n}\n\nfunction schemaToTypeString(schema: any): string {\n\tif (!schema) return 'unknown';\n\n\tswitch (schema.type) {\n\t\tcase 'string':\n\t\t\treturn 'string';\n\t\tcase 'number':\n\t\tcase 'integer':\n\t\t\treturn 'number';\n\t\tcase 'boolean':\n\t\t\treturn 'boolean';\n\t\tcase 'array':\n\t\t\treturn `Array<${schemaToTypeString(schema.items)}>`;\n\t\tcase 'object':\n\t\t\tif (schema.properties) {\n\t\t\t\tconst props = Object.entries(schema.properties)\n\t\t\t\t\t.map(\n\t\t\t\t\t\t([key, value]: [string, any]) =>\n\t\t\t\t\t\t\t`${key}: ${schemaToTypeString(value)}`,\n\t\t\t\t\t)\n\t\t\t\t\t.join('; ');\n\t\t\t\treturn `{ ${props} }`;\n\t\t\t}\n\t\t\treturn 'Record<string, unknown>';\n\t\tdefault:\n\t\t\treturn 'unknown';\n\t}\n}\n\nfunction generateReactQueryCode(\n\t_spec: OpenAPISpec,\n\toperations: OperationInfo[],\n\tapiName: string,\n): string {\n\tconst 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\tconst queryHooks = operations\n\t\t.filter((op) => op.method === 'GET')\n\t\t.map((op) => generateQueryHook(op, apiName))\n\t\t.join('\\n\\n');\n\n\tconst mutationHooks = operations\n\t\t.filter((op) => op.method !== 'GET')\n\t\t.map((op) => generateMutationHook(op, apiName))\n\t\t.join('\\n\\n');\n\n\tconst typeExports = generateTypeExports(operations);\n\n\treturn `${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\tconst hookName = `use${capitalize(op.operationId)}`;\n\tconst endpoint = op.endpoint;\n\tconst hasParams = op.parameters?.some((p) => p.in === 'path');\n\tconst hasQuery = op.parameters?.some((p) => p.in === 'query');\n\n\t// Generate properly typed hook\n\tlet params = '';\n\tlet args = '';\n\n\tif (hasParams || hasQuery) {\n\t\tconst paramParts: string[] = [];\n\t\tif (hasParams) {\n\t\t\tconst pathParams =\n\t\t\t\top.parameters?.filter((p) => p.in === 'path').map((p) => p.name) || [];\n\t\t\tparamParts.push(\n\t\t\t\t`params: { ${pathParams.map((p) => `${p}: string`).join('; ')} }`,\n\t\t\t);\n\t\t}\n\t\tif (hasQuery) {\n\t\t\tparamParts.push(`query?: Record<string, any>`);\n\t\t}\n\t\tparams = `config: { ${paramParts.join('; ')} }, `;\n\t\targs = ', config';\n\t}\n\n\treturn `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\tconst hookName = `use${capitalize(op.operationId)}`;\n\tconst endpoint = op.endpoint;\n\n\treturn `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\tconst exports = operations.map((op) => {\n\t\tconst typeName = capitalize(op.operationId);\n\t\tconst isQuery = op.method === 'GET';\n\n\t\tif (isQuery) {\n\t\t\treturn `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['data']>>;`;\n\t\t} else {\n\t\t\treturn `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['mutateAsync']>>;`;\n\t\t}\n\t});\n\n\treturn exports.join('\\n');\n}\n\nfunction capitalize(str: string): string {\n\treturn str.charAt(0).toUpperCase() + str.slice(1);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"openapi-react-query-DYbBq-WJ.cjs","names":["exec","options: ReactQueryOptions","spec: OpenAPISpec","operations: OperationInfo[]","operation: any","schema: any","_spec: OpenAPISpec","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\tinput?: string;\n\toutput?: string;\n\tname?: string;\n}\n\ninterface OpenAPISpec {\n\topenapi: string;\n\tinfo?: {\n\t\ttitle?: string;\n\t\tversion?: string;\n\t};\n\tpaths: Record<string, Record<string, any>>;\n}\n\nexport async function generateReactQueryCommand(\n\toptions: ReactQueryOptions = {},\n): Promise<void> {\n\tconst logger = console;\n\n\ttry {\n\t\t// Read OpenAPI spec\n\t\tconst inputPath = options.input || join(process.cwd(), 'openapi.json');\n\n\t\tif (!existsSync(inputPath)) {\n\t\t\tthrow new Error(\n\t\t\t\t`OpenAPI spec not found at ${inputPath}. Run 'npx @geekmidas/cli openapi' first.`,\n\t\t\t);\n\t\t}\n\n\t\tconst specContent = await readFile(inputPath, 'utf-8');\n\t\tconst spec: OpenAPISpec = JSON.parse(specContent);\n\n\t\t// Generate TypeScript types from OpenAPI spec\n\t\tconst outputDir = dirname(\n\t\t\toptions.output || join(process.cwd(), 'src', 'api', 'hooks.ts'),\n\t\t);\n\t\tconst typesPath = join(outputDir, 'openapi-types.d.ts');\n\n\t\tlogger.log('Generating TypeScript types from OpenAPI spec...');\n\n\t\ttry {\n\t\t\t// Use npx to run openapi-typescript\n\t\t\tawait execAsync(\n\t\t\t\t`npx openapi-typescript \"${inputPath}\" -o \"${typesPath}\"`,\n\t\t\t\t{ cwd: process.cwd() },\n\t\t\t);\n\t\t\tlogger.log(`TypeScript types generated: ${typesPath}`);\n\t\t} catch (_error) {\n\t\t\tlogger.warn(\n\t\t\t\t'Could not generate types with openapi-typescript. Install it for better type inference.',\n\t\t\t);\n\t\t\tlogger.warn('Run: npm install -D openapi-typescript');\n\n\t\t\t// Generate basic types file\n\t\t\tawait mkdir(dirname(typesPath), { recursive: true });\n\t\t\tawait writeFile(\n\t\t\t\ttypesPath,\n\t\t\t\t`// 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\t\t\t);\n\t\t}\n\n\t\t// Extract operation info\n\t\tconst operations = extractOperations(spec);\n\n\t\t// Generate TypeScript code\n\t\tconst code = generateReactQueryCode(\n\t\t\tspec,\n\t\t\toperations,\n\t\t\toptions.name || 'API',\n\t\t);\n\n\t\t// Write output\n\t\tconst outputPath =\n\t\t\toptions.output || join(process.cwd(), 'src', 'api', 'hooks.ts');\n\t\tawait mkdir(dirname(outputPath), { recursive: true });\n\t\tawait writeFile(outputPath, code);\n\n\t\tlogger.log(`React Query hooks generated: ${outputPath}`);\n\t\tlogger.log(`Generated ${operations.length} hooks`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`React Query generation failed: ${(error as Error).message}`,\n\t\t);\n\t}\n}\n\ninterface OperationInfo {\n\toperationId: string;\n\tpath: string;\n\tmethod: string;\n\tendpoint: string; // Full endpoint like 'GET /users/{id}'\n\tparameters?: Array<{ name: string; in: string; required?: boolean }>;\n\trequestBody?: boolean;\n\tresponseType?: string;\n}\n\nfunction extractOperations(spec: OpenAPISpec): OperationInfo[] {\n\tconst operations: OperationInfo[] = [];\n\n\tObject.entries(spec.paths).forEach(([path, methods]) => {\n\t\tObject.entries(methods).forEach(([method, operation]) => {\n\t\t\tif (operation.operationId) {\n\t\t\t\toperations.push({\n\t\t\t\t\toperationId: operation.operationId,\n\t\t\t\t\tpath,\n\t\t\t\t\tmethod: method.toUpperCase(),\n\t\t\t\t\tendpoint: `${method.toUpperCase()} ${path}`,\n\t\t\t\t\tparameters: operation.parameters,\n\t\t\t\t\trequestBody: !!operation.requestBody,\n\t\t\t\t\tresponseType: extractResponseType(operation),\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t});\n\n\treturn operations;\n}\n\nfunction extractResponseType(operation: any): string {\n\tconst responses = operation.responses;\n\tif (!responses) return 'unknown';\n\n\tconst successResponse = responses['200'] || responses['201'];\n\tif (!successResponse?.content?.['application/json']?.schema) {\n\t\treturn 'unknown';\n\t}\n\n\t// Basic type inference from schema\n\tconst schema = successResponse.content['application/json'].schema;\n\treturn schemaToTypeString(schema);\n}\n\nfunction schemaToTypeString(schema: any): string {\n\tif (!schema) return 'unknown';\n\n\tswitch (schema.type) {\n\t\tcase 'string':\n\t\t\treturn 'string';\n\t\tcase 'number':\n\t\tcase 'integer':\n\t\t\treturn 'number';\n\t\tcase 'boolean':\n\t\t\treturn 'boolean';\n\t\tcase 'array':\n\t\t\treturn `Array<${schemaToTypeString(schema.items)}>`;\n\t\tcase 'object':\n\t\t\tif (schema.properties) {\n\t\t\t\tconst props = Object.entries(schema.properties)\n\t\t\t\t\t.map(\n\t\t\t\t\t\t([key, value]: [string, any]) =>\n\t\t\t\t\t\t\t`${key}: ${schemaToTypeString(value)}`,\n\t\t\t\t\t)\n\t\t\t\t\t.join('; ');\n\t\t\t\treturn `{ ${props} }`;\n\t\t\t}\n\t\t\treturn 'Record<string, unknown>';\n\t\tdefault:\n\t\t\treturn 'unknown';\n\t}\n}\n\nfunction generateReactQueryCode(\n\t_spec: OpenAPISpec,\n\toperations: OperationInfo[],\n\tapiName: string,\n): string {\n\tconst 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\tconst queryHooks = operations\n\t\t.filter((op) => op.method === 'GET')\n\t\t.map((op) => generateQueryHook(op, apiName))\n\t\t.join('\\n\\n');\n\n\tconst mutationHooks = operations\n\t\t.filter((op) => op.method !== 'GET')\n\t\t.map((op) => generateMutationHook(op, apiName))\n\t\t.join('\\n\\n');\n\n\tconst typeExports = generateTypeExports(operations);\n\n\treturn `${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\tconst hookName = `use${capitalize(op.operationId)}`;\n\tconst endpoint = op.endpoint;\n\tconst hasParams = op.parameters?.some((p) => p.in === 'path');\n\tconst hasQuery = op.parameters?.some((p) => p.in === 'query');\n\n\t// Generate properly typed hook\n\tlet params = '';\n\tlet args = '';\n\n\tif (hasParams || hasQuery) {\n\t\tconst paramParts: string[] = [];\n\t\tif (hasParams) {\n\t\t\tconst pathParams =\n\t\t\t\top.parameters?.filter((p) => p.in === 'path').map((p) => p.name) || [];\n\t\t\tparamParts.push(\n\t\t\t\t`params: { ${pathParams.map((p) => `${p}: string`).join('; ')} }`,\n\t\t\t);\n\t\t}\n\t\tif (hasQuery) {\n\t\t\tparamParts.push(`query?: Record<string, any>`);\n\t\t}\n\t\tparams = `config: { ${paramParts.join('; ')} }, `;\n\t\targs = ', config';\n\t}\n\n\treturn `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\tconst hookName = `use${capitalize(op.operationId)}`;\n\tconst endpoint = op.endpoint;\n\n\treturn `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\tconst exports = operations.map((op) => {\n\t\tconst typeName = capitalize(op.operationId);\n\t\tconst isQuery = op.method === 'GET';\n\n\t\tif (isQuery) {\n\t\t\treturn `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['data']>>;`;\n\t\t} else {\n\t\t\treturn `export type ${typeName}Response = Awaited<ReturnType<ReturnType<typeof use${typeName}>['mutateAsync']>>;`;\n\t\t}\n\t});\n\n\treturn exports.join('\\n');\n}\n\nfunction capitalize(str: string): string {\n\treturn 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-DYbBq-WJ.cjs');
|
|
3
3
|
|
|
4
4
|
exports.generateReactQueryCommand = require_openapi_react_query.generateReactQueryCommand;
|
package/dist/openapi.d.mts
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const require_fullstack_secrets = require('./fullstack-secrets-COWz084x.cjs');
|
|
2
|
+
|
|
3
|
+
//#region src/secrets/reconcile.ts
|
|
4
|
+
/**
|
|
5
|
+
* Reconcile missing custom secrets for a workspace.
|
|
6
|
+
*
|
|
7
|
+
* Compares current secrets against what generateFullstackCustomSecrets()
|
|
8
|
+
* would produce and backfills any missing keys without overwriting
|
|
9
|
+
* existing values.
|
|
10
|
+
*
|
|
11
|
+
* @returns ReconcileResult if keys were added, null if secrets are up-to-date
|
|
12
|
+
*/
|
|
13
|
+
function reconcileMissingSecrets(secrets, workspace) {
|
|
14
|
+
const isMultiApp = Object.keys(workspace.apps).length > 1;
|
|
15
|
+
if (!isMultiApp) return null;
|
|
16
|
+
const expectedCustom = require_fullstack_secrets.generateFullstackCustomSecrets(workspace);
|
|
17
|
+
const addedKeys = [];
|
|
18
|
+
const mergedCustom = { ...secrets.custom };
|
|
19
|
+
for (const [key, value] of Object.entries(expectedCustom)) if (!(key in mergedCustom)) {
|
|
20
|
+
mergedCustom[key] = value;
|
|
21
|
+
addedKeys.push(key);
|
|
22
|
+
}
|
|
23
|
+
if (addedKeys.length === 0) return null;
|
|
24
|
+
return {
|
|
25
|
+
secrets: {
|
|
26
|
+
...secrets,
|
|
27
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28
|
+
custom: mergedCustom
|
|
29
|
+
},
|
|
30
|
+
addedKeys
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
exports.reconcileMissingSecrets = reconcileMissingSecrets;
|
|
36
|
+
//# sourceMappingURL=reconcile-7yarEvmK.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile-7yarEvmK.cjs","names":["secrets: StageSecrets","workspace: NormalizedWorkspace","addedKeys: string[]"],"sources":["../src/secrets/reconcile.ts"],"sourcesContent":["import { generateFullstackCustomSecrets } from '../setup/fullstack-secrets.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface ReconcileResult {\n\t/** The updated secrets with missing keys backfilled */\n\tsecrets: StageSecrets;\n\t/** Keys that were added */\n\taddedKeys: string[];\n}\n\n/**\n * Reconcile missing custom secrets for a workspace.\n *\n * Compares current secrets against what generateFullstackCustomSecrets()\n * would produce and backfills any missing keys without overwriting\n * existing values.\n *\n * @returns ReconcileResult if keys were added, null if secrets are up-to-date\n */\nexport function reconcileMissingSecrets(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n): ReconcileResult | null {\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (!isMultiApp) {\n\t\treturn null;\n\t}\n\n\tconst expectedCustom = generateFullstackCustomSecrets(workspace);\n\tconst addedKeys: string[] = [];\n\tconst mergedCustom = { ...secrets.custom };\n\n\tfor (const [key, value] of Object.entries(expectedCustom)) {\n\t\tif (!(key in mergedCustom)) {\n\t\t\tmergedCustom[key] = value;\n\t\t\taddedKeys.push(key);\n\t\t}\n\t}\n\n\tif (addedKeys.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsecrets: {\n\t\t\t...secrets,\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\tcustom: mergedCustom,\n\t\t},\n\t\taddedKeys,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,SAAgB,wBACfA,SACAC,WACyB;CACzB,MAAM,aAAa,OAAO,KAAK,UAAU,KAAK,CAAC,SAAS;AACxD,MAAK,WACJ,QAAO;CAGR,MAAM,iBAAiB,yDAA+B,UAAU;CAChE,MAAMC,YAAsB,CAAE;CAC9B,MAAM,eAAe,EAAE,GAAG,QAAQ,OAAQ;AAE1C,MAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,eAAe,CACxD,OAAM,OAAO,eAAe;AAC3B,eAAa,OAAO;AACpB,YAAU,KAAK,IAAI;CACnB;AAGF,KAAI,UAAU,WAAW,EACxB,QAAO;AAGR,QAAO;EACN,SAAS;GACR,GAAG;GACH,WAAW,qBAAI,QAAO,aAAa;GACnC,QAAQ;EACR;EACD;CACA;AACD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { generateFullstackCustomSecrets } from "./fullstack-secrets-UZAFWuH4.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/secrets/reconcile.ts
|
|
4
|
+
/**
|
|
5
|
+
* Reconcile missing custom secrets for a workspace.
|
|
6
|
+
*
|
|
7
|
+
* Compares current secrets against what generateFullstackCustomSecrets()
|
|
8
|
+
* would produce and backfills any missing keys without overwriting
|
|
9
|
+
* existing values.
|
|
10
|
+
*
|
|
11
|
+
* @returns ReconcileResult if keys were added, null if secrets are up-to-date
|
|
12
|
+
*/
|
|
13
|
+
function reconcileMissingSecrets(secrets, workspace) {
|
|
14
|
+
const isMultiApp = Object.keys(workspace.apps).length > 1;
|
|
15
|
+
if (!isMultiApp) return null;
|
|
16
|
+
const expectedCustom = generateFullstackCustomSecrets(workspace);
|
|
17
|
+
const addedKeys = [];
|
|
18
|
+
const mergedCustom = { ...secrets.custom };
|
|
19
|
+
for (const [key, value] of Object.entries(expectedCustom)) if (!(key in mergedCustom)) {
|
|
20
|
+
mergedCustom[key] = value;
|
|
21
|
+
addedKeys.push(key);
|
|
22
|
+
}
|
|
23
|
+
if (addedKeys.length === 0) return null;
|
|
24
|
+
return {
|
|
25
|
+
secrets: {
|
|
26
|
+
...secrets,
|
|
27
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28
|
+
custom: mergedCustom
|
|
29
|
+
},
|
|
30
|
+
addedKeys
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { reconcileMissingSecrets };
|
|
36
|
+
//# sourceMappingURL=reconcile-D2WCDQue.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile-D2WCDQue.mjs","names":["secrets: StageSecrets","workspace: NormalizedWorkspace","addedKeys: string[]"],"sources":["../src/secrets/reconcile.ts"],"sourcesContent":["import { generateFullstackCustomSecrets } from '../setup/fullstack-secrets.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface ReconcileResult {\n\t/** The updated secrets with missing keys backfilled */\n\tsecrets: StageSecrets;\n\t/** Keys that were added */\n\taddedKeys: string[];\n}\n\n/**\n * Reconcile missing custom secrets for a workspace.\n *\n * Compares current secrets against what generateFullstackCustomSecrets()\n * would produce and backfills any missing keys without overwriting\n * existing values.\n *\n * @returns ReconcileResult if keys were added, null if secrets are up-to-date\n */\nexport function reconcileMissingSecrets(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n): ReconcileResult | null {\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (!isMultiApp) {\n\t\treturn null;\n\t}\n\n\tconst expectedCustom = generateFullstackCustomSecrets(workspace);\n\tconst addedKeys: string[] = [];\n\tconst mergedCustom = { ...secrets.custom };\n\n\tfor (const [key, value] of Object.entries(expectedCustom)) {\n\t\tif (!(key in mergedCustom)) {\n\t\t\tmergedCustom[key] = value;\n\t\t\taddedKeys.push(key);\n\t\t}\n\t}\n\n\tif (addedKeys.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsecrets: {\n\t\t\t...secrets,\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\tcustom: mergedCustom,\n\t\t},\n\t\taddedKeys,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,SAAgB,wBACfA,SACAC,WACyB;CACzB,MAAM,aAAa,OAAO,KAAK,UAAU,KAAK,CAAC,SAAS;AACxD,MAAK,WACJ,QAAO;CAGR,MAAM,iBAAiB,+BAA+B,UAAU;CAChE,MAAMC,YAAsB,CAAE;CAC9B,MAAM,eAAe,EAAE,GAAG,QAAQ,OAAQ;AAE1C,MAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,eAAe,CACxD,OAAM,OAAO,eAAe;AAC3B,eAAa,OAAO;AACpB,YAAU,KAAK,IAAI;CACnB;AAGF,KAAI,UAAU,WAAW,EACxB,QAAO;AAGR,QAAO;EACN,SAAS;GACR,GAAG;GACH,WAAW,qBAAI,QAAO,aAAa;GACnC,QAAQ;EACR;EACD;CACA;AACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-
|
|
1
|
+
{"version":3,"file":"sync-CbeKrnQV.mjs","names":["workspaceName: string","stage: string","config: SSMStateConfig","clientConfig: SSMClientConfig","workspace: NormalizedWorkspace"],"sources":["../src/secrets/sync.ts"],"sourcesContent":["/**\n * Secrets Sync via AWS SSM Parameter Store\n *\n * Stores and retrieves encrypted StageSecrets as SecureString parameters.\n * Reuses the SSM infrastructure from the state provider.\n *\n * Parameter naming: /gkm/{workspaceName}/{stage}/secrets\n */\n\nimport {\n\tGetParameterCommand,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { SSMStateConfig } from '../deploy/StateProvider.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\n/**\n * Get the SSM parameter name for secrets.\n */\nfunction getSecretsParameterName(workspaceName: string, stage: string): string {\n\treturn `/gkm/${workspaceName}/${stage}/secrets`;\n}\n\n/**\n * Create an SSM client from workspace state config.\n */\nfunction createSSMClient(config: SSMStateConfig): SSMClient {\n\tconst clientConfig: SSMClientConfig = {\n\t\tregion: config.region,\n\t};\n\n\tif (config.profile) {\n\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\tclientConfig.credentials = fromIni({ profile: config.profile });\n\t}\n\n\treturn new SSMClient(clientConfig);\n}\n\n/**\n * Push secrets to SSM Parameter Store.\n *\n * Stores the full StageSecrets object as a SecureString parameter.\n */\nexport async function pushSecrets(\n\tstage: string,\n\tworkspace: NormalizedWorkspace,\n): Promise<void> {\n\tconst config = workspace.state;\n\tif (!config || config.provider !== 'ssm') {\n\t\tthrow new Error(\n\t\t\t'SSM state provider not configured. Add state: { provider: \"ssm\", region: \"...\" } to gkm.config.ts.',\n\t\t);\n\t}\n\n\tif (!workspace.name) {\n\t\tthrow new Error(\n\t\t\t'Workspace name is required for SSM secrets sync. Set \"name\" in gkm.config.ts.',\n\t\t);\n\t}\n\n\tconst client = createSSMClient(config as SSMStateConfig);\n\tconst parameterName = getSecretsParameterName(workspace.name, stage);\n\n\tconst { readStageSecrets } = await import('./storage.js');\n\tconst secrets = await readStageSecrets(stage, workspace.root);\n\n\tif (!secrets) {\n\t\tthrow new Error(\n\t\t\t`No secrets found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t}\n\n\tawait client.send(\n\t\tnew PutParameterCommand({\n\t\t\tName: parameterName,\n\t\t\tValue: JSON.stringify(secrets),\n\t\t\tType: 'SecureString',\n\t\t\tOverwrite: true,\n\t\t\tDescription: `GKM secrets for ${workspace.name}/${stage}`,\n\t\t}),\n\t);\n}\n\n/**\n * Pull secrets from SSM Parameter Store.\n *\n * @returns StageSecrets or null if no secrets are stored remotely\n */\nexport async function pullSecrets(\n\tstage: string,\n\tworkspace: NormalizedWorkspace,\n): Promise<StageSecrets | null> {\n\tconst config = workspace.state;\n\tif (!config || config.provider !== 'ssm') {\n\t\treturn null;\n\t}\n\n\tif (!workspace.name) {\n\t\treturn null;\n\t}\n\n\tconst client = createSSMClient(config as SSMStateConfig);\n\tconst parameterName = getSecretsParameterName(workspace.name, stage);\n\n\ttry {\n\t\tconst response = await client.send(\n\t\t\tnew GetParameterCommand({\n\t\t\t\tName: parameterName,\n\t\t\t\tWithDecryption: true,\n\t\t\t}),\n\t\t);\n\n\t\tif (!response.Parameter?.Value) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn JSON.parse(response.Parameter.Value) as StageSecrets;\n\t} catch (error) {\n\t\tif (error instanceof ParameterNotFound) {\n\t\t\treturn null;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Check if SSM is configured for the workspace.\n */\nexport function isSSMConfigured(workspace: NormalizedWorkspace): boolean {\n\treturn !!workspace.state && workspace.state.provider === 'ssm';\n}\n"],"mappings":";;;;;;;AAuBA,SAAS,wBAAwBA,eAAuBC,OAAuB;AAC9E,SAAQ,OAAO,cAAc,GAAG,MAAM;AACtC;;;;AAKD,SAAS,gBAAgBC,QAAmC;CAC3D,MAAMC,eAAgC,EACrC,QAAQ,OAAO,OACf;AAED,KAAI,OAAO,SAAS;EACnB,MAAM,EAAE,SAAS,GAAG,UAAQ,gCAAgC;AAC5D,eAAa,cAAc,QAAQ,EAAE,SAAS,OAAO,QAAS,EAAC;CAC/D;AAED,QAAO,IAAI,UAAU;AACrB;;;;;;AAOD,eAAsB,YACrBF,OACAG,WACgB;CAChB,MAAM,SAAS,UAAU;AACzB,MAAK,UAAU,OAAO,aAAa,MAClC,OAAM,IAAI,MACT;AAIF,MAAK,UAAU,KACd,OAAM,IAAI,MACT;CAIF,MAAM,SAAS,gBAAgB,OAAyB;CACxD,MAAM,gBAAgB,wBAAwB,UAAU,MAAM,MAAM;CAEpE,MAAM,EAAE,kBAAkB,GAAG,MAAM,OAAO;CAC1C,MAAM,UAAU,MAAM,iBAAiB,OAAO,UAAU,KAAK;AAE7D,MAAK,QACJ,OAAM,IAAI,OACR,8BAA8B,MAAM,mCAAmC,MAAM;AAIhF,OAAM,OAAO,KACZ,IAAI,oBAAoB;EACvB,MAAM;EACN,OAAO,KAAK,UAAU,QAAQ;EAC9B,MAAM;EACN,WAAW;EACX,cAAc,kBAAkB,UAAU,KAAK,GAAG,MAAM;CACxD,GACD;AACD;;;;;;AAOD,eAAsB,YACrBH,OACAG,WAC+B;CAC/B,MAAM,SAAS,UAAU;AACzB,MAAK,UAAU,OAAO,aAAa,MAClC,QAAO;AAGR,MAAK,UAAU,KACd,QAAO;CAGR,MAAM,SAAS,gBAAgB,OAAyB;CACxD,MAAM,gBAAgB,wBAAwB,UAAU,MAAM,MAAM;AAEpE,KAAI;EACH,MAAM,WAAW,MAAM,OAAO,KAC7B,IAAI,oBAAoB;GACvB,MAAM;GACN,gBAAgB;EAChB,GACD;AAED,OAAK,SAAS,WAAW,MACxB,QAAO;AAGR,SAAO,KAAK,MAAM,SAAS,UAAU,MAAM;CAC3C,SAAQ,OAAO;AACf,MAAI,iBAAiB,kBACpB,QAAO;AAER,QAAM;CACN;AACD;;;;AAKD,SAAgB,gBAAgBA,WAAyC;AACxE,UAAS,UAAU,SAAS,UAAU,MAAM,aAAa;AACzD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-
|
|
1
|
+
{"version":3,"file":"sync-DdkKaHqP.cjs","names":["workspaceName: string","stage: string","config: SSMStateConfig","clientConfig: SSMClientConfig","SSMClient","workspace: NormalizedWorkspace","PutParameterCommand","GetParameterCommand","ParameterNotFound"],"sources":["../src/secrets/sync.ts"],"sourcesContent":["/**\n * Secrets Sync via AWS SSM Parameter Store\n *\n * Stores and retrieves encrypted StageSecrets as SecureString parameters.\n * Reuses the SSM infrastructure from the state provider.\n *\n * Parameter naming: /gkm/{workspaceName}/{stage}/secrets\n */\n\nimport {\n\tGetParameterCommand,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { SSMStateConfig } from '../deploy/StateProvider.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\n/**\n * Get the SSM parameter name for secrets.\n */\nfunction getSecretsParameterName(workspaceName: string, stage: string): string {\n\treturn `/gkm/${workspaceName}/${stage}/secrets`;\n}\n\n/**\n * Create an SSM client from workspace state config.\n */\nfunction createSSMClient(config: SSMStateConfig): SSMClient {\n\tconst clientConfig: SSMClientConfig = {\n\t\tregion: config.region,\n\t};\n\n\tif (config.profile) {\n\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\tclientConfig.credentials = fromIni({ profile: config.profile });\n\t}\n\n\treturn new SSMClient(clientConfig);\n}\n\n/**\n * Push secrets to SSM Parameter Store.\n *\n * Stores the full StageSecrets object as a SecureString parameter.\n */\nexport async function pushSecrets(\n\tstage: string,\n\tworkspace: NormalizedWorkspace,\n): Promise<void> {\n\tconst config = workspace.state;\n\tif (!config || config.provider !== 'ssm') {\n\t\tthrow new Error(\n\t\t\t'SSM state provider not configured. Add state: { provider: \"ssm\", region: \"...\" } to gkm.config.ts.',\n\t\t);\n\t}\n\n\tif (!workspace.name) {\n\t\tthrow new Error(\n\t\t\t'Workspace name is required for SSM secrets sync. Set \"name\" in gkm.config.ts.',\n\t\t);\n\t}\n\n\tconst client = createSSMClient(config as SSMStateConfig);\n\tconst parameterName = getSecretsParameterName(workspace.name, stage);\n\n\tconst { readStageSecrets } = await import('./storage.js');\n\tconst secrets = await readStageSecrets(stage, workspace.root);\n\n\tif (!secrets) {\n\t\tthrow new Error(\n\t\t\t`No secrets found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t}\n\n\tawait client.send(\n\t\tnew PutParameterCommand({\n\t\t\tName: parameterName,\n\t\t\tValue: JSON.stringify(secrets),\n\t\t\tType: 'SecureString',\n\t\t\tOverwrite: true,\n\t\t\tDescription: `GKM secrets for ${workspace.name}/${stage}`,\n\t\t}),\n\t);\n}\n\n/**\n * Pull secrets from SSM Parameter Store.\n *\n * @returns StageSecrets or null if no secrets are stored remotely\n */\nexport async function pullSecrets(\n\tstage: string,\n\tworkspace: NormalizedWorkspace,\n): Promise<StageSecrets | null> {\n\tconst config = workspace.state;\n\tif (!config || config.provider !== 'ssm') {\n\t\treturn null;\n\t}\n\n\tif (!workspace.name) {\n\t\treturn null;\n\t}\n\n\tconst client = createSSMClient(config as SSMStateConfig);\n\tconst parameterName = getSecretsParameterName(workspace.name, stage);\n\n\ttry {\n\t\tconst response = await client.send(\n\t\t\tnew GetParameterCommand({\n\t\t\t\tName: parameterName,\n\t\t\t\tWithDecryption: true,\n\t\t\t}),\n\t\t);\n\n\t\tif (!response.Parameter?.Value) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn JSON.parse(response.Parameter.Value) as StageSecrets;\n\t} catch (error) {\n\t\tif (error instanceof ParameterNotFound) {\n\t\t\treturn null;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Check if SSM is configured for the workspace.\n */\nexport function isSSMConfigured(workspace: NormalizedWorkspace): boolean {\n\treturn !!workspace.state && workspace.state.provider === 'ssm';\n}\n"],"mappings":";;;;;;;AAuBA,SAAS,wBAAwBA,eAAuBC,OAAuB;AAC9E,SAAQ,OAAO,cAAc,GAAG,MAAM;AACtC;;;;AAKD,SAAS,gBAAgBC,QAAmC;CAC3D,MAAMC,eAAgC,EACrC,QAAQ,OAAO,OACf;AAED,KAAI,OAAO,SAAS;EACnB,MAAM,EAAE,SAAS,GAAG,QAAQ,gCAAgC;AAC5D,eAAa,cAAc,QAAQ,EAAE,SAAS,OAAO,QAAS,EAAC;CAC/D;AAED,QAAO,IAAIC,+BAAU;AACrB;;;;;;AAOD,eAAsB,YACrBH,OACAI,WACgB;CAChB,MAAM,SAAS,UAAU;AACzB,MAAK,UAAU,OAAO,aAAa,MAClC,OAAM,IAAI,MACT;AAIF,MAAK,UAAU,KACd,OAAM,IAAI,MACT;CAIF,MAAM,SAAS,gBAAgB,OAAyB;CACxD,MAAM,gBAAgB,wBAAwB,UAAU,MAAM,MAAM;CAEpE,MAAM,EAAE,kBAAkB,GAAG,2CAAM;CACnC,MAAM,UAAU,MAAM,iBAAiB,OAAO,UAAU,KAAK;AAE7D,MAAK,QACJ,OAAM,IAAI,OACR,8BAA8B,MAAM,mCAAmC,MAAM;AAIhF,OAAM,OAAO,KACZ,IAAIC,yCAAoB;EACvB,MAAM;EACN,OAAO,KAAK,UAAU,QAAQ;EAC9B,MAAM;EACN,WAAW;EACX,cAAc,kBAAkB,UAAU,KAAK,GAAG,MAAM;CACxD,GACD;AACD;;;;;;AAOD,eAAsB,YACrBL,OACAI,WAC+B;CAC/B,MAAM,SAAS,UAAU;AACzB,MAAK,UAAU,OAAO,aAAa,MAClC,QAAO;AAGR,MAAK,UAAU,KACd,QAAO;CAGR,MAAM,SAAS,gBAAgB,OAAyB;CACxD,MAAM,gBAAgB,wBAAwB,UAAU,MAAM,MAAM;AAEpE,KAAI;EACH,MAAM,WAAW,MAAM,OAAO,KAC7B,IAAIE,yCAAoB;GACvB,MAAM;GACN,gBAAgB;EAChB,GACD;AAED,OAAK,SAAS,WAAW,MACxB,QAAO;AAGR,SAAO,KAAK,MAAM,SAAS,UAAU,MAAM;CAC3C,SAAQ,OAAO;AACf,MAAI,iBAAiBC,uCACpB,QAAO;AAER,QAAM;CACN;AACD;;;;AAKD,SAAgB,gBAAgBH,WAAyC;AACxE,UAAS,UAAU,SAAS,UAAU,MAAM,aAAa;AACzD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types-
|
|
1
|
+
{"version":3,"file":"types-wXMIMOyK.d.mts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;AASiB,KAFL,MAAA,GAEmB,MAAA,GAAA,MAAA,EAAA;AAKd,UALA,cAAA,CAKoB;EAIpB,OAAA,CAAA,EAAA,OAAA;EAIA,SAAA,CAAA,EAAA,MAAA;AA4BjB;AAcY,UAlDK,mBAAA,SAA4B,cAkDf,CAAA,CAG9B;AAAiC,UAjDhB,eAAA,SAAwB,cAiDR,CAAA;AACM,UA9CtB,gBAAA,CA8CsB;EAAa;EAGnC,OAAA,CAAA,EAAA,OAAY;EAAA;EAAA,MAwBhB,CAAA,EAAA,OAAA;EAAqB;EAAqB,MAAA,CAAA,EAAA,OAAA;EAItC;EAAa,WAAA,CAAA,EAAA,MAAA;EAAA;EAIA,gBAJQ,CAAA,EAAA,OAAA;EAAc;EAOxC,QAAA,CAAA,EAAO,MAAA,EAAA;EAEF;EAiBA,WAAA,CAAA,EAAA,SAAY,GAAA,SAAA;EASZ;EAWA,OAAA,CAAA,EAAA,OAAW;EA6BX;AAajB;;;;;;EAQoC,iBAGhB,CAAA,EAAA,OAAA;;AAEsB;AAGzB,UAzJA,aAAA,CAyJS;EAAA;;;;EAGX,KACA,CAAA,EAAA,MAAA;EAAM;;;;EA+BoB,OAmBpB,CAAA,EAAA,MAAA;;;AA8BC,KA/NV,kBAAA,GA+NU,UAAA,GAAA,OAAA,GAAA,UAAA;;KA5NV,qBAAA,WACL,gCAAgC;UAGtB,YAAA;;;;;;;;;;;;;;;;;;;;;;;;eAwBJ,wBAAwB;;;UAIpB,YAAA,SAAqB;;;;eAIxB;;KAGF,OAAA;UAEK,eAAA;;;;;;;;;;;;;;;;UAiBA,YAAA;;;;;;;;UASA,aAAA;;;;;;;;;;UAWA,WAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA6BA,qBAAA;;;;;;;;;;;;UAaA,eAAA;;;qBAGC;qBACA;;;4BAGO;wBACJ;;;qBAGD;;sBAEC;;UAGJ,SAAA;UACR;cACI;UACJ;gBACM;;;cAGF;;;;;;;;;;UAUJ;;;;;;;;iCAQuB;;;;;;;;;;8BAUH;;;;;;;;;;;;;;;;;;;sBAmBR;;YAEV;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA4BD"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import "../types-
|
|
2
|
-
import { AppConfig, AppConfigInput, AppInput, AppsRecord, BackendFramework, ClientConfig, ConstrainedApps, DeployConfig, DeployTarget, DokployWorkspaceConfig, FrontendFramework, InferAppNames, InferredWorkspaceConfig, LoadedConfig, MailServiceConfig, ModelsConfig, NormalizedAppConfig, NormalizedWorkspace, PHASE_2_DEPLOY_TARGETS, SUPPORTED_DEPLOY_TARGETS, SecretsConfig, ServiceImageConfig, ServicesConfig, SharedConfig, WorkspaceConfig, WorkspaceConfigSchema, WorkspaceInput, defineWorkspace, formatValidationErrors, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getDeployTargetError, getEndpointForStage, isDeployTargetSupported, isPhase2DeployTarget, isWorkspaceConfig, normalizeWorkspace, processConfig, safeValidateWorkspaceConfig, validateWorkspaceConfig, wrapSingleAppAsWorkspace } from "../index-
|
|
1
|
+
import "../types-wXMIMOyK.mjs";
|
|
2
|
+
import { AppConfig, AppConfigInput, AppInput, AppsRecord, BackendFramework, ClientConfig, ConstrainedApps, DeployConfig, DeployTarget, DokployWorkspaceConfig, FrontendFramework, InferAppNames, InferredWorkspaceConfig, LoadedConfig, MailServiceConfig, ModelsConfig, NormalizedAppConfig, NormalizedWorkspace, PHASE_2_DEPLOY_TARGETS, SUPPORTED_DEPLOY_TARGETS, SecretsConfig, ServiceImageConfig, ServicesConfig, SharedConfig, WorkspaceConfig, WorkspaceConfigSchema, WorkspaceInput, defineWorkspace, formatValidationErrors, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getDeployTargetError, getEndpointForStage, isDeployTargetSupported, isPhase2DeployTarget, isWorkspaceConfig, normalizeWorkspace, processConfig, safeValidateWorkspaceConfig, validateWorkspaceConfig, wrapSingleAppAsWorkspace } from "../index-Bt2kX0-R.mjs";
|
|
3
3
|
export { AppConfig, AppConfigInput, AppInput, AppsRecord, BackendFramework, ClientConfig, ConstrainedApps, DeployConfig, DeployTarget, DokployWorkspaceConfig, FrontendFramework, InferAppNames, InferredWorkspaceConfig, LoadedConfig, MailServiceConfig, ModelsConfig, NormalizedAppConfig, NormalizedWorkspace, PHASE_2_DEPLOY_TARGETS, SUPPORTED_DEPLOY_TARGETS, SecretsConfig, ServiceImageConfig, ServicesConfig, SharedConfig, WorkspaceConfig, WorkspaceConfigSchema, WorkspaceInput, defineWorkspace, formatValidationErrors, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getDeployTargetError, getEndpointForStage, isDeployTargetSupported, isPhase2DeployTarget, isWorkspaceConfig, normalizeWorkspace, processConfig, safeValidateWorkspaceConfig, validateWorkspaceConfig, wrapSingleAppAsWorkspace };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geekmidas/cli",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
"yaml": "~2.8.2",
|
|
59
59
|
"@geekmidas/constructs": "~2.0.0",
|
|
60
60
|
"@geekmidas/envkit": "~1.0.3",
|
|
61
|
-
"@geekmidas/errors": "~1.0.0",
|
|
62
61
|
"@geekmidas/logger": "~1.0.0",
|
|
63
|
-
"@geekmidas/schema": "~1.0.0"
|
|
62
|
+
"@geekmidas/schema": "~1.0.0",
|
|
63
|
+
"@geekmidas/errors": "~1.0.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@types/lodash.kebabcase": "^4.1.9",
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
checkPortConflicts,
|
|
13
13
|
findAvailablePort,
|
|
14
14
|
generateAllDependencyEnvVars,
|
|
15
|
+
generateServerEntryContent,
|
|
15
16
|
isPortAvailable,
|
|
16
17
|
loadPortState,
|
|
17
18
|
loadSecretsForApp,
|
|
@@ -1678,3 +1679,51 @@ services:
|
|
|
1678
1679
|
expect(mappings).toEqual([]);
|
|
1679
1680
|
});
|
|
1680
1681
|
});
|
|
1682
|
+
|
|
1683
|
+
describe('generateServerEntryContent', () => {
|
|
1684
|
+
it('should use dynamic import for createApp when secrets are provided', () => {
|
|
1685
|
+
const content = generateServerEntryContent({
|
|
1686
|
+
secretsJsonPath: '/tmp/dev-secrets.json',
|
|
1687
|
+
});
|
|
1688
|
+
|
|
1689
|
+
// createApp must be a dynamic import so Credentials are populated first
|
|
1690
|
+
expect(content).toContain("await import('./app.js')");
|
|
1691
|
+
// Must NOT have a static import of createApp
|
|
1692
|
+
expect(content).not.toMatch(/^import.*createApp/m);
|
|
1693
|
+
});
|
|
1694
|
+
|
|
1695
|
+
it('should use dynamic import for createApp without secrets', () => {
|
|
1696
|
+
const content = generateServerEntryContent({});
|
|
1697
|
+
|
|
1698
|
+
expect(content).toContain("await import('./app.js')");
|
|
1699
|
+
expect(content).not.toMatch(/^import.*createApp/m);
|
|
1700
|
+
});
|
|
1701
|
+
|
|
1702
|
+
it('should inject Credentials assignment before dynamic import', () => {
|
|
1703
|
+
const content = generateServerEntryContent({
|
|
1704
|
+
secretsJsonPath: '/tmp/dev-secrets.json',
|
|
1705
|
+
});
|
|
1706
|
+
|
|
1707
|
+
const credentialsAssignIdx = content.indexOf('Object.assign(Credentials');
|
|
1708
|
+
const dynamicImportIdx = content.indexOf("await import('./app.js')");
|
|
1709
|
+
|
|
1710
|
+
expect(credentialsAssignIdx).toBeGreaterThan(-1);
|
|
1711
|
+
expect(dynamicImportIdx).toBeGreaterThan(-1);
|
|
1712
|
+
expect(credentialsAssignIdx).toBeLessThan(dynamicImportIdx);
|
|
1713
|
+
});
|
|
1714
|
+
|
|
1715
|
+
it('should not include credentials injection when no secrets path', () => {
|
|
1716
|
+
const content = generateServerEntryContent({});
|
|
1717
|
+
|
|
1718
|
+
expect(content).not.toContain('Object.assign(Credentials');
|
|
1719
|
+
expect(content).not.toContain('existsSync');
|
|
1720
|
+
});
|
|
1721
|
+
|
|
1722
|
+
it('should use custom app import path when provided', () => {
|
|
1723
|
+
const content = generateServerEntryContent({
|
|
1724
|
+
appImportPath: './custom-app.js',
|
|
1725
|
+
});
|
|
1726
|
+
|
|
1727
|
+
expect(content).toContain("await import('./custom-app.js')");
|
|
1728
|
+
});
|
|
1729
|
+
});
|
package/src/dev/index.ts
CHANGED
|
@@ -1832,6 +1832,85 @@ class EntryRunner {
|
|
|
1832
1832
|
}
|
|
1833
1833
|
}
|
|
1834
1834
|
|
|
1835
|
+
/**
|
|
1836
|
+
* Generate the content of the dev server entry file (server.ts).
|
|
1837
|
+
* Uses dynamic import for createApp so Credentials are populated
|
|
1838
|
+
* before any app modules evaluate.
|
|
1839
|
+
* @internal Exported for testing
|
|
1840
|
+
*/
|
|
1841
|
+
export function generateServerEntryContent(options: {
|
|
1842
|
+
secretsJsonPath?: string;
|
|
1843
|
+
runtime?: Runtime;
|
|
1844
|
+
enableOpenApi?: boolean;
|
|
1845
|
+
appImportPath?: string;
|
|
1846
|
+
}): string {
|
|
1847
|
+
const {
|
|
1848
|
+
secretsJsonPath,
|
|
1849
|
+
runtime = 'node',
|
|
1850
|
+
enableOpenApi = false,
|
|
1851
|
+
appImportPath = './app.js',
|
|
1852
|
+
} = options;
|
|
1853
|
+
|
|
1854
|
+
const credentialsInjection = secretsJsonPath
|
|
1855
|
+
? `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
1856
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
1857
|
+
|
|
1858
|
+
// Inject dev secrets into Credentials (must happen before app import)
|
|
1859
|
+
const secretsPath = '${secretsJsonPath}';
|
|
1860
|
+
if (existsSync(secretsPath)) {
|
|
1861
|
+
Object.assign(Credentials, JSON.parse(readFileSync(secretsPath, 'utf-8')));
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
`
|
|
1865
|
+
: '';
|
|
1866
|
+
|
|
1867
|
+
const serveCode =
|
|
1868
|
+
runtime === 'bun'
|
|
1869
|
+
? `Bun.serve({
|
|
1870
|
+
port,
|
|
1871
|
+
fetch: app.fetch,
|
|
1872
|
+
});`
|
|
1873
|
+
: `const { serve } = await import('@hono/node-server');
|
|
1874
|
+
const server = serve({
|
|
1875
|
+
fetch: app.fetch,
|
|
1876
|
+
port,
|
|
1877
|
+
});
|
|
1878
|
+
// Inject WebSocket support if available
|
|
1879
|
+
const injectWs = (app as any).__injectWebSocket;
|
|
1880
|
+
if (injectWs) {
|
|
1881
|
+
injectWs(server);
|
|
1882
|
+
console.log('🔌 Telescope real-time updates enabled');
|
|
1883
|
+
}`;
|
|
1884
|
+
|
|
1885
|
+
return `#!/usr/bin/env node
|
|
1886
|
+
/**
|
|
1887
|
+
* Development server entry point
|
|
1888
|
+
* This file is auto-generated by 'gkm dev'
|
|
1889
|
+
*/
|
|
1890
|
+
${credentialsInjection}
|
|
1891
|
+
const port = process.argv.includes('--port')
|
|
1892
|
+
? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
|
|
1893
|
+
: 3000;
|
|
1894
|
+
|
|
1895
|
+
// Dynamic import so Credentials are populated before env.ts evaluates
|
|
1896
|
+
const { createApp } = await import('${appImportPath}');
|
|
1897
|
+
|
|
1898
|
+
// createApp is async to support optional WebSocket setup
|
|
1899
|
+
const { app, start } = await createApp(undefined, ${enableOpenApi});
|
|
1900
|
+
|
|
1901
|
+
// Start the server
|
|
1902
|
+
start({
|
|
1903
|
+
port,
|
|
1904
|
+
serve: async (app, port) => {
|
|
1905
|
+
${serveCode}
|
|
1906
|
+
},
|
|
1907
|
+
}).catch((error) => {
|
|
1908
|
+
console.error('Failed to start server:', error);
|
|
1909
|
+
process.exit(1);
|
|
1910
|
+
});
|
|
1911
|
+
`;
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1835
1914
|
class DevServer {
|
|
1836
1915
|
private serverProcess: ChildProcess | null = null;
|
|
1837
1916
|
private isRunning = false;
|
|
@@ -1997,72 +2076,14 @@ class DevServer {
|
|
|
1997
2076
|
|
|
1998
2077
|
private async createServerEntry(): Promise<void> {
|
|
1999
2078
|
const { writeFile: fsWriteFile } = await import('node:fs/promises');
|
|
2000
|
-
const { relative, dirname } = await import('node:path');
|
|
2001
2079
|
|
|
2002
2080
|
const serverPath = join(this.appRoot, '.gkm', this.provider, 'server.ts');
|
|
2003
2081
|
|
|
2004
|
-
const
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
// Generate credentials injection code if secrets are available
|
|
2010
|
-
const credentialsInjection = this.secretsJsonPath
|
|
2011
|
-
? `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
2012
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2013
|
-
|
|
2014
|
-
// Inject dev secrets into Credentials (must happen before app import)
|
|
2015
|
-
const secretsPath = '${this.secretsJsonPath}';
|
|
2016
|
-
if (existsSync(secretsPath)) {
|
|
2017
|
-
Object.assign(Credentials, JSON.parse(readFileSync(secretsPath, 'utf-8')));
|
|
2018
|
-
}
|
|
2019
|
-
|
|
2020
|
-
`
|
|
2021
|
-
: '';
|
|
2022
|
-
|
|
2023
|
-
const serveCode =
|
|
2024
|
-
this.runtime === 'bun'
|
|
2025
|
-
? `Bun.serve({
|
|
2026
|
-
port,
|
|
2027
|
-
fetch: app.fetch,
|
|
2028
|
-
});`
|
|
2029
|
-
: `const { serve } = await import('@hono/node-server');
|
|
2030
|
-
const server = serve({
|
|
2031
|
-
fetch: app.fetch,
|
|
2032
|
-
port,
|
|
2033
|
-
});
|
|
2034
|
-
// Inject WebSocket support if available
|
|
2035
|
-
const injectWs = (app as any).__injectWebSocket;
|
|
2036
|
-
if (injectWs) {
|
|
2037
|
-
injectWs(server);
|
|
2038
|
-
console.log('🔌 Telescope real-time updates enabled');
|
|
2039
|
-
}`;
|
|
2040
|
-
|
|
2041
|
-
const content = `#!/usr/bin/env node
|
|
2042
|
-
/**
|
|
2043
|
-
* Development server entry point
|
|
2044
|
-
* This file is auto-generated by 'gkm dev'
|
|
2045
|
-
*/
|
|
2046
|
-
${credentialsInjection}import { createApp } from './${relativeAppPath.startsWith('.') ? relativeAppPath : `./${relativeAppPath}`}';
|
|
2047
|
-
|
|
2048
|
-
const port = process.argv.includes('--port')
|
|
2049
|
-
? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
|
|
2050
|
-
: 3000;
|
|
2051
|
-
|
|
2052
|
-
// createApp is async to support optional WebSocket setup
|
|
2053
|
-
const { app, start } = await createApp(undefined, ${this.enableOpenApi});
|
|
2054
|
-
|
|
2055
|
-
// Start the server
|
|
2056
|
-
start({
|
|
2057
|
-
port,
|
|
2058
|
-
serve: async (app, port) => {
|
|
2059
|
-
${serveCode}
|
|
2060
|
-
},
|
|
2061
|
-
}).catch((error) => {
|
|
2062
|
-
console.error('Failed to start server:', error);
|
|
2063
|
-
process.exit(1);
|
|
2064
|
-
});
|
|
2065
|
-
`;
|
|
2082
|
+
const content = generateServerEntryContent({
|
|
2083
|
+
secretsJsonPath: this.secretsJsonPath,
|
|
2084
|
+
runtime: this.runtime,
|
|
2085
|
+
enableOpenApi: this.enableOpenApi,
|
|
2086
|
+
});
|
|
2066
2087
|
|
|
2067
2088
|
await fsWriteFile(serverPath, content);
|
|
2068
2089
|
}
|