@geekmidas/cli 1.10.31 → 1.10.33
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 +19 -0
- package/bin/gkm.mjs +1 -1
- package/dist/{CachedStateProvider-3couMAQk.cjs → CachedStateProvider-37pG1HBx.cjs} +1 -1
- package/dist/{CachedStateProvider-CQr31ups.mjs → CachedStateProvider-C8A-cCm5.mjs} +1 -1
- package/dist/{CachedStateProvider-CQr31ups.mjs.map → CachedStateProvider-C8A-cCm5.mjs.map} +1 -1
- package/dist/CachedStateProvider-CC0DyO09.mjs +3 -0
- package/dist/{CachedStateProvider-Cyn17x3x.cjs → CachedStateProvider-CxsXOKkg.cjs} +1 -1
- package/dist/{CachedStateProvider-Cyn17x3x.cjs.map → CachedStateProvider-CxsXOKkg.cjs.map} +1 -1
- package/dist/{HostingerProvider-C5oLa7N7.cjs → HostingerProvider-5KYmwoK2.cjs} +1 -1
- package/dist/{HostingerProvider-C5oLa7N7.cjs.map → HostingerProvider-5KYmwoK2.cjs.map} +1 -1
- package/dist/{HostingerProvider-YvCg3Mus.mjs → HostingerProvider-ANWchdiK.mjs} +1 -1
- package/dist/{HostingerProvider-YvCg3Mus.mjs.map → HostingerProvider-ANWchdiK.mjs.map} +1 -1
- package/dist/{LocalStateProvider-DI6n11UM.cjs → LocalStateProvider-CLifRC0Y.cjs} +1 -1
- package/dist/{LocalStateProvider-DI6n11UM.cjs.map → LocalStateProvider-CLifRC0Y.cjs.map} +1 -1
- package/dist/{LocalStateProvider-BciOhGR0.mjs → LocalStateProvider-Dp0KkRcw.mjs} +1 -1
- package/dist/{LocalStateProvider-BciOhGR0.mjs.map → LocalStateProvider-Dp0KkRcw.mjs.map} +1 -1
- package/dist/{Route53Provider-CYlDOrYO.mjs → Route53Provider-QoPgcXxn.mjs} +1 -1
- package/dist/{Route53Provider-CYlDOrYO.mjs.map → Route53Provider-QoPgcXxn.mjs.map} +1 -1
- package/dist/{Route53Provider-DISZ6E8f.cjs → Route53Provider-owQQ4pn6.cjs} +1 -1
- package/dist/{Route53Provider-DISZ6E8f.cjs.map → Route53Provider-owQQ4pn6.cjs.map} +1 -1
- package/dist/{SSMStateProvider-BljH5efc.cjs → SSMStateProvider-CT8tjl9o.cjs} +1 -1
- package/dist/{SSMStateProvider-BljH5efc.cjs.map → SSMStateProvider-CT8tjl9o.cjs.map} +1 -1
- package/dist/{SSMStateProvider-DXT1dIdI.mjs → SSMStateProvider-CksOTB8M.mjs} +1 -1
- package/dist/{SSMStateProvider-DXT1dIdI.mjs.map → SSMStateProvider-CksOTB8M.mjs.map} +1 -1
- package/dist/{backup-provisioner-CCUcj28C.mjs → backup-provisioner-BEXoHTuC.mjs} +1 -1
- package/dist/{backup-provisioner-CCUcj28C.mjs.map → backup-provisioner-BEXoHTuC.mjs.map} +1 -1
- package/dist/{backup-provisioner-CpirEvO1.cjs → backup-provisioner-C4noe75O.cjs} +1 -1
- package/dist/{backup-provisioner-CpirEvO1.cjs.map → backup-provisioner-C4noe75O.cjs.map} +1 -1
- package/dist/{bundler-CLOenWgi.cjs → bundler-BG6P249p.cjs} +2 -2
- package/dist/{bundler-CLOenWgi.cjs.map → bundler-BG6P249p.cjs.map} +1 -1
- package/dist/{bundler-E5VcXsPH.mjs → bundler-c6QvGUBM.mjs} +2 -2
- package/dist/{bundler-E5VcXsPH.mjs.map → bundler-c6QvGUBM.mjs.map} +1 -1
- package/dist/config.d.mts +2 -2
- package/dist/{dokploy-api-sfZFlWuE.mjs → dokploy-api-BIT0683o.mjs} +1 -1
- package/dist/{dokploy-api-sfZFlWuE.mjs.map → dokploy-api-BIT0683o.mjs.map} +1 -1
- package/dist/dokploy-api-BOx71j6F.cjs +3 -0
- package/dist/dokploy-api-BZGk-lkD.mjs +3 -0
- package/dist/{dokploy-api-CqOIKHme.cjs → dokploy-api-Dvyq-LsM.cjs} +1 -1
- package/dist/{dokploy-api-CqOIKHme.cjs.map → dokploy-api-Dvyq-LsM.cjs.map} +1 -1
- package/dist/{encryption-BVS7Vmzs.cjs → encryption-CipsS0KW.cjs} +1 -1
- package/dist/{encryption-CO5Q4Bhf.mjs → encryption-DSkIlEZ6.mjs} +1 -1
- package/dist/{encryption-CO5Q4Bhf.mjs.map → encryption-DSkIlEZ6.mjs.map} +1 -1
- package/dist/encryption-Db2zvGqM.mjs +3 -0
- package/dist/{encryption-MfXAiFTm.cjs → encryption-DgKS-Dv9.cjs} +1 -1
- package/dist/{encryption-MfXAiFTm.cjs.map → encryption-DgKS-Dv9.cjs.map} +1 -1
- package/dist/{index-C-_rW4KF.d.mts → index-Dt_dZ7K4.d.mts} +2 -2
- package/dist/{index-C-_rW4KF.d.mts.map → index-Dt_dZ7K4.d.mts.map} +1 -1
- package/dist/index.cjs +407 -83
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +383 -59
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-w-_smBTO.cjs → openapi-BQVC9vE5.cjs} +39 -8
- package/dist/openapi-BQVC9vE5.cjs.map +1 -0
- package/dist/{openapi-DHq5r17U.mjs → openapi-DY3ctapV.mjs} +26 -7
- package/dist/openapi-DY3ctapV.mjs.map +1 -0
- package/dist/{openapi-react-query-COpH7Mmz.mjs → openapi-react-query-C4UdILaI.mjs} +1 -1
- package/dist/{openapi-react-query-COpH7Mmz.mjs.map → openapi-react-query-C4UdILaI.mjs.map} +1 -1
- package/dist/{openapi-react-query-CxyQTTdF.cjs → openapi-react-query-DYbBq-WJ.cjs} +1 -1
- package/dist/{openapi-react-query-CxyQTTdF.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.cjs +1 -2
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -2
- package/dist/{reconcile-C8KIy4Uy.cjs → reconcile-CZtzdrnc.cjs} +1 -1
- package/dist/{reconcile-C8KIy4Uy.cjs.map → reconcile-CZtzdrnc.cjs.map} +1 -1
- package/dist/{reconcile-D423q2QW.mjs → reconcile-DAAKa0YR.mjs} +1 -1
- package/dist/{reconcile-D423q2QW.mjs.map → reconcile-DAAKa0YR.mjs.map} +1 -1
- package/dist/{sync-fzT5WX_y.mjs → sync-COnAugP-.mjs} +1 -1
- package/dist/sync-D1Pa30oV.cjs +4 -0
- package/dist/{sync-BWY6yoUG.cjs → sync-DGXXSk2v.cjs} +1 -1
- package/dist/{sync-BWY6yoUG.cjs.map → sync-DGXXSk2v.cjs.map} +1 -1
- package/dist/{sync-Dc-QA4Mx.mjs → sync-D_NowTkZ.mjs} +1 -1
- package/dist/{sync-Dc-QA4Mx.mjs.map → sync-D_NowTkZ.mjs.map} +1 -1
- package/dist/{types-BqAbYt5a.d.mts → types-OszPdw9m.d.mts} +1 -1
- package/dist/{types-BqAbYt5a.d.mts.map → types-OszPdw9m.d.mts.map} +1 -1
- package/dist/workspace/index.d.mts +2 -2
- package/package.json +3 -3
- package/src/build/index.ts +5 -47
- package/src/build/types.ts +5 -0
- package/src/deploy/__tests__/index.spec.ts +1 -3
- package/src/generators/CronGenerator.ts +3 -1
- package/src/generators/EndpointGenerator.ts +6 -2
- package/src/generators/FunctionGenerator.ts +3 -1
- package/src/generators/OpenApiTsGenerator.ts +3 -1
- package/src/generators/SubscriberGenerator.ts +3 -1
- package/src/generators/__tests__/OpenApiTsGenerator.responseType.spec.ts +56 -0
- package/src/init/generators/docker.ts +2 -2
- package/src/init/versions.ts +3 -3
- package/dist/CachedStateProvider-C4QqvlPE.mjs +0 -3
- package/dist/client-generator-CUVVtAOo.mjs +0 -23
- package/dist/client-generator-CUVVtAOo.mjs.map +0 -1
- package/dist/client-generator-D-T4ypQo.cjs +0 -35
- package/dist/client-generator-D-T4ypQo.cjs.map +0 -1
- package/dist/dokploy-api-BPYkG-ZZ.cjs +0 -3
- package/dist/dokploy-api-BiSzWM4v.mjs +0 -3
- package/dist/encryption-Bd_m4q3g.mjs +0 -3
- package/dist/openapi-DHq5r17U.mjs.map +0 -1
- package/dist/openapi-w-_smBTO.cjs.map +0 -1
- package/dist/sniffer-BqSVVrWA.mjs +0 -356
- package/dist/sniffer-BqSVVrWA.mjs.map +0 -1
- package/dist/sniffer-CBhzByiY.cjs +0 -386
- package/dist/sniffer-CBhzByiY.cjs.map +0 -1
- package/dist/sniffer-Cpq_cbyz.mjs +0 -4
- package/dist/sniffer-eZ13G10M.cjs +0 -4
- package/dist/sync-B2CN_KS5.cjs +0 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-
|
|
1
|
+
{"version":3,"file":"reconcile-DAAKa0YR.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-DGXXSk2v.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":"sync-
|
|
1
|
+
{"version":3,"file":"sync-D_NowTkZ.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"}
|
|
@@ -275,4 +275,4 @@ interface GkmConfig {
|
|
|
275
275
|
}
|
|
276
276
|
//#endregion
|
|
277
277
|
export { EventsBackend, GkmConfig, HooksConfig, OpenApiConfig, ProvidersConfig, Routes, Runtime, StudioConfig, TelescopeConfig };
|
|
278
|
-
//# sourceMappingURL=types-
|
|
278
|
+
//# sourceMappingURL=types-OszPdw9m.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types-
|
|
1
|
+
{"version":3,"file":"types-OszPdw9m.d.mts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;AAYY,UALK,iBAAA,CAKwB;EAaxB,KAAA,EAAA,MAAA,GAAA,MAAc,EAAA;EAKd,SAAA,EAAA,CAAA,QAAA,EAAoB,MAAA,EAAA,GAAQ,MAAA;AAI7C;AAIiB,KA1BL,MAAA,GA0BK,MAAgB,GAAA,MAAA,EAAA,GA1BQ,iBA0BR;AAkDrB,UA/DK,cAAA,CA+DQ;EAGb,OAAA,CAAA,EAAA,OAAA;EASA,SAAA,CAAA,EAAA,MAAA;;AACL,UAvEU,mBAAA,SAA4B,cAuEtC,CAAA;AAA6C,UAnEnC,eAAA,SAAwB,cAmEW,CAAA,CAGpD;AAA6B,UAlEZ,gBAAA,CAkEY;EAAA;EAwBK,OAAG,CAAA,EAAA,OAAA;EAAkB;EAItC,MAAA,CAAA,EAAA,OAAa;EAAA;EAAA,MAIhB,CAAA,EAAA,OAAA;EAAgB;EAJsB,WAAA,CAAA,EAAA,MAAA;EAOxC;EAEK,gBAAA,CAAA,EAAe,OAAA;EAiBf;EASA,QAAA,CAAA,EAAA,MAAa,EAAA;EAWb;EA6BA,WAAA,CAAA,EAAA,SAAA,GAAqB,SAAA;EAarB;EAAe,OAAA,CAAA,EAAA,OAAA;EAAA;;;;;;AAaU;EAGzB,iBAAS,CAAA,EAAA,OAAA;;;AAEb,UA5KI,aAAA,CA4KJ;EAAM;;;;EAeC,KAQY,CAAA,EAAA,MAAA;EAAe;;;;EA2DzB,OAAA,CAAA,EAAA,MAAA;;;;;;;;;;;KAxOV,aAAA;;KAGA,kBAAA;;KASA,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, 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-OszPdw9m.mjs";
|
|
2
|
+
import { AppConfig, AppConfigInput, AppInput, AppsRecord, BackendFramework, 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-Dt_dZ7K4.mjs";
|
|
3
3
|
export { AppConfig, AppConfigInput, AppInput, AppsRecord, BackendFramework, 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.10.
|
|
3
|
+
"version": "1.10.33",
|
|
4
4
|
"description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
"prompts": "~2.4.2",
|
|
57
57
|
"tsx": "~4.20.3",
|
|
58
58
|
"yaml": "~2.8.2",
|
|
59
|
-
"@geekmidas/constructs": "~3.0.
|
|
59
|
+
"@geekmidas/constructs": "~3.0.9",
|
|
60
60
|
"@geekmidas/envkit": "~1.0.5",
|
|
61
61
|
"@geekmidas/errors": "~1.0.0",
|
|
62
|
-
"@geekmidas/logger": "~1.0.
|
|
62
|
+
"@geekmidas/logger": "~1.0.1",
|
|
63
63
|
"@geekmidas/schema": "~1.0.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
package/src/build/index.ts
CHANGED
|
@@ -158,6 +158,7 @@ export async function buildCommand(
|
|
|
158
158
|
hooks,
|
|
159
159
|
production,
|
|
160
160
|
dockerServices,
|
|
161
|
+
markOptional: options.markOptional ?? false,
|
|
161
162
|
};
|
|
162
163
|
|
|
163
164
|
// Initialize generators
|
|
@@ -196,26 +197,6 @@ export async function buildCommand(
|
|
|
196
197
|
const rootOutputDir = join(process.cwd(), '.gkm');
|
|
197
198
|
await mkdir(rootOutputDir, { recursive: true });
|
|
198
199
|
|
|
199
|
-
// When --mark-optional is set, sniff env vars from the envParser to determine
|
|
200
|
-
// which are optional. Optional vars get a `?` suffix in each construct's
|
|
201
|
-
// environment array (e.g. `PORT?` instead of `PORT`).
|
|
202
|
-
let optionalVarSet: Set<string> | undefined;
|
|
203
|
-
if (options.markOptional && config.envParser) {
|
|
204
|
-
try {
|
|
205
|
-
const { _sniffEnvParser } = await import('../deploy/sniffer.js');
|
|
206
|
-
const sniffed = await _sniffEnvParser(
|
|
207
|
-
config.envParser,
|
|
208
|
-
process.cwd(),
|
|
209
|
-
process.cwd(),
|
|
210
|
-
);
|
|
211
|
-
if (sniffed.optionalEnvVars.length > 0) {
|
|
212
|
-
optionalVarSet = new Set(sniffed.optionalEnvVars);
|
|
213
|
-
}
|
|
214
|
-
} catch {
|
|
215
|
-
// Non-fatal — constructs are still built without optional markers
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
200
|
// Build for each provider and generate per-provider manifests
|
|
220
201
|
let result: BuildResult = {};
|
|
221
202
|
for (const provider of resolved.providers) {
|
|
@@ -234,7 +215,6 @@ export async function buildCommand(
|
|
|
234
215
|
resolved.enableOpenApi,
|
|
235
216
|
options.skipBundle ?? false,
|
|
236
217
|
options.stage,
|
|
237
|
-
optionalVarSet,
|
|
238
218
|
);
|
|
239
219
|
// Keep the master key from the server provider
|
|
240
220
|
if (providerResult.masterKey) {
|
|
@@ -263,7 +243,6 @@ async function buildForProvider(
|
|
|
263
243
|
enableOpenApi: boolean,
|
|
264
244
|
skipBundle: boolean,
|
|
265
245
|
stage?: string,
|
|
266
|
-
optionalVarSet?: Set<string>,
|
|
267
246
|
): Promise<BuildResult> {
|
|
268
247
|
const outputDir = join(process.cwd(), '.gkm', provider);
|
|
269
248
|
|
|
@@ -272,8 +251,10 @@ async function buildForProvider(
|
|
|
272
251
|
|
|
273
252
|
logger.log(`\nGenerating handlers for provider: ${provider}`);
|
|
274
253
|
|
|
275
|
-
// Build all constructs in parallel
|
|
276
|
-
|
|
254
|
+
// Build all constructs in parallel.
|
|
255
|
+
// context.markOptional is forwarded to each generator so that
|
|
256
|
+
// getEnvironment({ markOptional }) produces `VARNAME?` for optional vars.
|
|
257
|
+
const [routes, functionInfos, cronInfos, subscriberInfos] = await Promise.all(
|
|
277
258
|
[
|
|
278
259
|
endpointGenerator.build(context, endpoints, outputDir, {
|
|
279
260
|
provider,
|
|
@@ -285,29 +266,6 @@ async function buildForProvider(
|
|
|
285
266
|
],
|
|
286
267
|
);
|
|
287
268
|
|
|
288
|
-
// Apply optional marker (`?` suffix) to env vars in each construct's environment
|
|
289
|
-
const markEnv = optionalVarSet
|
|
290
|
-
? (vars: string[] | undefined) =>
|
|
291
|
-
vars?.map((v) => (optionalVarSet.has(v) ? `${v}?` : v))
|
|
292
|
-
: (vars: string[] | undefined) => vars;
|
|
293
|
-
|
|
294
|
-
const routes = rawRoutes.map((r) => ({
|
|
295
|
-
...r,
|
|
296
|
-
environment: markEnv(r.environment),
|
|
297
|
-
}));
|
|
298
|
-
const functionInfos = rawFunctions.map((f) => ({
|
|
299
|
-
...f,
|
|
300
|
-
environment: markEnv(f.environment),
|
|
301
|
-
}));
|
|
302
|
-
const cronInfos = rawCrons.map((c) => ({
|
|
303
|
-
...c,
|
|
304
|
-
environment: markEnv(c.environment),
|
|
305
|
-
}));
|
|
306
|
-
const subscriberInfos = rawSubscribers.map((s) => ({
|
|
307
|
-
...s,
|
|
308
|
-
environment: markEnv(s.environment),
|
|
309
|
-
}));
|
|
310
|
-
|
|
311
269
|
logger.log(
|
|
312
270
|
`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers for ${provider}`,
|
|
313
271
|
);
|
package/src/build/types.ts
CHANGED
|
@@ -90,6 +90,11 @@ export interface BuildContext {
|
|
|
90
90
|
redis?: boolean;
|
|
91
91
|
rabbitmq?: boolean;
|
|
92
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* When true, optional env vars get a `?` suffix in each construct's
|
|
95
|
+
* environment array (e.g. `PORT?` instead of `PORT`).
|
|
96
|
+
*/
|
|
97
|
+
markOptional?: boolean;
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
export interface ProviderBuildResult {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { HttpResponse, http } from 'msw';
|
|
2
2
|
import { setupServer } from 'msw/node';
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { getAppBuildOrder } from '../../workspace/index.js';
|
|
4
5
|
import type { NormalizedWorkspace } from '../../workspace/types.js';
|
|
5
6
|
import { DokployApi } from '../dokploy-api';
|
|
6
7
|
import {
|
|
@@ -536,8 +537,6 @@ describe('workspaceDeployCommand', () => {
|
|
|
536
537
|
},
|
|
537
538
|
});
|
|
538
539
|
|
|
539
|
-
// Import getAppBuildOrder to verify ordering
|
|
540
|
-
const { getAppBuildOrder } = require('../../workspace/index.js');
|
|
541
540
|
const order = getAppBuildOrder(workspace);
|
|
542
541
|
|
|
543
542
|
// api and auth should come before web
|
|
@@ -577,7 +576,6 @@ describe('workspaceDeployCommand', () => {
|
|
|
577
576
|
},
|
|
578
577
|
});
|
|
579
578
|
|
|
580
|
-
const { getAppBuildOrder } = require('../../workspace/index.js');
|
|
581
579
|
const order = getAppBuildOrder(workspace);
|
|
582
580
|
|
|
583
581
|
// db -> api -> web
|
|
@@ -51,7 +51,9 @@ export class CronGenerator extends ConstructGenerator<
|
|
|
51
51
|
schedule: construct.schedule || 'rate(1 hour)',
|
|
52
52
|
timeout: construct.timeout,
|
|
53
53
|
memorySize: construct.memorySize,
|
|
54
|
-
environment: await construct.getEnvironment(
|
|
54
|
+
environment: await construct.getEnvironment({
|
|
55
|
+
markOptional: context.markOptional,
|
|
56
|
+
}),
|
|
55
57
|
});
|
|
56
58
|
|
|
57
59
|
logger.log(`Generated cron handler: ${key}`);
|
|
@@ -130,7 +130,9 @@ export class EndpointGenerator extends ConstructGenerator<
|
|
|
130
130
|
),
|
|
131
131
|
timeout: construct.timeout,
|
|
132
132
|
memorySize: construct.memorySize,
|
|
133
|
-
environment: await construct.getEnvironment(
|
|
133
|
+
environment: await construct.getEnvironment({
|
|
134
|
+
markOptional: context.markOptional,
|
|
135
|
+
}),
|
|
134
136
|
authorizer: construct.authorizer?.name ?? 'none',
|
|
135
137
|
};
|
|
136
138
|
|
|
@@ -160,7 +162,9 @@ export class EndpointGenerator extends ConstructGenerator<
|
|
|
160
162
|
),
|
|
161
163
|
timeout: construct.timeout,
|
|
162
164
|
memorySize: construct.memorySize,
|
|
163
|
-
environment: await construct.getEnvironment(
|
|
165
|
+
environment: await construct.getEnvironment({
|
|
166
|
+
markOptional: context.markOptional,
|
|
167
|
+
}),
|
|
164
168
|
authorizer: construct.authorizer?.name ?? 'none',
|
|
165
169
|
};
|
|
166
170
|
|
|
@@ -69,7 +69,9 @@ export class FunctionGenerator extends ConstructGenerator<
|
|
|
69
69
|
),
|
|
70
70
|
timeout: construct.timeout,
|
|
71
71
|
memorySize: construct.memorySize,
|
|
72
|
-
environment: await construct.getEnvironment(
|
|
72
|
+
environment: await construct.getEnvironment({
|
|
73
|
+
markOptional: context.markOptional,
|
|
74
|
+
}),
|
|
73
75
|
});
|
|
74
76
|
|
|
75
77
|
logger.log(`Generated function handler: ${key}`);
|
|
@@ -57,6 +57,7 @@ interface EndpointInfo {
|
|
|
57
57
|
description?: string;
|
|
58
58
|
tags?: string[];
|
|
59
59
|
operationId?: string;
|
|
60
|
+
responseType: string;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
interface SecuritySchemeInfo {
|
|
@@ -132,6 +133,7 @@ export class OpenApiTsGenerator {
|
|
|
132
133
|
description: ep.description,
|
|
133
134
|
tags: ep.tags,
|
|
134
135
|
operationId: ep.operationId,
|
|
136
|
+
responseType: ep.responseType ?? 'application/json',
|
|
135
137
|
};
|
|
136
138
|
});
|
|
137
139
|
}
|
|
@@ -575,7 +577,7 @@ export class OpenApiTsGenerator {
|
|
|
575
577
|
parts.push(`responses: {
|
|
576
578
|
200: {
|
|
577
579
|
content: {
|
|
578
|
-
'
|
|
580
|
+
'${info.responseType}': ${outputName};
|
|
579
581
|
};
|
|
580
582
|
};
|
|
581
583
|
}`);
|
|
@@ -70,7 +70,9 @@ export class SubscriberGenerator extends ConstructGenerator<
|
|
|
70
70
|
subscribedEvents: construct.subscribedEvents || [],
|
|
71
71
|
timeout: construct.timeout,
|
|
72
72
|
memorySize: construct.memorySize,
|
|
73
|
-
environment: await construct.getEnvironment(
|
|
73
|
+
environment: await construct.getEnvironment({
|
|
74
|
+
markOptional: context.markOptional,
|
|
75
|
+
}),
|
|
74
76
|
});
|
|
75
77
|
|
|
76
78
|
logger.log(`Generated subscriber handler: ${key}`);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { e } from '@geekmidas/constructs/endpoints';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { OpenApiTsGenerator } from '../OpenApiTsGenerator';
|
|
5
|
+
|
|
6
|
+
describe('OpenApiTsGenerator — responseType', () => {
|
|
7
|
+
it("emits 'application/json' in paths when responseType is default", async () => {
|
|
8
|
+
const endpoint = e
|
|
9
|
+
.get('/users')
|
|
10
|
+
.output(z.object({ id: z.string() }))
|
|
11
|
+
.handle(async () => ({ id: '1' }));
|
|
12
|
+
|
|
13
|
+
const generator = new OpenApiTsGenerator();
|
|
14
|
+
const content = await generator.generate([endpoint as any]);
|
|
15
|
+
|
|
16
|
+
// Default JSON content-type in the paths interface
|
|
17
|
+
expect(content).toContain("'application/json':");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("emits 'text/html' when the endpoint declares responseType('text/html')", async () => {
|
|
21
|
+
const endpoint = e
|
|
22
|
+
.get('/checkout-page')
|
|
23
|
+
.output(z.string())
|
|
24
|
+
.responseType('text/html')
|
|
25
|
+
.handle(async () => '<html></html>');
|
|
26
|
+
|
|
27
|
+
const generator = new OpenApiTsGenerator();
|
|
28
|
+
const content = await generator.generate([endpoint as any]);
|
|
29
|
+
|
|
30
|
+
// Paths interface should reference text/html, not application/json,
|
|
31
|
+
// for this endpoint's 200 response
|
|
32
|
+
expect(content).toContain("'text/html':");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('keeps JSON content-type for other endpoints when one uses a custom responseType', async () => {
|
|
36
|
+
const htmlEndpoint = e
|
|
37
|
+
.get('/page')
|
|
38
|
+
.output(z.string())
|
|
39
|
+
.responseType('text/html')
|
|
40
|
+
.handle(async () => '<html></html>');
|
|
41
|
+
|
|
42
|
+
const jsonEndpoint = e
|
|
43
|
+
.get('/users')
|
|
44
|
+
.output(z.object({ id: z.string() }))
|
|
45
|
+
.handle(async () => ({ id: '1' }));
|
|
46
|
+
|
|
47
|
+
const generator = new OpenApiTsGenerator();
|
|
48
|
+
const content = await generator.generate([
|
|
49
|
+
htmlEndpoint as any,
|
|
50
|
+
jsonEndpoint as any,
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
expect(content).toContain("'text/html':");
|
|
54
|
+
expect(content).toContain("'application/json':");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -259,7 +259,7 @@ ${volumes.join('\n')}
|
|
|
259
259
|
*/
|
|
260
260
|
function generateDockerEnv(
|
|
261
261
|
apps: DatabaseAppConfig[],
|
|
262
|
-
|
|
262
|
+
_eventsBackend?: EventsBackend,
|
|
263
263
|
): string {
|
|
264
264
|
const envVars = apps.map((app) => {
|
|
265
265
|
const envVar = `${app.name.toUpperCase()}_DB_PASSWORD`;
|
|
@@ -285,7 +285,7 @@ ${envVars.join('\n')}
|
|
|
285
285
|
*/
|
|
286
286
|
function generatePostgresInitScript(
|
|
287
287
|
apps: DatabaseAppConfig[],
|
|
288
|
-
|
|
288
|
+
_eventsBackend?: EventsBackend,
|
|
289
289
|
): string {
|
|
290
290
|
const userCreations = apps.map((app) => {
|
|
291
291
|
const userName = app.name.replace(/-/g, '_');
|
package/src/init/versions.ts
CHANGED
|
@@ -32,13 +32,13 @@ export const GEEKMIDAS_VERSIONS = {
|
|
|
32
32
|
'@geekmidas/cache': '~1.1.0',
|
|
33
33
|
'@geekmidas/client': '~4.0.4',
|
|
34
34
|
'@geekmidas/cloud': '~1.0.0',
|
|
35
|
-
'@geekmidas/constructs': '~3.0.
|
|
35
|
+
'@geekmidas/constructs': '~3.0.8',
|
|
36
36
|
'@geekmidas/db': '~1.0.1',
|
|
37
37
|
'@geekmidas/emailkit': '~1.0.0',
|
|
38
|
-
'@geekmidas/envkit': '~1.0.
|
|
38
|
+
'@geekmidas/envkit': '~1.0.5',
|
|
39
39
|
'@geekmidas/errors': '~1.0.0',
|
|
40
40
|
'@geekmidas/events': '~1.1.0',
|
|
41
|
-
'@geekmidas/logger': '~1.0.
|
|
41
|
+
'@geekmidas/logger': '~1.0.1',
|
|
42
42
|
'@geekmidas/rate-limit': '~2.0.0',
|
|
43
43
|
'@geekmidas/schema': '~1.0.0',
|
|
44
44
|
'@geekmidas/services': '~1.0.1',
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
|
|
3
|
-
//#region src/types.ts
|
|
4
|
-
function isPartitionedRoutes(routes) {
|
|
5
|
-
return typeof routes === "object" && routes !== null && !Array.isArray(routes) && "paths" in routes;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
//#endregion
|
|
9
|
-
//#region src/workspace/client-generator.ts
|
|
10
|
-
/**
|
|
11
|
-
* Normalize routes to an array of patterns.
|
|
12
|
-
* Handles string, string[], and PartitionedRoutes (extracts paths).
|
|
13
|
-
* @internal Exported for use in dev command
|
|
14
|
-
*/
|
|
15
|
-
function normalizeRoutes(routes) {
|
|
16
|
-
if (!routes) return [];
|
|
17
|
-
if (isPartitionedRoutes(routes)) return Array.isArray(routes.paths) ? routes.paths : [routes.paths];
|
|
18
|
-
return Array.isArray(routes) ? routes : [routes];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
//#endregion
|
|
22
|
-
export { isPartitionedRoutes, normalizeRoutes };
|
|
23
|
-
//# sourceMappingURL=client-generator-CUVVtAOo.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client-generator-CUVVtAOo.mjs","names":["routes: Routes | undefined","routes: Routes | undefined"],"sources":["../src/types.ts","../src/workspace/client-generator.ts"],"sourcesContent":["export type MainProvider = 'aws' | 'server';\nexport type LegacyProvider =\n\t| 'server'\n\t| 'aws-apigatewayv1'\n\t| 'aws-apigatewayv2'\n\t| 'aws-lambda';\n\nexport interface PartitionedRoutes {\n\tpaths: string | string[];\n\tpartition: (filepath: string) => string;\n}\n\nexport type Routes = string | string[] | PartitionedRoutes;\n\nexport function isPartitionedRoutes(\n\troutes: Routes | undefined,\n): routes is PartitionedRoutes {\n\treturn (\n\t\ttypeof routes === 'object' &&\n\t\troutes !== null &&\n\t\t!Array.isArray(routes) &&\n\t\t'paths' in routes\n\t);\n}\n\nexport interface ProviderConfig {\n\tenabled?: boolean;\n\toutputDir?: string;\n}\n\nexport interface AWSApiGatewayConfig extends ProviderConfig {\n\t// Additional AWS API Gateway specific options\n}\n\nexport interface AWSLambdaConfig extends ProviderConfig {\n\t// Additional AWS Lambda specific options\n}\n\nexport interface ProductionConfig {\n\t/** Enable production mode (default: false) */\n\tenabled?: boolean;\n\t/** Bundle server into single file (default: true) */\n\tbundle?: boolean;\n\t/** Minify bundled output (default: true) */\n\tminify?: boolean;\n\t/** Health check endpoint path (default: '/health') */\n\thealthCheck?: string;\n\t/** Enable graceful shutdown handling (default: true) */\n\tgracefulShutdown?: boolean;\n\t/** Packages to exclude from bundling (default: []) */\n\texternal?: string[];\n\t/** Include subscribers in production build (default: 'exclude' for serverless) */\n\tsubscribers?: 'include' | 'exclude';\n\t/** Include OpenAPI spec in production (default: false) */\n\topenapi?: boolean;\n\t/**\n\t * Enable build-time optimized handler generation (default: true)\n\t * Generates specialized handlers based on endpoint tier:\n\t * - minimal: Near-raw-Hono performance for simple endpoints\n\t * - standard: Optimized handlers for auth/services\n\t * - full: Uses HonoEndpoint.addRoutes for complex endpoints\n\t */\n\toptimizedHandlers?: boolean;\n}\n\n/** Service-specific configuration for docker-compose */\nexport interface ServiceConfig {\n\t/**\n\t * Full Docker image reference (e.g., 'postgis/postgis:16-3.4-alpine').\n\t * When specified, overrides the default image entirely.\n\t */\n\timage?: string;\n\t/**\n\t * Docker image version/tag (e.g., '15-alpine' for postgres).\n\t * Only used when `image` is not specified.\n\t */\n\tversion?: string;\n}\n\n/**\n * Supported event backend types.\n *\n * - `pgboss`: Reuses PostgreSQL with a dedicated user/schema. Auto-enables db service.\n * - `sns`: Adds LocalStack container with SNS+SQS services.\n * - `rabbitmq`: Adds RabbitMQ container.\n *\n * All backends generate EVENT_PUBLISHER_CONNECTION_STRING and EVENT_SUBSCRIBER_CONNECTION_STRING.\n */\nexport type EventsBackend = 'pgboss' | 'sns' | 'rabbitmq';\n\n/** Supported docker-compose service names */\nexport type ComposeServiceName =\n\t| 'postgres'\n\t| 'redis'\n\t| 'rabbitmq'\n\t| 'minio'\n\t| 'mailpit'\n\t| 'localstack';\n\n/** Services configuration - can be boolean (use defaults) or object with version */\nexport type ComposeServicesConfig = {\n\t[K in ComposeServiceName]?: boolean | ServiceConfig;\n};\n\nexport interface DockerConfig {\n\t/** Container registry URL (e.g., 'ghcr.io/myorg') */\n\tregistry?: string;\n\t/** Docker image name (default: derived from package.json name) */\n\timageName?: string;\n\t/** Base Docker image (default: 'node:22-alpine') */\n\tbaseImage?: string;\n\t/** Container port (default: 3000) */\n\tport?: number;\n\t/** docker-compose services to include */\n\tcompose?: {\n\t\t/**\n\t\t * Services to include in docker-compose.\n\t\t * Can be an object with service configs or an array of service names (legacy).\n\t\t *\n\t\t * @example Object format (recommended)\n\t\t * services: {\n\t\t * postgres: { version: '15-alpine' },\n\t\t * redis: true, // use default version\n\t\t * }\n\t\t *\n\t\t * @example Array format (legacy, uses default versions)\n\t\t * services: ['postgres', 'redis']\n\t\t */\n\t\tservices?: ComposeServicesConfig | ComposeServiceName[];\n\t};\n}\n\nexport interface ServerConfig extends ProviderConfig {\n\tenableOpenApi?: boolean;\n\tport?: number;\n\t/** Production build configuration */\n\tproduction?: ProductionConfig;\n}\n\nexport type Runtime = 'node' | 'bun';\n\nexport interface TelescopeConfig {\n\t/** Enable/disable telescope (default: true in development) */\n\tenabled?: boolean;\n\t/** Port for telescope to use (defaults to server port) */\n\tport?: number;\n\t/** Path prefix for telescope UI (default: /__telescope) */\n\tpath?: string;\n\t/** Ignore patterns for telescope (e.g., ['/health', '/metrics']) */\n\tignore?: string[];\n\t/** Record request/response bodies (default: true) */\n\trecordBody?: boolean;\n\t/** Maximum entries to keep in memory (default: 1000) */\n\tmaxEntries?: number;\n\t/** Enable WebSocket for real-time updates (default: true, requires @hono/node-ws for Node.js) */\n\twebsocket?: boolean;\n}\n\nexport interface StudioConfig {\n\t/** Enable/disable studio (default: true in development) */\n\tenabled?: boolean;\n\t/** Path prefix for studio UI (default: /__studio) */\n\tpath?: string;\n\t/** Schema to introspect (default: 'public') */\n\tschema?: string;\n}\n\nexport interface OpenApiConfig {\n\t/** Enable OpenAPI generation (default: true) */\n\tenabled?: boolean;\n\t/** API title */\n\ttitle?: string;\n\t/** API version */\n\tversion?: string;\n\t/** API description */\n\tdescription?: string;\n}\n\nexport interface HooksConfig {\n\t/**\n\t * Path to a module exporting server lifecycle hooks.\n\t * The module should export `beforeSetup` and/or `afterSetup` functions.\n\t *\n\t * @example\n\t * ```typescript\n\t * // src/config/hooks.ts\n\t * import type { Hono } from 'hono';\n\t * import type { Logger } from '@geekmidas/logger';\n\t * import type { EnvironmentParser } from '@geekmidas/envkit';\n\t *\n\t * // Called BEFORE gkm endpoints are registered\n\t * export function beforeSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.use('*', cors());\n\t * app.get('/custom/health', (c) => c.json({ status: 'ok' }));\n\t * }\n\t *\n\t * // Called AFTER gkm endpoints are registered\n\t * export function afterSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.notFound((c) => c.json({ error: 'Not found' }, 404));\n\t * app.onError((err, c) => c.json({ error: err.message }, 500));\n\t * }\n\t * ```\n\t */\n\tserver?: string;\n}\n\n/** Dokploy deployment configuration */\nexport interface DokployProviderConfig {\n\t/** Dokploy API endpoint (e.g., 'https://dokploy.example.com') */\n\tendpoint: string;\n\t/** Project ID in Dokploy */\n\tprojectId: string;\n\t/** Application ID in Dokploy */\n\tapplicationId: string;\n\t/** Container registry (overrides docker.registry if set) */\n\tregistry?: string;\n\t/** Registry ID in Dokploy (recommended for private registries) */\n\tregistryId?: string;\n}\n\nexport interface ProvidersConfig {\n\taws?: {\n\t\tapiGateway?: {\n\t\t\tv1?: boolean | AWSApiGatewayConfig;\n\t\t\tv2?: boolean | AWSApiGatewayConfig;\n\t\t};\n\t\tlambda?: {\n\t\t\tfunctions?: boolean | AWSLambdaConfig;\n\t\t\tcrons?: boolean | AWSLambdaConfig;\n\t\t};\n\t};\n\tserver?: boolean | ServerConfig;\n\t/** Dokploy deployment configuration */\n\tdokploy?: boolean | DokployProviderConfig;\n}\n\nexport interface GkmConfig {\n\troutes: Routes;\n\tfunctions?: Routes;\n\tcrons?: Routes;\n\tsubscribers?: Routes;\n\tenvParser: string;\n\tlogger: string;\n\tproviders?: ProvidersConfig;\n\t/**\n\t * Server lifecycle hooks for customizing the Hono app.\n\t * Allows adding custom routes, middleware, error handlers, etc.\n\t *\n\t * @example\n\t * hooks: {\n\t * server: './src/config/hooks'\n\t * }\n\t */\n\thooks?: HooksConfig;\n\t/**\n\t * Telescope configuration for debugging/monitoring.\n\t * Can be:\n\t * - A string path to a module that exports a Telescope instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A TelescopeConfig object for inline configuration\n\t */\n\ttelescope?: string | boolean | TelescopeConfig;\n\t/**\n\t * Studio configuration for database browsing.\n\t * Can be:\n\t * - A string path to a module that exports a Studio instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A StudioConfig object for inline configuration\n\t *\n\t * Requires a database connection configured via services.\n\t */\n\tstudio?: string | boolean | StudioConfig;\n\t/**\n\t * OpenAPI generation configuration.\n\t * Can be:\n\t * - A boolean to enable/disable with defaults (output: ./src/api/openapi.ts)\n\t * - An OpenApiConfig object for customization\n\t *\n\t * When enabled, OpenAPI spec is generated on startup and regenerated on route changes.\n\t *\n\t * @example\n\t * openapi: true\n\t *\n\t * @example\n\t * openapi: {\n\t * output: './src/api/openapi.ts',\n\t * title: 'My API',\n\t * version: '1.0.0',\n\t * }\n\t */\n\topenapi?: boolean | OpenApiConfig;\n\t/** Runtime to use for dev server (default: 'node') */\n\truntime?: Runtime;\n\t/**\n\t * Environment file(s) to load for development.\n\t * Can be:\n\t * - A string path to a single env file (e.g., '.env.local')\n\t * - An array of paths to load in order (later files override earlier)\n\t * - Defaults to '.env' if not specified\n\t *\n\t * @example\n\t * env: '.env.local'\n\t *\n\t * @example\n\t * env: ['.env', '.env.local']\n\t */\n\tenv?: string | string[];\n\t/**\n\t * Docker deployment configuration.\n\t * Used by `gkm docker` and `gkm prepack` commands.\n\t *\n\t * @example\n\t * docker: {\n\t * registry: 'ghcr.io/myorg',\n\t * imageName: 'my-api',\n\t * compose: {\n\t * services: ['postgres', 'redis']\n\t * }\n\t * }\n\t */\n\tdocker?: DockerConfig;\n}\n\nexport interface BuildOptions {\n\tprovider?: MainProvider;\n\t// Legacy support - will be deprecated\n\tproviders?: LegacyProvider[];\n\tenableOpenApi?: boolean;\n\t/** Build for production (no dev tools, bundled output) */\n\tproduction?: boolean;\n\t/** Skip bundling step in production build */\n\tskipBundle?: boolean;\n\t/** Stage for secrets injection (e.g., 'production', 'staging') */\n\tstage?: string;\n\t/**\n\t * When true, optional environment variables (those with `.optional()` or\n\t * `.default()` in the envParser) are suffixed with `?` within each\n\t * construct's `environment` array in the manifest (e.g. `PORT?`).\n\t * Defaults to false.\n\t */\n\tmarkOptional?: boolean;\n}\n\n/** Result from build command when secrets are injected */\nexport interface BuildResult {\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\nexport interface RouteInfo {\n\tpath: string;\n\tmethod: string;\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: string[];\n\tauthorizer: string;\n}\n\nexport interface FunctionInfo {\n\tname: string;\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: string[];\n}\n\nexport interface CronInfo {\n\tname: string;\n\thandler: string;\n\tschedule: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: string[];\n}\n\nexport interface SubscriberInfo {\n\tname: string;\n\thandler: string;\n\tsubscribedEvents: string[];\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: string[];\n}\n\nexport interface RoutesManifest {\n\troutes: RouteInfo[];\n}\n\nexport interface FunctionsManifest {\n\tfunctions: FunctionInfo[];\n}\n\nexport interface CronsManifest {\n\tcrons: CronInfo[];\n}\n\nexport interface SubscribersManifest {\n\tsubscribers: SubscriberInfo[];\n}\n","import { join } from 'node:path';\nimport { isPartitionedRoutes, type Routes } from '../types.js';\nimport type { NormalizedWorkspace } from './types.js';\n\n/**\n * Normalize routes to an array of patterns.\n * Handles string, string[], and PartitionedRoutes (extracts paths).\n * @internal Exported for use in dev command\n */\nexport function normalizeRoutes(routes: Routes | undefined): string[] {\n\tif (!routes) return [];\n\tif (isPartitionedRoutes(routes)) {\n\t\treturn Array.isArray(routes.paths) ? routes.paths : [routes.paths];\n\t}\n\treturn Array.isArray(routes) ? routes : [routes];\n}\n\n/**\n * Get the first routes pattern as a string (for simple cases).\n * @internal Exported for use in dev command\n */\nexport function getFirstRoute(\n\troutes: string | string[] | undefined,\n): string | null {\n\tconst normalized = normalizeRoutes(routes);\n\treturn normalized[0] || null;\n}\n\n/**\n * Check if a file path matches endpoint patterns that could affect OpenAPI schema.\n * Returns true for changes that should trigger client regeneration.\n */\nexport function shouldRegenerateClient(\n\tfilePath: string,\n\troutesPattern: string,\n): boolean {\n\t// Normalize path separators\n\tconst normalizedPath = filePath.replace(/\\\\/g, '/');\n\tconst normalizedPattern = routesPattern.replace(/\\\\/g, '/');\n\n\t// Check if the file matches the routes pattern\n\t// This is a simple check - the file should be within the routes directory\n\tconst patternDir = normalizedPattern.split('*')[0] || '';\n\n\tif (!normalizedPath.includes(patternDir.replace('./', ''))) {\n\t\treturn false;\n\t}\n\n\t// Check file extension - only TypeScript endpoint files\n\tif (!normalizedPath.endsWith('.ts') && !normalizedPath.endsWith('.tsx')) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n/**\n * Get backend apps that a frontend depends on.\n */\nexport function getBackendDependencies(\n\tworkspace: NormalizedWorkspace,\n\tfrontendAppName: string,\n): string[] {\n\tconst frontendApp = workspace.apps[frontendAppName];\n\tif (!frontendApp || frontendApp.type !== 'frontend') {\n\t\treturn [];\n\t}\n\n\treturn frontendApp.dependencies.filter((dep) => {\n\t\tconst depApp = workspace.apps[dep];\n\t\treturn depApp?.type === 'backend' && depApp.routes;\n\t});\n}\n\n/**\n * Get frontend apps that depend on a backend app.\n */\nexport function getDependentFrontends(\n\tworkspace: NormalizedWorkspace,\n\tbackendAppName: string,\n): string[] {\n\tconst dependentApps: string[] = [];\n\n\tfor (const [appName, app] of Object.entries(workspace.apps)) {\n\t\tif (app.type === 'frontend' && app.dependencies.includes(backendAppName)) {\n\t\t\tdependentApps.push(appName);\n\t\t}\n\t}\n\n\treturn dependentApps;\n}\n\n/**\n * Get the path to a backend's OpenAPI spec file.\n */\nexport function getBackendOpenApiPath(\n\tworkspace: NormalizedWorkspace,\n\tbackendAppName: string,\n): string | null {\n\tconst app = workspace.apps[backendAppName];\n\tif (!app || app.type !== 'backend') {\n\t\treturn null;\n\t}\n\n\treturn join(workspace.root, app.path, '.gkm', 'openapi.ts');\n}\n"],"mappings":";;;AAcA,SAAgB,oBACfA,QAC8B;AAC9B,eACQ,WAAW,YAClB,WAAW,SACV,MAAM,QAAQ,OAAO,IACtB,WAAW;AAEZ;;;;;;;;;ACdD,SAAgB,gBAAgBC,QAAsC;AACrE,MAAK,OAAQ,QAAO,CAAE;AACtB,KAAI,oBAAoB,OAAO,CAC9B,QAAO,MAAM,QAAQ,OAAO,MAAM,GAAG,OAAO,QAAQ,CAAC,OAAO,KAAM;AAEnE,QAAO,MAAM,QAAQ,OAAO,GAAG,SAAS,CAAC,MAAO;AAChD"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
2
|
-
const node_path = require_chunk.__toESM(require("node:path"));
|
|
3
|
-
|
|
4
|
-
//#region src/types.ts
|
|
5
|
-
function isPartitionedRoutes(routes) {
|
|
6
|
-
return typeof routes === "object" && routes !== null && !Array.isArray(routes) && "paths" in routes;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
//#endregion
|
|
10
|
-
//#region src/workspace/client-generator.ts
|
|
11
|
-
/**
|
|
12
|
-
* Normalize routes to an array of patterns.
|
|
13
|
-
* Handles string, string[], and PartitionedRoutes (extracts paths).
|
|
14
|
-
* @internal Exported for use in dev command
|
|
15
|
-
*/
|
|
16
|
-
function normalizeRoutes(routes) {
|
|
17
|
-
if (!routes) return [];
|
|
18
|
-
if (isPartitionedRoutes(routes)) return Array.isArray(routes.paths) ? routes.paths : [routes.paths];
|
|
19
|
-
return Array.isArray(routes) ? routes : [routes];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
//#endregion
|
|
23
|
-
Object.defineProperty(exports, 'isPartitionedRoutes', {
|
|
24
|
-
enumerable: true,
|
|
25
|
-
get: function () {
|
|
26
|
-
return isPartitionedRoutes;
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
Object.defineProperty(exports, 'normalizeRoutes', {
|
|
30
|
-
enumerable: true,
|
|
31
|
-
get: function () {
|
|
32
|
-
return normalizeRoutes;
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
//# sourceMappingURL=client-generator-D-T4ypQo.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client-generator-D-T4ypQo.cjs","names":["routes: Routes | undefined","routes: Routes | undefined"],"sources":["../src/types.ts","../src/workspace/client-generator.ts"],"sourcesContent":["export type MainProvider = 'aws' | 'server';\nexport type LegacyProvider =\n\t| 'server'\n\t| 'aws-apigatewayv1'\n\t| 'aws-apigatewayv2'\n\t| 'aws-lambda';\n\nexport interface PartitionedRoutes {\n\tpaths: string | string[];\n\tpartition: (filepath: string) => string;\n}\n\nexport type Routes = string | string[] | PartitionedRoutes;\n\nexport function isPartitionedRoutes(\n\troutes: Routes | undefined,\n): routes is PartitionedRoutes {\n\treturn (\n\t\ttypeof routes === 'object' &&\n\t\troutes !== null &&\n\t\t!Array.isArray(routes) &&\n\t\t'paths' in routes\n\t);\n}\n\nexport interface ProviderConfig {\n\tenabled?: boolean;\n\toutputDir?: string;\n}\n\nexport interface AWSApiGatewayConfig extends ProviderConfig {\n\t// Additional AWS API Gateway specific options\n}\n\nexport interface AWSLambdaConfig extends ProviderConfig {\n\t// Additional AWS Lambda specific options\n}\n\nexport interface ProductionConfig {\n\t/** Enable production mode (default: false) */\n\tenabled?: boolean;\n\t/** Bundle server into single file (default: true) */\n\tbundle?: boolean;\n\t/** Minify bundled output (default: true) */\n\tminify?: boolean;\n\t/** Health check endpoint path (default: '/health') */\n\thealthCheck?: string;\n\t/** Enable graceful shutdown handling (default: true) */\n\tgracefulShutdown?: boolean;\n\t/** Packages to exclude from bundling (default: []) */\n\texternal?: string[];\n\t/** Include subscribers in production build (default: 'exclude' for serverless) */\n\tsubscribers?: 'include' | 'exclude';\n\t/** Include OpenAPI spec in production (default: false) */\n\topenapi?: boolean;\n\t/**\n\t * Enable build-time optimized handler generation (default: true)\n\t * Generates specialized handlers based on endpoint tier:\n\t * - minimal: Near-raw-Hono performance for simple endpoints\n\t * - standard: Optimized handlers for auth/services\n\t * - full: Uses HonoEndpoint.addRoutes for complex endpoints\n\t */\n\toptimizedHandlers?: boolean;\n}\n\n/** Service-specific configuration for docker-compose */\nexport interface ServiceConfig {\n\t/**\n\t * Full Docker image reference (e.g., 'postgis/postgis:16-3.4-alpine').\n\t * When specified, overrides the default image entirely.\n\t */\n\timage?: string;\n\t/**\n\t * Docker image version/tag (e.g., '15-alpine' for postgres).\n\t * Only used when `image` is not specified.\n\t */\n\tversion?: string;\n}\n\n/**\n * Supported event backend types.\n *\n * - `pgboss`: Reuses PostgreSQL with a dedicated user/schema. Auto-enables db service.\n * - `sns`: Adds LocalStack container with SNS+SQS services.\n * - `rabbitmq`: Adds RabbitMQ container.\n *\n * All backends generate EVENT_PUBLISHER_CONNECTION_STRING and EVENT_SUBSCRIBER_CONNECTION_STRING.\n */\nexport type EventsBackend = 'pgboss' | 'sns' | 'rabbitmq';\n\n/** Supported docker-compose service names */\nexport type ComposeServiceName =\n\t| 'postgres'\n\t| 'redis'\n\t| 'rabbitmq'\n\t| 'minio'\n\t| 'mailpit'\n\t| 'localstack';\n\n/** Services configuration - can be boolean (use defaults) or object with version */\nexport type ComposeServicesConfig = {\n\t[K in ComposeServiceName]?: boolean | ServiceConfig;\n};\n\nexport interface DockerConfig {\n\t/** Container registry URL (e.g., 'ghcr.io/myorg') */\n\tregistry?: string;\n\t/** Docker image name (default: derived from package.json name) */\n\timageName?: string;\n\t/** Base Docker image (default: 'node:22-alpine') */\n\tbaseImage?: string;\n\t/** Container port (default: 3000) */\n\tport?: number;\n\t/** docker-compose services to include */\n\tcompose?: {\n\t\t/**\n\t\t * Services to include in docker-compose.\n\t\t * Can be an object with service configs or an array of service names (legacy).\n\t\t *\n\t\t * @example Object format (recommended)\n\t\t * services: {\n\t\t * postgres: { version: '15-alpine' },\n\t\t * redis: true, // use default version\n\t\t * }\n\t\t *\n\t\t * @example Array format (legacy, uses default versions)\n\t\t * services: ['postgres', 'redis']\n\t\t */\n\t\tservices?: ComposeServicesConfig | ComposeServiceName[];\n\t};\n}\n\nexport interface ServerConfig extends ProviderConfig {\n\tenableOpenApi?: boolean;\n\tport?: number;\n\t/** Production build configuration */\n\tproduction?: ProductionConfig;\n}\n\nexport type Runtime = 'node' | 'bun';\n\nexport interface TelescopeConfig {\n\t/** Enable/disable telescope (default: true in development) */\n\tenabled?: boolean;\n\t/** Port for telescope to use (defaults to server port) */\n\tport?: number;\n\t/** Path prefix for telescope UI (default: /__telescope) */\n\tpath?: string;\n\t/** Ignore patterns for telescope (e.g., ['/health', '/metrics']) */\n\tignore?: string[];\n\t/** Record request/response bodies (default: true) */\n\trecordBody?: boolean;\n\t/** Maximum entries to keep in memory (default: 1000) */\n\tmaxEntries?: number;\n\t/** Enable WebSocket for real-time updates (default: true, requires @hono/node-ws for Node.js) */\n\twebsocket?: boolean;\n}\n\nexport interface StudioConfig {\n\t/** Enable/disable studio (default: true in development) */\n\tenabled?: boolean;\n\t/** Path prefix for studio UI (default: /__studio) */\n\tpath?: string;\n\t/** Schema to introspect (default: 'public') */\n\tschema?: string;\n}\n\nexport interface OpenApiConfig {\n\t/** Enable OpenAPI generation (default: true) */\n\tenabled?: boolean;\n\t/** API title */\n\ttitle?: string;\n\t/** API version */\n\tversion?: string;\n\t/** API description */\n\tdescription?: string;\n}\n\nexport interface HooksConfig {\n\t/**\n\t * Path to a module exporting server lifecycle hooks.\n\t * The module should export `beforeSetup` and/or `afterSetup` functions.\n\t *\n\t * @example\n\t * ```typescript\n\t * // src/config/hooks.ts\n\t * import type { Hono } from 'hono';\n\t * import type { Logger } from '@geekmidas/logger';\n\t * import type { EnvironmentParser } from '@geekmidas/envkit';\n\t *\n\t * // Called BEFORE gkm endpoints are registered\n\t * export function beforeSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.use('*', cors());\n\t * app.get('/custom/health', (c) => c.json({ status: 'ok' }));\n\t * }\n\t *\n\t * // Called AFTER gkm endpoints are registered\n\t * export function afterSetup(app: Hono, ctx: { envParser: EnvironmentParser; logger: Logger }) {\n\t * app.notFound((c) => c.json({ error: 'Not found' }, 404));\n\t * app.onError((err, c) => c.json({ error: err.message }, 500));\n\t * }\n\t * ```\n\t */\n\tserver?: string;\n}\n\n/** Dokploy deployment configuration */\nexport interface DokployProviderConfig {\n\t/** Dokploy API endpoint (e.g., 'https://dokploy.example.com') */\n\tendpoint: string;\n\t/** Project ID in Dokploy */\n\tprojectId: string;\n\t/** Application ID in Dokploy */\n\tapplicationId: string;\n\t/** Container registry (overrides docker.registry if set) */\n\tregistry?: string;\n\t/** Registry ID in Dokploy (recommended for private registries) */\n\tregistryId?: string;\n}\n\nexport interface ProvidersConfig {\n\taws?: {\n\t\tapiGateway?: {\n\t\t\tv1?: boolean | AWSApiGatewayConfig;\n\t\t\tv2?: boolean | AWSApiGatewayConfig;\n\t\t};\n\t\tlambda?: {\n\t\t\tfunctions?: boolean | AWSLambdaConfig;\n\t\t\tcrons?: boolean | AWSLambdaConfig;\n\t\t};\n\t};\n\tserver?: boolean | ServerConfig;\n\t/** Dokploy deployment configuration */\n\tdokploy?: boolean | DokployProviderConfig;\n}\n\nexport interface GkmConfig {\n\troutes: Routes;\n\tfunctions?: Routes;\n\tcrons?: Routes;\n\tsubscribers?: Routes;\n\tenvParser: string;\n\tlogger: string;\n\tproviders?: ProvidersConfig;\n\t/**\n\t * Server lifecycle hooks for customizing the Hono app.\n\t * Allows adding custom routes, middleware, error handlers, etc.\n\t *\n\t * @example\n\t * hooks: {\n\t * server: './src/config/hooks'\n\t * }\n\t */\n\thooks?: HooksConfig;\n\t/**\n\t * Telescope configuration for debugging/monitoring.\n\t * Can be:\n\t * - A string path to a module that exports a Telescope instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A TelescopeConfig object for inline configuration\n\t */\n\ttelescope?: string | boolean | TelescopeConfig;\n\t/**\n\t * Studio configuration for database browsing.\n\t * Can be:\n\t * - A string path to a module that exports a Studio instance (recommended)\n\t * - A boolean to enable/disable with defaults\n\t * - A StudioConfig object for inline configuration\n\t *\n\t * Requires a database connection configured via services.\n\t */\n\tstudio?: string | boolean | StudioConfig;\n\t/**\n\t * OpenAPI generation configuration.\n\t * Can be:\n\t * - A boolean to enable/disable with defaults (output: ./src/api/openapi.ts)\n\t * - An OpenApiConfig object for customization\n\t *\n\t * When enabled, OpenAPI spec is generated on startup and regenerated on route changes.\n\t *\n\t * @example\n\t * openapi: true\n\t *\n\t * @example\n\t * openapi: {\n\t * output: './src/api/openapi.ts',\n\t * title: 'My API',\n\t * version: '1.0.0',\n\t * }\n\t */\n\topenapi?: boolean | OpenApiConfig;\n\t/** Runtime to use for dev server (default: 'node') */\n\truntime?: Runtime;\n\t/**\n\t * Environment file(s) to load for development.\n\t * Can be:\n\t * - A string path to a single env file (e.g., '.env.local')\n\t * - An array of paths to load in order (later files override earlier)\n\t * - Defaults to '.env' if not specified\n\t *\n\t * @example\n\t * env: '.env.local'\n\t *\n\t * @example\n\t * env: ['.env', '.env.local']\n\t */\n\tenv?: string | string[];\n\t/**\n\t * Docker deployment configuration.\n\t * Used by `gkm docker` and `gkm prepack` commands.\n\t *\n\t * @example\n\t * docker: {\n\t * registry: 'ghcr.io/myorg',\n\t * imageName: 'my-api',\n\t * compose: {\n\t * services: ['postgres', 'redis']\n\t * }\n\t * }\n\t */\n\tdocker?: DockerConfig;\n}\n\nexport interface BuildOptions {\n\tprovider?: MainProvider;\n\t// Legacy support - will be deprecated\n\tproviders?: LegacyProvider[];\n\tenableOpenApi?: boolean;\n\t/** Build for production (no dev tools, bundled output) */\n\tproduction?: boolean;\n\t/** Skip bundling step in production build */\n\tskipBundle?: boolean;\n\t/** Stage for secrets injection (e.g., 'production', 'staging') */\n\tstage?: string;\n\t/**\n\t * When true, optional environment variables (those with `.optional()` or\n\t * `.default()` in the envParser) are suffixed with `?` within each\n\t * construct's `environment` array in the manifest (e.g. `PORT?`).\n\t * Defaults to false.\n\t */\n\tmarkOptional?: boolean;\n}\n\n/** Result from build command when secrets are injected */\nexport interface BuildResult {\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\nexport interface RouteInfo {\n\tpath: string;\n\tmethod: string;\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: string[];\n\tauthorizer: string;\n}\n\nexport interface FunctionInfo {\n\tname: string;\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: string[];\n}\n\nexport interface CronInfo {\n\tname: string;\n\thandler: string;\n\tschedule: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: string[];\n}\n\nexport interface SubscriberInfo {\n\tname: string;\n\thandler: string;\n\tsubscribedEvents: string[];\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: string[];\n}\n\nexport interface RoutesManifest {\n\troutes: RouteInfo[];\n}\n\nexport interface FunctionsManifest {\n\tfunctions: FunctionInfo[];\n}\n\nexport interface CronsManifest {\n\tcrons: CronInfo[];\n}\n\nexport interface SubscribersManifest {\n\tsubscribers: SubscriberInfo[];\n}\n","import { join } from 'node:path';\nimport { isPartitionedRoutes, type Routes } from '../types.js';\nimport type { NormalizedWorkspace } from './types.js';\n\n/**\n * Normalize routes to an array of patterns.\n * Handles string, string[], and PartitionedRoutes (extracts paths).\n * @internal Exported for use in dev command\n */\nexport function normalizeRoutes(routes: Routes | undefined): string[] {\n\tif (!routes) return [];\n\tif (isPartitionedRoutes(routes)) {\n\t\treturn Array.isArray(routes.paths) ? routes.paths : [routes.paths];\n\t}\n\treturn Array.isArray(routes) ? routes : [routes];\n}\n\n/**\n * Get the first routes pattern as a string (for simple cases).\n * @internal Exported for use in dev command\n */\nexport function getFirstRoute(\n\troutes: string | string[] | undefined,\n): string | null {\n\tconst normalized = normalizeRoutes(routes);\n\treturn normalized[0] || null;\n}\n\n/**\n * Check if a file path matches endpoint patterns that could affect OpenAPI schema.\n * Returns true for changes that should trigger client regeneration.\n */\nexport function shouldRegenerateClient(\n\tfilePath: string,\n\troutesPattern: string,\n): boolean {\n\t// Normalize path separators\n\tconst normalizedPath = filePath.replace(/\\\\/g, '/');\n\tconst normalizedPattern = routesPattern.replace(/\\\\/g, '/');\n\n\t// Check if the file matches the routes pattern\n\t// This is a simple check - the file should be within the routes directory\n\tconst patternDir = normalizedPattern.split('*')[0] || '';\n\n\tif (!normalizedPath.includes(patternDir.replace('./', ''))) {\n\t\treturn false;\n\t}\n\n\t// Check file extension - only TypeScript endpoint files\n\tif (!normalizedPath.endsWith('.ts') && !normalizedPath.endsWith('.tsx')) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n/**\n * Get backend apps that a frontend depends on.\n */\nexport function getBackendDependencies(\n\tworkspace: NormalizedWorkspace,\n\tfrontendAppName: string,\n): string[] {\n\tconst frontendApp = workspace.apps[frontendAppName];\n\tif (!frontendApp || frontendApp.type !== 'frontend') {\n\t\treturn [];\n\t}\n\n\treturn frontendApp.dependencies.filter((dep) => {\n\t\tconst depApp = workspace.apps[dep];\n\t\treturn depApp?.type === 'backend' && depApp.routes;\n\t});\n}\n\n/**\n * Get frontend apps that depend on a backend app.\n */\nexport function getDependentFrontends(\n\tworkspace: NormalizedWorkspace,\n\tbackendAppName: string,\n): string[] {\n\tconst dependentApps: string[] = [];\n\n\tfor (const [appName, app] of Object.entries(workspace.apps)) {\n\t\tif (app.type === 'frontend' && app.dependencies.includes(backendAppName)) {\n\t\t\tdependentApps.push(appName);\n\t\t}\n\t}\n\n\treturn dependentApps;\n}\n\n/**\n * Get the path to a backend's OpenAPI spec file.\n */\nexport function getBackendOpenApiPath(\n\tworkspace: NormalizedWorkspace,\n\tbackendAppName: string,\n): string | null {\n\tconst app = workspace.apps[backendAppName];\n\tif (!app || app.type !== 'backend') {\n\t\treturn null;\n\t}\n\n\treturn join(workspace.root, app.path, '.gkm', 'openapi.ts');\n}\n"],"mappings":";;;;AAcA,SAAgB,oBACfA,QAC8B;AAC9B,eACQ,WAAW,YAClB,WAAW,SACV,MAAM,QAAQ,OAAO,IACtB,WAAW;AAEZ;;;;;;;;;ACdD,SAAgB,gBAAgBC,QAAsC;AACrE,MAAK,OAAQ,QAAO,CAAE;AACtB,KAAI,oBAAoB,OAAO,CAC9B,QAAO,MAAM,QAAQ,OAAO,MAAM,GAAG,OAAO,QAAQ,CAAC,OAAO,KAAM;AAEnE,QAAO,MAAM,QAAQ,OAAO,GAAG,SAAS,CAAC,MAAO;AAChD"}
|