@geekmidas/cli 2.0.2 → 10.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -120
- package/dist/{CachedStateProvider-BdX7klRc.cjs → CachedStateProvider-BLo7zv7S.cjs} +1 -1
- package/dist/CachedStateProvider-BxoAm-B2.mjs +3 -0
- package/dist/{CachedStateProvider-klLWZXWF.cjs → CachedStateProvider-CqPdW7CB.cjs} +1 -1
- package/dist/{CachedStateProvider-klLWZXWF.cjs.map → CachedStateProvider-CqPdW7CB.cjs.map} +1 -1
- package/dist/{CachedStateProvider-CzyFct42.mjs → CachedStateProvider-DTniehMP.mjs} +1 -1
- package/dist/{CachedStateProvider-CzyFct42.mjs.map → CachedStateProvider-DTniehMP.mjs.map} +1 -1
- package/dist/{HostingerProvider-B2ZYz7Yc.mjs → HostingerProvider-Cft7lEYr.mjs} +4 -3
- package/dist/HostingerProvider-Cft7lEYr.mjs.map +1 -0
- package/dist/{HostingerProvider-CrHfsgz9.cjs → HostingerProvider-h3EbEp-Z.cjs} +4 -3
- package/dist/HostingerProvider-h3EbEp-Z.cjs.map +1 -0
- package/dist/{LocalStateProvider-CP49rjzt.mjs → LocalStateProvider-Bn6tS-oL.mjs} +1 -1
- package/dist/{LocalStateProvider-CP49rjzt.mjs.map → LocalStateProvider-Bn6tS-oL.mjs.map} +1 -1
- package/dist/{LocalStateProvider-CalzNfNA.cjs → LocalStateProvider-Db9SEjyH.cjs} +1 -1
- package/dist/{LocalStateProvider-CalzNfNA.cjs.map → LocalStateProvider-Db9SEjyH.cjs.map} +1 -1
- package/dist/{Route53Provider-tlgBzrRQ.mjs → Route53Provider-RdpUDJQk.mjs} +1 -1
- package/dist/{Route53Provider-tlgBzrRQ.mjs.map → Route53Provider-RdpUDJQk.mjs.map} +1 -1
- package/dist/{Route53Provider-DwgXiLxt.cjs → Route53Provider-di2fMM5N.cjs} +1 -1
- package/dist/{Route53Provider-DwgXiLxt.cjs.map → Route53Provider-di2fMM5N.cjs.map} +1 -1
- package/dist/{SSMStateProvider-BlrTiV5m.mjs → SSMStateProvider-BRt2py-A.mjs} +1 -1
- package/dist/{SSMStateProvider-BlrTiV5m.mjs.map → SSMStateProvider-BRt2py-A.mjs.map} +1 -1
- package/dist/{SSMStateProvider-Bt6r8CTL.cjs → SSMStateProvider-DyX8s9HI.cjs} +1 -1
- package/dist/{SSMStateProvider-Bt6r8CTL.cjs.map → SSMStateProvider-DyX8s9HI.cjs.map} +1 -1
- package/dist/{backup-provisioner-Xunq-p_8.mjs → backup-provisioner-Bgj5Vi_L.mjs} +2 -2
- package/dist/{backup-provisioner-Xunq-p_8.mjs.map → backup-provisioner-Bgj5Vi_L.mjs.map} +1 -1
- package/dist/{backup-provisioner-B3HBN-2k.cjs → backup-provisioner-BrsHwYG0.cjs} +2 -2
- package/dist/{backup-provisioner-B3HBN-2k.cjs.map → backup-provisioner-BrsHwYG0.cjs.map} +1 -1
- package/dist/{bundler-Dufq3uvI.cjs → bundler-BY1OmV1e.cjs} +44 -14
- package/dist/bundler-BY1OmV1e.cjs.map +1 -0
- package/dist/{bundler-DkA4qzUD.mjs → bundler-Be8Zg4Wd.mjs} +45 -15
- package/dist/bundler-Be8Zg4Wd.mjs.map +1 -0
- package/dist/config-D8o6I_7Z.mjs +469 -0
- package/dist/config-D8o6I_7Z.mjs.map +1 -0
- package/dist/config-DbPE1KXF.cjs +535 -0
- package/dist/config-DbPE1KXF.cjs.map +1 -0
- package/dist/config.cjs +3 -2
- package/dist/config.d.cts +2 -2
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +2 -2
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +3 -2
- package/dist/{credentials-s1kLcIzK.mjs → credentials-B5zRHdAl.mjs} +21 -6
- package/dist/credentials-B5zRHdAl.mjs.map +1 -0
- package/dist/{credentials-C8DWtnMY.cjs → credentials-BapzjyV5.cjs} +26 -5
- package/dist/credentials-BapzjyV5.cjs.map +1 -0
- package/dist/credentials-Dh0Q6HzY.mjs +2548 -0
- package/dist/credentials-Dh0Q6HzY.mjs.map +1 -0
- package/dist/credentials-K961ThG3.cjs +2680 -0
- package/dist/credentials-K961ThG3.cjs.map +1 -0
- package/dist/deploy/sniffer-routes-worker.cjs +6 -2
- package/dist/deploy/sniffer-routes-worker.cjs.map +1 -1
- package/dist/deploy/sniffer-routes-worker.mjs +6 -2
- package/dist/deploy/sniffer-routes-worker.mjs.map +1 -1
- package/dist/discover-CAlFgYec.mjs +229 -0
- package/dist/discover-CAlFgYec.mjs.map +1 -0
- package/dist/discover-DTmFeloL.cjs +271 -0
- package/dist/discover-DTmFeloL.cjs.map +1 -0
- package/dist/dokploy-api-BS2xnJLZ.mjs +3 -0
- package/dist/{dokploy-api-CqOIKHme.cjs → dokploy-api-BsVH0y_s.cjs} +141 -32
- package/dist/dokploy-api-BsVH0y_s.cjs.map +1 -0
- package/dist/dokploy-api-BuxDRo3l.cjs +3 -0
- package/dist/{dokploy-api-sfZFlWuE.mjs → dokploy-api-aTFPz7_4.mjs} +141 -32
- package/dist/dokploy-api-aTFPz7_4.mjs.map +1 -0
- package/dist/emit-CbcRolsa.cjs +236 -0
- package/dist/emit-CbcRolsa.cjs.map +1 -0
- package/dist/emit-CiZS_5Ub.mjs +206 -0
- package/dist/emit-CiZS_5Ub.mjs.map +1 -0
- package/dist/{encryption-MfXAiFTm.cjs → encryption-C6jaU0hE.cjs} +1 -1
- package/dist/{encryption-MfXAiFTm.cjs.map → encryption-C6jaU0hE.cjs.map} +1 -1
- package/dist/{encryption-BVS7Vmzs.cjs → encryption-CHzzeR8a.cjs} +1 -1
- package/dist/encryption-CQUncu7v.mjs +3 -0
- package/dist/{encryption-CO5Q4Bhf.mjs → encryption-D-abyhZx.mjs} +1 -1
- package/dist/{encryption-CO5Q4Bhf.mjs.map → encryption-D-abyhZx.mjs.map} +1 -1
- package/dist/fullstack-secrets-C-GGohke.cjs +111 -0
- package/dist/fullstack-secrets-C-GGohke.cjs.map +1 -0
- package/dist/fullstack-secrets-D1o9-G5l.mjs +87 -0
- package/dist/fullstack-secrets-D1o9-G5l.mjs.map +1 -0
- package/dist/{fullstack-secrets-BYzi6Hwl.cjs → generator-CNzBw7g5.cjs} +1 -114
- package/dist/generator-CNzBw7g5.cjs.map +1 -0
- package/dist/{fullstack-secrets-l8s3II_H.mjs → generator-VE2JrFlY.mjs} +2 -85
- package/dist/generator-VE2JrFlY.mjs.map +1 -0
- package/dist/index-BThUzEIp.d.mts +175 -0
- package/dist/index-BThUzEIp.d.mts.map +1 -0
- package/dist/index-CHB4tSzX.d.cts +175 -0
- package/dist/index-CHB4tSzX.d.cts.map +1 -0
- package/dist/index.cjs +2329 -1584
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2306 -1561
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-Cfwi0fqF.mjs → openapi-CgpMWMmM.mjs} +174 -95
- package/dist/openapi-CgpMWMmM.mjs.map +1 -0
- package/dist/{openapi-BzNOC7_i.cjs → openapi-CljONd_M.cjs} +175 -108
- package/dist/openapi-CljONd_M.cjs.map +1 -0
- package/dist/{openapi-react-query-C4iIvpPf.mjs → openapi-react-query-Byltvm_7.mjs} +2 -2
- package/dist/openapi-react-query-Byltvm_7.mjs.map +1 -0
- package/dist/{openapi-react-query-CYWik72n.cjs → openapi-react-query-CmJ1xDbn.cjs} +2 -2
- package/dist/openapi-react-query-CmJ1xDbn.cjs.map +1 -0
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +4 -3
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +4 -3
- package/dist/reconcile/public.cjs +9 -0
- package/dist/reconcile/public.d.cts +125 -0
- package/dist/reconcile/public.d.cts.map +1 -0
- package/dist/reconcile/public.d.mts +125 -0
- package/dist/reconcile/public.d.mts.map +1 -0
- package/dist/reconcile/public.mjs +4 -0
- package/dist/{reconcile-B2FoJE0a.mjs → reconcile-CFs8C_1a.mjs} +5 -4
- package/dist/reconcile-CFs8C_1a.mjs.map +1 -0
- package/dist/{reconcile-BzqKJ2Rt.cjs → reconcile-D0pimNiy.cjs} +5 -4
- package/dist/reconcile-D0pimNiy.cjs.map +1 -0
- package/dist/setup-CJiQM2pX.cjs +473 -0
- package/dist/setup-CJiQM2pX.cjs.map +1 -0
- package/dist/setup-DMtU3p41.cjs +11 -0
- package/dist/setup-DOsYueeQ.mjs +443 -0
- package/dist/setup-DOsYueeQ.mjs.map +1 -0
- package/dist/setup-DSutDAlt.mjs +11 -0
- package/dist/{storage-B1wvztiJ.cjs → storage-BJmzYxt8.cjs} +1 -1
- package/dist/{storage-B1wvztiJ.cjs.map → storage-BJmzYxt8.cjs.map} +1 -1
- package/dist/{storage-DOEtT2Hr.cjs → storage-BM9hGpgv.cjs} +1 -1
- package/dist/{storage-dbb9RyBl.mjs → storage-Bi24WfoW.mjs} +1 -1
- package/dist/{storage-dbb9RyBl.mjs.map → storage-Bi24WfoW.mjs.map} +1 -1
- package/dist/{storage-Cs4WBsc4.mjs → storage-CYlGwWTj.mjs} +1 -1
- package/dist/{sync-CSooqRQ8.mjs → sync-CmEuNZsB.mjs} +2 -2
- package/dist/{sync-CSooqRQ8.mjs.map → sync-CmEuNZsB.mjs.map} +1 -1
- package/dist/sync-D306yOWu.cjs +4 -0
- package/dist/{sync-o_CtEbvR.cjs → sync-SizaChmC.cjs} +2 -2
- package/dist/{sync-o_CtEbvR.cjs.map → sync-SizaChmC.cjs.map} +1 -1
- package/dist/{sync-B5UHXwRM.mjs → sync-hOqAWHR6.mjs} +1 -1
- package/dist/{index-Bw548Bta.d.mts → types-C-arnQ1r.d.mts} +590 -143
- package/dist/types-C-arnQ1r.d.mts.map +1 -0
- package/dist/{index-DTpkeFb8.d.cts → types-abJQuk7m.d.cts} +590 -143
- package/dist/types-abJQuk7m.d.cts.map +1 -0
- package/dist/workspace/index.cjs +5 -1
- package/dist/workspace/index.d.cts +3 -3
- package/dist/workspace/index.d.mts +3 -3
- package/dist/workspace/index.mjs +2 -2
- package/dist/{workspace-D-37Ylqo.mjs → workspace-7FqprxuJ.mjs} +117 -30
- package/dist/workspace-7FqprxuJ.mjs.map +1 -0
- package/dist/workspace-8acupeaI.cjs +9 -0
- package/dist/{workspace-CQTT7HJQ.cjs → workspace-XvqO9jIv.cjs} +139 -28
- package/dist/workspace-XvqO9jIv.cjs.map +1 -0
- package/dist/workspace-yP5AHnrn.mjs +8 -0
- package/package.json +26 -9
- package/CHANGELOG.md +0 -623
- package/FUNCTION_CRON_SUPPORT.md +0 -266
- package/dist/CachedStateProvider-GcGziXh1.mjs +0 -3
- package/dist/HostingerProvider-B2ZYz7Yc.mjs.map +0 -1
- package/dist/HostingerProvider-CrHfsgz9.cjs.map +0 -1
- package/dist/bundler-DkA4qzUD.mjs.map +0 -1
- package/dist/bundler-Dufq3uvI.cjs.map +0 -1
- package/dist/config-Bbu1JKvf.mjs +0 -211
- package/dist/config-Bbu1JKvf.mjs.map +0 -1
- package/dist/config-DujV8DbR.cjs +0 -253
- package/dist/config-DujV8DbR.cjs.map +0 -1
- package/dist/credentials-C8DWtnMY.cjs.map +0 -1
- package/dist/credentials-s1kLcIzK.mjs.map +0 -1
- package/dist/dokploy-api-BPYkG-ZZ.cjs +0 -3
- package/dist/dokploy-api-BiSzWM4v.mjs +0 -3
- package/dist/dokploy-api-CqOIKHme.cjs.map +0 -1
- package/dist/dokploy-api-sfZFlWuE.mjs.map +0 -1
- package/dist/encryption-Bd_m4q3g.mjs +0 -3
- package/dist/fullstack-secrets-BYzi6Hwl.cjs.map +0 -1
- package/dist/fullstack-secrets-l8s3II_H.mjs.map +0 -1
- package/dist/index-Bw548Bta.d.mts.map +0 -1
- package/dist/index-DTpkeFb8.d.cts.map +0 -1
- package/dist/openapi-BzNOC7_i.cjs.map +0 -1
- package/dist/openapi-Cfwi0fqF.mjs.map +0 -1
- package/dist/openapi-react-query-C4iIvpPf.mjs.map +0 -1
- package/dist/openapi-react-query-CYWik72n.cjs.map +0 -1
- package/dist/reconcile-B2FoJE0a.mjs.map +0 -1
- package/dist/reconcile-BzqKJ2Rt.cjs.map +0 -1
- package/dist/setup-DDXUXzzl.mjs +0 -765
- package/dist/setup-DDXUXzzl.mjs.map +0 -1
- package/dist/setup-DLwhfyt-.cjs +0 -8
- package/dist/setup-Dh-B7YTX.mjs +0 -8
- package/dist/setup-WZ-xqAQO.cjs +0 -849
- package/dist/setup-WZ-xqAQO.cjs.map +0 -1
- package/dist/sync-BLA94rgC.cjs +0 -4
- package/dist/types-B3GQUg3k.d.mts +0 -280
- package/dist/types-B3GQUg3k.d.mts.map +0 -1
- package/dist/types-BWQDiooe.d.cts +0 -280
- package/dist/types-BWQDiooe.d.cts.map +0 -1
- package/dist/workspace-CQTT7HJQ.cjs.map +0 -1
- package/dist/workspace-D-37Ylqo.mjs.map +0 -1
- package/docs/OPENAPI_TYPESCRIPT_DESIGN.md +0 -408
- package/docs/dev-server-resilience-design.md +0 -189
- package/docs/manifest-refactor-design.md +0 -287
- package/examples/cron-example.ts +0 -45
- package/examples/env.ts +0 -33
- package/examples/function-example.ts +0 -40
- package/examples/gkm.config.json +0 -22
- package/examples/gkm.config.ts +0 -14
- package/examples/gkm.minimal.config.json +0 -7
- package/examples/gkm.production.config.json +0 -27
- package/examples/logger.ts +0 -10
- package/scripts/sync-versions.ts +0 -108
- package/src/__tests__/EndpointGenerator.hooks.spec.ts +0 -204
- package/src/__tests__/config.spec.ts +0 -422
- package/src/__tests__/loadEnvFiles.spec.ts +0 -131
- package/src/__tests__/normalizeHooksConfig.spec.ts +0 -63
- package/src/__tests__/openapi-react-query.spec.ts +0 -506
- package/src/__tests__/openapi.spec.ts +0 -625
- package/src/__tests__/test-helpers.ts +0 -181
- package/src/auth/__tests__/credentials.spec.ts +0 -331
- package/src/auth/__tests__/index.spec.ts +0 -237
- package/src/auth/credentials.ts +0 -286
- package/src/auth/index.ts +0 -233
- package/src/build/__tests__/bundler.spec.ts +0 -446
- package/src/build/__tests__/endpoint-analyzer.spec.ts +0 -623
- package/src/build/__tests__/handler-templates.spec.ts +0 -1287
- package/src/build/__tests__/index-new.spec.ts +0 -550
- package/src/build/__tests__/manifests.spec.ts +0 -583
- package/src/build/__tests__/partitions.spec.ts +0 -110
- package/src/build/__tests__/workspace-build.spec.ts +0 -215
- package/src/build/bundler.ts +0 -256
- package/src/build/endpoint-analyzer.ts +0 -236
- package/src/build/handler-templates.ts +0 -1253
- package/src/build/index.ts +0 -616
- package/src/build/manifests.ts +0 -285
- package/src/build/partitions.ts +0 -58
- package/src/build/providerResolver.ts +0 -184
- package/src/build/types.ts +0 -104
- package/src/config.ts +0 -330
- package/src/credentials/__tests__/fullDockerPorts.spec.ts +0 -144
- package/src/credentials/__tests__/helpers.ts +0 -112
- package/src/credentials/__tests__/prepareEntryCredentials.spec.ts +0 -125
- package/src/credentials/__tests__/readonlyDockerPorts.spec.ts +0 -190
- package/src/credentials/__tests__/workspaceCredentials.spec.ts +0 -209
- package/src/credentials/index.ts +0 -843
- package/src/debug.ts +0 -68
- package/src/deploy/CachedStateProvider.ts +0 -86
- package/src/deploy/LocalStateProvider.ts +0 -57
- package/src/deploy/SSMStateProvider.ts +0 -117
- package/src/deploy/StateProvider.ts +0 -175
- package/src/deploy/__tests__/CachedStateProvider.spec.ts +0 -235
- package/src/deploy/__tests__/HostingerProvider.spec.ts +0 -347
- package/src/deploy/__tests__/LocalStateProvider.spec.ts +0 -130
- package/src/deploy/__tests__/Route53Provider.spec.ts +0 -425
- package/src/deploy/__tests__/SSMStateProvider.spec.ts +0 -201
- package/src/deploy/__tests__/__fixtures__/entry-apps/async-entry.ts +0 -24
- package/src/deploy/__tests__/__fixtures__/entry-apps/nested-config-entry.ts +0 -24
- package/src/deploy/__tests__/__fixtures__/entry-apps/no-env-entry.ts +0 -12
- package/src/deploy/__tests__/__fixtures__/entry-apps/simple-entry.ts +0 -14
- package/src/deploy/__tests__/__fixtures__/entry-apps/throwing-entry.ts +0 -16
- package/src/deploy/__tests__/__fixtures__/env-parsers/non-function-export.ts +0 -10
- package/src/deploy/__tests__/__fixtures__/env-parsers/optional-env-parser.ts +0 -21
- package/src/deploy/__tests__/__fixtures__/env-parsers/parseable-env-parser.ts +0 -18
- package/src/deploy/__tests__/__fixtures__/env-parsers/throwing-env-parser.ts +0 -16
- package/src/deploy/__tests__/__fixtures__/env-parsers/valid-env-parser.ts +0 -16
- package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/auth.ts +0 -16
- package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/health.ts +0 -13
- package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/users.ts +0 -15
- package/src/deploy/__tests__/__fixtures__/route-apps/services.ts +0 -55
- package/src/deploy/__tests__/backup-provisioner.spec.ts +0 -428
- package/src/deploy/__tests__/createDnsProvider.spec.ts +0 -195
- package/src/deploy/__tests__/createStateProvider.spec.ts +0 -126
- package/src/deploy/__tests__/dns-orchestration.spec.ts +0 -192
- package/src/deploy/__tests__/dns-verification.spec.ts +0 -229
- package/src/deploy/__tests__/docker.spec.ts +0 -149
- package/src/deploy/__tests__/dokploy-api.spec.ts +0 -697
- package/src/deploy/__tests__/dokploy.spec.ts +0 -435
- package/src/deploy/__tests__/domain.spec.ts +0 -235
- package/src/deploy/__tests__/env-resolver.spec.ts +0 -903
- package/src/deploy/__tests__/index.spec.ts +0 -1091
- package/src/deploy/__tests__/init.spec.ts +0 -793
- package/src/deploy/__tests__/secrets.spec.ts +0 -303
- package/src/deploy/__tests__/sniffer.spec.ts +0 -838
- package/src/deploy/__tests__/state-e2e.spec.ts +0 -387
- package/src/deploy/__tests__/state.spec.ts +0 -876
- package/src/deploy/__tests__/undeploy.spec.ts +0 -758
- package/src/deploy/backup-provisioner.ts +0 -316
- package/src/deploy/dns/DnsProvider.ts +0 -201
- package/src/deploy/dns/HostingerProvider.ts +0 -174
- package/src/deploy/dns/Route53Provider.ts +0 -340
- package/src/deploy/dns/hostinger-api.ts +0 -261
- package/src/deploy/dns/index.ts +0 -627
- package/src/deploy/docker.ts +0 -260
- package/src/deploy/dokploy-api.ts +0 -1101
- package/src/deploy/dokploy.ts +0 -177
- package/src/deploy/domain.ts +0 -134
- package/src/deploy/env-resolver.ts +0 -305
- package/src/deploy/index.ts +0 -2094
- package/src/deploy/init.ts +0 -427
- package/src/deploy/secrets.ts +0 -187
- package/src/deploy/sniffer-envkit-patch.ts +0 -61
- package/src/deploy/sniffer-hooks.ts +0 -57
- package/src/deploy/sniffer-loader.ts +0 -28
- package/src/deploy/sniffer-routes-worker.ts +0 -104
- package/src/deploy/sniffer-worker.ts +0 -75
- package/src/deploy/sniffer.ts +0 -578
- package/src/deploy/state-commands.ts +0 -274
- package/src/deploy/state.ts +0 -482
- package/src/deploy/types.ts +0 -102
- package/src/deploy/undeploy.ts +0 -407
- package/src/dev/__tests__/entry-integration.spec.ts +0 -347
- package/src/dev/__tests__/entry.spec.ts +0 -144
- package/src/dev/__tests__/index.spec.ts +0 -2084
- package/src/dev/index.ts +0 -1649
- package/src/docker/__tests__/compose.spec.ts +0 -1142
- package/src/docker/__tests__/templates.spec.ts +0 -694
- package/src/docker/compose.ts +0 -808
- package/src/docker/index.ts +0 -523
- package/src/docker/templates.ts +0 -1067
- package/src/exec/index.ts +0 -118
- package/src/generators/CronGenerator.ts +0 -105
- package/src/generators/EndpointGenerator.ts +0 -954
- package/src/generators/FunctionGenerator.ts +0 -117
- package/src/generators/Generator.ts +0 -147
- package/src/generators/OpenApiTsGenerator.ts +0 -821
- package/src/generators/QueueGenerator.ts +0 -245
- package/src/generators/SubscriberGenerator.ts +0 -282
- package/src/generators/TopicGenerator.ts +0 -45
- package/src/generators/__tests__/CronGenerator.spec.ts +0 -445
- package/src/generators/__tests__/EndpointGenerator.spec.ts +0 -544
- package/src/generators/__tests__/FunctionGenerator.spec.ts +0 -256
- package/src/generators/__tests__/Generator.bustCache.spec.ts +0 -72
- package/src/generators/__tests__/OpenApiTsGenerator.registry.spec.ts +0 -98
- package/src/generators/__tests__/OpenApiTsGenerator.responseType.spec.ts +0 -56
- package/src/generators/__tests__/QueueGenerator.spec.ts +0 -141
- package/src/generators/__tests__/SubscriberGenerator.spec.ts +0 -410
- package/src/generators/index.ts +0 -11
- package/src/index.ts +0 -972
- package/src/init/__tests__/generators.spec.ts +0 -1070
- package/src/init/__tests__/init.spec.ts +0 -577
- package/src/init/__tests__/utils.spec.ts +0 -104
- package/src/init/generators/auth.ts +0 -225
- package/src/init/generators/config.ts +0 -316
- package/src/init/generators/docker.ts +0 -388
- package/src/init/generators/env.ts +0 -62
- package/src/init/generators/index.ts +0 -6
- package/src/init/generators/mobile-expo.ts +0 -576
- package/src/init/generators/models.ts +0 -153
- package/src/init/generators/monorepo.ts +0 -475
- package/src/init/generators/package.ts +0 -95
- package/src/init/generators/source.ts +0 -15
- package/src/init/generators/test.ts +0 -178
- package/src/init/generators/ui.ts +0 -1473
- package/src/init/generators/web-tanstack.ts +0 -348
- package/src/init/generators/web.ts +0 -363
- package/src/init/index.ts +0 -542
- package/src/init/templates/api.ts +0 -404
- package/src/init/templates/index.ts +0 -342
- package/src/init/templates/minimal.ts +0 -207
- package/src/init/templates/serverless.ts +0 -148
- package/src/init/templates/worker.ts +0 -216
- package/src/init/utils.ts +0 -197
- package/src/init/versions.ts +0 -52
- package/src/openapi-react-query.ts +0 -288
- package/src/openapi.ts +0 -259
- package/src/secrets/__tests__/encryption.spec.ts +0 -226
- package/src/secrets/__tests__/generator.spec.ts +0 -575
- package/src/secrets/__tests__/index.spec.ts +0 -91
- package/src/secrets/__tests__/reconcile.spec.ts +0 -123
- package/src/secrets/__tests__/storage.spec.ts +0 -645
- package/src/secrets/encryption.ts +0 -91
- package/src/secrets/generator.ts +0 -329
- package/src/secrets/index.ts +0 -417
- package/src/secrets/keystore.ts +0 -144
- package/src/secrets/reconcile.ts +0 -53
- package/src/secrets/storage.ts +0 -337
- package/src/secrets/sync.ts +0 -136
- package/src/secrets/types.ts +0 -70
- package/src/setup/__tests__/auto-setup.spec.ts +0 -147
- package/src/setup/__tests__/reconcile-secrets.spec.ts +0 -439
- package/src/setup/fullstack-secrets.ts +0 -140
- package/src/setup/index.ts +0 -411
- package/src/test/__tests__/__fixtures__/workspace.ts +0 -104
- package/src/test/__tests__/api.spec.ts +0 -199
- package/src/test/__tests__/auth.spec.ts +0 -162
- package/src/test/__tests__/index.spec.ts +0 -502
- package/src/test/__tests__/web.spec.ts +0 -210
- package/src/test/index.ts +0 -198
- package/src/types.ts +0 -363
- package/src/upgrade/__tests__/index.spec.ts +0 -354
- package/src/upgrade/index.ts +0 -253
- package/src/workspace/__tests__/client-generator.spec.ts +0 -326
- package/src/workspace/__tests__/index.spec.ts +0 -766
- package/src/workspace/__tests__/publicEnv.spec.ts +0 -64
- package/src/workspace/__tests__/schema.spec.ts +0 -737
- package/src/workspace/__tests__/type-inference.spec.ts +0 -251
- package/src/workspace/client-generator.ts +0 -109
- package/src/workspace/index.ts +0 -433
- package/src/workspace/publicEnv.ts +0 -57
- package/src/workspace/schema.ts +0 -861
- package/src/workspace/types.ts +0 -943
- package/tsconfig.json +0 -9
- package/tsdown.config.ts +0 -22
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["logger","endpoint: string","token: string","DokployApi","prompt","message: string","resolve","err: Error","char: Buffer","options: LoginOptions","options: LogoutOptions","config: GkmConfig","options: BuildOptions","providers: LegacyProvider[]","mainProvider: MainProvider","providersConfig?: ProvidersConfig","providersConfig: ProvidersConfig","config:\n\t\t| boolean\n\t\t| AWSApiGatewayConfig\n\t\t| AWSLambdaConfig\n\t\t| ServerConfig\n\t\t| undefined","context: BuildContext","constructs: GeneratedConstruct<\n\t\t\tCron<any, any, any, any, any, any, any, any>\n\t\t>[]","outputDir: string","options?: GeneratorOptions","logger","cronInfos: CronInfo[]","value: any","sourceFile: string","exportName: string","value: any","context: BuildContext","constructs: GeneratedConstruct<\n\t\t\tFunction<any, any, any, any, any, any, any, any, any, any, any, any>\n\t\t>[]","outputDir: string","options?: GeneratorOptions","logger","functionInfos: FunctionInfo[]","sourceFile: string","exportName: string","value: any","context: BuildContext","constructs: GeneratedConstruct<Queue<any, any, any, any>>[]","outputDir: string","options?: GeneratorOptions","logger","queueInfos: QueueInfo[]","sourceFile: string","exportName: string","queues: GeneratedConstruct<Queue<any, any, any, any>>[]","exports","value: any","context: BuildContext","constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","outputDir: string","options?: GeneratorOptions","logger","subscriberInfos: SubscriberInfo[]","sourceFile: string","exportName: string","_subscriber: Subscriber<any, any, any, any, any, any>","subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","exports","value: any","_context: BuildContext","constructs: GeneratedConstruct<Topic<any, any>>[]","_outputDir: string","_options?: GeneratorOptions","logger","logger","commandArgs: string[]","options: ExecOptions","resolve","code: number | null","error: Error","logger","config: GkmConfig['telescope']","isEnabled","telescopeConfig: TelescopeConfig","config: GkmConfig['studio']","studioConfig: StudioConfig","config: GkmConfig['hooks']","cwd: string","cliProduction: boolean","configProduction?: ProductionConfig","config: GkmConfig","options: DevOptions","appRoot: string","secretsRoot: string","workspaceAppName: string | undefined","workspaceAppPort: number | undefined","workspace: NormalizedWorkspace | undefined","buildContext: BuildContext","runtime: Runtime","secretsJsonPath: string | undefined","rebuildTimeout: NodeJS.Timeout | null","workspace: NormalizedWorkspace","env: Record<string, string>","conflicts: { app1: string; app2: string; port: number }[]","FRONTEND_FRAMEWORKS: Record<FrontendFramework, FrontendFrameworkSpec>","fullPath: string","deps: Record<string, string>","order: FrontendFramework[]","name","appName: string","appPath: string","workspaceRoot: string","framework?: FrontendFramework","errors: string[]","warnings: string[]","pkg: {\n\t\tdependencies?: Record<string, string>;\n\t\tdevDependencies?: Record<string, string>;\n\t\tscripts?: Record<string, string>;\n\t}","pkg","results: FrontendValidationResult[]","turboFilter: string[]","turboEnv: Record<string, string>","resolve","config: any","context: BuildContext","provider: LegacyProvider","enableOpenApi: boolean","wrapperPath: string","entryPath: string","watch: boolean","port: number","restartTimeout: NodeJS.Timeout | null","options: {\n\tsecretsJsonPath?: string;\n\truntime?: Runtime;\n\tenableOpenApi?: boolean;\n\tappImportPath?: string;\n}","requestedPort: number","portExplicit: boolean","telescope: NormalizedTelescopeConfig | undefined","studio: NormalizedStudioConfig | undefined","secretsJsonPath?: string","lines: string[]","s: string","logger","field: ManifestField<T>","fieldName: string","typeName: string","partitioned: boolean","outputDir: string","routes: ManifestField<RouteInfo>","functions: ManifestField<FunctionInfo>","crons: ManifestField<CronInfo>","subscribers: ManifestField<SubscriberInfo>","queues: ManifestField<QueueInfo>","topics: ManifestField<TopicInfo>","appInfo: ServerAppInfo","result: Record<string, RouteInfo[]>","r: RouteInfo","result: Record<\n\t\tstring,\n\t\t{ path: string; method: string; authorizer: string }[]\n\t>","s: SubscriberInfo","result: Record<\n\t\tstring,\n\t\t{ name: string; subscribedEvents: readonly string[] }[]\n\t>","q: QueueInfo","result: Record<string, { name: string }[]>","t: TopicInfo","result: Record<string, { name: string; events: readonly string[] }[]>","constructs: GeneratedConstruct<T>[]","infos: T[]","constructs: GeneratedConstruct<any>[]","groups: Record<string, T[]>","logger","options: BuildOptions","buildContext: BuildContext","result: BuildResult","provider: LegacyProvider","context: BuildContext","rootOutputDir: string","endpointGenerator: EndpointGenerator","functionGenerator: FunctionGenerator","cronGenerator: CronGenerator","subscriberGenerator: SubscriberGenerator","queueGenerator: QueueGenerator","topicGenerator: TopicGenerator","endpoints: GeneratedConstruct<Endpoint<any, any, any, any, any, any>>[]","functions: GeneratedConstruct<Function<any, any, any, any>>[]","crons: GeneratedConstruct<Cron<any, any, any, any>>[]","subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","queues: GeneratedConstruct<Queue<any, any, any, any>>[]","topics: GeneratedConstruct<Topic<any, any>>[]","enableOpenApi: boolean","skipBundle: boolean","stage?: string","routeMetadata: RouteInfo[]","appInfo: ServerAppInfo","masterKey: string | undefined","detectPackageManager","pm: 'pnpm' | 'npm' | 'yarn'","filter?: string","workspace: NormalizedWorkspace","results: AppBuildResult[]","name","resolve","_appName: string","app: NormalizedAppConfig","routes: Routes","infos: T[]","constructs: GeneratedConstruct<any>[]","error: unknown","stage: string","projectId: string","environmentId: string","state: DokployStageState | null","appName: string","state: DokployStageState","applicationId: string","postgresId: string","redisId: string","credentials: AppDbCredentials","secretName: string","value: string","hostname: string","serverIp: string","name: string","type: string","name","type","record: Omit<CreatedDnsRecord, 'createdAt'>","backupState: BackupState","backupId: string","value: unknown","options: CreateDnsProviderOptions","logger","config: DnsConfig","hostname: string","dnsConfig: Record<string, DnsProviderConfig>","appHostnames: Map<string, string>","rootDomain: string","serverIp: string","records: RequiredDnsRecord[]","type","status: string","providerConfig: DnsProviderConfig","provider: DnsProvider | null","results: RequiredDnsRecord[]","upsertRecords: UpsertDnsRecord[]","dnsConfig: DnsConfig | undefined","dokployEndpoint: string","state?: DokployStageState","allRecords: RequiredDnsRecord[]","state: DokployStageState","results: DnsVerificationResult[]","appName: string","app: NormalizedAppConfig","stage: string","dokployConfig: DokployWorkspaceConfig | undefined","isMainFrontend: boolean","allApps: Record<string, NormalizedAppConfig>","name","DEFAULT_SERVICE_IMAGES: Record<ComposeServiceName, string>","DEFAULT_SERVICE_VERSIONS: Record<ComposeServiceName, string>","serviceName: ComposeServiceName","services: ComposeServicesConfig | ComposeServiceName[]","name","version","options: ComposeOptions","options: Omit<ComposeOptions, 'services'>","workspace: NormalizedWorkspace","options: WorkspaceComposeOptions","serviceName: 'postgres' | 'redis' | 'minio'","config: boolean | { version?: string; image?: string } | undefined","defaults: Record<'postgres' | 'redis' | 'minio', string>","baseImages: Record<'postgres' | 'redis' | 'minio', string>","appName: string","app: NormalizedAppConfig","allApps: [string, NormalizedAppConfig][]","options: {\n\t\tregistry?: string;\n\t\tprojectName: string;\n\t\thasPostgres: boolean;\n\t\thasRedis: boolean;\n\t\thasMinio: boolean;\n\t\thasMail: boolean;\n\t\teventsBackend?: import('../types').EventsBackend;\n\t}","dependencies: string[]","dependencies","LOCKFILES: [string, PackageManager][]","detectPackageManager","cwd: string","pm: PackageManager","commands: Record<PackageManager, string>","options: MultiStageDockerfileOptions","options: DockerTemplateOptions","resolveDockerConfig","config: GkmConfig","pkg","options: FrontendDockerfileOptions","options: FrontendDockerfileOptions & { healthCheckPath?: string }","options: EntryDockerfileOptions","logger","options: DockerOptions","pkg","result: DockerGeneratedFiles","cwd: string","imageName: string","appPath: string","workspace: NormalizedWorkspace","results: AppDockerResult[]","dockerfile: string","getAppNameFromCwd","pkg","logger","registry: string | undefined","imageName: string","tag: string","imageRef: string","appName?: string","buildArgs?: string[]","options: DockerDeployOptions","config: GkmConfig","logger","getApiToken","createApi","endpoint: string","options: DokployDeployOptions","registryOptions: {\n\t\tregistryId?: string;\n\t\tusername?: string;\n\t\tpassword?: string;\n\t\tregistryUrl?: string;\n\t}","envVars: Record<string, string>","state: DokployStageState","appName: string","secretName: string","credentials: AppDbCredentials","postgres: { host: string; port: number; database: string }","redis: {\n\thost: string;\n\tport: number;\n\tpassword?: string;\n}","varName: string","context: EnvResolverContext","requiredVars: string[]","resolved: Record<string, string>","missing: string[]","stage: string","logger","providedEndpoint?: string","endpoint: string","config: DokployDeployConfig","cwd: string","newContent: string","options: DeployInitOptions","projectId: string","environmentId: string","options: {\n\tendpoint?: string;\n\tresource: 'projects' | 'registries';\n}","value: unknown","options: CreateStateProviderOptions","CachedStateProvider","stageSecrets: StageSecrets","sniffedEnv: SniffedEnvironment","filtered: EmbeddableSecrets","found: string[]","missing: string[]","filteredSecrets: FilteredAppSecrets","sniffedApps: Map<string, SniffedEnvironment>","encryptedApps: Map<string, EncryptedAppSecrets>","appsWithSecrets: string[]","appsWithoutSecrets: string[]","appsWithMissingSecrets: Array<{ appName: string; missing: string[] }>","require","baseName: string","app: NormalizedAppConfig","appName: string","workspacePath: string","options: SniffAppOptions","sniffedVars: string[]","sniffedOptional: string[]","configPaths: string[]","envVars: string[]","optionalEnvVars: string[]","markOptional: boolean","entryPath: string","appPath: string","stdout","routes: string | string[]","envParserPath: string","SnifferEnvironmentParser: any","sniffWithFireAndForget: any","apps: Record<string, NormalizedAppConfig>","logger","message: string","resolve","char: Buffer","host: string","port: number","user: string","password: string","database: string","PgClient","api: DokployApi","postgres: DokployPostgres","serverHostname: string","users: DbUserConfig[]","endpoint: string","projectId: string","environmentId: string | undefined","projectName: string","services?: DockerComposeServices","existingServiceIds?: { postgresId?: string; redisId?: string }","serviceUrls: ServiceUrls","serviceIds: { postgresId?: string; redisId?: string }","postgres: DokployPostgres | null","redis: DokployRedis | null","config: GkmConfig","dockerConfig: DockerDeployConfig","stage: string","environmentId","provisionResult","environmentId: string","applicationId: string","dokployConfig: DokployDeployConfig","workspace: NormalizedWorkspace","options: DeployOptions","name","provisionedPostgres: DokployPostgres | null","provisionedRedis: DokployRedis | null","usersToCreate: DbUserConfig[]","publicUrls: Record<string, string>","results: AppDeployResult[]","frontendUrls: string[]","application: DokployApplication | null","buildArgs: string[]","dependencyUrls: Record<string, string>","envContext: EnvResolverContext","envVars: string[]","publicUrlArgNames: string[]","dokployConfig: DokployDeployConfig | undefined","dockerServices: DockerComposeServices | undefined","masterKey: string | undefined","result: DeployResult","options: StateCommandOptions","options: StateCommandOptions & { json?: boolean }","state: DokployStageState","name","require","options: TemplateOptions","options: TemplateOptions","template: TemplateConfig","files: GeneratedFile[]","files","_template: TemplateConfig","_helpers: ConfigHelperOptions","options: TemplateOptions","template: TemplateConfig","dbApps?: DatabaseAppConfig[]","services: string[]","volumes: string[]","files: GeneratedFile[]","apps: DatabaseAppConfig[]","_eventsBackend?: EventsBackend","options: TemplateOptions","_template: TemplateConfig","files: GeneratedFile[]","options: TemplateOptions","options: TemplateOptions","options: TemplateOptions","_template: TemplateConfig","files: GeneratedFile[]","apiTemplate: TemplateConfig","options: TemplateOptions","name","file: string","files: GeneratedFile[]","minimalTemplate: TemplateConfig","options: TemplateOptions","file: string","files: GeneratedFile[]","serverlessTemplate: TemplateConfig","options: TemplateOptions","file: string","files: GeneratedFile[]","workerTemplate: TemplateConfig","options: TemplateOptions","file: string","files: GeneratedFile[]","OPENAPI_OUTPUT_PATH","templates: Record<\n\tExclude<TemplateName, 'fullstack'>,\n\tTemplateConfig\n>","name: TemplateName","name","options: TemplateOptions","template: TemplateConfig","dependencies","devDependencies","scripts","name","obj: Record<string, string>","OPENAPI_OUTPUT_PATH","options: TemplateOptions","template: TemplateConfig","options: TemplateOptions","_template: TemplateConfig","options: TemplateOptions","options: TemplateOptions","options: TemplateOptions","cwd: string","name: string","name","pkgManager: PackageManager","script: string","lockfileByPm: Record<PackageManager, string>","pm: PackageManager","pkg","root: string","results: string[]","projectName?: string","options: InitOptions","value: string","name","template: TemplateName","servicesArray: string[]","eventsBackend: EventsBackend | undefined","services: ServicesSelection","pkgManager: PackageManager","deployTarget: DeployTarget","frontendFramework: FullstackFrontendFramework | undefined","templateOptions: TemplateOptions","isMonorepo","dbApps: DatabaseAppConfig[]","secretServices: ComposeServiceName[]","customSecrets: Record<string, string>","projectName: string","options: TemplateOptions","devCommand","logger","services: ComposeServicesConfig | ComposeServiceName[] | undefined","name","options: SecretsInitOptions","projectName: string | undefined","workspaceSecrets: Record<string, string> | undefined","chunks: Buffer[]","key: string","value: string | undefined","options: SecretsSetOptions","options: SecretsShowOptions","options: SecretsRotateOptions","file: string","options: SecretsImportOptions","importedSecrets: Record<string, string>","url: string","options: TestOptions","filtered: Record<string, string>","args: string[]","resolve","env: Record<string, string>","options: UpgradeOptions","dependencies","packageJsonPaths: string[]","results: FoundDependency[]","pkg","name","version","packageNames: string[]","dep: FoundDependency","latestVersions: Map<string, string>","infos: UpgradeInfo[]","status: string","upgradable: UpgradeInfo[]","pm: PackageManager","packages: string[]","cwd: string","pkg","name: string | undefined","options: InitOptions","name","options: SetupOptions","options: {\n\t\t\tprovider?: string;\n\t\t\tproviders?: string;\n\t\t\tenableOpenapi?: boolean;\n\t\t\tproduction?: boolean;\n\t\t\tskipBundle?: boolean;\n\t\t\tstage?: string;\n\t\t\tmarkOptional?: boolean;\n\t\t}","options: {\n\t\t\tport?: string;\n\t\t\tentry?: string;\n\t\t\twatch?: boolean;\n\t\t\tenableOpenapi?: boolean;\n\t\t}","commandArgs: string[]","pattern: string | undefined","options: TestOptions","options: { app?: string }","options: { input?: string; output?: string; name?: string }","options: DockerOptions","options: {\n\t\t\tbuild?: boolean;\n\t\t\tpush?: boolean;\n\t\t\ttag?: string;\n\t\t\tregistry?: string;\n\t\t\tslim?: boolean;\n\t\t\tskipBundle?: boolean;\n\t\t\tturbo?: boolean;\n\t\t\tturboPackage?: string;\n\t\t}","options: { stage: string; force?: boolean }","key: string","value: string | undefined","options: { stage: string }","options: { stage: string; reveal?: boolean }","options: { stage: string; service?: ComposeServiceName }","file: string","options: { stage: string; merge?: boolean }","options: {\n\t\t\tprovider: string;\n\t\t\tstage: string;\n\t\t\ttag?: string;\n\t\t\tskipPush?: boolean;\n\t\t\tskipBuild?: boolean;\n\t\t}","options: {\n\t\t\tendpoint?: string;\n\t\t\tproject: string;\n\t\t\tapp: string;\n\t\t\tprojectId?: string;\n\t\t\tregistryId?: string;\n\t\t}","options: {\n\t\t\tendpoint?: string;\n\t\t\tprojects?: boolean;\n\t\t\tregistries?: boolean;\n\t\t}","options: { service: string; token?: string; endpoint?: string }","options: { service: string }","options: { stage: string; json?: boolean }","options: UpgradeOptions"],"sources":["../package.json","../src/auth/index.ts","../src/build/providerResolver.ts","../src/generators/CronGenerator.ts","../src/generators/FunctionGenerator.ts","../src/generators/QueueGenerator.ts","../src/generators/SubscriberGenerator.ts","../src/generators/TopicGenerator.ts","../src/exec/index.ts","../src/dev/index.ts","../src/build/manifests.ts","../src/build/partitions.ts","../src/build/index.ts","../src/debug.ts","../src/deploy/state.ts","../src/deploy/dns/DnsProvider.ts","../src/deploy/dns/index.ts","../src/deploy/domain.ts","../src/docker/compose.ts","../src/docker/templates.ts","../src/docker/index.ts","../src/deploy/docker.ts","../src/deploy/dokploy.ts","../src/deploy/env-resolver.ts","../src/deploy/init.ts","../src/deploy/StateProvider.ts","../src/deploy/secrets.ts","../src/deploy/sniffer.ts","../src/deploy/index.ts","../src/deploy/state-commands.ts","../src/init/versions.ts","../src/init/generators/auth.ts","../src/init/generators/config.ts","../src/init/generators/docker.ts","../src/init/generators/env.ts","../src/init/generators/mobile-expo.ts","../src/init/generators/models.ts","../src/init/generators/monorepo.ts","../src/init/templates/api.ts","../src/init/templates/minimal.ts","../src/init/templates/serverless.ts","../src/init/templates/worker.ts","../src/init/templates/index.ts","../src/init/generators/package.ts","../src/init/generators/source.ts","../src/init/generators/test.ts","../src/init/generators/ui.ts","../src/init/generators/web.ts","../src/init/generators/web-tanstack.ts","../src/init/utils.ts","../src/init/index.ts","../src/secrets/index.ts","../src/test/index.ts","../src/upgrade/index.ts","../src/index.ts"],"sourcesContent":["{\n\t\"name\": \"@geekmidas/cli\",\n\t\"version\": \"2.0.1\",\n\t\"description\": \"CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs\",\n\t\"private\": false,\n\t\"type\": \"module\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"import\": {\n\t\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\t\"default\": \"./dist/index.mjs\"\n\t\t\t},\n\t\t\t\"require\": {\n\t\t\t\t\"types\": \"./dist/index.d.cts\",\n\t\t\t\t\"default\": \"./dist/index.cjs\"\n\t\t\t}\n\t\t},\n\t\t\"./config\": {\n\t\t\t\"import\": {\n\t\t\t\t\"types\": \"./dist/config.d.mts\",\n\t\t\t\t\"default\": \"./dist/config.mjs\"\n\t\t\t},\n\t\t\t\"require\": {\n\t\t\t\t\"types\": \"./dist/config.d.cts\",\n\t\t\t\t\"default\": \"./dist/config.cjs\"\n\t\t\t}\n\t\t},\n\t\t\"./workspace\": {\n\t\t\t\"import\": {\n\t\t\t\t\"types\": \"./dist/workspace/index.d.mts\",\n\t\t\t\t\"default\": \"./dist/workspace/index.mjs\"\n\t\t\t},\n\t\t\t\"require\": {\n\t\t\t\t\"types\": \"./dist/workspace/index.d.cts\",\n\t\t\t\t\"default\": \"./dist/workspace/index.cjs\"\n\t\t\t}\n\t\t},\n\t\t\"./openapi\": {\n\t\t\t\"import\": {\n\t\t\t\t\"types\": \"./dist/openapi.d.mts\",\n\t\t\t\t\"default\": \"./dist/openapi.mjs\"\n\t\t\t},\n\t\t\t\"require\": {\n\t\t\t\t\"types\": \"./dist/openapi.d.cts\",\n\t\t\t\t\"default\": \"./dist/openapi.cjs\"\n\t\t\t}\n\t\t},\n\t\t\"./openapi-react-query\": {\n\t\t\t\"import\": {\n\t\t\t\t\"types\": \"./dist/openapi-react-query.d.mts\",\n\t\t\t\t\"default\": \"./dist/openapi-react-query.mjs\"\n\t\t\t},\n\t\t\t\"require\": {\n\t\t\t\t\"types\": \"./dist/openapi-react-query.d.cts\",\n\t\t\t\t\"default\": \"./dist/openapi-react-query.cjs\"\n\t\t\t}\n\t\t}\n\t},\n\t\"bin\": {\n\t\t\"gkm\": \"./bin/gkm.mjs\"\n\t},\n\t\"scripts\": {\n\t\t\"ts\": \"tsc --noEmit --skipLibCheck src/**/*.ts\",\n\t\t\"sync-versions\": \"tsx scripts/sync-versions.ts\",\n\t\t\"prebuild\": \"pnpm sync-versions\",\n\t\t\"test\": \"vitest\",\n\t\t\"test:once\": \"vitest run\",\n\t\t\"test:coverage\": \"vitest run --coverage\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"https://github.com/geekmidas/toolbox\"\n\t},\n\t\"dependencies\": {\n\t\t\"@apidevtools/swagger-parser\": \"^10.1.0\",\n\t\t\"@aws-sdk/client-iam\": \"~3.992.0\",\n\t\t\"@aws-sdk/client-route-53\": \"~3.992.0\",\n\t\t\"@aws-sdk/client-s3\": \"~3.992.0\",\n\t\t\"@aws-sdk/client-ssm\": \"~3.992.0\",\n\t\t\"@aws-sdk/credential-providers\": \"~3.992.0\",\n\t\t\"@geekmidas/constructs\": \"workspace:~\",\n\t\t\"@geekmidas/envkit\": \"workspace:~\",\n\t\t\"@geekmidas/errors\": \"workspace:~\",\n\t\t\"@geekmidas/logger\": \"workspace:~\",\n\t\t\"@geekmidas/manifest\": \"workspace:*\",\n\t\t\"@geekmidas/schema\": \"workspace:~\",\n\t\t\"chokidar\": \"~4.0.3\",\n\t\t\"commander\": \"^12.1.0\",\n\t\t\"dotenv\": \"~17.2.3\",\n\t\t\"fast-glob\": \"^3.3.2\",\n\t\t\"hono\": \"~4.8.0\",\n\t\t\"lodash.kebabcase\": \"^4.1.1\",\n\t\t\"openapi-typescript\": \"^7.4.2\",\n\t\t\"pg\": \"~8.17.1\",\n\t\t\"prompts\": \"~2.4.2\",\n\t\t\"tsx\": \"~4.20.3\",\n\t\t\"yaml\": \"~2.8.2\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@geekmidas/testkit\": \"workspace:*\",\n\t\t\"@types/lodash.kebabcase\": \"^4.1.9\",\n\t\t\"@types/node\": \"~24.9.1\",\n\t\t\"@types/pg\": \"~8.16.0\",\n\t\t\"@types/prompts\": \"~2.4.9\",\n\t\t\"typescript\": \"^5.8.2\",\n\t\t\"vitest\": \"^3.2.4\",\n\t\t\"zod\": \"~4.1.13\"\n\t},\n\t\"peerDependencies\": {\n\t\t\"@geekmidas/telescope\": \"workspace:~\"\n\t},\n\t\"peerDependenciesMeta\": {\n\t\t\"@geekmidas/telescope\": {\n\t\t\t\"optional\": true\n\t\t}\n\t}\n}\n","import { stdin as input, stdout as output } from 'node:process';\nimport * as readline from 'node:readline/promises';\nimport {\n\tgetCredentialsPath,\n\tgetDokployCredentials,\n\tremoveDokployCredentials,\n\tstoreDokployCredentials,\n} from './credentials';\n\nconst logger = console;\n\nexport interface LoginOptions {\n\t/** Service to login to */\n\tservice: 'dokploy';\n\t/** API token (if not provided, will prompt) */\n\ttoken?: string;\n\t/** Endpoint URL */\n\tendpoint?: string;\n}\n\nexport interface LogoutOptions {\n\t/** Service to logout from */\n\tservice?: 'dokploy' | 'all';\n}\n\n/**\n * Validate Dokploy token by making a test API call\n */\nexport async function validateDokployToken(\n\tendpoint: string,\n\ttoken: string,\n): Promise<boolean> {\n\tconst { DokployApi } = await import('../deploy/dokploy-api');\n\tconst api = new DokployApi({ baseUrl: endpoint, token });\n\treturn api.validateToken();\n}\n\n/**\n * Prompt for input (handles both TTY and non-TTY)\n */\nasync function prompt(message: string, hidden = false): Promise<string> {\n\tif (!process.stdin.isTTY) {\n\t\tthrow new Error(\n\t\t\t'Interactive input required. Please provide --token option.',\n\t\t);\n\t}\n\n\tif (hidden) {\n\t\t// For hidden input, use raw mode directly without readline\n\t\tprocess.stdout.write(message);\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tlet value = '';\n\n\t\t\tconst cleanup = () => {\n\t\t\t\tprocess.stdin.setRawMode(false);\n\t\t\t\tprocess.stdin.pause();\n\t\t\t\tprocess.stdin.removeListener('data', onData);\n\t\t\t\tprocess.stdin.removeListener('error', onError);\n\t\t\t};\n\n\t\t\tconst onError = (err: Error) => {\n\t\t\t\tcleanup();\n\t\t\t\treject(err);\n\t\t\t};\n\n\t\t\tconst onData = (char: Buffer) => {\n\t\t\t\tconst c = char.toString();\n\n\t\t\t\tif (c === '\\n' || c === '\\r') {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\tresolve(value);\n\t\t\t\t} else if (c === '\\u0003') {\n\t\t\t\t\t// Ctrl+C\n\t\t\t\t\tcleanup();\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t} else if (c === '\\u007F' || c === '\\b') {\n\t\t\t\t\t// Backspace\n\t\t\t\t\tif (value.length > 0) {\n\t\t\t\t\t\tvalue = value.slice(0, -1);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tvalue += c;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tprocess.stdin.setRawMode(true);\n\t\t\tprocess.stdin.resume();\n\t\t\tprocess.stdin.on('data', onData);\n\t\t\tprocess.stdin.on('error', onError);\n\t\t});\n\t} else {\n\t\t// For visible input, use readline\n\t\tconst rl = readline.createInterface({ input, output });\n\t\ttry {\n\t\t\treturn await rl.question(message);\n\t\t} finally {\n\t\t\trl.close();\n\t\t}\n\t}\n}\n\n/**\n * Login to a service\n */\nexport async function loginCommand(options: LoginOptions): Promise<void> {\n\tconst { service, token: providedToken, endpoint: providedEndpoint } = options;\n\n\tif (service === 'dokploy') {\n\t\tlogger.log('\\n🔐 Logging in to Dokploy...\\n');\n\n\t\t// Get endpoint\n\t\tlet endpoint = providedEndpoint;\n\t\tif (!endpoint) {\n\t\t\tendpoint = await prompt(\n\t\t\t\t'Dokploy URL (e.g., https://dokploy.example.com): ',\n\t\t\t);\n\t\t}\n\n\t\t// Normalize endpoint (remove trailing slash)\n\t\tendpoint = endpoint.replace(/\\/$/, '');\n\n\t\t// Validate endpoint format\n\t\ttry {\n\t\t\tnew URL(endpoint);\n\t\t} catch {\n\t\t\tlogger.error('Invalid URL format');\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Get token\n\t\tlet token = providedToken;\n\t\tif (!token) {\n\t\t\tlogger.log(`\\nGenerate a token at: ${endpoint}/settings/profile\\n`);\n\t\t\ttoken = await prompt('API Token: ', true);\n\t\t}\n\n\t\tif (!token) {\n\t\t\tlogger.error('Token is required');\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Validate token\n\t\tlogger.log('\\nValidating credentials...');\n\t\tconst isValid = await validateDokployToken(endpoint, token);\n\n\t\tif (!isValid) {\n\t\t\tlogger.error(\n\t\t\t\t'\\n✗ Invalid credentials. Please check your token and try again.',\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Store credentials\n\t\tawait storeDokployCredentials(token, endpoint);\n\n\t\tlogger.log('\\n✓ Successfully logged in to Dokploy!');\n\t\tlogger.log(` Endpoint: ${endpoint}`);\n\t\tlogger.log(` Credentials stored in: ${getCredentialsPath()}`);\n\t\tlogger.log(\n\t\t\t'\\nYou can now use deploy commands without setting DOKPLOY_API_TOKEN.',\n\t\t);\n\t}\n}\n\n/**\n * Logout from a service\n */\nexport async function logoutCommand(options: LogoutOptions): Promise<void> {\n\tconst { service = 'dokploy' } = options;\n\n\tif (service === 'all') {\n\t\tconst dokployRemoved = await removeDokployCredentials();\n\n\t\tif (dokployRemoved) {\n\t\t\tlogger.log('\\n✓ Logged out from all services');\n\t\t} else {\n\t\t\tlogger.log('\\nNo stored credentials found');\n\t\t}\n\t\treturn;\n\t}\n\n\tif (service === 'dokploy') {\n\t\tconst removed = await removeDokployCredentials();\n\n\t\tif (removed) {\n\t\t\tlogger.log('\\n✓ Logged out from Dokploy');\n\t\t} else {\n\t\t\tlogger.log('\\nNo Dokploy credentials found');\n\t\t}\n\t}\n}\n\n/**\n * Show current login status\n */\nexport async function whoamiCommand(): Promise<void> {\n\tlogger.log('\\n📋 Current credentials:\\n');\n\n\tconst dokploy = await getDokployCredentials();\n\n\tif (dokploy) {\n\t\tlogger.log(' Dokploy:');\n\t\tlogger.log(` Endpoint: ${dokploy.endpoint}`);\n\t\tlogger.log(` Token: ${maskToken(dokploy.token)}`);\n\t} else {\n\t\tlogger.log(' Dokploy: Not logged in');\n\t}\n\n\tlogger.log(`\\n Credentials file: ${getCredentialsPath()}`);\n}\n\n/**\n * Mask a token for display\n */\nexport function maskToken(token: string): string {\n\tif (token.length <= 8) {\n\t\treturn '****';\n\t}\n\treturn `${token.slice(0, 4)}...${token.slice(-4)}`;\n}\n\n// Re-export credentials utilities for use in other modules\nexport {\n\tgetDokployCredentials,\n\tgetDokployEndpoint,\n\tgetDokployRegistryId,\n\tgetDokployToken,\n\tstoreDokployCredentials,\n\tstoreDokployRegistryId,\n} from './credentials';\n","import type {\n\tAWSApiGatewayConfig,\n\tAWSLambdaConfig,\n\tBuildOptions,\n\tGkmConfig,\n\tLegacyProvider,\n\tMainProvider,\n\tProvidersConfig,\n\tServerConfig,\n} from '../types';\n\nexport interface ResolvedProviders {\n\tproviders: LegacyProvider[];\n\tenableOpenApi: boolean;\n}\n\n/**\n * Resolves provider configuration from the new simplified system\n * to the internal legacy format for backward compatibility\n */\nexport function resolveProviders(\n\tconfig: GkmConfig,\n\toptions: BuildOptions,\n): ResolvedProviders {\n\tconst providers: LegacyProvider[] = [];\n\tlet enableOpenApi = options.enableOpenApi || false;\n\n\t// Handle legacy providers option (deprecated)\n\tif (options.providers) {\n\t\treturn {\n\t\t\tproviders: options.providers,\n\t\t\tenableOpenApi,\n\t\t};\n\t}\n\n\t// Handle new provider option\n\tif (options.provider) {\n\t\tconst resolvedProviders = resolveMainProvider(\n\t\t\toptions.provider,\n\t\t\tconfig.providers,\n\t\t);\n\t\tproviders.push(...resolvedProviders.providers);\n\t\tenableOpenApi = resolvedProviders.enableOpenApi || enableOpenApi;\n\t}\n\t// Default: build all configured providers\n\telse if (config.providers) {\n\t\tconst resolvedProviders = resolveAllConfiguredProviders(config.providers);\n\t\tproviders.push(...resolvedProviders.providers);\n\t\tenableOpenApi = resolvedProviders.enableOpenApi || enableOpenApi;\n\t}\n\t// Fallback: use default AWS configuration\n\telse {\n\t\tproviders.push('aws-apigatewayv2', 'aws-lambda');\n\t}\n\n\treturn {\n\t\tproviders: [...new Set(providers)], // Remove duplicates\n\t\tenableOpenApi,\n\t};\n}\n\nfunction resolveMainProvider(\n\tmainProvider: MainProvider,\n\tprovidersConfig?: ProvidersConfig,\n): ResolvedProviders {\n\tconst providers: LegacyProvider[] = [];\n\tlet enableOpenApi = false;\n\n\tif (mainProvider === 'aws') {\n\t\tconst awsConfig = providersConfig?.aws;\n\n\t\t// Resolve API Gateway providers\n\t\tif (awsConfig?.apiGateway) {\n\t\t\tif (isEnabled(awsConfig.apiGateway.v1)) {\n\t\t\t\tproviders.push('aws-apigatewayv1');\n\t\t\t}\n\t\t\tif (isEnabled(awsConfig.apiGateway.v2)) {\n\t\t\t\tproviders.push('aws-apigatewayv2');\n\t\t\t}\n\t\t} else {\n\t\t\t// Default: enable v2 if no specific config\n\t\t\tproviders.push('aws-apigatewayv2');\n\t\t}\n\n\t\t// Resolve Lambda providers\n\t\tif (awsConfig?.lambda) {\n\t\t\tif (\n\t\t\t\tisEnabled(awsConfig.lambda.functions) ||\n\t\t\t\tisEnabled(awsConfig.lambda.crons)\n\t\t\t) {\n\t\t\t\tproviders.push('aws-lambda');\n\t\t\t}\n\t\t} else {\n\t\t\t// Default: enable lambda if no specific config\n\t\t\tproviders.push('aws-lambda');\n\t\t}\n\t} else if (mainProvider === 'server') {\n\t\tproviders.push('server');\n\t\tconst serverConfig = providersConfig?.server;\n\n\t\tif (typeof serverConfig === 'object' && serverConfig?.enableOpenApi) {\n\t\t\tenableOpenApi = true;\n\t\t}\n\t}\n\n\treturn { providers, enableOpenApi };\n}\n\nfunction resolveAllConfiguredProviders(\n\tprovidersConfig: ProvidersConfig,\n): ResolvedProviders {\n\tconst providers: LegacyProvider[] = [];\n\tlet enableOpenApi = false;\n\n\t// AWS providers\n\tif (providersConfig.aws) {\n\t\tconst awsProviders = resolveMainProvider('aws', providersConfig);\n\t\tproviders.push(...awsProviders.providers);\n\t}\n\n\t// Server provider\n\tif (providersConfig.server && isEnabled(providersConfig.server)) {\n\t\tproviders.push('server');\n\t\tif (\n\t\t\ttypeof providersConfig.server === 'object' &&\n\t\t\tprovidersConfig.server.enableOpenApi\n\t\t) {\n\t\t\tenableOpenApi = true;\n\t\t}\n\t}\n\n\treturn { providers, enableOpenApi };\n}\n\nfunction isEnabled(\n\tconfig:\n\t\t| boolean\n\t\t| AWSApiGatewayConfig\n\t\t| AWSLambdaConfig\n\t\t| ServerConfig\n\t\t| undefined,\n): boolean {\n\tif (config === undefined) return false;\n\tif (typeof config === 'boolean') return config;\n\treturn config.enabled !== false; // Default to true if enabled is not explicitly false\n}\n\n/**\n * Gets configuration for a specific AWS service\n */\nexport function getAWSServiceConfig<\n\tT extends AWSApiGatewayConfig | AWSLambdaConfig,\n>(\n\tconfig: GkmConfig,\n\tservice: 'apiGateway' | 'lambda',\n\tsubService?: 'v1' | 'v2' | 'functions' | 'crons',\n): T | undefined {\n\tconst awsConfig = config.providers?.aws;\n\tif (!awsConfig) return undefined;\n\n\tif (service === 'apiGateway' && awsConfig.apiGateway) {\n\t\tconst apiConfig = subService\n\t\t\t? awsConfig.apiGateway[subService as 'v1' | 'v2']\n\t\t\t: undefined;\n\t\treturn typeof apiConfig === 'object' ? (apiConfig as T) : undefined;\n\t}\n\n\tif (service === 'lambda' && awsConfig.lambda) {\n\t\tconst lambdaConfig = subService\n\t\t\t? awsConfig.lambda[subService as 'functions' | 'crons']\n\t\t\t: undefined;\n\t\treturn typeof lambdaConfig === 'object' ? (lambdaConfig as T) : undefined;\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Gets server configuration\n */\nexport function getServerConfig(config: GkmConfig): ServerConfig | undefined {\n\tconst serverConfig = config.providers?.server;\n\treturn typeof serverConfig === 'object' ? serverConfig : undefined;\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Cron } from '@geekmidas/constructs/crons';\nimport type { BuildContext } from '../build/types';\nimport type { CronInfo } from '../types';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\n\nexport class CronGenerator extends ConstructGenerator<\n\tCron<any, any, any, any, any, any, any, any>,\n\tCronInfo[]\n> {\n\tasync build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<\n\t\t\tCron<any, any, any, any, any, any, any, any>\n\t\t>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<CronInfo[]> {\n\t\tconst provider = options?.provider || 'aws-lambda';\n\t\tconst logger = console;\n\t\tconst cronInfos: CronInfo[] = [];\n\n\t\tif (constructs.length === 0 || provider !== 'aws-lambda') {\n\t\t\treturn cronInfos;\n\t\t}\n\n\t\t// Create crons subdirectory\n\t\tconst cronsDir = join(outputDir, 'crons');\n\t\tawait mkdir(cronsDir, { recursive: true });\n\n\t\t// Generate cron handlers\n\t\tfor (const { key, construct, path } of constructs) {\n\t\t\tconst handlerFile = await this.generateCronHandler(\n\t\t\t\tcronsDir,\n\t\t\t\tpath.relative,\n\t\t\t\tkey,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tcronInfos.push({\n\t\t\t\tname: key,\n\t\t\t\thandler: relative(process.cwd(), handlerFile).replace(\n\t\t\t\t\t/\\.ts$/,\n\t\t\t\t\t'.handler',\n\t\t\t\t),\n\t\t\t\tschedule: construct.schedule || 'rate(1 hour)',\n\t\t\t\ttimeout: construct.timeout,\n\t\t\t\tmemorySize: construct.memorySize,\n\t\t\t\tenvironment: await construct.getEnvironment({\n\t\t\t\t\tmarkOptional: context.markOptional,\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tlogger.log(`Generated cron handler: ${key}`);\n\t\t}\n\n\t\treturn cronInfos;\n\t}\n\n\tisConstruct(\n\t\tvalue: any,\n\t): value is Cron<any, any, any, any, any, any, any, any> {\n\t\treturn Cron.isCron(value);\n\t}\n\n\tprivate async generateCronHandler(\n\t\toutputDir: string,\n\t\tsourceFile: string,\n\t\texportName: string,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst handlerFileName = `${exportName}.ts`;\n\t\tconst handlerPath = join(outputDir, handlerFileName);\n\n\t\tconst relativePath = relative(dirname(handlerPath), sourceFile);\n\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\tconst relativeEnvParserPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.envParserPath,\n\t\t);\n\t\tconst relativeLoggerPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.loggerPath,\n\t\t);\n\n\t\tconst content = `import { AWSScheduledFunction } from '@geekmidas/constructs/crons';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\nimport ${context.loggerImportPattern} from '${relativeLoggerPath}';\n\nconst adapter = new AWSScheduledFunction(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n\t\tawait writeFile(handlerPath, content);\n\t\treturn handlerPath;\n\t}\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Function } from '@geekmidas/constructs/functions';\nimport type { BuildContext } from '../build/types';\nimport type { FunctionInfo } from '../types';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\n\nexport class FunctionGenerator extends ConstructGenerator<\n\tFunction<any, any, any, any, any, any, any, any, any, any, any, any>,\n\tFunctionInfo[]\n> {\n\tisConstruct(\n\t\tvalue: any,\n\t): value is Function<\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany\n\t> {\n\t\treturn Function.isFunction(value);\n\t}\n\n\tasync build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<\n\t\t\tFunction<any, any, any, any, any, any, any, any, any, any, any, any>\n\t\t>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<FunctionInfo[]> {\n\t\tconst provider = options?.provider || 'aws-lambda';\n\t\tconst logger = console;\n\t\tconst functionInfos: FunctionInfo[] = [];\n\n\t\tif (constructs.length === 0 || provider !== 'aws-lambda') {\n\t\t\treturn functionInfos;\n\t\t}\n\n\t\t// Create functions subdirectory\n\t\tconst functionsDir = join(outputDir, 'functions');\n\t\tawait mkdir(functionsDir, { recursive: true });\n\n\t\t// Generate function handlers\n\t\tfor (const { key, construct, path } of constructs) {\n\t\t\tconst handlerFile = await this.generateFunctionHandler(\n\t\t\t\tfunctionsDir,\n\t\t\t\tpath.relative,\n\t\t\t\tkey,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tfunctionInfos.push({\n\t\t\t\tname: key,\n\t\t\t\thandler: relative(process.cwd(), handlerFile).replace(\n\t\t\t\t\t/\\.ts$/,\n\t\t\t\t\t'.handler',\n\t\t\t\t),\n\t\t\t\ttimeout: construct.timeout,\n\t\t\t\tmemorySize: construct.memorySize,\n\t\t\t\tenvironment: await construct.getEnvironment({\n\t\t\t\t\tmarkOptional: context.markOptional,\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tlogger.log(`Generated function handler: ${key}`);\n\t\t}\n\n\t\treturn functionInfos;\n\t}\n\n\tprivate async generateFunctionHandler(\n\t\toutputDir: string,\n\t\tsourceFile: string,\n\t\texportName: string,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst handlerFileName = `${exportName}.ts`;\n\t\tconst handlerPath = join(outputDir, handlerFileName);\n\n\t\tconst relativePath = relative(dirname(handlerPath), sourceFile);\n\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\tconst relativeEnvParserPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.envParserPath,\n\t\t);\n\t\tconst relativeLoggerPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.loggerPath,\n\t\t);\n\n\t\tconst content = `import { AWSLambdaFunction } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\nimport ${context.loggerImportPattern} from '${relativeLoggerPath}';\n\nconst adapter = new AWSLambdaFunction(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n\t\tawait writeFile(handlerPath, content);\n\t\treturn handlerPath;\n\t}\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Queue } from '@geekmidas/constructs/queue';\nimport type { BuildContext } from '../build/types';\nimport type { QueueInfo } from '../types';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\n\n/**\n * Generates the runtime for `q` queue workers.\n *\n * - **server** (`gkm dev`): a single `queues.ts` exposing `setupQueues()` that\n * runs an in-process pg-boss poller alongside the Hono server — no SQS/Lambda.\n * Each queue subscribes by its **name** on the shared event connection.\n * - **aws-lambda**: one handler file per queue wrapping `AWSLambdaQueue`, backed\n * by an SQS event-source mapping.\n */\nexport class QueueGenerator extends ConstructGenerator<\n\tQueue<any, any, any, any>,\n\tQueueInfo[]\n> {\n\tisConstruct(value: any): value is Queue<any, any, any, any> {\n\t\treturn Queue.isQueue(value);\n\t}\n\n\tasync build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<Queue<any, any, any, any>>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<QueueInfo[]> {\n\t\tconst provider = options?.provider || 'aws-lambda';\n\t\tconst logger = console;\n\t\tconst queueInfos: QueueInfo[] = [];\n\n\t\tif (provider === 'server') {\n\t\t\t// Generate queues.ts for in-process polling (even if empty, so the\n\t\t\t// server entry can always import setupQueues).\n\t\t\tawait this.generateServerQueuesFile(outputDir, constructs);\n\t\t\tlogger.log(\n\t\t\t\t`Generated server queues file with ${constructs.length} queues (polling mode)`,\n\t\t\t);\n\t\t\treturn queueInfos;\n\t\t}\n\n\t\tif (constructs.length === 0 || provider !== 'aws-lambda') {\n\t\t\treturn queueInfos;\n\t\t}\n\n\t\tconst queuesDir = join(outputDir, 'queues');\n\t\tawait mkdir(queuesDir, { recursive: true });\n\n\t\tfor (const { key, construct, path } of constructs) {\n\t\t\tconst handlerFile = await this.generateQueueHandler(\n\t\t\t\tqueuesDir,\n\t\t\t\tpath.relative,\n\t\t\t\tkey,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tqueueInfos.push({\n\t\t\t\tname: construct.name,\n\t\t\t\thandler: relative(process.cwd(), handlerFile).replace(\n\t\t\t\t\t/\\.ts$/,\n\t\t\t\t\t'.handler',\n\t\t\t\t),\n\t\t\t\tbatchSize: construct.batchSize,\n\t\t\t\tfifo: construct.fifo,\n\t\t\t\ttimeout: construct.timeout,\n\t\t\t\tenvironment: await construct.getEnvironment({\n\t\t\t\t\tmarkOptional: context.markOptional,\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tlogger.log(`Generated queue handler: ${key}`);\n\t\t}\n\n\t\treturn queueInfos;\n\t}\n\n\tprivate async generateQueueHandler(\n\t\toutputDir: string,\n\t\tsourceFile: string,\n\t\texportName: string,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst handlerPath = join(outputDir, `${exportName}.ts`);\n\t\tconst importPath = relative(dirname(handlerPath), sourceFile).replace(\n\t\t\t/\\.ts$/,\n\t\t\t'.js',\n\t\t);\n\t\tconst relativeEnvParserPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.envParserPath,\n\t\t);\n\n\t\tconst content = `import { AWSLambdaQueue } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\n\nconst adapter = new AWSLambdaQueue(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n\t\tawait writeFile(handlerPath, content);\n\t\treturn handlerPath;\n\t}\n\n\tprivate async generateServerQueuesFile(\n\t\toutputDir: string,\n\t\tqueues: GeneratedConstruct<Queue<any, any, any, any>>[],\n\t): Promise<string> {\n\t\tawait mkdir(outputDir, { recursive: true });\n\n\t\tconst queuesPath = join(outputDir, 'queues.ts');\n\n\t\t// Group imports by file\n\t\tconst importsByFile = new Map<string, string[]>();\n\t\tfor (const { path, key } of queues) {\n\t\t\tconst importPath = relative(dirname(queuesPath), path.relative).replace(\n\t\t\t\t/\\.ts$/,\n\t\t\t\t'.js',\n\t\t\t);\n\t\t\tif (!importsByFile.has(importPath)) {\n\t\t\t\timportsByFile.set(importPath, []);\n\t\t\t}\n\t\t\timportsByFile.get(importPath)?.push(key);\n\t\t}\n\n\t\tconst imports = Array.from(importsByFile.entries())\n\t\t\t.map(\n\t\t\t\t([importPath, exports]) =>\n\t\t\t\t\t`import { ${exports.join(', ')} } from '${importPath}';`,\n\t\t\t)\n\t\t\t.join('\\n');\n\n\t\tconst allExportNames = queues.map(({ key }) => key);\n\n\t\tconst content = `/**\n * Generated queue workers setup\n *\n * ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY\n * This runs an in-process pg-boss poller alongside the HTTP server. Each queue\n * subscribes by its name on the shared event connection\n * (EVENT_SUBSCRIBER_CONNECTION_STRING), so producers using\n * <NAME>_PUBLISHER_CONNECTION_STRING (pgboss:// locally) reach it.\n *\n * For production, queues run as AWS Lambda with SQS event-source mappings.\n */\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { EventConnectionFactory, Subscriber } from '@geekmidas/events';\nimport type { EventConnection, EventSubscriber } from '@geekmidas/events';\nimport { ServiceDiscovery } from '@geekmidas/services';\n${imports}\n\nconst queues = [\n ${allExportNames.join(',\\n ')}\n];\n\nconst activeSubscribers: EventSubscriber<any>[] = [];\n\nexport async function setupQueues(\n envParser: EnvironmentParser<any>,\n logger: Logger,\n): Promise<void> {\n if (queues.length === 0) {\n return;\n }\n\n logger.info('Setting up queue workers in polling mode (local development)');\n\n const config = envParser.create((get) => ({\n connectionString: get('EVENT_SUBSCRIBER_CONNECTION_STRING').string(),\n })).parse();\n\n const serviceDiscovery = ServiceDiscovery.getInstance(envParser);\n\n let connection: EventConnection;\n try {\n connection = await EventConnectionFactory.fromConnectionString(config.connectionString);\n const connectionType = new URL(config.connectionString).protocol.replace(':', '');\n logger.info({ connectionType }, 'Created shared event connection for queues');\n } catch (error) {\n logger.error({ error }, 'Failed to create event connection for queues');\n return;\n }\n\n for (const queue of queues) {\n try {\n const eventSubscriber = await Subscriber.fromConnection(connection);\n\n const services = queue.services.length > 0\n ? await serviceDiscovery.register(queue.services)\n : {};\n\n // A queue subscribes to a single \"type\" — its own name.\n await eventSubscriber.subscribe([queue.name], async (message) => {\n try {\n const validation = await queue.messageSchema['~standard'].validate(message.payload);\n if (validation.issues) {\n logger.error({ issues: validation.issues, queue: queue.name }, 'Queue message failed validation');\n return;\n }\n\n await queue.handler({\n messages: [validation.value],\n services: services,\n logger: queue.logger,\n });\n\n logger.debug({ queue: queue.name }, 'Successfully processed queue message');\n } catch (error) {\n logger.error({ error, queue: queue.name }, 'Failed to process queue message');\n // Message will become visible again for retry.\n }\n });\n\n activeSubscribers.push(eventSubscriber);\n logger.info({ queue: queue.name }, 'Queue worker started polling');\n } catch (error) {\n logger.error({ error, queue: queue.name }, 'Failed to setup queue worker');\n }\n }\n\n const shutdown = () => {\n logger.info('Stopping all queue workers');\n for (const _ of activeSubscribers) {\n connection.stop();\n }\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n}\n`;\n\n\t\tawait writeFile(queuesPath, content);\n\t\treturn queuesPath;\n\t}\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Subscriber } from '@geekmidas/constructs/subscribers';\nimport type { BuildContext } from '../build/types';\nimport type { SubscriberInfo } from '../types';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\n\nexport class SubscriberGenerator extends ConstructGenerator<\n\tSubscriber<any, any, any, any, any, any>,\n\tSubscriberInfo[]\n> {\n\tisConstruct(value: any): value is Subscriber<any, any, any, any, any, any> {\n\t\treturn Subscriber.isSubscriber(value);\n\t}\n\n\tasync build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<SubscriberInfo[]> {\n\t\tconst provider = options?.provider || 'aws-lambda';\n\t\tconst logger = console;\n\t\tconst subscriberInfos: SubscriberInfo[] = [];\n\n\t\tif (provider === 'server') {\n\t\t\t// Generate subscribers.ts for server-based polling (even if empty)\n\t\t\tawait this.generateServerSubscribersFile(outputDir, constructs);\n\n\t\t\tlogger.log(\n\t\t\t\t`Generated server subscribers file with ${constructs.length} subscribers (polling mode)`,\n\t\t\t);\n\n\t\t\t// Return empty array as server subscribers don't have individual handlers\n\t\t\treturn subscriberInfos;\n\t\t}\n\n\t\tif (constructs.length === 0) {\n\t\t\treturn subscriberInfos;\n\t\t}\n\n\t\tif (provider !== 'aws-lambda') {\n\t\t\treturn subscriberInfos;\n\t\t}\n\n\t\t// Create subscribers subdirectory\n\t\tconst subscribersDir = join(outputDir, 'subscribers');\n\t\tawait mkdir(subscribersDir, { recursive: true });\n\n\t\t// Generate subscriber handlers\n\t\tfor (const { key, construct, path } of constructs) {\n\t\t\tconst handlerFile = await this.generateSubscriberHandler(\n\t\t\t\tsubscribersDir,\n\t\t\t\tpath.relative,\n\t\t\t\tkey,\n\t\t\t\tconstruct,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tsubscriberInfos.push({\n\t\t\t\tname: key,\n\t\t\t\thandler: relative(process.cwd(), handlerFile).replace(\n\t\t\t\t\t/\\.ts$/,\n\t\t\t\t\t'.handler',\n\t\t\t\t),\n\t\t\t\tsubscribedEvents: construct.subscribedEvents || [],\n\t\t\t\t// A subscriber bound to a topic (via `s.topic(topic)`) records the\n\t\t\t\t// binding so infra wires the SNS subscription.\n\t\t\t\t...(construct.topicName\n\t\t\t\t\t? { transport: 'topic' as const, topic: construct.topicName }\n\t\t\t\t\t: {}),\n\t\t\t\ttimeout: construct.timeout,\n\t\t\t\tmemorySize: construct.memorySize,\n\t\t\t\tenvironment: await construct.getEnvironment({\n\t\t\t\t\tmarkOptional: context.markOptional,\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tlogger.log(`Generated subscriber handler: ${key}`);\n\t\t}\n\n\t\treturn subscriberInfos;\n\t}\n\n\tprivate async generateSubscriberHandler(\n\t\toutputDir: string,\n\t\tsourceFile: string,\n\t\texportName: string,\n\t\t_subscriber: Subscriber<any, any, any, any, any, any>,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst handlerFileName = `${exportName}.ts`;\n\t\tconst handlerPath = join(outputDir, handlerFileName);\n\n\t\tconst relativePath = relative(dirname(handlerPath), sourceFile);\n\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\tconst relativeEnvParserPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.envParserPath,\n\t\t);\n\n\t\tconst content = `import { AWSLambdaSubscriber } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\n\nconst adapter = new AWSLambdaSubscriber(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n\t\tawait writeFile(handlerPath, content);\n\t\treturn handlerPath;\n\t}\n\n\tprivate async generateServerSubscribersFile(\n\t\toutputDir: string,\n\t\tsubscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n\t): Promise<string> {\n\t\t// Ensure output directory exists\n\t\tawait mkdir(outputDir, { recursive: true });\n\n\t\tconst subscribersFileName = 'subscribers.ts';\n\t\tconst subscribersPath = join(outputDir, subscribersFileName);\n\n\t\t// Group imports by file\n\t\tconst importsByFile = new Map<string, string[]>();\n\n\t\tfor (const { path, key } of subscribers) {\n\t\t\tconst relativePath = relative(dirname(subscribersPath), path.relative);\n\t\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\t\tif (!importsByFile.has(importPath)) {\n\t\t\t\timportsByFile.set(importPath, []);\n\t\t\t}\n\t\t\timportsByFile.get(importPath)?.push(key);\n\t\t}\n\n\t\t// Generate import statements\n\t\tconst imports = Array.from(importsByFile.entries())\n\t\t\t.map(\n\t\t\t\t([importPath, exports]) =>\n\t\t\t\t\t`import { ${exports.join(', ')} } from '${importPath}';`,\n\t\t\t)\n\t\t\t.join('\\n');\n\n\t\tconst allExportNames = subscribers.map(({ key }) => key);\n\n\t\tconst content = `/**\n * Generated subscribers setup\n *\n * ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY\n * This uses event polling which is not suitable for production.\n *\n * For production, use AWS Lambda with SQS/SNS event source mappings.\n * Lambda automatically:\n * - Scales based on queue depth\n * - Handles batch processing and retries\n * - Manages dead letter queues\n * - Provides better cost optimization\n *\n * This polling implementation is useful for:\n * - Local development and testing\n * - Understanding event flow without Lambda deployment\n *\n * Supported connection strings:\n * - sqs://region/account-id/queue-name (SQS queue)\n * - sns://region/account-id/topic-name (SNS topic)\n * - rabbitmq://host:port/queue-name (RabbitMQ)\n * - pgboss://user:pass@host:port/database (pg-boss / PostgreSQL)\n * - basic://in-memory (In-memory for testing)\n */\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { EventConnectionFactory, Subscriber } from '@geekmidas/events';\nimport type { EventConnection, EventSubscriber } from '@geekmidas/events';\nimport { ServiceDiscovery } from '@geekmidas/services';\n${imports}\n\nconst subscribers = [\n ${allExportNames.join(',\\n ')}\n];\n\nconst activeSubscribers: EventSubscriber<any>[] = [];\n\nexport async function setupSubscribers(\n envParser: EnvironmentParser<any>,\n logger: Logger,\n): Promise<void> {\n if (subscribers.length === 0) {\n return;\n }\n\n logger.info('Setting up subscribers in polling mode (local development)');\n\n const config = envParser.create((get) => ({\n connectionString: get('EVENT_SUBSCRIBER_CONNECTION_STRING').string(),\n })).parse();\n\n const serviceDiscovery = ServiceDiscovery.getInstance(envParser);\n\n // Create connection once, outside the loop (more efficient)\n // EventConnectionFactory automatically determines the right connection type\n let connection: EventConnection;\n try {\n connection = await EventConnectionFactory.fromConnectionString(config.connectionString);\n\n const connectionType = new URL(config.connectionString).protocol.replace(':', '');\n logger.info({ connectionType }, 'Created shared event connection');\n } catch (error) {\n logger.error({ error }, 'Failed to create event connection');\n return;\n }\n\n for (const subscriber of subscribers) {\n try {\n // Create subscriber from shared connection\n const eventSubscriber = await Subscriber.fromConnection(connection);\n\n // Register services\n const services = subscriber.services.length > 0\n ? await serviceDiscovery.register(subscriber.services)\n : {};\n\n // Subscribe to events\n const subscribedEvents = subscriber.subscribedEvents || [];\n\n if (subscribedEvents.length === 0) {\n logger.warn({ subscriber: subscriber.constructor.name }, 'Subscriber has no subscribed events, skipping');\n continue;\n }\n\n await eventSubscriber.subscribe(subscribedEvents, async (event) => {\n try {\n // Process single event (batch of 1)\n await subscriber.handler({\n events: [event],\n services: services as any,\n logger: subscriber.logger,\n });\n\n logger.debug({ eventType: event.type }, 'Successfully processed event');\n } catch (error) {\n logger.error({ error, event }, 'Failed to process event');\n // Event will become visible again for retry\n }\n });\n\n activeSubscribers.push(eventSubscriber);\n\n logger.info(\n {\n events: subscribedEvents,\n },\n 'Subscriber started polling'\n );\n } catch (error) {\n logger.error({ error, subscriber: subscriber.constructor.name }, 'Failed to setup subscriber');\n }\n }\n\n // Setup graceful shutdown\n const shutdown = () => {\n logger.info('Stopping all subscribers');\n for (const eventSubscriber of activeSubscribers) {\n connection.stop();\n }\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n}\n`;\n\n\t\tawait writeFile(subscribersPath, content);\n\t\treturn subscribersPath;\n\t}\n}\n","import { Topic } from '@geekmidas/constructs/topic';\nimport type { BuildContext } from '../build/types';\nimport type { TopicInfo } from '../types';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\n\n/**\n * Discovers `t` topics into `manifest.topics`. A topic is a *resource*, not a\n * function — it has no handler to generate. Producers reach it via its derived\n * publisher (`topic.publisher`, whose connection string is sniffed onto the\n * producing construct) and subscribers bind via `s.topic(topic)`; this generator\n * only records the topic + its event contract so infra can provision the SNS\n * topic. Identical output for every provider (locally pg-boss routes by event\n * name, so there's nothing to run).\n */\nexport class TopicGenerator extends ConstructGenerator<\n\tTopic<any, any>,\n\tTopicInfo[]\n> {\n\tisConstruct(value: any): value is Topic<any, any> {\n\t\treturn Topic.isTopic(value);\n\t}\n\n\tasync build(\n\t\t_context: BuildContext,\n\t\tconstructs: GeneratedConstruct<Topic<any, any>>[],\n\t\t_outputDir: string,\n\t\t_options?: GeneratorOptions,\n\t): Promise<TopicInfo[]> {\n\t\tconst logger = console;\n\t\tconst topicInfos = constructs.map(({ construct }) => ({\n\t\t\tname: construct.name,\n\t\t\tevents: construct.eventTypes,\n\t\t}));\n\n\t\tfor (const topic of topicInfos) {\n\t\t\tlogger.log(`Discovered topic: ${topic.name}`);\n\t\t}\n\n\t\treturn topicInfos;\n\t}\n}\n","import { spawn } from 'node:child_process';\nimport { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcreateCredentialsPreload,\n\tloadEnvFiles,\n\tprepareEntryCredentials,\n} from '../credentials';\n\nconst logger = console;\n\n/**\n * Options for the exec command.\n */\nexport interface ExecOptions {\n\t/** Working directory */\n\tcwd?: string;\n}\n\n/**\n * Run a command with secrets injected into Credentials.\n * Uses Node's --import flag to preload a script that populates Credentials\n * before the command loads any modules that depend on them.\n *\n * @example\n * ```bash\n * gkm exec -- npx @better-auth/cli migrate\n * gkm exec -- npx prisma migrate dev\n * ```\n */\nexport async function execCommand(\n\tcommandArgs: string[],\n\toptions: ExecOptions = {},\n): Promise<void> {\n\tconst cwd = options.cwd ?? process.cwd();\n\n\tif (commandArgs.length === 0) {\n\t\tthrow new Error('No command specified. Usage: gkm exec -- <command>');\n\t}\n\n\t// Load .env files\n\tconst defaultEnv = loadEnvFiles('.env');\n\tif (defaultEnv.loaded.length > 0) {\n\t\tlogger.log(`📦 Loaded env: ${defaultEnv.loaded.join(', ')}`);\n\t}\n\n\t// Prepare credentials: loads secrets, resolves Docker ports, rewrites URLs,\n\t// injects dependency URLs. Uses readonly port mode (no probing for new ports).\n\tconst { credentials, secretsJsonPath, appName } =\n\t\tawait prepareEntryCredentials({ cwd, resolveDockerPorts: 'readonly' });\n\n\tif (appName) {\n\t\tlogger.log(`📦 App: ${appName}`);\n\t}\n\n\tconst secretCount = Object.keys(credentials).filter(\n\t\t(k) => k !== 'PORT',\n\t).length;\n\tif (secretCount > 0) {\n\t\tlogger.log(`🔐 Loaded ${secretCount} secret(s)`);\n\t}\n\n\t// Create preload script that injects Credentials\n\t// Written as .mjs (plain ESM) so it doesn't need tsx — this avoids\n\t// breaking frameworks like Next.js whose workers inherit NODE_OPTIONS.\n\tconst preloadDir = join(cwd, '.gkm');\n\tawait mkdir(preloadDir, { recursive: true });\n\tconst preloadPath = join(preloadDir, 'credentials-preload.mjs');\n\tawait createCredentialsPreload(preloadPath, secretsJsonPath);\n\n\t// Build command\n\tconst [cmd, ...args] = commandArgs;\n\n\tif (!cmd) {\n\t\tthrow new Error('No command specified');\n\t}\n\n\tlogger.log(`🚀 Running: ${[cmd, ...args].join(' ')}`);\n\n\t// Build NODE_OPTIONS for the child process.\n\t// Strip any --import flags from the inherited NODE_OPTIONS — the gkm binary\n\t// adds --import=tsx for itself, but the spawned command (e.g. next, prisma)\n\t// doesn't need it and it breaks frameworks that spawn their own workers.\n\tconst existingNodeOptions = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst preloadImport = `--import=${preloadPath}`;\n\n\tconst nodeOptions = [existingNodeOptions, preloadImport]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\t// Spawn the command with secrets in both:\n\t// 1. Environment variables (for tools that read process.env directly)\n\t// 2. Preload script (for tools that use Credentials object)\n\tconst child = spawn(cmd, args, {\n\t\tcwd,\n\t\tstdio: 'inherit',\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t...credentials, // Inject secrets as env vars\n\t\t\tNODE_OPTIONS: nodeOptions,\n\t\t},\n\t});\n\n\t// Wait for the command to complete\n\tconst exitCode = await new Promise<number>((resolve) => {\n\t\tchild.on('close', (code: number | null) => resolve(code ?? 0));\n\t\tchild.on('error', (error: Error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t});\n\n\tif (exitCode !== 0) {\n\t\tprocess.exit(exitCode);\n\t}\n}\n","import { type ChildProcess, execSync, spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, resolve } from 'node:path';\nimport chokidar from 'chokidar';\nimport fg from 'fast-glob';\nimport { resolveProviders } from '../build/providerResolver';\nimport type {\n\tBuildContext,\n\tNormalizedHooksConfig,\n\tNormalizedProductionConfig,\n\tNormalizedStudioConfig,\n\tNormalizedTelescopeConfig,\n} from '../build/types';\nimport {\n\tgetAppNameFromCwd,\n\tloadAppConfig,\n\tloadWorkspaceConfig,\n\tparseModuleConfig,\n} from '../config';\nimport {\n\tcreateEntryWrapper,\n\tfindAvailablePort,\n\tisPortAvailable,\n\tloadEnvFiles,\n\tloadSecretsForApp,\n\tprepareEntryCredentials,\n\tresolveServicePorts,\n\trewriteUrlsWithPorts,\n\tstartWorkspaceServices,\n} from '../credentials';\nimport {\n\tCronGenerator,\n\tEndpointGenerator,\n\tFunctionGenerator,\n\tQueueGenerator,\n\tSubscriberGenerator,\n\tTopicGenerator,\n} from '../generators';\nimport {\n\tgenerateOpenApi,\n\tOPENAPI_OUTPUT_PATH,\n\tresolveOpenApiConfig,\n} from '../openapi';\nimport {\n\treadStageSecrets,\n\tsecretsExist,\n\ttoEmbeddableSecrets,\n} from '../secrets/storage.js';\nimport type {\n\tGkmConfig,\n\tLegacyProvider,\n\tProductionConfig,\n\tRuntime,\n\tServerConfig,\n\tStudioConfig,\n\tTelescopeConfig,\n} from '../types';\nimport {\n\ttype FrontendFramework,\n\tgetAppBuildOrder,\n\tgetDependencyEnvVars,\n\ttype NormalizedWorkspace,\n} from '../workspace/index.js';\n\n// Re-export shared utilities from credentials module so existing imports\n// from '../dev' or '../dev/index' continue to work.\nexport {\n\tbuildDockerComposeEnv,\n\ttype ComposePortMapping,\n\tcreateCredentialsPreload,\n\tcreateEntryWrapper,\n\ttype EntryCredentialsResult,\n\tfindAvailablePort,\n\tfindSecretsRoot,\n\tgetContainerHostPort,\n\tisPortAvailable,\n\tloadEnvFiles,\n\tloadPortState,\n\tloadSecretsForApp,\n\ttype PortState,\n\tparseComposePortMappings,\n\tparseComposeServiceNames,\n\tprepareEntryCredentials,\n\ttype ResolvedServicePorts,\n\treplacePortInUrl,\n\tresolveServicePorts,\n\trewriteUrlsWithPorts,\n\tsavePortState,\n\tstartComposeServices,\n\tstartWorkspaceServices,\n} from '../credentials';\n\n// Re-export execCommand from its own module\nexport { type ExecOptions, execCommand } from '../exec';\n\nconst logger = console;\n\n/**\n * Normalize telescope configuration\n * @internal Exported for testing\n */\nexport function normalizeTelescopeConfig(\n\tconfig: GkmConfig['telescope'],\n): NormalizedTelescopeConfig | undefined {\n\tif (config === false) {\n\t\treturn undefined;\n\t}\n\n\t// Handle string path (e.g., './src/config/telescope')\n\tif (typeof config === 'string') {\n\t\tconst { path: telescopePath, importPattern: telescopeImportPattern } =\n\t\t\tparseModuleConfig(config, 'telescope');\n\n\t\treturn {\n\t\t\tenabled: true,\n\t\t\ttelescopePath,\n\t\t\ttelescopeImportPattern,\n\t\t\tpath: '/__telescope',\n\t\t\tignore: [],\n\t\t\trecordBody: true,\n\t\t\tmaxEntries: 1000,\n\t\t\twebsocket: true,\n\t\t};\n\t}\n\n\t// Default to enabled in development mode\n\tconst isEnabled =\n\t\tconfig === true || config === undefined || config.enabled !== false;\n\n\tif (!isEnabled) {\n\t\treturn undefined;\n\t}\n\n\tconst telescopeConfig: TelescopeConfig =\n\t\ttypeof config === 'object' ? config : {};\n\n\treturn {\n\t\tenabled: true,\n\t\tpath: telescopeConfig.path ?? '/__telescope',\n\t\tignore: telescopeConfig.ignore ?? [],\n\t\trecordBody: telescopeConfig.recordBody ?? true,\n\t\tmaxEntries: telescopeConfig.maxEntries ?? 1000,\n\t\twebsocket: telescopeConfig.websocket ?? true,\n\t};\n}\n\n/**\n * Normalize studio configuration\n * @internal Exported for testing\n */\nexport function normalizeStudioConfig(\n\tconfig: GkmConfig['studio'],\n): NormalizedStudioConfig | undefined {\n\tif (config === false) {\n\t\treturn undefined;\n\t}\n\n\t// Handle string path (e.g., './src/config/studio')\n\tif (typeof config === 'string') {\n\t\tconst { path: studioPath, importPattern: studioImportPattern } =\n\t\t\tparseModuleConfig(config, 'studio');\n\n\t\treturn {\n\t\t\tenabled: true,\n\t\t\tstudioPath,\n\t\t\tstudioImportPattern,\n\t\t\tpath: '/__studio',\n\t\t\tschema: 'public',\n\t\t};\n\t}\n\n\t// Default to enabled in development mode\n\tconst isEnabled =\n\t\tconfig === true || config === undefined || config.enabled !== false;\n\n\tif (!isEnabled) {\n\t\treturn undefined;\n\t}\n\n\tconst studioConfig: StudioConfig = typeof config === 'object' ? config : {};\n\n\treturn {\n\t\tenabled: true,\n\t\tpath: studioConfig.path ?? '/__studio',\n\t\tschema: studioConfig.schema ?? 'public',\n\t};\n}\n\n/**\n * Normalize hooks configuration\n * @internal Exported for testing\n */\nexport function normalizeHooksConfig(\n\tconfig: GkmConfig['hooks'],\n\tcwd: string = process.cwd(),\n): NormalizedHooksConfig | undefined {\n\tif (!config?.server) {\n\t\treturn undefined;\n\t}\n\n\t// Resolve the path (handle .ts extension)\n\tconst serverPath = config.server.endsWith('.ts')\n\t\t? config.server\n\t\t: `${config.server}.ts`;\n\n\tconst resolvedPath = resolve(cwd, serverPath);\n\n\treturn {\n\t\tserverHooksPath: resolvedPath,\n\t};\n}\n\n/**\n * Normalize production configuration\n * @internal Exported for testing\n */\nexport function normalizeProductionConfig(\n\tcliProduction: boolean,\n\tconfigProduction?: ProductionConfig,\n): NormalizedProductionConfig | undefined {\n\t// Production mode is only enabled if --production CLI flag is passed\n\tif (!cliProduction) {\n\t\treturn undefined;\n\t}\n\n\t// Merge CLI flag with config options\n\tconst config = configProduction ?? {};\n\n\treturn {\n\t\tenabled: true,\n\t\tbundle: config.bundle ?? true,\n\t\tminify: config.minify ?? true,\n\t\thealthCheck: config.healthCheck ?? '/health',\n\t\tgracefulShutdown: config.gracefulShutdown ?? true,\n\t\texternal: config.external ?? [],\n\t\tsubscribers: config.subscribers ?? 'exclude',\n\t\topenapi: config.openapi ?? false,\n\t\toptimizedHandlers: config.optimizedHandlers ?? true, // Default to optimized handlers in production\n\t};\n}\n\n/**\n * Get production config from GkmConfig\n * @internal\n */\nexport function getProductionConfigFromGkm(\n\tconfig: GkmConfig,\n): ProductionConfig | undefined {\n\tconst serverConfig = config.providers?.server;\n\tif (typeof serverConfig === 'object') {\n\t\treturn (serverConfig as ServerConfig).production;\n\t}\n\treturn undefined;\n}\n\nexport interface DevOptions {\n\tport?: number;\n\tportExplicit?: boolean;\n\tenableOpenApi?: boolean;\n\t/** Specific app to run in workspace mode (default: all apps) */\n\tapp?: string;\n\t/** Filter apps by pattern (passed to turbo --filter) */\n\tfilter?: string;\n\t/** Entry file to run (bypasses gkm config) */\n\tentry?: string;\n\t/** Watch for file changes (default: true with --entry) */\n\twatch?: boolean;\n}\n\nexport async function devCommand(options: DevOptions): Promise<void> {\n\t// Handle --entry mode: run any file with secret injection\n\tif (options.entry) {\n\t\treturn entryDevCommand(options);\n\t}\n\n\t// Load default .env file BEFORE loading config\n\t// This ensures env vars are available when config and its dependencies are loaded\n\tconst defaultEnv = loadEnvFiles('.env');\n\tif (defaultEnv.loaded.length > 0) {\n\t\tlogger.log(`📦 Loaded env: ${defaultEnv.loaded.join(', ')}`);\n\t}\n\n\t// Check if we're in an app subdirectory\n\tconst appName = getAppNameFromCwd();\n\tlet config: GkmConfig;\n\tlet appRoot: string = process.cwd();\n\tlet secretsRoot: string = process.cwd(); // Where .gkm/secrets/ lives\n\tlet workspaceAppName: string | undefined; // Set if in workspace mode\n\tlet workspaceAppPort: number | undefined; // Port from workspace config\n\tlet workspace: NormalizedWorkspace | undefined; // Set if in workspace mode\n\n\tif (appName) {\n\t\t// Try to load app-specific config from workspace\n\t\ttry {\n\t\t\tconst appConfig = await loadAppConfig();\n\t\t\tconfig = appConfig.gkmConfig;\n\t\t\tappRoot = appConfig.appRoot;\n\t\t\tsecretsRoot = appConfig.workspaceRoot;\n\t\t\tworkspaceAppName = appConfig.appName;\n\t\t\tworkspaceAppPort = appConfig.app.port;\n\t\t\tworkspace = appConfig.workspace;\n\t\t\tlogger.log(\n\t\t\t\t`📦 Running app: ${appConfig.appName} on port ${workspaceAppPort}`,\n\t\t\t);\n\n\t\t\t// Check if app has an entry point (non-gkm app like better-auth)\n\t\t\tif (appConfig.app.entry) {\n\t\t\t\tlogger.log(`📄 Using entry point: ${appConfig.app.entry}`);\n\t\t\t\treturn entryDevCommand({\n\t\t\t\t\t...options,\n\t\t\t\t\tentry: appConfig.app.entry,\n\t\t\t\t\tport: workspaceAppPort,\n\t\t\t\t\tportExplicit: true,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// Not in a workspace or app not found in workspace - fall back to regular loading\n\t\t\tconst loadedConfig = await loadWorkspaceConfig();\n\n\t\t\t// Route to workspace dev mode for multi-app workspaces\n\t\t\tif (loadedConfig.type === 'workspace') {\n\t\t\t\tlogger.log('📦 Detected workspace configuration');\n\t\t\t\treturn workspaceDevCommand(loadedConfig.workspace, options);\n\t\t\t}\n\n\t\t\tconfig = loadedConfig.raw as GkmConfig;\n\t\t}\n\t} else {\n\t\t// Try to load workspace config\n\t\tconst loadedConfig = await loadWorkspaceConfig();\n\n\t\t// Route to workspace dev mode for multi-app workspaces\n\t\tif (loadedConfig.type === 'workspace') {\n\t\t\tlogger.log('📦 Detected workspace configuration');\n\t\t\treturn workspaceDevCommand(loadedConfig.workspace, options);\n\t\t}\n\n\t\t// Single-app mode - use existing logic\n\t\tconfig = loadedConfig.raw as GkmConfig;\n\t}\n\n\t// Load any additional env files specified in config\n\tif (config.env) {\n\t\tconst { loaded, missing } = loadEnvFiles(config.env, appRoot);\n\t\tif (loaded.length > 0) {\n\t\t\tlogger.log(`📦 Loaded env: ${loaded.join(', ')}`);\n\t\t}\n\t\tif (missing.length > 0) {\n\t\t\tlogger.warn(`⚠️ Missing env files: ${missing.join(', ')}`);\n\t\t}\n\t}\n\n\t// Force server provider for dev mode\n\tconst resolved = resolveProviders(config, { provider: 'server' });\n\n\tlogger.log('🚀 Starting development server...');\n\tlogger.log(`Loading routes from: ${config.routes}`);\n\tif (config.functions) {\n\t\tlogger.log(`Loading functions from: ${config.functions}`);\n\t}\n\tif (config.crons) {\n\t\tlogger.log(`Loading crons from: ${config.crons}`);\n\t}\n\tif (config.subscribers) {\n\t\tlogger.log(`Loading subscribers from: ${config.subscribers}`);\n\t}\n\tlogger.log(`Using envParser: ${config.envParser}`);\n\n\t// Parse envParser and logger configuration\n\tconst { path: envParserPath, importPattern: envParserImportPattern } =\n\t\tparseModuleConfig(config.envParser, 'envParser');\n\tconst { path: loggerPath, importPattern: loggerImportPattern } =\n\t\tparseModuleConfig(config.logger, 'logger');\n\n\t// Normalize telescope configuration\n\tconst telescope = normalizeTelescopeConfig(config.telescope);\n\tif (telescope) {\n\t\tlogger.log(`🔭 Telescope enabled at ${telescope.path}`);\n\t}\n\n\t// Normalize studio configuration\n\tconst studio = normalizeStudioConfig(config.studio);\n\tif (studio) {\n\t\tlogger.log(`🗄️ Studio enabled at ${studio.path}`);\n\t}\n\n\t// Normalize hooks configuration\n\tconst hooks = normalizeHooksConfig(config.hooks, appRoot);\n\tif (hooks) {\n\t\tlogger.log(`🪝 Server hooks enabled from ${config.hooks?.server}`);\n\t}\n\n\t// Resolve OpenAPI configuration\n\tconst openApiConfig = resolveOpenApiConfig(config);\n\t// Enable OpenAPI docs endpoint if either root config or provider config enables it\n\tconst enableOpenApi = openApiConfig.enabled || resolved.enableOpenApi;\n\tif (enableOpenApi) {\n\t\tlogger.log(`📄 OpenAPI output: ${OPENAPI_OUTPUT_PATH}`);\n\t}\n\n\tconst buildContext: BuildContext = {\n\t\tenvParserPath,\n\t\tenvParserImportPattern,\n\t\tloggerPath,\n\t\tloggerImportPattern,\n\t\ttelescope,\n\t\tstudio,\n\t\thooks,\n\t};\n\n\t// Build initial version\n\tawait buildServer(\n\t\tconfig,\n\t\tbuildContext,\n\t\tresolved.providers[0] as LegacyProvider,\n\t\tenableOpenApi,\n\t\tappRoot,\n\t);\n\n\t// Generate OpenAPI spec on startup\n\tif (enableOpenApi) {\n\t\tawait generateOpenApi(config);\n\t}\n\n\t// Determine runtime (default to node)\n\tconst runtime: Runtime = config.runtime ?? 'node';\n\n\t// Load secrets for dev mode, resolve Docker ports, and write to JSON file\n\tlet secretsJsonPath: string | undefined;\n\tconst appSecrets = await loadSecretsForApp(secretsRoot, workspaceAppName);\n\n\t// Resolve Docker service ports and rewrite connection URLs\n\tconst resolvedPorts = await resolveServicePorts(secretsRoot);\n\tif (Object.keys(resolvedPorts.ports).length > 0) {\n\t\tconst rewritten = rewriteUrlsWithPorts(appSecrets, resolvedPorts);\n\t\tObject.assign(appSecrets, rewritten);\n\t\tlogger.log(\n\t\t\t`🔌 Applied ${Object.keys(resolvedPorts.ports).length} port mapping(s)`,\n\t\t);\n\t}\n\n\t// Inject dependency URLs if in workspace mode\n\tif (workspace && workspaceAppName) {\n\t\tconst depEnv = getDependencyEnvVars(workspace, workspaceAppName);\n\t\tObject.assign(appSecrets, depEnv);\n\t}\n\n\tif (Object.keys(appSecrets).length > 0) {\n\t\tconst secretsDir = join(secretsRoot, '.gkm');\n\t\tawait mkdir(secretsDir, { recursive: true });\n\t\tconst secretsFileName = workspaceAppName\n\t\t\t? `dev-secrets-${workspaceAppName}.json`\n\t\t\t: 'dev-secrets.json';\n\t\tsecretsJsonPath = join(secretsDir, secretsFileName);\n\t\tawait writeFile(secretsJsonPath, JSON.stringify(appSecrets, null, 2));\n\t\tlogger.log(`🔐 Loaded ${Object.keys(appSecrets).length} secret(s)`);\n\t}\n\n\t// Start the dev server\n\t// Priority: explicit --port option > workspace app port > default 3000\n\tconst devServer = new DevServer(\n\t\tresolved.providers[0] as LegacyProvider,\n\t\toptions.port ?? workspaceAppPort ?? 3000,\n\t\toptions.portExplicit ?? false,\n\t\tenableOpenApi,\n\t\ttelescope,\n\t\tstudio,\n\t\truntime,\n\t\tappRoot,\n\t\tsecretsJsonPath,\n\t);\n\n\tawait devServer.start();\n\n\t// Watch for file changes\n\tconst envParserFile = config.envParser.split('#')[0] ?? config.envParser;\n\tconst loggerFile = config.logger.split('#')[0] ?? config.logger;\n\n\t// Get hooks file path for watching\n\tconst hooksFileParts = config.hooks?.server?.split('#');\n\tconst hooksFile = hooksFileParts?.[0];\n\n\tconst watchPatterns = [\n\t\tconfig.routes,\n\t\t...(config.functions ? [config.functions] : []),\n\t\t...(config.crons ? [config.crons] : []),\n\t\t...(config.subscribers ? [config.subscribers] : []),\n\t\t// Add .ts extension if not present for config files\n\t\tenvParserFile.endsWith('.ts') ? envParserFile : `${envParserFile}.ts`,\n\t\tloggerFile.endsWith('.ts') ? loggerFile : `${loggerFile}.ts`,\n\t\t// Add hooks file to watch list\n\t\t...(hooksFile\n\t\t\t? [hooksFile.endsWith('.ts') ? hooksFile : `${hooksFile}.ts`]\n\t\t\t: []),\n\t]\n\t\t.flat()\n\t\t.filter((p): p is string => typeof p === 'string');\n\n\t// Normalize patterns - remove leading ./ when using cwd option\n\tconst normalizedPatterns = watchPatterns.map((p) =>\n\t\tp.startsWith('./') ? p.slice(2) : p,\n\t);\n\n\tlogger.log(`👀 Watching for changes in: ${normalizedPatterns.join(', ')}`);\n\n\t// Resolve glob patterns to actual files (chokidar 4.x doesn't support globs)\n\tconst resolvedFiles = await fg(normalizedPatterns, {\n\t\tcwd: appRoot,\n\t\tabsolute: false,\n\t\tonlyFiles: true,\n\t});\n\n\t// Also watch the directories for new files\n\tconst dirsToWatch = [\n\t\t...new Set(\n\t\t\tresolvedFiles.map((f) => {\n\t\t\t\tconst parts = f.split('/');\n\t\t\t\treturn parts.slice(0, -1).join('/');\n\t\t\t}),\n\t\t),\n\t];\n\n\tlogger.log(\n\t\t`📁 Found ${resolvedFiles.length} files in ${dirsToWatch.length} directories`,\n\t);\n\n\tconst watcher = chokidar.watch([...resolvedFiles, ...dirsToWatch], {\n\t\tignored: /(^|[/\\\\])\\../, // ignore dotfiles\n\t\tpersistent: true,\n\t\tignoreInitial: true,\n\t\tcwd: appRoot,\n\t});\n\n\twatcher.on('ready', () => {\n\t\tlogger.log('🔍 File watcher ready');\n\t});\n\n\twatcher.on('error', (error) => {\n\t\tlogger.error('❌ Watcher error:', error);\n\t});\n\n\tlet rebuildTimeout: NodeJS.Timeout | null = null;\n\n\twatcher.on('change', async (path) => {\n\t\tlogger.log(`📝 File changed: ${path}`);\n\n\t\t// Debounce rebuilds\n\t\tif (rebuildTimeout) {\n\t\t\tclearTimeout(rebuildTimeout);\n\t\t}\n\n\t\trebuildTimeout = setTimeout(async () => {\n\t\t\ttry {\n\t\t\t\tlogger.log('🔄 Rebuilding...');\n\t\t\t\tawait buildServer(\n\t\t\t\t\tconfig,\n\t\t\t\t\tbuildContext,\n\t\t\t\t\tresolved.providers[0] as LegacyProvider,\n\t\t\t\t\tenableOpenApi,\n\t\t\t\t\tappRoot,\n\t\t\t\t\ttrue, // bust module cache on rebuild\n\t\t\t\t);\n\n\t\t\t\t// Regenerate OpenAPI if enabled\n\t\t\t\tif (enableOpenApi) {\n\t\t\t\t\tawait generateOpenApi(config, { silent: true, bustCache: true });\n\t\t\t\t}\n\n\t\t\t\tlogger.log('✅ Rebuild complete, restarting server...');\n\t\t\t\tawait devServer.restart();\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error as Error;\n\t\t\t\tlogger.error(`❌ Rebuild failed: ${err.message}`);\n\t\t\t\tif (err.stack) {\n\t\t\t\t\tlogger.error(err.stack);\n\t\t\t\t}\n\t\t\t}\n\t\t}, 300);\n\t});\n\n\t// Handle graceful shutdown\n\tlet isShuttingDown = false;\n\tconst shutdown = () => {\n\t\tif (isShuttingDown) return;\n\t\tisShuttingDown = true;\n\n\t\tlogger.log('\\n🛑 Shutting down...');\n\n\t\t// Use sync-style shutdown to ensure it completes before exit\n\t\tPromise.all([watcher.close(), devServer.stop()])\n\t\t\t.catch((err) => {\n\t\t\t\tlogger.error('Error during shutdown:', err);\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tprocess.exit(0);\n\t\t\t});\n\t};\n\n\tprocess.on('SIGINT', shutdown);\n\tprocess.on('SIGTERM', shutdown);\n}\n\n/**\n * Generate all dependency environment variables for all apps.\n * Returns a flat object with all {APP_NAME}_URL variables.\n * @internal Exported for testing\n */\nexport function generateAllDependencyEnvVars(\n\tworkspace: NormalizedWorkspace,\n\turlPrefix = 'http://localhost',\n): Record<string, string> {\n\tconst env: Record<string, string> = {};\n\n\tfor (const appName of Object.keys(workspace.apps)) {\n\t\tconst appEnv = getDependencyEnvVars(workspace, appName, urlPrefix);\n\t\tObject.assign(env, appEnv);\n\t}\n\n\treturn env;\n}\n\n/**\n * Check for port conflicts across all apps.\n * Returns list of conflicts if any ports are duplicated.\n * @internal Exported for testing\n */\nexport function checkPortConflicts(\n\tworkspace: NormalizedWorkspace,\n): { app1: string; app2: string; port: number }[] {\n\tconst conflicts: { app1: string; app2: string; port: number }[] = [];\n\tconst portToApp = new Map<number, string>();\n\n\tfor (const [appName, app] of Object.entries(workspace.apps)) {\n\t\tconst existingApp = portToApp.get(app.port);\n\t\tif (existingApp) {\n\t\t\tconflicts.push({ app1: existingApp, app2: appName, port: app.port });\n\t\t} else {\n\t\t\tportToApp.set(app.port, appName);\n\t\t}\n\t}\n\n\treturn conflicts;\n}\n\n/**\n * Per-framework validation spec for supported frontend frameworks.\n */\ninterface FrontendFrameworkSpec {\n\tdisplayName: string;\n\tconfigFiles: string[];\n\tdependency: string;\n\tinstallCommand: string;\n}\n\nconst FRONTEND_FRAMEWORKS: Record<FrontendFramework, FrontendFrameworkSpec> = {\n\tnextjs: {\n\t\tdisplayName: 'Next.js',\n\t\tconfigFiles: [\n\t\t\t'next.config.js',\n\t\t\t'next.config.ts',\n\t\t\t'next.config.mjs',\n\t\t\t'next.config.cjs',\n\t\t],\n\t\tdependency: 'next',\n\t\tinstallCommand: 'pnpm add next react react-dom',\n\t},\n\tremix: {\n\t\tdisplayName: 'Remix',\n\t\tconfigFiles: [\n\t\t\t'remix.config.js',\n\t\t\t'remix.config.mjs',\n\t\t\t'remix.config.ts',\n\t\t\t'vite.config.js',\n\t\t\t'vite.config.ts',\n\t\t\t'vite.config.mjs',\n\t\t\t'vite.config.cjs',\n\t\t],\n\t\tdependency: '@remix-run/dev',\n\t\tinstallCommand: 'pnpm add -D @remix-run/dev',\n\t},\n\tvite: {\n\t\tdisplayName: 'Vite',\n\t\tconfigFiles: [\n\t\t\t'vite.config.js',\n\t\t\t'vite.config.ts',\n\t\t\t'vite.config.mjs',\n\t\t\t'vite.config.cjs',\n\t\t],\n\t\tdependency: 'vite',\n\t\tinstallCommand: 'pnpm add -D vite',\n\t},\n\t'tanstack-start': {\n\t\tdisplayName: 'TanStack Start',\n\t\tconfigFiles: [\n\t\t\t'vite.config.js',\n\t\t\t'vite.config.ts',\n\t\t\t'vite.config.mjs',\n\t\t\t'vite.config.cjs',\n\t\t],\n\t\tdependency: '@tanstack/react-start',\n\t\tinstallCommand: 'pnpm add @tanstack/react-start @tanstack/react-router',\n\t},\n\texpo: {\n\t\tdisplayName: 'Expo',\n\t\tconfigFiles: ['app.config.ts', 'app.config.js', 'app.json'],\n\t\tdependency: 'expo',\n\t\tinstallCommand: 'pnpm add expo',\n\t},\n};\n\n/**\n * Auto-detect the frontend framework by scanning package.json deps and config\n * files. Dependency match wins over config file match because deps are more\n * distinctive (a Remix app has both vite.config.ts AND @remix-run/dev).\n */\nfunction detectFrontendFramework(\n\tfullPath: string,\n\tdeps: Record<string, string>,\n): FrontendFramework | undefined {\n\t// tanstack-start before vite/remix so its more specific dep wins; vite last\n\t// because plain vite is the fallback when neither nextjs/remix/tanstack match.\n\t// Order matters: more specific deps win over general ones.\n\t// expo first (own dep), then tanstack-start (uses vite), remix (uses vite),\n\t// nextjs (own dep), and plain vite last as the fallback.\n\tconst order: FrontendFramework[] = [\n\t\t'expo',\n\t\t'tanstack-start',\n\t\t'nextjs',\n\t\t'remix',\n\t\t'vite',\n\t];\n\tfor (const name of order) {\n\t\tif (deps[FRONTEND_FRAMEWORKS[name].dependency]) {\n\t\t\treturn name;\n\t\t}\n\t}\n\tfor (const name of order) {\n\t\tconst hasConfig = FRONTEND_FRAMEWORKS[name].configFiles.some((file) =>\n\t\t\texistsSync(join(fullPath, file)),\n\t\t);\n\t\tif (hasConfig) {\n\t\t\treturn name;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Validation result for a frontend app.\n */\nexport interface FrontendValidationResult {\n\tappName: string;\n\tvalid: boolean;\n\terrors: string[];\n\twarnings: string[];\n}\n\n/**\n * Validate a frontend app configuration.\n *\n * If `framework` is provided, validates strictly against that framework's\n * expected config file and dependency. Otherwise auto-detects the framework\n * from package.json and config files. If no recognized framework is found, the\n * app is allowed through with a warning provided it has a `dev` script.\n *\n * @internal Exported for testing\n */\nexport async function validateFrontendApp(\n\tappName: string,\n\tappPath: string,\n\tworkspaceRoot: string,\n\tframework?: FrontendFramework,\n): Promise<FrontendValidationResult> {\n\tconst errors: string[] = [];\n\tconst warnings: string[] = [];\n\tconst fullPath = join(workspaceRoot, appPath);\n\n\tconst packageJsonPath = join(fullPath, 'package.json');\n\tif (!existsSync(packageJsonPath)) {\n\t\terrors.push(\n\t\t\t`package.json not found at ${appPath}. Run: pnpm init in the app directory.`,\n\t\t);\n\t\treturn { appName, valid: false, errors, warnings };\n\t}\n\n\tlet pkg: {\n\t\tdependencies?: Record<string, string>;\n\t\tdevDependencies?: Record<string, string>;\n\t\tscripts?: Record<string, string>;\n\t};\n\ttry {\n\t\t// eslint-disable-next-line @typescript-eslint/no-require-imports\n\t\tpkg = require(packageJsonPath);\n\t} catch {\n\t\terrors.push(`Failed to read package.json at ${packageJsonPath}`);\n\t\treturn { appName, valid: false, errors, warnings };\n\t}\n\n\tconst deps: Record<string, string> = {\n\t\t...pkg.dependencies,\n\t\t...pkg.devDependencies,\n\t};\n\n\tconst resolvedFramework =\n\t\tframework ?? detectFrontendFramework(fullPath, deps);\n\n\tif (resolvedFramework) {\n\t\tconst spec = FRONTEND_FRAMEWORKS[resolvedFramework];\n\t\tconst hasConfig = spec.configFiles.some((file) =>\n\t\t\texistsSync(join(fullPath, file)),\n\t\t);\n\t\tif (!hasConfig) {\n\t\t\terrors.push(\n\t\t\t\t`${spec.displayName} config file not found. Expected one of: ${spec.configFiles.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (!deps[spec.dependency]) {\n\t\t\terrors.push(\n\t\t\t\t`${spec.displayName} not found in dependencies. Run: ${spec.installCommand}`,\n\t\t\t);\n\t\t}\n\t} else {\n\t\twarnings.push(\n\t\t\t'No recognized frontend framework detected (Next.js, Vite, Remix). ' +\n\t\t\t\t'The app will run via its \"dev\" script. Set `framework` in your app config to enable strict validation.',\n\t\t);\n\t}\n\n\tif (!pkg.scripts?.dev) {\n\t\tif (resolvedFramework) {\n\t\t\twarnings.push(\n\t\t\t\t'No \"dev\" script found in package.json. Turbo expects a \"dev\" script to run.',\n\t\t\t);\n\t\t} else {\n\t\t\terrors.push(\n\t\t\t\t'No \"dev\" script found in package.json. Without a recognized framework or a dev script, there is nothing to run.',\n\t\t\t);\n\t\t}\n\t}\n\n\treturn {\n\t\tappName,\n\t\tvalid: errors.length === 0,\n\t\terrors,\n\t\twarnings,\n\t};\n}\n\n/**\n * Validate all frontend apps in the workspace.\n * Returns validation results for each frontend app.\n * @internal Exported for testing\n */\nexport async function validateFrontendApps(\n\tworkspace: NormalizedWorkspace,\n): Promise<FrontendValidationResult[]> {\n\tconst results: FrontendValidationResult[] = [];\n\n\tfor (const [appName, app] of Object.entries(workspace.apps)) {\n\t\tif (app.type === 'web') {\n\t\t\tconst result = await validateFrontendApp(\n\t\t\t\tappName,\n\t\t\t\tapp.path,\n\t\t\t\tworkspace.root,\n\t\t\t\tapp.framework as FrontendFramework | undefined,\n\t\t\t);\n\t\t\tresults.push(result);\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Load secrets for development stage.\n * Returns env vars to inject, or empty object if secrets not configured/found.\n * @internal Exported for testing\n */\nexport async function loadDevSecrets(\n\tworkspace: NormalizedWorkspace,\n): Promise<Record<string, string>> {\n\t// Check if secrets are enabled in workspace config\n\tif (!workspace.secrets.enabled) {\n\t\treturn {};\n\t}\n\n\t// Try 'dev' stage first, then 'development'\n\tconst stages = ['dev', 'development'];\n\n\tfor (const stage of stages) {\n\t\tif (secretsExist(stage, workspace.root)) {\n\t\t\tconst secrets = await readStageSecrets(stage, workspace.root);\n\t\t\tif (secrets) {\n\t\t\t\tlogger.log(`🔐 Loading secrets from stage: ${stage}`);\n\t\t\t\treturn toEmbeddableSecrets(secrets);\n\t\t\t}\n\t\t}\n\t}\n\n\tlogger.warn(\n\t\t'⚠️ Secrets enabled but no dev/development secrets found. Run \"gkm setup\" to initialize your development environment',\n\t);\n\treturn {};\n}\n\n/**\n * Workspace dev command - orchestrates multi-app development using Turbo.\n *\n * Flow:\n * 1. Check for port conflicts\n * 2. Start docker-compose services (db, cache, mail)\n * 3. Generate dependency URLs ({APP_NAME}_URL)\n * 4. Spawn turbo run dev with injected env vars\n */\nasync function workspaceDevCommand(\n\tworkspace: NormalizedWorkspace,\n\toptions: DevOptions,\n): Promise<void> {\n\tconst appCount = Object.keys(workspace.apps).length;\n\tconst backendApps = Object.entries(workspace.apps).filter(\n\t\t([_, app]) => app.type === 'backend',\n\t);\n\tconst frontendApps = Object.entries(workspace.apps).filter(\n\t\t([_, app]) => app.type === 'web',\n\t);\n\tconst mobileApps = Object.entries(workspace.apps).filter(\n\t\t([_, app]) => app.type === 'mobile',\n\t);\n\n\tlogger.log(`\\n🚀 Starting workspace: ${workspace.name}`);\n\tconst counts = [\n\t\t`${backendApps.length} backend`,\n\t\t`${frontendApps.length} web`,\n\t];\n\tif (mobileApps.length > 0) counts.push(`${mobileApps.length} mobile`);\n\tlogger.log(` ${counts.join(', ')} app(s)`);\n\n\t// Check for port conflicts\n\tconst conflicts = checkPortConflicts(workspace);\n\tif (conflicts.length > 0) {\n\t\tfor (const conflict of conflicts) {\n\t\t\tlogger.error(\n\t\t\t\t`❌ Port conflict: Apps \"${conflict.app1}\" and \"${conflict.app2}\" both use port ${conflict.port}`,\n\t\t\t);\n\t\t}\n\t\tthrow new Error(\n\t\t\t'Port conflicts detected. Please assign unique ports to each app.',\n\t\t);\n\t}\n\n\t// Validate frontend apps (Next.js setup)\n\tif (frontendApps.length > 0) {\n\t\tlogger.log('\\n🔍 Validating frontend apps...');\n\t\tconst validationResults = await validateFrontendApps(workspace);\n\n\t\tlet hasErrors = false;\n\t\tfor (const result of validationResults) {\n\t\t\tif (!result.valid) {\n\t\t\t\thasErrors = true;\n\t\t\t\tlogger.error(\n\t\t\t\t\t`\\n❌ Frontend app \"${result.appName}\" validation failed:`,\n\t\t\t\t);\n\t\t\t\tfor (const error of result.errors) {\n\t\t\t\t\tlogger.error(` • ${error}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const warning of result.warnings) {\n\t\t\t\tlogger.warn(` ⚠️ ${result.appName}: ${warning}`);\n\t\t\t}\n\t\t}\n\n\t\tif (hasErrors) {\n\t\t\tthrow new Error(\n\t\t\t\t'Frontend app validation failed. Fix the issues above and try again.',\n\t\t\t);\n\t\t}\n\t\tlogger.log('✅ Frontend apps validated');\n\t}\n\n\t// Frontend apps import API clients directly from backend packages\n\t// (e.g. import { createApi } from '@myapp/api/client')\n\t// No file copying needed — pnpm workspace resolution handles it.\n\n\t// Resolve dynamic service ports from docker-compose.yml\n\tconst resolvedPorts = await resolveServicePorts(workspace.root);\n\n\t// Load secrets BEFORE starting Docker so POSTGRES_USER, POSTGRES_PASSWORD,\n\t// etc. are available for docker-compose variable interpolation\n\tconst rawSecrets = await loadDevSecrets(workspace);\n\n\t// Start docker-compose services with resolved ports AND secrets\n\tawait startWorkspaceServices(workspace, resolvedPorts.dockerEnv, rawSecrets);\n\n\t// Rewrite URLs with resolved ports (hostnames and port numbers)\n\tconst secretsEnv = rewriteUrlsWithPorts(rawSecrets, resolvedPorts);\n\tif (Object.keys(secretsEnv).length > 0) {\n\t\tlogger.log(` Loaded ${Object.keys(secretsEnv).length} secret(s)`);\n\t}\n\n\t// Generate dependency URLs\n\tconst dependencyEnv = generateAllDependencyEnvVars(workspace);\n\tif (Object.keys(dependencyEnv).length > 0) {\n\t\tlogger.log('📡 Dependency URLs:');\n\t\tfor (const [key, value] of Object.entries(dependencyEnv)) {\n\t\t\tlogger.log(` ${key}=${value}`);\n\t\t}\n\t}\n\n\t// Build turbo filter\n\tlet turboFilter: string[] = [];\n\tif (options.app) {\n\t\t// Run specific app\n\t\tif (!workspace.apps[options.app]) {\n\t\t\tconst appNames = Object.keys(workspace.apps).join(', ');\n\t\t\tthrow new Error(\n\t\t\t\t`App \"${options.app}\" not found. Available apps: ${appNames}`,\n\t\t\t);\n\t\t}\n\t\tturboFilter = ['--filter', options.app];\n\t\tlogger.log(`\\n🎯 Running single app: ${options.app}`);\n\t} else if (options.filter) {\n\t\t// Use custom filter\n\t\tturboFilter = ['--filter', options.filter];\n\t\tlogger.log(`\\n🔍 Using filter: ${options.filter}`);\n\t} else {\n\t\t// Run all apps\n\t\tlogger.log(`\\n🎯 Running all ${appCount} apps`);\n\t}\n\n\t// List apps and their ports\n\tconst buildOrder = getAppBuildOrder(workspace);\n\tlogger.log('\\n📋 Apps (in dependency order):');\n\tfor (const appName of buildOrder) {\n\t\tconst app = workspace.apps[appName];\n\t\tif (!app) continue;\n\t\tconst deps =\n\t\t\tapp.dependencies.length > 0\n\t\t\t\t? ` (depends on: ${app.dependencies.join(', ')})`\n\t\t\t\t: '';\n\t\tconst icon =\n\t\t\tapp.type === 'backend' ? '🔧' : app.type === 'mobile' ? '📱' : '🌐';\n\t\tlogger.log(` ${icon} ${appName} → http://localhost:${app.port}${deps}`);\n\t}\n\n\t// Find the config file path for GKM_CONFIG_PATH\n\tconst configFiles = ['gkm.config.ts', 'gkm.config.js', 'gkm.config.json'];\n\tlet configPath = '';\n\tfor (const file of configFiles) {\n\t\tconst fullPath = join(workspace.root, file);\n\t\tif (existsSync(fullPath)) {\n\t\t\tconfigPath = fullPath;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Prepare environment variables\n\t// Order matters: secrets first, then dependencies (dependencies can override)\n\tconst turboEnv: Record<string, string> = {\n\t\t...process.env,\n\t\t...secretsEnv,\n\t\t...dependencyEnv,\n\t\tNODE_ENV: 'development',\n\t\t// Inject config path so child processes can find the workspace config\n\t\t...(configPath ? { GKM_CONFIG_PATH: configPath } : {}),\n\t};\n\n\t// Spawn turbo run dev\n\tlogger.log('\\n🏃 Starting turbo run dev...\\n');\n\n\tconst turboProcess = spawn('pnpm', ['turbo', 'run', 'dev', ...turboFilter], {\n\t\tcwd: workspace.root,\n\t\tstdio: 'inherit',\n\t\tenv: turboEnv,\n\t\tdetached: true,\n\t});\n\n\t// No file watcher needed — frontend apps import API clients directly\n\t// from backend packages via workspace dependencies.\n\n\t// Handle graceful shutdown\n\tlet isShuttingDown = false;\n\tconst shutdown = () => {\n\t\tif (isShuttingDown) return;\n\t\tisShuttingDown = true;\n\n\t\tlogger.log('\\n🛑 Shutting down workspace...');\n\n\t\t// Kill turbo process group\n\t\tconst pid = turboProcess.pid;\n\t\tif (pid) {\n\t\t\ttry {\n\t\t\t\tprocess.kill(-pid, 'SIGTERM');\n\t\t\t} catch {\n\t\t\t\ttry {\n\t\t\t\t\tprocess.kill(pid, 'SIGTERM');\n\t\t\t\t} catch {\n\t\t\t\t\t// Process already dead\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Force kill after timeout if processes are still alive\n\t\tsetTimeout(() => {\n\t\t\tif (pid) {\n\t\t\t\ttry {\n\t\t\t\t\tprocess.kill(-pid, 'SIGKILL');\n\t\t\t\t} catch {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tprocess.kill(pid, 'SIGKILL');\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Process already dead\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tprocess.exit(0);\n\t\t}, 3000);\n\t};\n\n\tprocess.on('SIGINT', shutdown);\n\tprocess.on('SIGTERM', shutdown);\n\n\t// Wait for turbo to exit\n\treturn new Promise((resolve, reject) => {\n\t\tturboProcess.on('error', (error) => {\n\t\t\tlogger.error('❌ Turbo error:', error);\n\t\t\treject(error);\n\t\t});\n\n\t\tturboProcess.on('exit', (code) => {\n\t\t\tif (code !== null && code !== 0) {\n\t\t\t\treject(new Error(`Turbo exited with code ${code}`));\n\t\t\t} else {\n\t\t\t\tresolve();\n\t\t\t}\n\t\t});\n\t});\n}\n\nasync function buildServer(\n\tconfig: any,\n\tcontext: BuildContext,\n\tprovider: LegacyProvider,\n\tenableOpenApi: boolean,\n\tappRoot: string = process.cwd(),\n\tbustCache = false,\n): Promise<void> {\n\t// Initialize generators\n\tconst endpointGenerator = new EndpointGenerator();\n\tconst functionGenerator = new FunctionGenerator();\n\tconst cronGenerator = new CronGenerator();\n\tconst subscriberGenerator = new SubscriberGenerator();\n\tconst queueGenerator = new QueueGenerator();\n\tconst topicGenerator = new TopicGenerator();\n\n\t// Load all constructs (resolve paths relative to appRoot)\n\tconst [\n\t\tallEndpoints,\n\t\tallFunctions,\n\t\tallCrons,\n\t\tallSubscribers,\n\t\tallQueues,\n\t\tallTopics,\n\t] = await Promise.all([\n\t\tendpointGenerator.load(config.routes, appRoot, bustCache),\n\t\tconfig.functions\n\t\t\t? functionGenerator.load(config.functions, appRoot, bustCache)\n\t\t\t: [],\n\t\tconfig.crons ? cronGenerator.load(config.crons, appRoot, bustCache) : [],\n\t\tconfig.subscribers\n\t\t\t? subscriberGenerator.load(config.subscribers, appRoot, bustCache)\n\t\t\t: [],\n\t\tconfig.queues ? queueGenerator.load(config.queues, appRoot, bustCache) : [],\n\t\tconfig.topics ? topicGenerator.load(config.topics, appRoot, bustCache) : [],\n\t]);\n\n\t// Ensure .gkm directory exists in app root\n\tconst outputDir = join(appRoot, '.gkm', provider);\n\tawait mkdir(outputDir, { recursive: true });\n\n\t// Build for server provider\n\tawait Promise.all([\n\t\tendpointGenerator.build(context, allEndpoints, outputDir, {\n\t\t\tprovider,\n\t\t\tenableOpenApi,\n\t\t}),\n\t\tfunctionGenerator.build(context, allFunctions, outputDir, { provider }),\n\t\tcronGenerator.build(context, allCrons, outputDir, { provider }),\n\t\tsubscriberGenerator.build(context, allSubscribers, outputDir, { provider }),\n\t\tqueueGenerator.build(context, allQueues, outputDir, { provider }),\n\t\ttopicGenerator.build(context, allTopics, outputDir, { provider }),\n\t]);\n}\n\n/**\n * Run any TypeScript file with secret injection.\n * Does not require gkm.config.ts.\n */\nasync function entryDevCommand(options: DevOptions): Promise<void> {\n\tconst { entry, watch = true } = options;\n\n\tif (!entry) {\n\t\tthrow new Error('--entry requires a file path');\n\t}\n\n\tconst entryPath = resolve(process.cwd(), entry);\n\n\tif (!existsSync(entryPath)) {\n\t\tthrow new Error(`Entry file not found: ${entryPath}`);\n\t}\n\n\t// Load .env files\n\tconst defaultEnv = loadEnvFiles('.env');\n\tif (defaultEnv.loaded.length > 0) {\n\t\tlogger.log(`📦 Loaded env: ${defaultEnv.loaded.join(', ')}`);\n\t}\n\n\t// Prepare credentials (loads workspace config, secrets, injects PORT)\n\t// Only pass explicitPort if --port was actually specified by the user\n\tconst { credentials, resolvedPort, secretsJsonPath, appName } =\n\t\tawait prepareEntryCredentials({\n\t\t\texplicitPort: options.portExplicit ? options.port : undefined,\n\t\t});\n\n\tif (appName) {\n\t\tlogger.log(`📦 App: ${appName} (port ${resolvedPort})`);\n\t}\n\n\tlogger.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);\n\n\tif (Object.keys(credentials).length > 1) {\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`,\n\t\t);\n\t}\n\n\t// Create wrapper entry that injects secrets before importing user's file\n\tconst wrapperDir = join(process.cwd(), '.gkm');\n\tawait mkdir(wrapperDir, { recursive: true });\n\tconst wrapperPath = join(wrapperDir, 'entry-wrapper.ts');\n\tawait createEntryWrapper(wrapperPath, entryPath, secretsJsonPath);\n\n\t// Start with tsx\n\tconst runner = new EntryRunner(wrapperPath, entryPath, watch, resolvedPort);\n\tawait runner.start();\n\n\t// Handle graceful shutdown\n\tlet isShuttingDown = false;\n\tconst shutdown = () => {\n\t\tif (isShuttingDown) return;\n\t\tisShuttingDown = true;\n\n\t\tlogger.log('\\n🛑 Shutting down...');\n\t\trunner.stop();\n\t\tprocess.exit(0);\n\t};\n\n\tprocess.on('SIGINT', shutdown);\n\tprocess.on('SIGTERM', shutdown);\n\n\t// Keep the process alive\n\tawait new Promise(() => {});\n}\n\n/**\n * Runs and watches a TypeScript entry file using tsx.\n */\nclass EntryRunner {\n\tprivate childProcess: ChildProcess | null = null;\n\tprivate watcher: ReturnType<typeof chokidar.watch> | null = null;\n\tprivate isRunning = false;\n\n\tconstructor(\n\t\tprivate wrapperPath: string,\n\t\tprivate entryPath: string,\n\t\tprivate watch: boolean,\n\t\tprivate port: number,\n\t) {}\n\n\tasync start(): Promise<void> {\n\t\tawait this.runProcess();\n\n\t\tif (this.watch) {\n\t\t\t// Watch the entry file's directory for changes\n\t\t\tconst watchDir = dirname(this.entryPath);\n\n\t\t\tthis.watcher = chokidar.watch(watchDir, {\n\t\t\t\tignored: /(^|[/\\\\])\\../,\n\t\t\t\tpersistent: true,\n\t\t\t\tignoreInitial: true,\n\t\t\t});\n\n\t\t\tlet restartTimeout: NodeJS.Timeout | null = null;\n\n\t\t\tthis.watcher.on('change', (path) => {\n\t\t\t\tlogger.log(`📝 File changed: ${path}`);\n\n\t\t\t\t// Debounce restarts\n\t\t\t\tif (restartTimeout) {\n\t\t\t\t\tclearTimeout(restartTimeout);\n\t\t\t\t}\n\n\t\t\t\trestartTimeout = setTimeout(async () => {\n\t\t\t\t\tlogger.log('🔄 Restarting...');\n\t\t\t\t\tawait this.restart();\n\t\t\t\t}, 300);\n\t\t\t});\n\n\t\t\tlogger.log(`👀 Watching for changes in: ${watchDir}`);\n\t\t}\n\t}\n\n\tprivate async runProcess(): Promise<void> {\n\t\t// Pass PORT as environment variable\n\t\tconst env = { ...process.env, PORT: String(this.port) };\n\n\t\tthis.childProcess = spawn('npx', ['tsx', this.wrapperPath], {\n\t\t\tstdio: 'inherit',\n\t\t\tenv,\n\t\t\tdetached: true,\n\t\t});\n\n\t\tthis.isRunning = true;\n\n\t\tthis.childProcess.on('error', (error) => {\n\t\t\tlogger.error('❌ Process error:', error);\n\t\t});\n\n\t\tthis.childProcess.on('exit', (code) => {\n\t\t\tif (code !== null && code !== 0 && code !== 143) {\n\t\t\t\t// 143 = SIGTERM\n\t\t\t\tlogger.error(`❌ Process exited with code ${code}`);\n\t\t\t}\n\t\t\tthis.isRunning = false;\n\t\t});\n\n\t\t// Give the process a moment to start\n\t\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\n\t\tif (this.isRunning) {\n\t\t\tlogger.log('');\n\t\t\tlogger.log(\n\t\t\t\t` \\x1b[32m✓ Ready\\x1b[0m at \\x1b[36mhttp://localhost:${this.port}\\x1b[0m`,\n\t\t\t);\n\t\t\tlogger.log('');\n\t\t}\n\t}\n\n\tasync restart(): Promise<void> {\n\t\tthis.stopProcess();\n\t\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\t\tawait this.runProcess();\n\t}\n\n\tstop(): void {\n\t\tthis.watcher?.close();\n\t\tthis.stopProcess();\n\t}\n\n\tprivate stopProcess(): void {\n\t\tif (this.childProcess && this.isRunning) {\n\t\t\tconst pid = this.childProcess.pid;\n\t\t\tif (pid) {\n\t\t\t\ttry {\n\t\t\t\t\tprocess.kill(-pid, 'SIGTERM');\n\t\t\t\t} catch {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tprocess.kill(pid, 'SIGTERM');\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Process already dead\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.childProcess = null;\n\t\t\tthis.isRunning = false;\n\t\t}\n\t}\n}\n\n/**\n * Generate the content of the dev server entry file (server.ts).\n * Uses dynamic import for createApp so Credentials are populated\n * before any app modules evaluate.\n * @internal Exported for testing\n */\nexport function generateServerEntryContent(options: {\n\tsecretsJsonPath?: string;\n\truntime?: Runtime;\n\tenableOpenApi?: boolean;\n\tappImportPath?: string;\n}): string {\n\tconst {\n\t\tsecretsJsonPath,\n\t\truntime = 'node',\n\t\tenableOpenApi = false,\n\t\tappImportPath = './app.js',\n\t} = options;\n\n\tconst credentialsInjection = secretsJsonPath\n\t\t? `import { existsSync, readFileSync } from 'node:fs';\n\n// Inject dev secrets via globalThis (must happen before app import)\nconst secretsPath = '${secretsJsonPath}';\nif (existsSync(secretsPath)) {\n const __secrets = JSON.parse(readFileSync(secretsPath, 'utf-8'));\n globalThis.__gkm_credentials__ = __secrets;\n Object.assign(process.env, __secrets);\n}\n\n`\n\t\t: '';\n\n\tconst serveCode =\n\t\truntime === 'bun'\n\t\t\t? `Bun.serve({\n port,\n fetch: app.fetch,\n });`\n\t\t\t: `const { serve } = await import('@hono/node-server');\n const server = serve({\n fetch: app.fetch,\n port,\n });\n // Inject WebSocket support if available\n const injectWs = (app as any).__injectWebSocket;\n if (injectWs) {\n injectWs(server);\n console.log('🔌 Telescope real-time updates enabled');\n }`;\n\n\treturn `#!/usr/bin/env node\n/**\n * Development server entry point\n * This file is auto-generated by 'gkm dev'\n */\n${credentialsInjection}\nconst port = process.argv.includes('--port')\n ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])\n : 3000;\n\n// Dynamic import so Credentials are populated before env.ts evaluates\nconst { createApp } = await import('${appImportPath}');\n\n// createApp is async to support optional WebSocket setup\nconst { app, start } = await createApp(undefined, ${enableOpenApi});\n\n// Start the server\nstart({\n port,\n serve: async (app, port) => {\n ${serveCode}\n },\n}).catch((error) => {\n console.error('Failed to start server:', error);\n process.exit(1);\n});\n`;\n}\n\nclass DevServer {\n\tprivate serverProcess: ChildProcess | null = null;\n\tprivate isRunning = false;\n\tprivate actualPort: number;\n\tprivate startTime = Date.now();\n\n\tconstructor(\n\t\tprivate provider: LegacyProvider,\n\t\tprivate requestedPort: number,\n\t\tprivate portExplicit: boolean,\n\t\tprivate enableOpenApi: boolean,\n\t\tprivate telescope: NormalizedTelescopeConfig | undefined,\n\t\tprivate studio: NormalizedStudioConfig | undefined,\n\t\tprivate runtime: Runtime = 'node',\n\t\tprivate appRoot: string = process.cwd(),\n\t\tprivate secretsJsonPath?: string,\n\t) {\n\t\tthis.actualPort = requestedPort;\n\t}\n\n\tasync start(): Promise<void> {\n\t\tthis.startTime = Date.now();\n\t\tif (this.isRunning) {\n\t\t\tawait this.stop();\n\t\t}\n\n\t\t// Check port availability\n\t\tif (this.portExplicit) {\n\t\t\t// Port was explicitly specified - throw if unavailable\n\t\t\tconst available = await isPortAvailable(this.requestedPort);\n\t\t\tif (!available) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Port ${this.requestedPort} is already in use. ` +\n\t\t\t\t\t\t`Either stop the process using that port or omit -p/--port to auto-select an available port.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.actualPort = this.requestedPort;\n\t\t} else {\n\t\t\t// Find an available port starting from the default\n\t\t\tthis.actualPort = await findAvailablePort(this.requestedPort);\n\n\t\t\tif (this.actualPort !== this.requestedPort) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst serverEntryPath = join(\n\t\t\tthis.appRoot,\n\t\t\t'.gkm',\n\t\t\tthis.provider,\n\t\t\t'server.ts',\n\t\t);\n\n\t\t// Create server entry file\n\t\tawait this.createServerEntry();\n\n\t\tlogger.log(`\\n⏳ Starting server...`);\n\n\t\t// Start the server using tsx (TypeScript execution)\n\t\t// Use detached: true so we can kill the entire process tree\n\t\tthis.serverProcess = spawn(\n\t\t\t'npx',\n\t\t\t['tsx', serverEntryPath, '--port', this.actualPort.toString()],\n\t\t\t{\n\t\t\t\tstdio: 'inherit',\n\t\t\t\tenv: { ...process.env, NODE_ENV: 'development' },\n\t\t\t\tdetached: true,\n\t\t\t},\n\t\t);\n\n\t\tthis.isRunning = true;\n\n\t\tthis.serverProcess.on('error', (error) => {\n\t\t\tlogger.error('❌ Server error:', error);\n\t\t});\n\n\t\tthis.serverProcess.on('exit', (code, signal) => {\n\t\t\tif (code !== null && code !== 0 && signal !== 'SIGTERM') {\n\t\t\t\tlogger.error(`❌ Server exited with code ${code}`);\n\t\t\t}\n\t\t\tthis.isRunning = false;\n\t\t});\n\n\t\t// Give the server a moment to start\n\t\tawait new Promise((resolve) => setTimeout(resolve, 1000));\n\n\t\tif (this.isRunning) {\n\t\t\tconst base = `http://localhost:${this.actualPort}`;\n\t\t\tconst lines: string[] = [` Local: ${base}`];\n\t\t\tif (this.enableOpenApi) {\n\t\t\t\tlines.push(` API Docs: ${base}/__docs`);\n\t\t\t}\n\t\t\tif (this.telescope) {\n\t\t\t\tlines.push(` Telescope: ${base}${this.telescope.path}`);\n\t\t\t}\n\t\t\tif (this.studio) {\n\t\t\t\tlines.push(` Studio: ${base}${this.studio.path}`);\n\t\t\t}\n\n\t\t\tconst maxLen = Math.max(...lines.map((l) => l.length));\n\t\t\tconst pad = (s: string) => s.padEnd(maxLen);\n\t\t\tconst border = '─'.repeat(maxLen + 2);\n\n\t\t\tlogger.log('');\n\t\t\tlogger.log(\n\t\t\t\t` \\x1b[32m✓ Ready\\x1b[0m in ${((Date.now() - this.startTime) / 1000).toFixed(1)}s`,\n\t\t\t);\n\t\t\tlogger.log('');\n\t\t\tlogger.log(` ┌${border}┐`);\n\t\t\tfor (const line of lines) {\n\t\t\t\tlogger.log(` │ ${pad(line)} │`);\n\t\t\t}\n\t\t\tlogger.log(` └${border}┘`);\n\t\t\tlogger.log('');\n\t\t}\n\t}\n\n\tasync stop(): Promise<void> {\n\t\tconst port = this.actualPort;\n\n\t\tif (this.serverProcess && this.isRunning) {\n\t\t\tconst pid = this.serverProcess.pid;\n\n\t\t\t// Use SIGKILL directly since the server ignores SIGTERM\n\t\t\tif (pid) {\n\t\t\t\ttry {\n\t\t\t\t\tprocess.kill(-pid, 'SIGKILL');\n\t\t\t\t} catch {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tprocess.kill(pid, 'SIGKILL');\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Process might already be dead\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.serverProcess = null;\n\t\t\tthis.isRunning = false;\n\t\t}\n\n\t\t// Also kill any processes still holding the port\n\t\tthis.killProcessesOnPort(port);\n\t}\n\n\tprivate killProcessesOnPort(port: number): void {\n\t\ttry {\n\t\t\t// Use lsof to find PIDs on the port and kill them with -9\n\t\t\texecSync(`lsof -ti tcp:${port} | xargs kill -9 2>/dev/null || true`, {\n\t\t\t\tstdio: 'ignore',\n\t\t\t});\n\t\t} catch {\n\t\t\t// Ignore errors - port may already be free\n\t\t}\n\t}\n\n\tasync restart(): Promise<void> {\n\t\tconst portToReuse = this.actualPort;\n\t\tawait this.stop();\n\n\t\t// Wait for port to be released (up to 3 seconds)\n\t\tlet attempts = 0;\n\t\twhile (attempts < 30) {\n\t\t\tif (await isPortAvailable(portToReuse)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 100));\n\t\t\tattempts++;\n\t\t}\n\n\t\t// Force reuse the same port\n\t\tthis.requestedPort = portToReuse;\n\t\tawait this.start();\n\t}\n\n\tprivate async createServerEntry(): Promise<void> {\n\t\tconst { writeFile: fsWriteFile } = await import('node:fs/promises');\n\n\t\tconst serverPath = join(this.appRoot, '.gkm', this.provider, 'server.ts');\n\n\t\tconst content = generateServerEntryContent({\n\t\t\tsecretsJsonPath: this.secretsJsonPath,\n\t\t\truntime: this.runtime,\n\t\t\tenableOpenApi: this.enableOpenApi,\n\t\t});\n\n\t\tawait fsWriteFile(serverPath, content);\n\t}\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport type {\n\tCronInfo,\n\tFunctionInfo,\n\tQueueInfo,\n\tRouteInfo,\n\tSubscriberInfo,\n\tTopicInfo,\n} from '../types';\n\nconst logger = console;\n\nexport type ManifestProvider = 'aws' | 'server';\n\nexport interface ServerAppInfo {\n\thandler: string;\n\tendpoints: string;\n}\n\n/**\n * A manifest field is either a flat array (no partition) or\n * an object keyed by partition name (partitioned).\n */\nexport type ManifestField<T> = T[] | Record<string, T[]>;\n\nfunction isPartitioned<T>(\n\tfield: ManifestField<T>,\n): field is Record<string, T[]> {\n\treturn !Array.isArray(field);\n}\n\n/**\n * Serialize a manifest field to a TypeScript string.\n * Flat arrays serialize as JSON arrays, partitioned fields as objects of arrays.\n */\nfunction serializeField<T>(field: ManifestField<T>, indent = 2): string {\n\tif (Array.isArray(field)) {\n\t\treturn JSON.stringify(field, null, indent);\n\t}\n\t// Partitioned: { admin: [...], default: [...] }\n\tconst entries = Object.entries(field)\n\t\t.map(\n\t\t\t([key, value]) =>\n\t\t\t\t` ${JSON.stringify(key)}: ${JSON.stringify(value, null, indent)}`,\n\t\t)\n\t\t.join(',\\n');\n\treturn `{\\n${entries},\\n }`;\n}\n\n/**\n * Count total items across a manifest field (flat or partitioned).\n */\nfunction countItems<T>(field: ManifestField<T>): number {\n\tif (Array.isArray(field)) return field.length;\n\treturn Object.values(field).reduce((sum, arr) => sum + arr.length, 0);\n}\n\n/**\n * Generate derived types for a construct field.\n * @param fieldName - The field name in the manifest (e.g., 'routes')\n * @param typeName - The exported type name (e.g., 'Route')\n * @param partitioned - Whether this field is partitioned\n */\nfunction generateDerivedType(\n\tfieldName: string,\n\ttypeName: string,\n\tpartitioned: boolean,\n): string {\n\tif (partitioned) {\n\t\tconst partitionTypeName = `${typeName}Partition`;\n\t\treturn [\n\t\t\t`export type ${partitionTypeName} = keyof typeof manifest.${fieldName};`,\n\t\t\t`export type ${typeName}<P extends ${partitionTypeName} = ${partitionTypeName}> = (typeof manifest.${fieldName})[P][number];`,\n\t\t].join('\\n');\n\t}\n\treturn `export type ${typeName} = (typeof manifest.${fieldName})[number];`;\n}\n\nexport async function generateAwsManifest(\n\toutputDir: string,\n\troutes: ManifestField<RouteInfo>,\n\tfunctions: ManifestField<FunctionInfo>,\n\tcrons: ManifestField<CronInfo>,\n\tsubscribers: ManifestField<SubscriberInfo>,\n\tqueues: ManifestField<QueueInfo> = [],\n\ttopics: ManifestField<TopicInfo> = [],\n): Promise<void> {\n\tconst manifestDir = join(outputDir, 'manifest');\n\tawait mkdir(manifestDir, { recursive: true });\n\n\t// Filter out 'ALL' method routes (server-specific)\n\tconst awsRoutes = filterAllRoutes(routes);\n\n\tconst routesPartitioned = isPartitioned(awsRoutes);\n\tconst functionsPartitioned = isPartitioned(functions);\n\tconst cronsPartitioned = isPartitioned(crons);\n\tconst subscribersPartitioned = isPartitioned(subscribers);\n\tconst queuesPartitioned = isPartitioned(queues);\n\tconst topicsPartitioned = isPartitioned(topics);\n\n\tconst content = `export const manifest = {\n routes: ${serializeField(awsRoutes)},\n functions: ${serializeField(functions)},\n crons: ${serializeField(crons)},\n subscribers: ${serializeField(subscribers)},\n queues: ${serializeField(queues)},\n topics: ${serializeField(topics)},\n} as const;\n\n// Derived types\n${generateDerivedType('routes', 'Route', routesPartitioned)}\n${generateDerivedType('functions', 'Function', functionsPartitioned)}\n${generateDerivedType('crons', 'Cron', cronsPartitioned)}\n${generateDerivedType('subscribers', 'Subscriber', subscribersPartitioned)}\n${generateDerivedType('queues', 'Queue', queuesPartitioned)}\n${generateDerivedType('topics', 'Topic', topicsPartitioned)}\n\n// Useful union types\nexport type Authorizer = Route['authorizer'];\nexport type HttpMethod = Route['method'];\nexport type RoutePath = Route['path'];\n`;\n\n\tconst manifestPath = join(manifestDir, 'aws.ts');\n\tawait writeFile(manifestPath, content);\n\n\tlogger.log(\n\t\t`Generated AWS manifest with ${countItems(awsRoutes)} routes, ${countItems(functions)} functions, ${countItems(crons)} crons, ${countItems(subscribers)} subscribers, ${countItems(queues)} queues, ${countItems(topics)} topics`,\n\t);\n\tlogger.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);\n}\n\nexport async function generateServerManifest(\n\toutputDir: string,\n\tappInfo: ServerAppInfo,\n\troutes: ManifestField<RouteInfo>,\n\tsubscribers: ManifestField<SubscriberInfo>,\n\tqueues: ManifestField<QueueInfo> = [],\n\ttopics: ManifestField<TopicInfo> = [],\n): Promise<void> {\n\tconst manifestDir = join(outputDir, 'manifest');\n\tawait mkdir(manifestDir, { recursive: true });\n\n\t// For server, extract route metadata (path, method, authorizer)\n\tconst serverRoutes = mapRouteMetadata(filterAllRoutes(routes));\n\n\t// Server subscribers only need name and events\n\tconst serverSubscribers = mapSubscriberMetadata(subscribers);\n\n\t// Server queues only need their name (the worker is wired by setupQueues)\n\tconst serverQueues = mapQueueMetadata(queues);\n\n\t// Server topics only need name + events (pg-boss routes by event name locally)\n\tconst serverTopics = mapTopicMetadata(topics);\n\n\tconst routesPartitioned = isPartitioned(serverRoutes);\n\tconst subscribersPartitioned = isPartitioned(serverSubscribers);\n\tconst queuesPartitioned = isPartitioned(serverQueues);\n\tconst topicsPartitioned = isPartitioned(serverTopics);\n\n\tconst content = `export const manifest = {\n app: ${JSON.stringify(appInfo, null, 2)},\n routes: ${serializeField(serverRoutes)},\n subscribers: ${serializeField(serverSubscribers)},\n queues: ${serializeField(serverQueues)},\n topics: ${serializeField(serverTopics)},\n} as const;\n\n// Derived types\n${generateDerivedType('routes', 'Route', routesPartitioned)}\n${generateDerivedType('subscribers', 'Subscriber', subscribersPartitioned)}\n${generateDerivedType('queues', 'Queue', queuesPartitioned)}\n${generateDerivedType('topics', 'Topic', topicsPartitioned)}\n\n// Useful union types\nexport type Authorizer = Route['authorizer'];\nexport type HttpMethod = Route['method'];\nexport type RoutePath = Route['path'];\n`;\n\n\tconst manifestPath = join(manifestDir, 'server.ts');\n\tawait writeFile(manifestPath, content);\n\n\tlogger.log(\n\t\t`Generated server manifest with ${countItems(serverRoutes)} routes, ${countItems(serverSubscribers)} subscribers, ${countItems(serverQueues)} queues, ${countItems(serverTopics)} topics`,\n\t);\n\tlogger.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);\n}\n\n/**\n * Filter out 'ALL' method routes from a manifest field (flat or partitioned).\n */\nfunction filterAllRoutes(\n\troutes: ManifestField<RouteInfo>,\n): ManifestField<RouteInfo> {\n\tif (Array.isArray(routes)) {\n\t\treturn routes.filter((r) => r.method !== 'ALL');\n\t}\n\tconst result: Record<string, RouteInfo[]> = {};\n\tfor (const [partition, partitionRoutes] of Object.entries(routes)) {\n\t\tresult[partition] = partitionRoutes.filter((r) => r.method !== 'ALL');\n\t}\n\treturn result;\n}\n\n/**\n * Map routes to server metadata (path, method, authorizer only).\n */\nfunction mapRouteMetadata(\n\troutes: ManifestField<RouteInfo>,\n): ManifestField<{ path: string; method: string; authorizer: string }> {\n\tconst mapFn = (r: RouteInfo) => ({\n\t\tpath: r.path,\n\t\tmethod: r.method,\n\t\tauthorizer: r.authorizer,\n\t});\n\n\tif (Array.isArray(routes)) {\n\t\treturn routes.map(mapFn);\n\t}\n\tconst result: Record<\n\t\tstring,\n\t\t{ path: string; method: string; authorizer: string }[]\n\t> = {};\n\tfor (const [partition, partitionRoutes] of Object.entries(routes)) {\n\t\tresult[partition] = partitionRoutes.map(mapFn);\n\t}\n\treturn result;\n}\n\n/**\n * Map subscribers to server metadata (name, subscribedEvents only).\n */\nfunction mapSubscriberMetadata(\n\tsubscribers: ManifestField<SubscriberInfo>,\n): ManifestField<{ name: string; subscribedEvents: readonly string[] }> {\n\tconst mapFn = (s: SubscriberInfo) => ({\n\t\tname: s.name,\n\t\tsubscribedEvents: s.subscribedEvents,\n\t});\n\n\tif (Array.isArray(subscribers)) {\n\t\treturn subscribers.map(mapFn);\n\t}\n\tconst result: Record<\n\t\tstring,\n\t\t{ name: string; subscribedEvents: readonly string[] }[]\n\t> = {};\n\tfor (const [partition, partitionSubs] of Object.entries(subscribers)) {\n\t\tresult[partition] = partitionSubs.map(mapFn);\n\t}\n\treturn result;\n}\n\nfunction mapQueueMetadata(\n\tqueues: ManifestField<QueueInfo>,\n): ManifestField<{ name: string }> {\n\tconst mapFn = (q: QueueInfo) => ({ name: q.name });\n\n\tif (Array.isArray(queues)) {\n\t\treturn queues.map(mapFn);\n\t}\n\tconst result: Record<string, { name: string }[]> = {};\n\tfor (const [partition, partitionQueues] of Object.entries(queues)) {\n\t\tresult[partition] = partitionQueues.map(mapFn);\n\t}\n\treturn result;\n}\n\nfunction mapTopicMetadata(\n\ttopics: ManifestField<TopicInfo>,\n): ManifestField<{ name: string; events: readonly string[] }> {\n\tconst mapFn = (t: TopicInfo) => ({ name: t.name, events: t.events });\n\n\tif (Array.isArray(topics)) {\n\t\treturn topics.map(mapFn);\n\t}\n\tconst result: Record<string, { name: string; events: readonly string[] }[]> =\n\t\t{};\n\tfor (const [partition, partitionTopics] of Object.entries(topics)) {\n\t\tresult[partition] = partitionTopics.map(mapFn);\n\t}\n\treturn result;\n}\n","import type { Construct } from '@geekmidas/constructs';\nimport type { GeneratedConstruct } from '../generators/Generator';\n\nexport const DEFAULT_PARTITION = 'default';\n\n/**\n * Group constructs by their partition field.\n * Constructs without a partition are placed in the 'default' group.\n */\nexport function groupByPartition<T extends Construct>(\n\tconstructs: GeneratedConstruct<T>[],\n): Map<string, GeneratedConstruct<T>[]> {\n\tconst groups = new Map<string, GeneratedConstruct<T>[]>();\n\n\tfor (const construct of constructs) {\n\t\tconst partition = construct.partition ?? DEFAULT_PARTITION;\n\t\tconst group = groups.get(partition);\n\t\tif (group) {\n\t\t\tgroup.push(construct);\n\t\t} else {\n\t\t\tgroups.set(partition, [construct]);\n\t\t}\n\t}\n\n\treturn groups;\n}\n\n/**\n * Check if any construct across the given arrays has a non-undefined partition.\n * When true, the manifest should use the partitioned shape for that construct type.\n */\nexport function hasPartitions<T extends Construct>(\n\tconstructs: GeneratedConstruct<T>[],\n): boolean {\n\treturn constructs.some((c) => c.partition !== undefined);\n}\n\n/**\n * Group an info array by partition, using the partition values from\n * the corresponding construct array. Both arrays must be the same length\n * and in the same order.\n */\nexport function groupInfosByPartition<T>(\n\tinfos: T[],\n\tconstructs: GeneratedConstruct<any>[],\n): Record<string, T[]> {\n\tconst groups: Record<string, T[]> = {};\n\n\tfor (let i = 0; i < infos.length; i++) {\n\t\tconst partition = constructs[i]?.partition ?? DEFAULT_PARTITION;\n\t\tif (!groups[partition]) {\n\t\t\tgroups[partition] = [];\n\t\t}\n\t\tgroups[partition].push(infos[i]!);\n\t}\n\n\treturn groups;\n}\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir } from 'node:fs/promises';\nimport { join, relative, resolve } from 'node:path';\nimport type { Cron } from '@geekmidas/constructs/crons';\nimport type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport type { Function } from '@geekmidas/constructs/functions';\nimport type { Queue } from '@geekmidas/constructs/queue';\nimport type { Subscriber } from '@geekmidas/constructs/subscribers';\nimport type { Topic } from '@geekmidas/constructs/topic';\nimport {\n\tloadAppConfig,\n\tloadConfig,\n\tloadWorkspaceConfig,\n\tparseModuleConfig,\n} from '../config';\nimport {\n\tgetProductionConfigFromGkm,\n\tnormalizeHooksConfig,\n\tnormalizeProductionConfig,\n\tnormalizeStudioConfig,\n\tnormalizeTelescopeConfig,\n} from '../dev';\nimport {\n\tCronGenerator,\n\tEndpointGenerator,\n\tFunctionGenerator,\n\ttype GeneratedConstruct,\n\tQueueGenerator,\n\tSubscriberGenerator,\n\tTopicGenerator,\n} from '../generators';\nimport { generateOpenApi, openapiCommand } from '../openapi.js';\nimport {\n\ttype BuildOptions,\n\ttype BuildResult,\n\tisPartitionedRoutes,\n\ttype LegacyProvider,\n\ttype RouteInfo,\n\ttype Routes,\n} from '../types';\nimport {\n\tgetAppBuildOrder,\n\ttype NormalizedAppConfig,\n\ttype NormalizedWorkspace,\n} from '../workspace/index.js';\nimport {\n\tgenerateAwsManifest,\n\tgenerateServerManifest,\n\ttype ManifestField,\n\ttype ServerAppInfo,\n} from './manifests';\nimport { groupInfosByPartition, hasPartitions } from './partitions';\nimport { resolveProviders } from './providerResolver';\nimport type { BuildContext } from './types';\n\nconst logger = console;\n\nexport async function buildCommand(\n\toptions: BuildOptions,\n): Promise<BuildResult> {\n\t// Load config with workspace detection\n\tconst loadedConfig = await loadWorkspaceConfig();\n\n\t// Route to workspace build mode for multi-app workspaces\n\t// BUT only if we're at the workspace root (prevents recursive builds when\n\t// Turbo runs gkm build in each app subdirectory)\n\tif (loadedConfig.type === 'workspace') {\n\t\tconst cwd = resolve(process.cwd());\n\t\tconst workspaceRoot = resolve(loadedConfig.workspace.root);\n\t\tconst isAtWorkspaceRoot = cwd === workspaceRoot;\n\n\t\tif (isAtWorkspaceRoot) {\n\t\t\tlogger.log('📦 Detected workspace configuration');\n\t\t\treturn workspaceBuildCommand(loadedConfig.workspace, options);\n\t\t}\n\t\t// When running from inside an app directory, use app-specific config\n\t}\n\n\t// Single-app build - use app config if in workspace, otherwise legacy config\n\tconst config =\n\t\tloadedConfig.type === 'workspace'\n\t\t\t? (await loadAppConfig()).gkmConfig\n\t\t\t: await loadConfig();\n\n\t// Resolve providers from new config format\n\tconst resolved = resolveProviders(config, options);\n\n\t// Normalize production configuration\n\tconst productionConfigFromGkm = getProductionConfigFromGkm(config);\n\tconst production = normalizeProductionConfig(\n\t\toptions.production ?? false,\n\t\tproductionConfigFromGkm,\n\t);\n\n\tif (production) {\n\t\tlogger.log(`🏭 Building for PRODUCTION`);\n\t}\n\n\tlogger.log(`Building with providers: ${resolved.providers.join(', ')}`);\n\tlogger.log(`Loading routes from: ${formatRoutes(config.routes)}`);\n\tif (config.functions) {\n\t\tlogger.log(`Loading functions from: ${formatRoutes(config.functions)}`);\n\t}\n\tif (config.crons) {\n\t\tlogger.log(`Loading crons from: ${formatRoutes(config.crons)}`);\n\t}\n\tif (config.subscribers) {\n\t\tlogger.log(`Loading subscribers from: ${formatRoutes(config.subscribers)}`);\n\t}\n\tlogger.log(`Using envParser: ${config.envParser}`);\n\n\t// Parse envParser and logger configuration\n\tconst { path: envParserPath, importPattern: envParserImportPattern } =\n\t\tparseModuleConfig(config.envParser, 'envParser');\n\tconst { path: loggerPath, importPattern: loggerImportPattern } =\n\t\tparseModuleConfig(config.logger, 'logger');\n\n\t// Normalize telescope configuration (disabled in production)\n\tconst telescope = production\n\t\t? undefined\n\t\t: normalizeTelescopeConfig(config.telescope);\n\tif (telescope) {\n\t\tlogger.log(`🔭 Telescope enabled at ${telescope.path}`);\n\t}\n\n\t// Normalize studio configuration (disabled in production)\n\tconst studio = production ? undefined : normalizeStudioConfig(config.studio);\n\tif (studio) {\n\t\tlogger.log(`🗄️ Studio enabled at ${studio.path}`);\n\t}\n\n\t// Normalize hooks configuration\n\tconst hooks = normalizeHooksConfig(config.hooks);\n\tif (hooks) {\n\t\tlogger.log(`🪝 Server hooks enabled`);\n\t}\n\n\t// Extract docker compose services for env var auto-population\n\tconst services = config.docker?.compose?.services;\n\tconst dockerServices = services\n\t\t? Array.isArray(services)\n\t\t\t? {\n\t\t\t\t\tpostgres: services.includes('postgres'),\n\t\t\t\t\tredis: services.includes('redis'),\n\t\t\t\t\trabbitmq: services.includes('rabbitmq'),\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tpostgres: Boolean(services.postgres),\n\t\t\t\t\tredis: Boolean(services.redis),\n\t\t\t\t\trabbitmq: Boolean(services.rabbitmq),\n\t\t\t\t}\n\t\t: undefined;\n\n\tconst buildContext: BuildContext = {\n\t\tenvParserPath,\n\t\tenvParserImportPattern,\n\t\tloggerPath,\n\t\tloggerImportPattern,\n\t\ttelescope,\n\t\tstudio,\n\t\thooks,\n\t\tproduction,\n\t\tdockerServices,\n\t\tmarkOptional: options.markOptional ?? false,\n\t};\n\n\t// Initialize generators\n\tconst endpointGenerator = new EndpointGenerator();\n\tconst functionGenerator = new FunctionGenerator();\n\tconst cronGenerator = new CronGenerator();\n\tconst subscriberGenerator = new SubscriberGenerator();\n\tconst queueGenerator = new QueueGenerator();\n\tconst topicGenerator = new TopicGenerator();\n\n\t// Load all constructs in parallel\n\tconst [\n\t\tallEndpoints,\n\t\tallFunctions,\n\t\tallCrons,\n\t\tallSubscribers,\n\t\tallQueues,\n\t\tallTopics,\n\t] = await Promise.all([\n\t\tendpointGenerator.load(config.routes),\n\t\tconfig.functions ? functionGenerator.load(config.functions) : [],\n\t\tconfig.crons ? cronGenerator.load(config.crons) : [],\n\t\tconfig.subscribers ? subscriberGenerator.load(config.subscribers) : [],\n\t\tconfig.queues ? queueGenerator.load(config.queues) : [],\n\t\tconfig.topics ? topicGenerator.load(config.topics) : [],\n\t]);\n\n\tlogger.log(`Found ${allEndpoints.length} endpoints`);\n\tlogger.log(`Found ${allFunctions.length} functions`);\n\tlogger.log(`Found ${allCrons.length} crons`);\n\tlogger.log(`Found ${allSubscribers.length} subscribers`);\n\tlogger.log(`Found ${allQueues.length} queues`);\n\tlogger.log(`Found ${allTopics.length} topics`);\n\n\tif (\n\t\tallEndpoints.length === 0 &&\n\t\tallFunctions.length === 0 &&\n\t\tallCrons.length === 0 &&\n\t\tallSubscribers.length === 0 &&\n\t\tallQueues.length === 0 &&\n\t\tallTopics.length === 0\n\t) {\n\t\tlogger.log(\n\t\t\t'No endpoints, functions, crons, subscribers, queues, or topics found to process',\n\t\t);\n\t\treturn {};\n\t}\n\n\t// Ensure .gkm directory exists\n\tconst rootOutputDir = join(process.cwd(), '.gkm');\n\tawait mkdir(rootOutputDir, { recursive: true });\n\n\t// Build for each provider and generate per-provider manifests\n\tlet result: BuildResult = {};\n\tfor (const provider of resolved.providers) {\n\t\tconst providerResult = await buildForProvider(\n\t\t\tprovider,\n\t\t\tbuildContext,\n\t\t\trootOutputDir,\n\t\t\tendpointGenerator,\n\t\t\tfunctionGenerator,\n\t\t\tcronGenerator,\n\t\t\tsubscriberGenerator,\n\t\t\tqueueGenerator,\n\t\t\ttopicGenerator,\n\t\t\tallEndpoints,\n\t\t\tallFunctions,\n\t\t\tallCrons,\n\t\t\tallSubscribers,\n\t\t\tallQueues,\n\t\t\tallTopics,\n\t\t\tresolved.enableOpenApi,\n\t\t\toptions.skipBundle ?? false,\n\t\t\toptions.stage,\n\t\t);\n\t\t// Keep the master key from the server provider\n\t\tif (providerResult.masterKey) {\n\t\t\tresult = providerResult;\n\t\t}\n\t}\n\n\t// Generate OpenAPI spec as part of the build\n\tawait generateOpenApi(config, { bustCache: true });\n\n\treturn result;\n}\n\nasync function buildForProvider(\n\tprovider: LegacyProvider,\n\tcontext: BuildContext,\n\trootOutputDir: string,\n\tendpointGenerator: EndpointGenerator,\n\tfunctionGenerator: FunctionGenerator,\n\tcronGenerator: CronGenerator,\n\tsubscriberGenerator: SubscriberGenerator,\n\tqueueGenerator: QueueGenerator,\n\ttopicGenerator: TopicGenerator,\n\tendpoints: GeneratedConstruct<Endpoint<any, any, any, any, any, any>>[],\n\tfunctions: GeneratedConstruct<Function<any, any, any, any>>[],\n\tcrons: GeneratedConstruct<Cron<any, any, any, any>>[],\n\tsubscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n\tqueues: GeneratedConstruct<Queue<any, any, any, any>>[],\n\ttopics: GeneratedConstruct<Topic<any, any>>[],\n\tenableOpenApi: boolean,\n\tskipBundle: boolean,\n\tstage?: string,\n): Promise<BuildResult> {\n\tconst outputDir = join(process.cwd(), '.gkm', provider);\n\n\t// Ensure output directory exists\n\tawait mkdir(outputDir, { recursive: true });\n\n\tlogger.log(`\\nGenerating handlers for provider: ${provider}`);\n\n\t// Build all constructs in parallel.\n\t// context.markOptional is forwarded to each generator so that\n\t// getEnvironment({ markOptional }) produces `VARNAME?` for optional vars.\n\tconst [\n\t\troutes,\n\t\tfunctionInfos,\n\t\tcronInfos,\n\t\tsubscriberInfos,\n\t\tqueueInfos,\n\t\ttopicInfos,\n\t] = await Promise.all([\n\t\tendpointGenerator.build(context, endpoints, outputDir, {\n\t\t\tprovider,\n\t\t\tenableOpenApi,\n\t\t}),\n\t\tfunctionGenerator.build(context, functions, outputDir, { provider }),\n\t\tcronGenerator.build(context, crons, outputDir, { provider }),\n\t\tsubscriberGenerator.build(context, subscribers, outputDir, { provider }),\n\t\tqueueGenerator.build(context, queues, outputDir, { provider }),\n\t\ttopicGenerator.build(context, topics, outputDir, { provider }),\n\t]);\n\n\tlogger.log(\n\t\t`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers, ${queueInfos.length} queues, ${topicInfos.length} topics for ${provider}`,\n\t);\n\n\t// Assemble manifest fields (flat or partitioned per construct type)\n\tconst manifestRoutes = assembleManifestField(routes, endpoints);\n\tconst manifestFunctions = assembleManifestField(functionInfos, functions);\n\tconst manifestCrons = assembleManifestField(cronInfos, crons);\n\tconst manifestQueues = assembleManifestField(queueInfos, queues);\n\tconst manifestTopics = assembleManifestField(topicInfos, topics);\n\tconst manifestSubscribers = assembleManifestField(\n\t\tsubscriberInfos,\n\t\tsubscribers,\n\t);\n\n\t// Generate provider-specific manifest\n\tif (provider === 'server') {\n\t\t// For server, collect actual route metadata from endpoint constructs\n\t\tconst routeMetadata: RouteInfo[] = await Promise.all(\n\t\t\tendpoints.map(async ({ construct }) => ({\n\t\t\t\tpath: construct._path,\n\t\t\t\tmethod: construct.method,\n\t\t\t\thandler: '', // Not needed for server manifest\n\t\t\t\tauthorizer: construct.authorizer?.name ?? 'none',\n\t\t\t})),\n\t\t);\n\n\t\tconst serverRouteField = assembleManifestField(routeMetadata, endpoints);\n\n\t\tconst appInfo: ServerAppInfo = {\n\t\t\thandler: relative(process.cwd(), join(outputDir, 'app.ts')),\n\t\t\tendpoints: relative(process.cwd(), join(outputDir, 'endpoints.ts')),\n\t\t};\n\n\t\tawait generateServerManifest(\n\t\t\trootOutputDir,\n\t\t\tappInfo,\n\t\t\tserverRouteField,\n\t\t\tmanifestSubscribers,\n\t\t\tmanifestQueues,\n\t\t\tmanifestTopics,\n\t\t);\n\n\t\t// Bundle for production if enabled\n\t\tlet masterKey: string | undefined;\n\t\tif (context.production?.bundle && !skipBundle) {\n\t\t\tlogger.log(`\\n📦 Bundling production server...`);\n\t\t\tconst { bundleServer } = await import('./bundler');\n\n\t\t\t// Collect all constructs for environment variable validation\n\t\t\tconst allConstructs = [\n\t\t\t\t...endpoints.map((e) => e.construct),\n\t\t\t\t...functions.map((f) => f.construct),\n\t\t\t\t...crons.map((c) => c.construct),\n\t\t\t\t...subscribers.map((s) => s.construct),\n\t\t\t\t...queues.map((q) => q.construct),\n\t\t\t];\n\n\t\t\t// Get docker compose services for auto-populating env vars\n\t\t\tconst dockerServices = context.dockerServices;\n\n\t\t\tconst bundleResult = await bundleServer({\n\t\t\t\tentryPoint: join(outputDir, 'server.ts'),\n\t\t\t\toutputDir: join(outputDir, 'dist'),\n\t\t\t\tminify: context.production.minify,\n\t\t\t\tsourcemap: false,\n\t\t\t\texternal: context.production.external,\n\t\t\t\tstage,\n\t\t\t\tconstructs: allConstructs,\n\t\t\t\tdockerServices,\n\t\t\t});\n\t\t\tmasterKey = bundleResult.masterKey;\n\t\t\tlogger.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);\n\n\t\t\t// Display master key if secrets were injected\n\t\t\tif (masterKey) {\n\t\t\t\tlogger.log(`\\n🔐 Secrets encrypted for deployment`);\n\t\t\t\tlogger.log(` Deploy with: GKM_MASTER_KEY=${masterKey}`);\n\t\t\t}\n\t\t}\n\n\t\treturn { masterKey };\n\t} else {\n\t\t// For AWS providers, generate AWS manifest\n\t\tawait generateAwsManifest(\n\t\t\trootOutputDir,\n\t\t\tmanifestRoutes,\n\t\t\tmanifestFunctions,\n\t\t\tmanifestCrons,\n\t\t\tmanifestSubscribers,\n\t\t\tmanifestQueues,\n\t\t\tmanifestTopics,\n\t\t);\n\t}\n\n\treturn {};\n}\n\n/**\n * Result of building a single app in a workspace.\n */\nexport interface AppBuildResult {\n\tappName: string;\n\ttype: 'backend' | 'web' | 'mobile';\n\tsuccess: boolean;\n\toutputPath?: string;\n\terror?: string;\n}\n\n/**\n * Result of workspace build command.\n */\nexport interface WorkspaceBuildResult extends BuildResult {\n\tapps: AppBuildResult[];\n}\n\n/**\n * Detect available package manager.\n * @internal Exported for testing\n */\nexport function detectPackageManager(): 'pnpm' | 'npm' | 'yarn' {\n\tif (existsSync('pnpm-lock.yaml')) return 'pnpm';\n\tif (existsSync('yarn.lock')) return 'yarn';\n\treturn 'npm';\n}\n\n/**\n * Get the turbo command for running builds.\n * @internal Exported for testing\n */\nexport function getTurboCommand(\n\tpm: 'pnpm' | 'npm' | 'yarn',\n\tfilter?: string,\n): string {\n\tconst filterArg = filter ? ` --filter=${filter}` : '';\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn `pnpm exec turbo run build${filterArg}`;\n\t\tcase 'yarn':\n\t\t\treturn `yarn turbo run build${filterArg}`;\n\t\tcase 'npm':\n\t\t\treturn `npx turbo run build${filterArg}`;\n\t}\n}\n\n/**\n * Build all apps in a workspace using Turbo for dependency-ordered parallel builds.\n * @internal Exported for testing\n */\nexport async function workspaceBuildCommand(\n\tworkspace: NormalizedWorkspace,\n\toptions: BuildOptions,\n): Promise<WorkspaceBuildResult> {\n\tconst results: AppBuildResult[] = [];\n\tconst apps = Object.entries(workspace.apps);\n\tconst backendApps = apps.filter(([, app]) => app.type === 'backend');\n\tconst frontendApps = apps.filter(([, app]) => app.type === 'web');\n\n\tlogger.log(`\\n🏗️ Building workspace: ${workspace.name}`);\n\tlogger.log(\n\t\t` Backend apps: ${backendApps.map(([name]) => name).join(', ') || 'none'}`,\n\t);\n\tlogger.log(\n\t\t` Frontend apps: ${frontendApps.map(([name]) => name).join(', ') || 'none'}`,\n\t);\n\n\tif (options.production) {\n\t\tlogger.log(` 🏭 Production mode enabled`);\n\t}\n\n\t// Get build order (topologically sorted by dependencies)\n\tconst buildOrder = getAppBuildOrder(workspace);\n\tlogger.log(` Build order: ${buildOrder.join(' → ')}`);\n\n\t// Use Turbo for parallel builds with dependency awareness\n\tconst pm = detectPackageManager();\n\tlogger.log(`\\n📦 Using ${pm} with Turbo for parallel builds...\\n`);\n\n\ttry {\n\t\t// Run turbo build which handles dependency ordering and parallelization\n\t\tconst turboCommand = getTurboCommand(pm);\n\t\tlogger.log(`Running: ${turboCommand}`);\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst child = spawn(turboCommand, {\n\t\t\t\tshell: true,\n\t\t\t\tcwd: workspace.root,\n\t\t\t\tstdio: 'inherit',\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t\t// Pass production flag to builds\n\t\t\t\t\tNODE_ENV: options.production ? 'production' : 'development',\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tchild.on('close', (code) => {\n\t\t\t\tif (code === 0) {\n\t\t\t\t\tresolve();\n\t\t\t\t} else {\n\t\t\t\t\treject(new Error(`Turbo build failed with exit code ${code}`));\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tchild.on('error', (err) => {\n\t\t\t\treject(err);\n\t\t\t});\n\t\t});\n\n\t\t// Mark all apps as successful\n\t\tfor (const [appName, app] of apps) {\n\t\t\tconst outputPath = getAppOutputPath(workspace, appName, app);\n\t\t\tresults.push({\n\t\t\t\tappName,\n\t\t\t\ttype: app.type,\n\t\t\t\tsuccess: true,\n\t\t\t\toutputPath,\n\t\t\t});\n\t\t}\n\n\t\tlogger.log(`\\n✅ Workspace build complete!`);\n\n\t\t// Generate OpenAPI specs and copy to frontend apps\n\t\tawait openapiCommand({ cwd: workspace.root });\n\n\t\t// Summary\n\t\tlogger.log(`\\n📋 Build Summary:`);\n\t\tfor (const result of results) {\n\t\t\tconst icon =\n\t\t\t\tresult.type === 'backend'\n\t\t\t\t\t? '⚙️'\n\t\t\t\t\t: result.type === 'mobile'\n\t\t\t\t\t\t? '📱'\n\t\t\t\t\t\t: '🌐';\n\t\t\tlogger.log(\n\t\t\t\t` ${icon} ${result.appName}: ${result.outputPath || 'built'}`,\n\t\t\t);\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage =\n\t\t\terror instanceof Error ? error.message : 'Build failed';\n\t\tlogger.log(`\\n❌ Build failed: ${errorMessage}`);\n\n\t\t// Mark all apps as failed\n\t\tfor (const [appName, app] of apps) {\n\t\t\tresults.push({\n\t\t\t\tappName,\n\t\t\t\ttype: app.type,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: errorMessage,\n\t\t\t});\n\t\t}\n\n\t\tthrow error;\n\t}\n\n\treturn { apps: results };\n}\n\n/**\n * Get the output path for a built app.\n */\nfunction getAppOutputPath(\n\tworkspace: NormalizedWorkspace,\n\t_appName: string,\n\tapp: NormalizedAppConfig,\n): string {\n\tconst appPath = join(workspace.root, app.path);\n\n\tif (app.type === 'mobile') {\n\t\t// Mobile builds are produced by the framework's own toolchain\n\t\t// (e.g. EAS Build) — no local output path.\n\t\treturn '';\n\t}\n\tif (app.type === 'web') {\n\t\tswitch (app.framework) {\n\t\t\tcase 'vite':\n\t\t\tcase 'tanstack-start':\n\t\t\t\treturn join(appPath, 'dist');\n\t\t\tcase 'remix':\n\t\t\t\treturn join(appPath, 'build');\n\t\t\tdefault:\n\t\t\t\treturn join(appPath, '.next');\n\t\t}\n\t}\n\t// Backend .gkm output\n\treturn join(appPath, '.gkm');\n}\n\n/**\n * Format routes for logging, handling PartitionedRoutes.\n */\nfunction formatRoutes(routes: Routes): string {\n\tif (isPartitionedRoutes(routes)) {\n\t\tconst paths = Array.isArray(routes.paths)\n\t\t\t? routes.paths.join(', ')\n\t\t\t: routes.paths;\n\t\treturn `${paths} (partitioned)`;\n\t}\n\treturn Array.isArray(routes) ? routes.join(', ') : routes;\n}\n\n/**\n * Assemble a ManifestField from build infos and constructs.\n * If any construct has a partition, returns a Record<string, T[]>.\n * Otherwise, returns a flat T[].\n */\nfunction assembleManifestField<T>(\n\tinfos: T[],\n\tconstructs: GeneratedConstruct<any>[],\n): ManifestField<T> {\n\tif (!hasPartitions(constructs)) {\n\t\treturn infos;\n\t}\n\treturn groupInfosByPartition(infos, constructs);\n}\n","let _debug = false;\n\n/**\n * Enable debug mode globally.\n * When enabled, verbose error details are shown everywhere.\n */\nexport function enableDebug(): void {\n\t_debug = true;\n}\n\n/**\n * Check if debug mode is active.\n * Activated by `--debug` flag or `GKM_DEBUG=1` env var.\n */\nexport function isDebug(): boolean {\n\treturn _debug || process.env.GKM_DEBUG === '1';\n}\n\n/**\n * Log a message only when debug mode is active.\n */\nexport function debug(...args: unknown[]): void {\n\tif (isDebug()) {\n\t\tconsole.debug('[debug]', ...args);\n\t}\n}\n\n/**\n * Format a fatal error for display.\n * Always includes the full stack trace since these are process-ending errors.\n */\nexport function formatError(error: unknown): string {\n\tif (!(error instanceof Error)) {\n\t\treturn String(error);\n\t}\n\n\tlet output = error.stack ?? error.message;\n\n\t// Include cause chain if present\n\tlet cause = error.cause;\n\twhile (cause) {\n\t\tif (cause instanceof Error) {\n\t\t\toutput += `\\n\\nCaused by: ${cause.stack ?? cause.message}`;\n\t\t\tcause = cause.cause;\n\t\t} else {\n\t\t\toutput += `\\n\\nCaused by: ${String(cause)}`;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn output;\n}\n\n/**\n * Format a non-fatal error for display.\n * Shows only the message by default, full stack in debug mode.\n */\nexport function formatWarning(error: unknown): string {\n\tif (!(error instanceof Error)) {\n\t\treturn String(error);\n\t}\n\n\tif (!isDebug()) {\n\t\treturn error.message;\n\t}\n\n\treturn error.stack ?? error.message;\n}\n","/**\n * Deploy state management for Dokploy deployments\n *\n * Stores resource IDs (applications, services) per stage to avoid\n * re-creating resources on subsequent deploys.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/**\n * Per-app database credentials\n */\nexport interface AppDbCredentials {\n\tdbUser: string;\n\tdbPassword: string;\n}\n\n/**\n * DNS verification record for a hostname\n */\nexport interface DnsVerificationRecord {\n\tserverIp: string;\n\tverifiedAt: string;\n}\n\n/**\n * A DNS record that was created during deploy\n */\nexport interface CreatedDnsRecord {\n\t/** The domain this record belongs to (e.g., 'example.com') */\n\tdomain: string;\n\t/** Record name/subdomain (e.g., 'api' or '@' for root) */\n\tname: string;\n\t/** Record type (A, CNAME, etc.) */\n\ttype: string;\n\t/** Record value (IP address, hostname, etc.) */\n\tvalue: string;\n\t/** TTL in seconds */\n\tttl: number;\n\t/** When this record was created */\n\tcreatedAt: string;\n}\n\n/**\n * Backup destination state\n */\nexport interface BackupState {\n\t/** S3 bucket name for backups */\n\tbucketName: string;\n\t/** S3 bucket ARN */\n\tbucketArn: string;\n\t/** IAM user name created for backup access */\n\tiamUserName: string;\n\t/** IAM access key ID */\n\tiamAccessKeyId: string;\n\t/** IAM secret access key */\n\tiamSecretAccessKey: string;\n\t/** Dokploy destination ID */\n\tdestinationId: string;\n\t/** Dokploy backup schedule ID for postgres (if configured) */\n\tpostgresBackupId?: string;\n\t/** AWS region where bucket was created */\n\tregion: string;\n\t/** Timestamp when backup was configured */\n\tcreatedAt: string;\n}\n\n/**\n * State for a single stage deployment\n */\nexport interface DokployStageState {\n\tprovider: 'dokploy';\n\tstage: string;\n\t/** Dokploy project ID - created on first deploy */\n\tprojectId: string;\n\tenvironmentId: string;\n\tapplications: Record<string, string>; // appName -> applicationId\n\tservices: {\n\t\tpostgresId?: string;\n\t\tredisId?: string;\n\t};\n\t/** Per-app database credentials for reuse on subsequent deploys */\n\tappCredentials?: Record<string, AppDbCredentials>;\n\t/** Auto-generated secrets per app (e.g., BETTER_AUTH_SECRET) */\n\tgeneratedSecrets?: Record<string, Record<string, string>>;\n\t/** DNS verification state per hostname */\n\tdnsVerified?: Record<string, DnsVerificationRecord>;\n\t/** DNS records created during deploy (keyed by \"name:type\", e.g., \"api:A\") */\n\tdnsRecords?: Record<string, CreatedDnsRecord>;\n\t/** Backup destination state */\n\tbackups?: BackupState;\n\tlastDeployedAt: string;\n}\n\n/**\n * Get the state file path for a stage\n */\nfunction getStateFilePath(workspaceRoot: string, stage: string): string {\n\treturn join(workspaceRoot, '.gkm', `deploy-${stage}.json`);\n}\n\n/**\n * Read the deploy state for a stage\n * Returns null if state file doesn't exist\n */\nexport async function readStageState(\n\tworkspaceRoot: string,\n\tstage: string,\n): Promise<DokployStageState | null> {\n\tconst filePath = getStateFilePath(workspaceRoot, stage);\n\n\ttry {\n\t\tconst content = await readFile(filePath, 'utf-8');\n\t\treturn JSON.parse(content) as DokployStageState;\n\t} catch (error) {\n\t\t// File doesn't exist or is invalid - return null\n\t\tif ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n\t\t\treturn null;\n\t\t}\n\t\t// Log other errors but don't fail\n\t\tconsole.warn(`Warning: Could not read deploy state: ${error}`);\n\t\treturn null;\n\t}\n}\n\n/**\n * Write the deploy state for a stage\n */\nexport async function writeStageState(\n\tworkspaceRoot: string,\n\tstage: string,\n\tstate: DokployStageState,\n): Promise<void> {\n\tconst filePath = getStateFilePath(workspaceRoot, stage);\n\tconst dir = join(workspaceRoot, '.gkm');\n\n\t// Ensure .gkm directory exists\n\tawait mkdir(dir, { recursive: true });\n\n\t// Update last deployed timestamp\n\tstate.lastDeployedAt = new Date().toISOString();\n\n\tawait writeFile(filePath, JSON.stringify(state, null, 2));\n}\n\n/**\n * Create a new empty state for a stage\n */\nexport function createEmptyState(\n\tstage: string,\n\tprojectId: string,\n\tenvironmentId: string,\n): DokployStageState {\n\treturn {\n\t\tprovider: 'dokploy',\n\t\tstage,\n\t\tprojectId,\n\t\tenvironmentId,\n\t\tapplications: {},\n\t\tservices: {},\n\t\tlastDeployedAt: new Date().toISOString(),\n\t};\n}\n\n/**\n * Get application ID from state\n */\nexport function getApplicationId(\n\tstate: DokployStageState | null,\n\tappName: string,\n): string | undefined {\n\treturn state?.applications[appName];\n}\n\n/**\n * Set application ID in state (mutates state)\n */\nexport function setApplicationId(\n\tstate: DokployStageState,\n\tappName: string,\n\tapplicationId: string,\n): void {\n\tstate.applications[appName] = applicationId;\n}\n\n/**\n * Get postgres ID from state\n */\nexport function getPostgresId(\n\tstate: DokployStageState | null,\n): string | undefined {\n\treturn state?.services.postgresId;\n}\n\n/**\n * Set postgres ID in state (mutates state)\n */\nexport function setPostgresId(\n\tstate: DokployStageState,\n\tpostgresId: string,\n): void {\n\tstate.services.postgresId = postgresId;\n}\n\n/**\n * Get redis ID from state\n */\nexport function getRedisId(\n\tstate: DokployStageState | null,\n): string | undefined {\n\treturn state?.services.redisId;\n}\n\n/**\n * Set redis ID in state (mutates state)\n */\nexport function setRedisId(state: DokployStageState, redisId: string): void {\n\tstate.services.redisId = redisId;\n}\n\n/**\n * Get app credentials from state\n */\nexport function getAppCredentials(\n\tstate: DokployStageState | null,\n\tappName: string,\n): AppDbCredentials | undefined {\n\treturn state?.appCredentials?.[appName];\n}\n\n/**\n * Set app credentials in state (mutates state)\n */\nexport function setAppCredentials(\n\tstate: DokployStageState,\n\tappName: string,\n\tcredentials: AppDbCredentials,\n): void {\n\tif (!state.appCredentials) {\n\t\tstate.appCredentials = {};\n\t}\n\tstate.appCredentials[appName] = credentials;\n}\n\n/**\n * Get all app credentials from state\n */\nexport function getAllAppCredentials(\n\tstate: DokployStageState | null,\n): Record<string, AppDbCredentials> {\n\treturn state?.appCredentials ?? {};\n}\n\n// ============================================================================\n// Generated Secrets\n// ============================================================================\n\n/**\n * Get a generated secret for an app\n */\nexport function getGeneratedSecret(\n\tstate: DokployStageState | null,\n\tappName: string,\n\tsecretName: string,\n): string | undefined {\n\treturn state?.generatedSecrets?.[appName]?.[secretName];\n}\n\n/**\n * Set a generated secret for an app (mutates state)\n */\nexport function setGeneratedSecret(\n\tstate: DokployStageState,\n\tappName: string,\n\tsecretName: string,\n\tvalue: string,\n): void {\n\tif (!state.generatedSecrets) {\n\t\tstate.generatedSecrets = {};\n\t}\n\tif (!state.generatedSecrets[appName]) {\n\t\tstate.generatedSecrets[appName] = {};\n\t}\n\tstate.generatedSecrets[appName][secretName] = value;\n}\n\n/**\n * Get all generated secrets for an app\n */\nexport function getAppGeneratedSecrets(\n\tstate: DokployStageState | null,\n\tappName: string,\n): Record<string, string> {\n\treturn state?.generatedSecrets?.[appName] ?? {};\n}\n\n/**\n * Get all generated secrets from state\n */\nexport function getAllGeneratedSecrets(\n\tstate: DokployStageState | null,\n): Record<string, Record<string, string>> {\n\treturn state?.generatedSecrets ?? {};\n}\n\n// ============================================================================\n// DNS Verification\n// ============================================================================\n\n/**\n * Get DNS verification record for a hostname\n */\nexport function getDnsVerification(\n\tstate: DokployStageState | null,\n\thostname: string,\n): DnsVerificationRecord | undefined {\n\treturn state?.dnsVerified?.[hostname];\n}\n\n/**\n * Set DNS verification record for a hostname (mutates state)\n */\nexport function setDnsVerification(\n\tstate: DokployStageState,\n\thostname: string,\n\tserverIp: string,\n): void {\n\tif (!state.dnsVerified) {\n\t\tstate.dnsVerified = {};\n\t}\n\tstate.dnsVerified[hostname] = {\n\t\tserverIp,\n\t\tverifiedAt: new Date().toISOString(),\n\t};\n}\n\n/**\n * Check if a hostname is already verified with the given IP\n */\nexport function isDnsVerified(\n\tstate: DokployStageState | null,\n\thostname: string,\n\tserverIp: string,\n): boolean {\n\tconst record = state?.dnsVerified?.[hostname];\n\treturn record?.serverIp === serverIp;\n}\n\n/**\n * Get all DNS verification records from state\n */\nexport function getAllDnsVerifications(\n\tstate: DokployStageState | null,\n): Record<string, DnsVerificationRecord> {\n\treturn state?.dnsVerified ?? {};\n}\n\n// ============================================================================\n// DNS Records\n// ============================================================================\n\n/**\n * Get the key for a DNS record in state\n */\nfunction getDnsRecordKey(name: string, type: string): string {\n\treturn `${name}:${type}`;\n}\n\n/**\n * Get a created DNS record from state\n */\nexport function getDnsRecord(\n\tstate: DokployStageState | null,\n\tname: string,\n\ttype: string,\n): CreatedDnsRecord | undefined {\n\treturn state?.dnsRecords?.[getDnsRecordKey(name, type)];\n}\n\n/**\n * Set a created DNS record in state (mutates state)\n */\nexport function setDnsRecord(\n\tstate: DokployStageState,\n\trecord: Omit<CreatedDnsRecord, 'createdAt'>,\n): void {\n\tif (!state.dnsRecords) {\n\t\tstate.dnsRecords = {};\n\t}\n\tconst key = getDnsRecordKey(record.name, record.type);\n\tstate.dnsRecords[key] = {\n\t\t...record,\n\t\tcreatedAt: new Date().toISOString(),\n\t};\n}\n\n/**\n * Remove a DNS record from state (mutates state)\n */\nexport function removeDnsRecord(\n\tstate: DokployStageState,\n\tname: string,\n\ttype: string,\n): void {\n\tif (state.dnsRecords) {\n\t\tdelete state.dnsRecords[getDnsRecordKey(name, type)];\n\t}\n}\n\n/**\n * Get all created DNS records from state\n */\nexport function getAllDnsRecords(\n\tstate: DokployStageState | null,\n): CreatedDnsRecord[] {\n\tif (!state?.dnsRecords) {\n\t\treturn [];\n\t}\n\treturn Object.values(state.dnsRecords);\n}\n\n/**\n * Clear all DNS records from state (mutates state)\n */\nexport function clearDnsRecords(state: DokployStageState): void {\n\tstate.dnsRecords = {};\n\tstate.dnsVerified = {};\n}\n\n// ============================================================================\n// Backup State\n// ============================================================================\n\n/**\n * Get backup state from state\n */\nexport function getBackupState(\n\tstate: DokployStageState | null,\n): BackupState | undefined {\n\treturn state?.backups;\n}\n\n/**\n * Set backup state (mutates state)\n */\nexport function setBackupState(\n\tstate: DokployStageState,\n\tbackupState: BackupState,\n): void {\n\tstate.backups = backupState;\n}\n\n/**\n * Get backup destination ID from state\n */\nexport function getBackupDestinationId(\n\tstate: DokployStageState | null,\n): string | undefined {\n\treturn state?.backups?.destinationId;\n}\n\n/**\n * Get postgres backup ID from state\n */\nexport function getPostgresBackupId(\n\tstate: DokployStageState | null,\n): string | undefined {\n\treturn state?.backups?.postgresBackupId;\n}\n\n/**\n * Set postgres backup ID in state (mutates state)\n */\nexport function setPostgresBackupId(\n\tstate: DokployStageState,\n\tbackupId: string,\n): void {\n\tif (state.backups) {\n\t\tstate.backups.postgresBackupId = backupId;\n\t}\n}\n","/**\n * DNS Provider Interface\n *\n * Abstracts DNS operations for different providers.\n * Built-in providers: HostingerProvider, Route53Provider\n * Users can also supply custom implementations.\n */\n\nimport type { z } from 'zod/v4';\nimport type {\n\tCloudflareDnsProviderSchema,\n\tCustomDnsProviderSchema,\n\tDnsProviderSchema,\n\tDnsRecordSchema,\n\tDnsRecordTypeSchema,\n\tHostingerDnsProviderSchema,\n\tManualDnsProviderSchema,\n\tRoute53DnsProviderSchema,\n\tUpsertDnsRecordSchema,\n\tUpsertResultSchema,\n} from '../../workspace/schema';\n\n// =============================================================================\n// DNS Record Types (derived from Zod schemas)\n// =============================================================================\n\n/**\n * DNS record types supported across providers.\n */\nexport type DnsRecordType = z.infer<typeof DnsRecordTypeSchema>;\n\n/**\n * A DNS record as returned by the provider.\n */\nexport type DnsRecord = z.infer<typeof DnsRecordSchema>;\n\n/**\n * A DNS record to create or update.\n */\nexport type UpsertDnsRecord = z.infer<typeof UpsertDnsRecordSchema>;\n\n/**\n * Result of an upsert operation.\n */\nexport type UpsertResult = z.infer<typeof UpsertResultSchema>;\n\n// =============================================================================\n// DNS Provider Interface\n// =============================================================================\n\n/**\n * A record to delete from DNS.\n */\nexport interface DeleteDnsRecord {\n\t/** Record name/subdomain (e.g., 'api' or '@' for root) */\n\tname: string;\n\t/** Record type (A, CNAME, etc.) */\n\ttype: DnsRecordType;\n}\n\n/**\n * Result of a delete operation.\n */\nexport interface DeleteResult {\n\t/** The record that was requested for deletion */\n\trecord: DeleteDnsRecord;\n\t/** Whether the record was deleted */\n\tdeleted: boolean;\n\t/** Whether the record was not found (already deleted) */\n\tnotFound: boolean;\n\t/** Error message if deletion failed */\n\terror?: string;\n}\n\n/**\n * Interface for DNS providers.\n *\n * Implementations must handle:\n * - Getting all records for a domain\n * - Creating or updating records for a domain\n * - Deleting records from a domain\n */\nexport interface DnsProvider {\n\t/** Provider name for logging */\n\treadonly name: string;\n\n\t/**\n\t * Get all DNS records for a domain.\n\t *\n\t * @param domain - Root domain (e.g., 'example.com')\n\t * @returns Array of DNS records\n\t */\n\tgetRecords(domain: string): Promise<DnsRecord[]>;\n\n\t/**\n\t * Create or update DNS records.\n\t *\n\t * @param domain - Root domain (e.g., 'example.com')\n\t * @param records - Records to create or update\n\t * @returns Results of the upsert operations\n\t */\n\tupsertRecords(\n\t\tdomain: string,\n\t\trecords: UpsertDnsRecord[],\n\t): Promise<UpsertResult[]>;\n\n\t/**\n\t * Delete DNS records.\n\t *\n\t * @param domain - Root domain (e.g., 'example.com')\n\t * @param records - Records to delete\n\t * @returns Results of the delete operations\n\t */\n\tdeleteRecords(\n\t\tdomain: string,\n\t\trecords: DeleteDnsRecord[],\n\t): Promise<DeleteResult[]>;\n}\n\n// =============================================================================\n// DNS Provider Config Types (derived from Zod schemas)\n// =============================================================================\n\nexport type HostingerDnsConfig = z.infer<typeof HostingerDnsProviderSchema>;\nexport type Route53DnsConfig = z.infer<typeof Route53DnsProviderSchema>;\nexport type CloudflareDnsConfig = z.infer<typeof CloudflareDnsProviderSchema>;\nexport type ManualDnsConfig = z.infer<typeof ManualDnsProviderSchema>;\nexport type CustomDnsConfig = z.infer<typeof CustomDnsProviderSchema>;\n/** Single DNS provider config (for one domain) */\nexport type DnsConfig = z.infer<typeof DnsProviderSchema>;\n\n// =============================================================================\n// DNS Provider Factory\n// =============================================================================\n\n/**\n * Check if value is a DnsProvider implementation.\n */\nexport function isDnsProvider(value: unknown): value is DnsProvider {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\ttypeof (value as DnsProvider).name === 'string' &&\n\t\ttypeof (value as DnsProvider).getRecords === 'function' &&\n\t\ttypeof (value as DnsProvider).upsertRecords === 'function' &&\n\t\ttypeof (value as DnsProvider).deleteRecords === 'function'\n\t);\n}\n\nexport interface CreateDnsProviderOptions {\n\t/** DNS config from workspace */\n\tconfig: DnsConfig;\n}\n\n/**\n * Create a DNS provider based on configuration.\n *\n * - 'hostinger': HostingerProvider\n * - 'route53': Route53Provider\n * - 'manual': Returns null (user handles DNS)\n * - Custom: Use provided DnsProvider implementation\n */\nexport async function createDnsProvider(\n\toptions: CreateDnsProviderOptions,\n): Promise<DnsProvider | null> {\n\tconst { config } = options;\n\n\t// Manual mode - no provider needed\n\tif (config.provider === 'manual') {\n\t\treturn null;\n\t}\n\n\t// Custom provider implementation\n\tif (isDnsProvider(config.provider)) {\n\t\treturn config.provider;\n\t}\n\n\t// Built-in providers\n\tconst provider = config.provider;\n\n\tif (provider === 'hostinger') {\n\t\tconst { HostingerProvider } = await import('./HostingerProvider');\n\t\treturn new HostingerProvider();\n\t}\n\n\tif (provider === 'route53') {\n\t\tconst { Route53Provider } = await import('./Route53Provider');\n\t\tconst route53Config = config as Route53DnsConfig;\n\t\treturn new Route53Provider({\n\t\t\tregion: route53Config.region,\n\t\t\tprofile: route53Config.profile,\n\t\t\thostedZoneId: route53Config.hostedZoneId,\n\t\t});\n\t}\n\n\tif (provider === 'cloudflare') {\n\t\tthrow new Error('Cloudflare DNS provider not yet implemented');\n\t}\n\n\tthrow new Error(`Unknown DNS provider: ${JSON.stringify(config)}`);\n}\n","/**\n * DNS orchestration for deployments\n *\n * Handles automatic DNS record creation for deployed applications.\n */\n\nimport { lookup } from 'node:dns/promises';\nimport type {\n\tDnsConfig,\n\tDnsProvider as DnsProviderConfig,\n} from '../../workspace/types';\nimport {\n\ttype DokployStageState,\n\tisDnsVerified,\n\tsetDnsRecord,\n\tsetDnsVerification,\n} from '../state';\nimport {\n\tcreateDnsProvider,\n\ttype DnsProvider,\n\ttype DnsConfig as SchemaDnsConfig,\n\ttype UpsertDnsRecord,\n} from './DnsProvider';\n\nconst logger = console;\n\n/**\n * Check if DNS config is legacy format (single domain with `domain` property)\n */\nexport function isLegacyDnsConfig(\n\tconfig: DnsConfig,\n): config is SchemaDnsConfig & { domain: string } {\n\treturn (\n\t\ttypeof config === 'object' &&\n\t\tconfig !== null &&\n\t\t'provider' in config &&\n\t\t'domain' in config\n\t);\n}\n\n/**\n * Normalize DNS config to new multi-domain format\n */\nexport function normalizeDnsConfig(\n\tconfig: DnsConfig,\n): Record<string, DnsProviderConfig> {\n\tif (isLegacyDnsConfig(config)) {\n\t\t// Convert legacy format to new format\n\t\tconst { domain, ...providerConfig } = config;\n\t\treturn { [domain]: providerConfig as DnsProviderConfig };\n\t}\n\treturn config as Record<string, DnsProviderConfig>;\n}\n\n/**\n * Find the root domain for a hostname from available DNS configs\n *\n * @example\n * findRootDomain('api.geekmidas.com', { 'geekmidas.com': {...}, 'geekmidas.dev': {...} })\n * // Returns 'geekmidas.com'\n */\nexport function findRootDomain(\n\thostname: string,\n\tdnsConfig: Record<string, DnsProviderConfig>,\n): string | null {\n\t// Sort domains by length descending to match most specific first\n\tconst domains = Object.keys(dnsConfig).sort((a, b) => b.length - a.length);\n\n\tfor (const domain of domains) {\n\t\tif (hostname === domain || hostname.endsWith(`.${domain}`)) {\n\t\t\treturn domain;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Group hostnames by their root domain\n */\nexport function groupHostnamesByDomain(\n\tappHostnames: Map<string, string>,\n\tdnsConfig: Record<string, DnsProviderConfig>,\n): Map<string, Map<string, string>> {\n\tconst grouped = new Map<string, Map<string, string>>();\n\n\tfor (const [appName, hostname] of appHostnames) {\n\t\tconst rootDomain = findRootDomain(hostname, dnsConfig);\n\t\tif (!rootDomain) {\n\t\t\tlogger.log(` ⚠ No DNS config found for hostname: ${hostname}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!grouped.has(rootDomain)) {\n\t\t\tgrouped.set(rootDomain, new Map());\n\t\t}\n\t\tgrouped.get(rootDomain)!.set(appName, hostname);\n\t}\n\n\treturn grouped;\n}\n\n/**\n * Required DNS record for an app\n */\nexport interface RequiredDnsRecord {\n\t/** Full hostname (e.g., 'api.joemoer.traflabs.io') */\n\thostname: string;\n\t/** Subdomain part for the DNS provider (e.g., 'api.joemoer') */\n\tsubdomain: string;\n\t/** Record type */\n\ttype: 'A' | 'CNAME';\n\t/** Target value (IP or hostname) */\n\tvalue: string;\n\t/** App name */\n\tappName: string;\n\t/** Whether the record was created */\n\tcreated?: boolean;\n\t/** Whether the record already existed */\n\texisted?: boolean;\n\t/** Error if creation failed */\n\terror?: string;\n}\n\n/**\n * Result of DNS record creation\n */\nexport interface DnsCreationResult {\n\trecords: RequiredDnsRecord[];\n\tsuccess: boolean;\n\tserverIp: string;\n}\n\n/**\n * Resolve IP address from a hostname\n */\nexport async function resolveHostnameToIp(hostname: string): Promise<string> {\n\ttry {\n\t\tconst addresses = await lookup(hostname, { family: 4 });\n\t\treturn addresses.address;\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to resolve IP for ${hostname}: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n}\n\n/**\n * Extract subdomain from full hostname relative to root domain\n *\n * @example\n * extractSubdomain('api.joemoer.traflabs.io', 'traflabs.io') => 'api.joemoer'\n * extractSubdomain('joemoer.traflabs.io', 'traflabs.io') => 'joemoer'\n */\nexport function extractSubdomain(hostname: string, rootDomain: string): string {\n\tif (!hostname.endsWith(rootDomain)) {\n\t\tthrow new Error(\n\t\t\t`Hostname ${hostname} is not under root domain ${rootDomain}`,\n\t\t);\n\t}\n\n\tconst subdomain = hostname.slice(0, -(rootDomain.length + 1)); // +1 for the dot\n\treturn subdomain || '@'; // '@' represents the root domain itself\n}\n\n/**\n * Generate required DNS records for a deployment\n */\nexport function generateRequiredRecords(\n\tappHostnames: Map<string, string>, // appName -> hostname\n\trootDomain: string,\n\tserverIp: string,\n): RequiredDnsRecord[] {\n\tconst records: RequiredDnsRecord[] = [];\n\n\tfor (const [appName, hostname] of appHostnames) {\n\t\tconst subdomain = extractSubdomain(hostname, rootDomain);\n\t\trecords.push({\n\t\t\thostname,\n\t\t\tsubdomain,\n\t\t\ttype: 'A',\n\t\t\tvalue: serverIp,\n\t\t\tappName,\n\t\t});\n\t}\n\n\treturn records;\n}\n\n/**\n * Print DNS records table\n */\nexport function printDnsRecordsTable(\n\trecords: RequiredDnsRecord[],\n\trootDomain: string,\n): void {\n\tlogger.log(`\\n 📋 DNS Records for ${rootDomain}:`);\n\tlogger.log(\n\t\t' ┌─────────────────────────────────────┬──────┬─────────────────┬────────┐',\n\t);\n\tlogger.log(\n\t\t' │ Subdomain │ Type │ Value │ Status │',\n\t);\n\tlogger.log(\n\t\t' ├─────────────────────────────────────┼──────┼─────────────────┼────────┤',\n\t);\n\n\tfor (const record of records) {\n\t\tconst subdomain = record.subdomain.padEnd(35);\n\t\tconst type = record.type.padEnd(4);\n\t\tconst value = record.value.padEnd(15);\n\t\tlet status: string;\n\n\t\tif (record.error) {\n\t\t\tstatus = '✗';\n\t\t} else if (record.created) {\n\t\t\tstatus = '✓ new';\n\t\t} else if (record.existed) {\n\t\t\tstatus = '✓';\n\t\t} else {\n\t\t\tstatus = '?';\n\t\t}\n\n\t\tlogger.log(\n\t\t\t` │ ${subdomain} │ ${type} │ ${value} │ ${status.padEnd(6)} │`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t' └─────────────────────────────────────┴──────┴─────────────────┴────────┘',\n\t);\n}\n\n/**\n * Print DNS records in a simple format for manual setup\n */\nexport function printDnsRecordsSimple(\n\trecords: RequiredDnsRecord[],\n\trootDomain: string,\n): void {\n\tlogger.log('\\n 📋 Required DNS Records:');\n\tlogger.log(` Add these A records to your DNS provider (${rootDomain}):\\n`);\n\n\tfor (const record of records) {\n\t\tlogger.log(` ${record.subdomain} → ${record.value} (A record)`);\n\t}\n\n\tlogger.log('');\n}\n\n/**\n * Create DNS records for a single domain using its configured provider\n */\nexport async function createDnsRecordsForDomain(\n\trecords: RequiredDnsRecord[],\n\trootDomain: string,\n\tproviderConfig: DnsProviderConfig,\n): Promise<RequiredDnsRecord[]> {\n\t// Get TTL from config, default to 300. Manual mode doesn't have ttl property.\n\tconst ttl =\n\t\t'ttl' in providerConfig && providerConfig.ttl ? providerConfig.ttl : 300;\n\n\t// Get DNS provider from factory\n\tlet provider: DnsProvider | null;\n\ttry {\n\t\t// Cast to schema-derived DnsConfig for provider factory\n\t\tprovider = await createDnsProvider({\n\t\t\tconfig: providerConfig as SchemaDnsConfig,\n\t\t});\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : 'Unknown error';\n\t\tlogger.log(\n\t\t\t` ⚠ Failed to create DNS provider for ${rootDomain}: ${message}`,\n\t\t);\n\t\treturn records.map((r) => ({ ...r, error: message }));\n\t}\n\n\t// Manual mode - no provider, just mark records as needing manual creation\n\tif (!provider) {\n\t\treturn records.map((r) => ({ ...r, created: false, existed: false }));\n\t}\n\n\tconst results: RequiredDnsRecord[] = [];\n\n\t// Convert RequiredDnsRecord to UpsertDnsRecord format\n\tconst upsertRecords: UpsertDnsRecord[] = records.map((r) => ({\n\t\tname: r.subdomain,\n\t\ttype: r.type,\n\t\tttl,\n\t\tvalue: r.value,\n\t}));\n\n\ttry {\n\t\t// Use provider to upsert records\n\t\tconst upsertResults = await provider.upsertRecords(\n\t\t\trootDomain,\n\t\t\tupsertRecords,\n\t\t);\n\n\t\t// Map results back to RequiredDnsRecord format\n\t\tfor (const [i, record] of records.entries()) {\n\t\t\tconst result = upsertResults[i];\n\n\t\t\t// Handle case where upsertResults has fewer items (shouldn't happen but be safe)\n\t\t\tif (!result) {\n\t\t\t\tresults.push({\n\t\t\t\t\thostname: record.hostname,\n\t\t\t\t\tsubdomain: record.subdomain,\n\t\t\t\t\ttype: record.type,\n\t\t\t\t\tvalue: record.value,\n\t\t\t\t\tappName: record.appName,\n\t\t\t\t\terror: 'No result returned from provider',\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (result.unchanged) {\n\t\t\t\tresults.push({\n\t\t\t\t\thostname: record.hostname,\n\t\t\t\t\tsubdomain: record.subdomain,\n\t\t\t\t\ttype: record.type,\n\t\t\t\t\tvalue: record.value,\n\t\t\t\t\tappName: record.appName,\n\t\t\t\t\texisted: true,\n\t\t\t\t\tcreated: false,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tresults.push({\n\t\t\t\t\thostname: record.hostname,\n\t\t\t\t\tsubdomain: record.subdomain,\n\t\t\t\t\ttype: record.type,\n\t\t\t\t\tvalue: record.value,\n\t\t\t\t\tappName: record.appName,\n\t\t\t\t\tcreated: result.created,\n\t\t\t\t\texisted: !result.created,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : 'Unknown error';\n\t\tlogger.log(\n\t\t\t` ⚠ Failed to create DNS records for ${rootDomain}: ${message}`,\n\t\t);\n\t\treturn records.map((r) => ({\n\t\t\thostname: r.hostname,\n\t\t\tsubdomain: r.subdomain,\n\t\t\ttype: r.type,\n\t\t\tvalue: r.value,\n\t\t\tappName: r.appName,\n\t\t\terror: message,\n\t\t}));\n\t}\n\n\treturn results;\n}\n\n/**\n * Create DNS records using the configured provider\n * @deprecated Use createDnsRecordsForDomain for multi-domain support\n */\nexport async function createDnsRecords(\n\trecords: RequiredDnsRecord[],\n\tdnsConfig: DnsConfig,\n): Promise<RequiredDnsRecord[]> {\n\t// Handle legacy config format\n\tif (!isLegacyDnsConfig(dnsConfig)) {\n\t\tthrow new Error(\n\t\t\t'createDnsRecords requires legacy DnsConfig with domain property. Use createDnsRecordsForDomain instead.',\n\t\t);\n\t}\n\tconst { domain: rootDomain, ...providerConfig } = dnsConfig;\n\treturn createDnsRecordsForDomain(\n\t\trecords,\n\t\trootDomain,\n\t\tproviderConfig as DnsProviderConfig,\n\t);\n}\n\n/**\n * Main DNS orchestration function for deployments\n *\n * Supports both legacy single-domain format and new multi-domain format:\n * - Legacy: { provider: 'hostinger', domain: 'example.com' }\n * - Multi: { 'example.com': { provider: 'hostinger' }, 'example.dev': { provider: 'route53' } }\n *\n * @param appHostnames - Map of app names to hostnames\n * @param dnsConfig - DNS configuration (legacy or multi-domain)\n * @param dokployEndpoint - Dokploy server endpoint to resolve IP from\n * @param state - Optional state to save created records for later deletion\n */\nexport async function orchestrateDns(\n\tappHostnames: Map<string, string>, // appName -> hostname\n\tdnsConfig: DnsConfig | undefined,\n\tdokployEndpoint: string,\n\tstate?: DokployStageState,\n): Promise<DnsCreationResult | null> {\n\tif (!dnsConfig) {\n\t\treturn null;\n\t}\n\n\t// Normalize config to multi-domain format\n\tconst normalizedConfig = normalizeDnsConfig(dnsConfig);\n\n\t// Resolve Dokploy server IP from endpoint\n\tlogger.log('\\n🌐 Setting up DNS records...');\n\tlet serverIp: string;\n\n\ttry {\n\t\tconst endpointUrl = new URL(dokployEndpoint);\n\t\tserverIp = await resolveHostnameToIp(endpointUrl.hostname);\n\t\tlogger.log(` Server IP: ${serverIp} (from ${endpointUrl.hostname})`);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : 'Unknown error';\n\t\tlogger.log(` ⚠ Failed to resolve server IP: ${message}`);\n\t\treturn null;\n\t}\n\n\t// Group hostnames by their root domain\n\tconst groupedHostnames = groupHostnamesByDomain(\n\t\tappHostnames,\n\t\tnormalizedConfig,\n\t);\n\n\tif (groupedHostnames.size === 0) {\n\t\tlogger.log(\n\t\t\t' No DNS records needed (no hostnames match configured domains)',\n\t\t);\n\t\treturn { records: [], success: true, serverIp };\n\t}\n\n\tconst allRecords: RequiredDnsRecord[] = [];\n\tlet hasFailures = false;\n\n\t// Process each domain group with its specific provider\n\tfor (const [rootDomain, domainHostnames] of groupedHostnames) {\n\t\tconst providerConfig = normalizedConfig[rootDomain];\n\t\tif (!providerConfig) {\n\t\t\tlogger.log(` ⚠ No provider config for ${rootDomain}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst providerName =\n\t\t\ttypeof providerConfig.provider === 'string'\n\t\t\t\t? providerConfig.provider\n\t\t\t\t: 'custom';\n\n\t\t// Generate required records for this domain\n\t\tconst requiredRecords = generateRequiredRecords(\n\t\t\tdomainHostnames,\n\t\t\trootDomain,\n\t\t\tserverIp,\n\t\t);\n\n\t\tif (requiredRecords.length === 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Create records for this domain\n\t\tlogger.log(\n\t\t\t` Creating DNS records for ${rootDomain} (${providerName})...`,\n\t\t);\n\t\tconst domainRecords = await createDnsRecordsForDomain(\n\t\t\trequiredRecords,\n\t\t\trootDomain,\n\t\t\tproviderConfig,\n\t\t);\n\n\t\tallRecords.push(...domainRecords);\n\n\t\tconst created = domainRecords.filter((r) => r.created).length;\n\t\tconst existed = domainRecords.filter((r) => r.existed).length;\n\t\tconst failed = domainRecords.filter((r) => r.error).length;\n\n\t\tif (created > 0) {\n\t\t\tlogger.log(` ✓ Created ${created} DNS record(s) for ${rootDomain}`);\n\t\t}\n\t\tif (existed > 0) {\n\t\t\tlogger.log(` ✓ ${existed} record(s) already exist for ${rootDomain}`);\n\t\t}\n\t\tif (failed > 0) {\n\t\t\tlogger.log(` ⚠ ${failed} record(s) failed for ${rootDomain}`);\n\t\t\thasFailures = true;\n\t\t}\n\n\t\t// Save created/existing records to state for later deletion during undeploy\n\t\tif (state) {\n\t\t\tfor (const record of domainRecords) {\n\t\t\t\tif (record.created || record.existed) {\n\t\t\t\t\tsetDnsRecord(state, {\n\t\t\t\t\t\tdomain: rootDomain,\n\t\t\t\t\t\tname: record.subdomain,\n\t\t\t\t\t\ttype: record.type,\n\t\t\t\t\t\tvalue: record.value,\n\t\t\t\t\t\tttl:\n\t\t\t\t\t\t\t'ttl' in providerConfig && providerConfig.ttl\n\t\t\t\t\t\t\t\t? providerConfig.ttl\n\t\t\t\t\t\t\t\t: 300,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Print summary table for this domain\n\t\tprintDnsRecordsTable(domainRecords, rootDomain);\n\n\t\t// If manual mode or some failed, print simple instructions\n\t\tif (providerConfig.provider === 'manual' || failed > 0) {\n\t\t\tprintDnsRecordsSimple(\n\t\t\t\tdomainRecords.filter((r) => !r.created && !r.existed),\n\t\t\t\trootDomain,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn {\n\t\trecords: allRecords,\n\t\tsuccess: !hasFailures,\n\t\tserverIp,\n\t};\n}\n\n/**\n * Result of DNS verification for a single hostname\n */\nexport interface DnsVerificationResult {\n\thostname: string;\n\tappName: string;\n\tverified: boolean;\n\tresolvedIp?: string;\n\texpectedIp: string;\n\terror?: string;\n\tskipped?: boolean; // True if already verified in state\n}\n\n/**\n * Verify DNS records resolve correctly after deployment.\n *\n * This function:\n * 1. Checks state for previously verified hostnames (skips if already verified with same IP)\n * 2. Attempts to resolve each hostname to an IP\n * 3. Compares resolved IP with expected server IP\n * 4. Updates state with verification results\n *\n * @param appHostnames - Map of app names to hostnames\n * @param serverIp - Expected IP address the hostnames should resolve to\n * @param state - Deploy state for caching verification results\n * @returns Array of verification results\n */\nexport async function verifyDnsRecords(\n\tappHostnames: Map<string, string>,\n\tserverIp: string,\n\tstate: DokployStageState,\n): Promise<DnsVerificationResult[]> {\n\tconst results: DnsVerificationResult[] = [];\n\n\tlogger.log('\\n🔍 Verifying DNS records...');\n\n\tfor (const [appName, hostname] of appHostnames) {\n\t\t// Check if already verified with same IP\n\t\tif (isDnsVerified(state, hostname, serverIp)) {\n\t\t\tlogger.log(` ✓ ${hostname} (previously verified)`);\n\t\t\tresults.push({\n\t\t\t\thostname,\n\t\t\t\tappName,\n\t\t\t\tverified: true,\n\t\t\t\texpectedIp: serverIp,\n\t\t\t\tskipped: true,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Attempt to resolve hostname\n\t\ttry {\n\t\t\tconst resolvedIp = await resolveHostnameToIp(hostname);\n\n\t\t\tif (resolvedIp === serverIp) {\n\t\t\t\t// DNS verified successfully\n\t\t\t\tsetDnsVerification(state, hostname, serverIp);\n\t\t\t\tlogger.log(` ✓ ${hostname} → ${resolvedIp}`);\n\t\t\t\tresults.push({\n\t\t\t\t\thostname,\n\t\t\t\t\tappName,\n\t\t\t\t\tverified: true,\n\t\t\t\t\tresolvedIp,\n\t\t\t\t\texpectedIp: serverIp,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// DNS resolves but to wrong IP\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ⚠ ${hostname} resolves to ${resolvedIp}, expected ${serverIp}`,\n\t\t\t\t);\n\t\t\t\tresults.push({\n\t\t\t\t\thostname,\n\t\t\t\t\tappName,\n\t\t\t\t\tverified: false,\n\t\t\t\t\tresolvedIp,\n\t\t\t\t\texpectedIp: serverIp,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// DNS resolution failed\n\t\t\tconst message = error instanceof Error ? error.message : 'Unknown error';\n\t\t\tlogger.log(` ⚠ ${hostname} DNS not propagated (${message})`);\n\t\t\tresults.push({\n\t\t\t\thostname,\n\t\t\t\tappName,\n\t\t\t\tverified: false,\n\t\t\t\texpectedIp: serverIp,\n\t\t\t\terror: message,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Summary\n\tconst verified = results.filter((r) => r.verified).length;\n\tconst skipped = results.filter((r) => r.skipped).length;\n\tconst pending = results.filter((r) => !r.verified).length;\n\n\tif (pending > 0) {\n\t\tlogger.log(`\\n ${verified} verified, ${pending} pending propagation`);\n\t\tlogger.log(' DNS changes may take 5-30 minutes to propagate');\n\t} else if (skipped > 0) {\n\t\tlogger.log(` ${verified} verified (${skipped} from cache)`);\n\t}\n\n\treturn results;\n}\n","import { getPublicEnvPrefix } from '../workspace/index.js';\nimport type {\n\tDokployWorkspaceConfig,\n\tNormalizedAppConfig,\n} from '../workspace/types.js';\n\n/**\n * Resolve the hostname for an app based on stage configuration.\n *\n * Domain resolution priority:\n * 1. Explicit app.domain override (string or stage-specific)\n * 2. Default pattern based on app type:\n * - Main frontend app gets base domain (e.g., 'myapp.com')\n * - Other apps get prefixed domain (e.g., 'api.myapp.com')\n *\n * @param appName - The name of the app\n * @param app - The normalized app configuration\n * @param stage - The deployment stage (e.g., 'production', 'development')\n * @param dokployConfig - Dokploy workspace configuration with domain mappings\n * @param isMainFrontend - Whether this is the main frontend app\n * @returns The resolved hostname for the app\n * @throws Error if no domain configuration is found for the stage\n */\nexport function resolveHost(\n\tappName: string,\n\tapp: NormalizedAppConfig,\n\tstage: string,\n\tdokployConfig: DokployWorkspaceConfig | undefined,\n\tisMainFrontend: boolean,\n): string {\n\t// 1. Check for explicit app domain override\n\tif (app.domain) {\n\t\tif (typeof app.domain === 'string') {\n\t\t\treturn app.domain;\n\t\t}\n\t\tif (app.domain[stage]) {\n\t\t\treturn app.domain[stage]!;\n\t\t}\n\t}\n\n\t// 2. Get base domain for this stage\n\tconst baseDomain = dokployConfig?.domains?.[stage];\n\tif (!baseDomain) {\n\t\tthrow new Error(\n\t\t\t`No domain configured for stage \"${stage}\". ` +\n\t\t\t\t`Add deploy.dokploy.domains.${stage} to gkm.config.ts`,\n\t\t);\n\t}\n\n\t// 3. Main frontend app gets base domain, others get prefix\n\tif (isMainFrontend) {\n\t\treturn baseDomain;\n\t}\n\n\treturn `${appName}.${baseDomain}`;\n}\n\n/**\n * Determine if an app is the \"main\" frontend (gets base domain).\n *\n * An app is considered the main frontend if:\n * 1. It's named 'web' and is a frontend type\n * 2. It's the first frontend app in the apps list\n *\n * @param appName - The name of the app to check\n * @param app - The app configuration\n * @param allApps - All apps in the workspace\n * @returns True if this is the main frontend app\n */\nexport function isMainFrontendApp(\n\tappName: string,\n\tapp: NormalizedAppConfig,\n\tallApps: Record<string, NormalizedAppConfig>,\n): boolean {\n\tif (app.type !== 'web') {\n\t\treturn false;\n\t}\n\n\t// App named 'web' is always main\n\tif (appName === 'web') {\n\t\treturn true;\n\t}\n\n\t// Otherwise, check if this is the first frontend\n\tfor (const [name, a] of Object.entries(allApps)) {\n\t\tif (a.type === 'web') {\n\t\t\treturn name === appName;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Generate public URL build args for a web/mobile app based on its dependencies.\n *\n * The prefix is chosen by the app's framework (NEXT_PUBLIC_, VITE_,\n * EXPO_PUBLIC_, or none for Remix). Apps without a public prefix get no\n * build args — they should fetch URLs at runtime instead.\n *\n * @param app - The web/mobile app configuration\n * @param deployedUrls - Map of app name to deployed public URL\n * @returns Array of build args like 'NEXT_PUBLIC_API_URL=https://api.example.com'\n */\nexport function generatePublicUrlBuildArgs(\n\tapp: NormalizedAppConfig,\n\tdeployedUrls: Record<string, string>,\n): string[] {\n\tconst prefix = getPublicEnvPrefix(app);\n\tif (!prefix) return [];\n\n\tconst buildArgs: string[] = [];\n\tfor (const dep of app.dependencies) {\n\t\tconst publicUrl = deployedUrls[dep];\n\t\tif (publicUrl) {\n\t\t\tbuildArgs.push(`${prefix}${dep.toUpperCase()}_URL=${publicUrl}`);\n\t\t}\n\t}\n\n\treturn buildArgs;\n}\n\n/**\n * Get public URL arg names from app dependencies.\n *\n * @param app - The web/mobile app configuration\n * @returns Array of arg names like 'NEXT_PUBLIC_API_URL', or [] for frameworks\n * without a public prefix (e.g. Remix).\n */\nexport function getPublicUrlArgNames(app: NormalizedAppConfig): string[] {\n\tconst prefix = getPublicEnvPrefix(app);\n\tif (!prefix) return [];\n\treturn app.dependencies.map((dep) => `${prefix}${dep.toUpperCase()}_URL`);\n}\n","import type {\n\tComposeServiceName,\n\tComposeServicesConfig,\n\tServiceConfig,\n} from '../types';\nimport type {\n\tNormalizedAppConfig,\n\tNormalizedWorkspace,\n} from '../workspace/types.js';\n\n/** Default Docker images for services */\nexport const DEFAULT_SERVICE_IMAGES: Record<ComposeServiceName, string> = {\n\tpostgres: 'postgres',\n\tredis: 'redis',\n\trabbitmq: 'rabbitmq',\n\tminio: 'minio/minio',\n\tmailpit: 'axllent/mailpit',\n\tlocalstack: 'localstack/localstack',\n};\n\n/** Default Docker image versions for services */\nexport const DEFAULT_SERVICE_VERSIONS: Record<ComposeServiceName, string> = {\n\tpostgres: '18-alpine',\n\tredis: '7-alpine',\n\trabbitmq: '3-management-alpine',\n\tminio: 'latest',\n\tmailpit: 'latest',\n\tlocalstack: 'latest',\n};\n\nexport interface ComposeOptions {\n\timageName: string;\n\tregistry: string;\n\tport: number;\n\thealthCheckPath: string;\n\t/** Services config - object format or legacy array format */\n\tservices: ComposeServicesConfig | ComposeServiceName[];\n}\n\n/** Get the default full image reference for a service */\nfunction getDefaultImage(serviceName: ComposeServiceName): string {\n\treturn `${DEFAULT_SERVICE_IMAGES[serviceName]}:${DEFAULT_SERVICE_VERSIONS[serviceName]}`;\n}\n\n/** Normalize services config to a consistent format - returns Map of service name to full image reference */\nfunction normalizeServices(\n\tservices: ComposeServicesConfig | ComposeServiceName[],\n): Map<ComposeServiceName, string> {\n\tconst result = new Map<ComposeServiceName, string>();\n\n\tif (Array.isArray(services)) {\n\t\t// Legacy array format - use default images\n\t\tfor (const name of services) {\n\t\t\tresult.set(name, getDefaultImage(name));\n\t\t}\n\t} else {\n\t\t// Object format\n\t\tfor (const [name, config] of Object.entries(services)) {\n\t\t\tconst serviceName = name as ComposeServiceName;\n\t\t\tif (config === true) {\n\t\t\t\t// boolean true - use default image\n\t\t\t\tresult.set(serviceName, getDefaultImage(serviceName));\n\t\t\t} else if (config && typeof config === 'object') {\n\t\t\t\tconst serviceConfig = config as ServiceConfig;\n\t\t\t\tif (serviceConfig.image) {\n\t\t\t\t\t// Full image reference provided\n\t\t\t\t\tresult.set(serviceName, serviceConfig.image);\n\t\t\t\t} else {\n\t\t\t\t\t// Version only - use default image name with custom version\n\t\t\t\t\tconst version =\n\t\t\t\t\t\tserviceConfig.version ?? DEFAULT_SERVICE_VERSIONS[serviceName];\n\t\t\t\t\tresult.set(\n\t\t\t\t\t\tserviceName,\n\t\t\t\t\t\t`${DEFAULT_SERVICE_IMAGES[serviceName]}:${version}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// false or undefined - skip\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Generate docker-compose.yml for production deployment\n */\nexport function generateDockerCompose(options: ComposeOptions): string {\n\tconst { imageName, registry, port, healthCheckPath, services } = options;\n\n\t// Normalize services to Map<name, version>\n\tconst serviceMap = normalizeServices(services);\n\n\tconst imageRef = registry ? `\\${REGISTRY:-${registry}}/` : '';\n\n\tlet yaml = `# Use \"gkm dev\" or \"gkm test\" to start services.\n# Running \"docker compose up\" directly will not inject secrets or resolve ports.\nversion: '3.8'\n\nservices:\n api:\n build:\n context: ../..\n dockerfile: .gkm/docker/Dockerfile\n image: ${imageRef}\\${IMAGE_NAME:-${imageName}}:\\${TAG:-latest}\n container_name: ${imageName}\n restart: unless-stopped\n ports:\n - \"\\${PORT:-${port}}:${port}\"\n environment:\n - NODE_ENV=production\n`;\n\n\t// Add environment variables based on services\n\tif (serviceMap.has('postgres')) {\n\t\tyaml += ` - DATABASE_URL=\\${DATABASE_URL:-postgresql://\\${POSTGRES_USER:-postgres}:\\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\\${POSTGRES_DB:-app}}\n`;\n\t}\n\n\tif (serviceMap.has('redis')) {\n\t\tyaml += ` - REDIS_URL=\\${REDIS_URL:-redis://redis:6379}\n`;\n\t}\n\n\tif (serviceMap.has('rabbitmq')) {\n\t\tyaml += ` - RABBITMQ_URL=\\${RABBITMQ_URL:-amqp://rabbitmq:5672}\n`;\n\t}\n\n\tif (serviceMap.has('minio')) {\n\t\tyaml += ` - STORAGE_ENDPOINT=\\${STORAGE_ENDPOINT:-http://minio:9000}\n - STORAGE_ACCESS_KEY_ID=\\${STORAGE_ACCESS_KEY_ID:-${imageName}}\n - STORAGE_SECRET_ACCESS_KEY=\\${STORAGE_SECRET_ACCESS_KEY:-${imageName}}\n - STORAGE_BUCKET=\\${STORAGE_BUCKET:-${imageName}}\n - STORAGE_REGION=\\${STORAGE_REGION:-eu-west-1}\n - STORAGE_FORCE_PATH_STYLE=true\n`;\n\t}\n\n\tif (serviceMap.has('mailpit')) {\n\t\tyaml += ` - SMTP_HOST=\\${SMTP_HOST:-mailpit}\n - SMTP_PORT=\\${SMTP_PORT:-1025}\n - SMTP_USER=\\${SMTP_USER:-${imageName}}\n - SMTP_PASS=\\${SMTP_PASS:-${imageName}}\n - SMTP_SECURE=\\${SMTP_SECURE:-false}\n - MAIL_FROM=\\${MAIL_FROM:-noreply@localhost}\n`;\n\t}\n\n\tif (serviceMap.has('localstack')) {\n\t\tyaml += ` - AWS_ACCESS_KEY_ID=\\${AWS_ACCESS_KEY_ID:-localstack}\n - AWS_SECRET_ACCESS_KEY=\\${AWS_SECRET_ACCESS_KEY:-localstack}\n - AWS_REGION=\\${AWS_REGION:-us-east-1}\n - AWS_ENDPOINT_URL=\\${AWS_ENDPOINT_URL:-http://localstack:4566}\n`;\n\t}\n\n\tyaml += ` healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:${port}${healthCheckPath}\"]\n interval: 30s\n timeout: 3s\n retries: 3\n`;\n\n\t// Add depends_on if there are services\n\tif (serviceMap.size > 0) {\n\t\tyaml += ` depends_on:\n`;\n\t\tfor (const serviceName of serviceMap.keys()) {\n\t\t\tyaml += ` ${serviceName}:\n condition: service_healthy\n`;\n\t\t}\n\t}\n\n\tyaml += ` networks:\n - app-network\n`;\n\n\t// Add service definitions with images\n\tconst postgresImage = serviceMap.get('postgres');\n\tif (postgresImage) {\n\t\tyaml += `\n postgres:\n image: ${postgresImage}\n container_name: postgres\n restart: unless-stopped\n environment:\n POSTGRES_USER: \\${POSTGRES_USER:-postgres}\n POSTGRES_PASSWORD: \\${POSTGRES_PASSWORD:-postgres}\n POSTGRES_DB: \\${POSTGRES_DB:-app}\n volumes:\n - dbdata:/var/lib/postgresql/18/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U $$POSTGRES_USER\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\tconst redisImage = serviceMap.get('redis');\n\tif (redisImage) {\n\t\tyaml += `\n redis:\n image: ${redisImage}\n container_name: redis\n restart: unless-stopped\n volumes:\n - redis_data:/data\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\tconst rabbitmqImage = serviceMap.get('rabbitmq');\n\tif (rabbitmqImage) {\n\t\tyaml += `\n rabbitmq:\n image: ${rabbitmqImage}\n container_name: rabbitmq\n restart: unless-stopped\n environment:\n RABBITMQ_DEFAULT_USER: \\${RABBITMQ_USER:-guest}\n RABBITMQ_DEFAULT_PASS: \\${RABBITMQ_PASSWORD:-guest}\n ports:\n - \"15672:15672\" # Management UI\n volumes:\n - rabbitmq_data:/var/lib/rabbitmq\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"-q\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\tconst minioImage = serviceMap.get('minio');\n\tif (minioImage) {\n\t\tyaml += `\n minio:\n image: ${minioImage}\n container_name: minio\n restart: unless-stopped\n entrypoint: sh\n command: -c 'mkdir -p /data/\\${STORAGE_BUCKET:-${imageName}} && /usr/bin/docker-entrypoint.sh server --console-address \":9001\" /data'\n environment:\n MINIO_ROOT_USER: \\${STORAGE_ACCESS_KEY_ID:-${imageName}}\n MINIO_ROOT_PASSWORD: \\${STORAGE_SECRET_ACCESS_KEY:-${imageName}}\n ports:\n - \"\\${MINIO_API_PORT:-9000}:9000\"\n - \"\\${MINIO_CONSOLE_PORT:-9001}:9001\"\n volumes:\n - minio_data:/data\n healthcheck:\n test: [\"CMD\", \"mc\", \"ready\", \"local\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\tconst mailpitImage = serviceMap.get('mailpit');\n\tif (mailpitImage) {\n\t\tyaml += `\n mailpit:\n image: ${mailpitImage}\n container_name: mailpit\n restart: unless-stopped\n environment:\n MP_SMTP_AUTH: \\${SMTP_USER:-${imageName}}:\\${SMTP_PASS:-${imageName}}\n MP_SMTP_AUTH_ALLOW_INSECURE: true\n ports:\n - \"\\${MAILPIT_PORT:-8025}:8025\" # Web UI / API\n - \"\\${SMTP_PORT:-1025}:1025\" # SMTP\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8025\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\tconst localstackImage = serviceMap.get('localstack');\n\tif (localstackImage) {\n\t\tyaml += `\n localstack:\n image: ${localstackImage}\n container_name: localstack\n restart: unless-stopped\n environment:\n SERVICES: sns,sqs\n AWS_DEFAULT_REGION: \\${AWS_REGION:-us-east-1}\n AWS_ACCESS_KEY_ID: \\${AWS_ACCESS_KEY_ID:-localstack}\n AWS_SECRET_ACCESS_KEY: \\${AWS_SECRET_ACCESS_KEY:-localstack}\n ports:\n - \"\\${LOCALSTACK_PORT:-4566}:4566\"\n volumes:\n - localstack_data:/var/lib/localstack\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:4566/_localstack/health\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\t// Add volumes\n\tyaml += `\nvolumes:\n`;\n\n\tif (serviceMap.has('postgres')) {\n\t\tyaml += ` dbdata:\n`;\n\t}\n\n\tif (serviceMap.has('redis')) {\n\t\tyaml += ` redis_data:\n`;\n\t}\n\n\tif (serviceMap.has('rabbitmq')) {\n\t\tyaml += ` rabbitmq_data:\n`;\n\t}\n\n\tif (serviceMap.has('minio')) {\n\t\tyaml += ` minio_data:\n`;\n\t}\n\n\tif (serviceMap.has('localstack')) {\n\t\tyaml += ` localstack_data:\n`;\n\t}\n\n\t// Add networks\n\tyaml += `\nnetworks:\n app-network:\n driver: bridge\n`;\n\n\treturn yaml;\n}\n\n/**\n * Generate a minimal docker-compose.yml for API only\n */\nexport function generateMinimalDockerCompose(\n\toptions: Omit<ComposeOptions, 'services'>,\n): string {\n\tconst { imageName, registry, port, healthCheckPath } = options;\n\n\tconst imageRef = registry ? `\\${REGISTRY:-${registry}}/` : '';\n\n\treturn `# Use \"gkm dev\" or \"gkm test\" to start services.\n# Running \"docker compose up\" directly will not inject secrets or resolve ports.\nversion: '3.8'\n\nservices:\n api:\n build:\n context: ../..\n dockerfile: .gkm/docker/Dockerfile\n image: ${imageRef}\\${IMAGE_NAME:-${imageName}}:\\${TAG:-latest}\n container_name: ${imageName}\n restart: unless-stopped\n ports:\n - \"\\${PORT:-${port}}:${port}\"\n environment:\n - NODE_ENV=production\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:${port}${healthCheckPath}\"]\n interval: 30s\n timeout: 3s\n retries: 3\n networks:\n - app-network\n\nnetworks:\n app-network:\n driver: bridge\n`;\n}\n\n/**\n * Options for workspace compose generation.\n */\nexport interface WorkspaceComposeOptions {\n\t/** Container registry URL */\n\tregistry?: string;\n}\n\n/**\n * Generate docker-compose.yml for a workspace with all apps as services.\n * Apps can communicate with each other via service names.\n * @internal Exported for testing\n */\nexport function generateWorkspaceCompose(\n\tworkspace: NormalizedWorkspace,\n\toptions: WorkspaceComposeOptions = {},\n): string {\n\tconst { registry } = options;\n\tconst apps = Object.entries(workspace.apps);\n\tconst services = workspace.services;\n\n\t// Determine which infrastructure services to include\n\tconst hasPostgres = services.db !== undefined && services.db !== false;\n\tconst hasRedis = services.cache !== undefined && services.cache !== false;\n\tconst hasMail = services.mail !== undefined && services.mail !== false;\n\tconst hasMinio = services.storage !== undefined && services.storage !== false;\n\tconst eventsBackend = services.events;\n\tconst hasLocalStack = eventsBackend === 'sns';\n\tconst hasRabbitMQ =\n\t\teventsBackend === 'rabbitmq' ||\n\t\t(services as Record<string, unknown>).rabbitmq !== undefined;\n\n\t// Get image versions from config\n\tconst postgresImage = getInfraServiceImage('postgres', services.db);\n\tconst redisImage = getInfraServiceImage('redis', services.cache);\n\tconst minioImage = getInfraServiceImage('minio', services.storage);\n\n\tlet yaml = `# Docker Compose for ${workspace.name} workspace\n# Use \"gkm dev\" or \"gkm test\" to start services.\n# Running \"docker compose up\" directly will not inject secrets or resolve ports.\n\nservices:\n`;\n\n\t// Generate service for each app (mobile apps deploy via their own\n\t// toolchain — no compose service)\n\tfor (const [appName, app] of apps) {\n\t\tif (app.type === 'mobile') continue;\n\t\tyaml += generateAppService(appName, app, apps, {\n\t\t\tregistry,\n\t\t\tprojectName: workspace.name,\n\t\t\thasPostgres,\n\t\t\thasRedis,\n\t\t\thasMinio,\n\t\t\thasMail,\n\t\t\teventsBackend,\n\t\t});\n\t}\n\n\t// Add infrastructure services\n\tif (hasPostgres) {\n\t\tyaml += `\n postgres:\n image: ${postgresImage}\n container_name: ${workspace.name}-postgres\n restart: unless-stopped\n environment:\n POSTGRES_USER: \\${POSTGRES_USER:-postgres}\n POSTGRES_PASSWORD: \\${POSTGRES_PASSWORD:-postgres}\n POSTGRES_DB: \\${POSTGRES_DB:-app}\n volumes:\n - dbdata:/var/lib/postgresql/18/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U $$POSTGRES_USER\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\tif (hasRedis) {\n\t\tyaml += `\n redis:\n image: ${redisImage}\n container_name: ${workspace.name}-redis\n restart: unless-stopped\n volumes:\n - redis_data:/data\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\tif (hasMail) {\n\t\tyaml += `\n mailpit:\n image: axllent/mailpit:latest\n container_name: ${workspace.name}-mailpit\n restart: unless-stopped\n environment:\n MP_SMTP_AUTH: \\${SMTP_USER:-${workspace.name}}:\\${SMTP_PASS:-${workspace.name}}\n MP_SMTP_AUTH_ALLOW_INSECURE: true\n ports:\n - \"\\${MAILPIT_PORT:-8025}:8025\" # Web UI / API\n - \"\\${SMTP_PORT:-1025}:1025\" # SMTP\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8025\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\tif (hasMinio) {\n\t\tyaml += `\n minio:\n image: ${minioImage}\n container_name: ${workspace.name}-minio\n restart: unless-stopped\n entrypoint: sh\n command: -c 'mkdir -p /data/\\${STORAGE_BUCKET:-${workspace.name}} && /usr/bin/docker-entrypoint.sh server --console-address \":9001\" /data'\n environment:\n MINIO_ROOT_USER: \\${STORAGE_ACCESS_KEY_ID:-${workspace.name}}\n MINIO_ROOT_PASSWORD: \\${STORAGE_SECRET_ACCESS_KEY:-${workspace.name}}\n ports:\n - \"\\${MINIO_API_PORT:-9000}:9000\"\n - \"\\${MINIO_CONSOLE_PORT:-9001}:9001\"\n volumes:\n - minio_data:/data\n healthcheck:\n test: [\"CMD\", \"mc\", \"ready\", \"local\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\tif (hasLocalStack) {\n\t\tyaml += `\n localstack:\n image: localstack/localstack:latest\n container_name: ${workspace.name}-localstack\n restart: unless-stopped\n environment:\n SERVICES: sns,sqs\n AWS_DEFAULT_REGION: \\${AWS_REGION:-us-east-1}\n AWS_ACCESS_KEY_ID: \\${AWS_ACCESS_KEY_ID:-localstack}\n AWS_SECRET_ACCESS_KEY: \\${AWS_SECRET_ACCESS_KEY:-localstack}\n ports:\n - \"\\${LOCALSTACK_PORT:-4566}:4566\"\n volumes:\n - localstack_data:/var/lib/localstack\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:4566/_localstack/health\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\tif (hasRabbitMQ) {\n\t\tyaml += `\n rabbitmq:\n image: rabbitmq:3-management-alpine\n container_name: ${workspace.name}-rabbitmq\n restart: unless-stopped\n environment:\n RABBITMQ_DEFAULT_USER: \\${RABBITMQ_USER:-guest}\n RABBITMQ_DEFAULT_PASS: \\${RABBITMQ_PASSWORD:-guest}\n ports:\n - \"\\${RABBITMQ_HOST_PORT:-5672}:5672\"\n - \"\\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672\"\n volumes:\n - rabbitmq_data:/var/lib/rabbitmq\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"-q\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\t// Add volumes section\n\tyaml += `\nvolumes:\n`;\n\n\tif (hasPostgres) {\n\t\tyaml += ` dbdata:\n`;\n\t}\n\n\tif (hasRedis) {\n\t\tyaml += ` redis_data:\n`;\n\t}\n\n\tif (hasMinio) {\n\t\tyaml += ` minio_data:\n`;\n\t}\n\n\tif (hasLocalStack) {\n\t\tyaml += ` localstack_data:\n`;\n\t}\n\n\tif (hasRabbitMQ) {\n\t\tyaml += ` rabbitmq_data:\n`;\n\t}\n\n\t// Add networks section\n\tyaml += `\nnetworks:\n workspace-network:\n driver: bridge\n`;\n\n\treturn yaml;\n}\n\n/**\n * Get infrastructure service image with version.\n */\nfunction getInfraServiceImage(\n\tserviceName: 'postgres' | 'redis' | 'minio',\n\tconfig: boolean | { version?: string; image?: string } | undefined,\n): string {\n\tconst defaults: Record<'postgres' | 'redis' | 'minio', string> = {\n\t\tpostgres: 'postgres:18-alpine',\n\t\tredis: 'redis:7-alpine',\n\t\tminio: 'minio/minio:latest',\n\t};\n\n\tif (!config || config === true) {\n\t\treturn defaults[serviceName];\n\t}\n\n\tif (typeof config === 'object') {\n\t\tif (config.image) {\n\t\t\treturn config.image;\n\t\t}\n\t\tif (config.version) {\n\t\t\tconst baseImages: Record<'postgres' | 'redis' | 'minio', string> = {\n\t\t\t\tpostgres: 'postgres',\n\t\t\t\tredis: 'redis',\n\t\t\t\tminio: 'minio/minio',\n\t\t\t};\n\t\t\treturn `${baseImages[serviceName]}:${config.version}`;\n\t\t}\n\t}\n\n\treturn defaults[serviceName];\n}\n\n/**\n * Generate a service definition for an app.\n */\nfunction generateAppService(\n\tappName: string,\n\tapp: NormalizedAppConfig,\n\tallApps: [string, NormalizedAppConfig][],\n\toptions: {\n\t\tregistry?: string;\n\t\tprojectName: string;\n\t\thasPostgres: boolean;\n\t\thasRedis: boolean;\n\t\thasMinio: boolean;\n\t\thasMail: boolean;\n\t\teventsBackend?: import('../types').EventsBackend;\n\t},\n): string {\n\tconst {\n\t\tregistry,\n\t\tprojectName,\n\t\thasPostgres,\n\t\thasRedis,\n\t\thasMinio,\n\t\thasMail,\n\t\teventsBackend,\n\t} = options;\n\tconst imageRef = registry ? `\\${REGISTRY:-${registry}}/` : '';\n\n\t// Health check path - frontends use /, backends use /health\n\tconst healthCheckPath = app.type === 'web' ? '/' : '/health';\n\tconst healthCheckCmd =\n\t\tapp.type === 'web'\n\t\t\t? `[\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:${app.port}/\"]`\n\t\t\t: `[\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:${app.port}${healthCheckPath}\"]`;\n\n\tlet yaml = `\n ${appName}:\n build:\n context: .\n dockerfile: .gkm/docker/Dockerfile.${appName}\n image: ${imageRef}\\${${appName.toUpperCase()}_IMAGE:-${appName}}:\\${TAG:-latest}\n container_name: ${appName}\n restart: unless-stopped\n ports:\n - \"\\${${appName.toUpperCase()}_PORT:-${app.port}}:${app.port}\"\n environment:\n - NODE_ENV=production\n - PORT=${app.port}\n`;\n\n\t// Add dependency URLs - apps can reach other apps by service name\n\tfor (const dep of app.dependencies) {\n\t\tconst depApp = allApps.find(([name]) => name === dep)?.[1];\n\t\tif (depApp) {\n\t\t\tyaml += ` - ${dep.toUpperCase()}_URL=http://${dep}:${depApp.port}\n`;\n\t\t}\n\t}\n\n\t// Add infrastructure service URLs for backend apps\n\tif (app.type === 'backend') {\n\t\tif (hasPostgres) {\n\t\t\tyaml += ` - DATABASE_URL=\\${DATABASE_URL:-postgresql://\\${POSTGRES_USER:-postgres}:\\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\\${POSTGRES_DB:-app}}\n`;\n\t\t}\n\t\tif (hasRedis) {\n\t\t\tyaml += ` - REDIS_URL=\\${REDIS_URL:-redis://redis:6379}\n`;\n\t\t}\n\t\tif (hasMinio) {\n\t\t\tyaml += ` - STORAGE_ENDPOINT=\\${STORAGE_ENDPOINT:-http://minio:9000}\n - STORAGE_ACCESS_KEY_ID=\\${STORAGE_ACCESS_KEY_ID:-${projectName}}\n - STORAGE_SECRET_ACCESS_KEY=\\${STORAGE_SECRET_ACCESS_KEY:-${projectName}}\n - STORAGE_BUCKET=\\${STORAGE_BUCKET:-${projectName}}\n - STORAGE_REGION=\\${STORAGE_REGION:-eu-west-1}\n - STORAGE_FORCE_PATH_STYLE=true\n`;\n\t\t}\n\t\tif (hasMail) {\n\t\t\tyaml += ` - SMTP_HOST=\\${SMTP_HOST:-mailpit}\n - SMTP_PORT=\\${SMTP_PORT:-1025}\n - SMTP_USER=\\${SMTP_USER:-${projectName}}\n - SMTP_PASS=\\${SMTP_PASS:-${projectName}}\n - SMTP_SECURE=\\${SMTP_SECURE:-false}\n - MAIL_FROM=\\${MAIL_FROM:-noreply@localhost}\n`;\n\t\t}\n\t\tif (eventsBackend) {\n\t\t\tyaml += ` - EVENT_PUBLISHER_CONNECTION_STRING=\\${EVENT_PUBLISHER_CONNECTION_STRING}\n - EVENT_SUBSCRIBER_CONNECTION_STRING=\\${EVENT_SUBSCRIBER_CONNECTION_STRING}\n`;\n\t\t\tif (eventsBackend === 'sns') {\n\t\t\t\tyaml += ` - AWS_ACCESS_KEY_ID=\\${AWS_ACCESS_KEY_ID:-localstack}\n - AWS_SECRET_ACCESS_KEY=\\${AWS_SECRET_ACCESS_KEY:-localstack}\n - AWS_REGION=\\${AWS_REGION:-us-east-1}\n - AWS_ENDPOINT_URL=\\${AWS_ENDPOINT_URL:-http://localstack:4566}\n`;\n\t\t\t}\n\t\t}\n\t}\n\n\tyaml += ` healthcheck:\n test: ${healthCheckCmd}\n interval: 30s\n timeout: 3s\n retries: 3\n`;\n\n\t// Add depends_on for dependencies and infrastructure\n\tconst dependencies: string[] = [...app.dependencies];\n\tif (app.type === 'backend') {\n\t\tif (hasPostgres) dependencies.push('postgres');\n\t\tif (hasRedis) dependencies.push('redis');\n\t\tif (hasMinio) dependencies.push('minio');\n\t\tif (hasMail) dependencies.push('mailpit');\n\t\tif (eventsBackend === 'sns') dependencies.push('localstack');\n\t\tif (eventsBackend === 'rabbitmq') dependencies.push('rabbitmq');\n\t}\n\n\tif (dependencies.length > 0) {\n\t\tyaml += ` depends_on:\n`;\n\t\tfor (const dep of dependencies) {\n\t\t\tyaml += ` ${dep}:\n condition: service_healthy\n`;\n\t\t}\n\t}\n\n\tyaml += ` networks:\n - workspace-network\n`;\n\n\treturn yaml;\n}\n","import { existsSync } from 'node:fs';\nimport { dirname, join, parse } from 'node:path';\nimport type { DockerConfig, GkmConfig } from '../types';\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';\n\nexport interface DockerTemplateOptions {\n\timageName: string;\n\tbaseImage: string;\n\tport: number;\n\thealthCheckPath: string;\n\t/** Whether the build is pre-built (slim Dockerfile) or needs building */\n\tprebuilt: boolean;\n\t/** Detected package manager */\n\tpackageManager: PackageManager;\n}\n\nexport interface FrontendDockerfileOptions {\n\timageName: string;\n\tbaseImage: string;\n\tport: number;\n\t/** App path relative to workspace root */\n\tappPath: string;\n\t/** Package name for turbo prune */\n\tturboPackage: string;\n\t/** Detected package manager */\n\tpackageManager: PackageManager;\n\t/**\n\t * Public URL build args to include in the Dockerfile.\n\t * These will be declared as ARG and converted to ENV for Next.js build.\n\t * Example: ['NEXT_PUBLIC_API_URL', 'NEXT_PUBLIC_AUTH_URL']\n\t */\n\tpublicUrlArgs?: string[];\n}\n\nexport interface MultiStageDockerfileOptions extends DockerTemplateOptions {\n\t/** Enable turbo prune for monorepo optimization */\n\tturbo?: boolean;\n\t/** Package name for turbo prune (defaults to current directory name) */\n\tturboPackage?: string;\n}\n\nconst LOCKFILES: [string, PackageManager][] = [\n\t['pnpm-lock.yaml', 'pnpm'],\n\t['bun.lockb', 'bun'],\n\t['yarn.lock', 'yarn'],\n\t['package-lock.json', 'npm'],\n];\n\n/**\n * Detect package manager from lockfiles\n * Walks up the directory tree to find lockfile (for monorepos)\n */\nexport function detectPackageManager(\n\tcwd: string = process.cwd(),\n): PackageManager {\n\tlet dir = cwd;\n\tconst root = parse(dir).root;\n\n\t// Walk up the directory tree\n\twhile (dir !== root) {\n\t\tfor (const [lockfile, pm] of LOCKFILES) {\n\t\t\tif (existsSync(join(dir, lockfile))) {\n\t\t\t\treturn pm;\n\t\t\t}\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\n\t// Check root directory\n\tfor (const [lockfile, pm] of LOCKFILES) {\n\t\tif (existsSync(join(root, lockfile))) {\n\t\t\treturn pm;\n\t\t}\n\t}\n\n\treturn 'pnpm'; // default\n}\n\n/**\n * Find the lockfile path by walking up the directory tree\n * Returns the full path to the lockfile, or null if not found\n */\nexport function findLockfilePath(cwd: string = process.cwd()): string | null {\n\tlet dir = cwd;\n\tconst root = parse(dir).root;\n\n\t// Walk up the directory tree\n\twhile (dir !== root) {\n\t\tfor (const [lockfile] of LOCKFILES) {\n\t\t\tconst lockfilePath = join(dir, lockfile);\n\t\t\tif (existsSync(lockfilePath)) {\n\t\t\t\treturn lockfilePath;\n\t\t\t}\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\n\t// Check root directory\n\tfor (const [lockfile] of LOCKFILES) {\n\t\tconst lockfilePath = join(root, lockfile);\n\t\tif (existsSync(lockfilePath)) {\n\t\t\treturn lockfilePath;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Get the lockfile name for a package manager\n */\nexport function getLockfileName(pm: PackageManager): string {\n\tconst lockfileMap: Record<PackageManager, string> = {\n\t\tpnpm: 'pnpm-lock.yaml',\n\t\tnpm: 'package-lock.json',\n\t\tyarn: 'yarn.lock',\n\t\tbun: 'bun.lockb',\n\t};\n\treturn lockfileMap[pm];\n}\n\n/**\n * Check if we're in a monorepo (lockfile is in a parent directory)\n */\nexport function isMonorepo(cwd: string = process.cwd()): boolean {\n\tconst lockfilePath = findLockfilePath(cwd);\n\tif (!lockfilePath) {\n\t\treturn false;\n\t}\n\n\t// Check if lockfile is in a parent directory (not in cwd)\n\tconst lockfileDir = dirname(lockfilePath);\n\treturn lockfileDir !== cwd;\n}\n\n/**\n * Check if turbo.json exists (walks up directory tree)\n */\nexport function hasTurboConfig(cwd: string = process.cwd()): boolean {\n\tlet dir = cwd;\n\tconst root = parse(dir).root;\n\n\twhile (dir !== root) {\n\t\tif (existsSync(join(dir, 'turbo.json'))) {\n\t\t\treturn true;\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\n\treturn existsSync(join(root, 'turbo.json'));\n}\n\n/**\n * Get install command for turbo builds (without frozen lockfile)\n * Turbo prune creates a subset that may not perfectly match the lockfile\n */\nfunction getTurboInstallCmd(pm: PackageManager): string {\n\tconst commands: Record<PackageManager, string> = {\n\t\tpnpm: 'pnpm install',\n\t\tnpm: 'npm install',\n\t\tyarn: 'yarn install',\n\t\tbun: 'bun install',\n\t};\n\treturn commands[pm];\n}\n\n/**\n * Get package manager specific commands and paths\n */\nfunction getPmConfig(pm: PackageManager) {\n\tconst configs = {\n\t\tpnpm: {\n\t\t\tinstall: 'corepack enable && corepack prepare pnpm@latest --activate',\n\t\t\tlockfile: 'pnpm-lock.yaml',\n\t\t\tfetch: 'pnpm fetch',\n\t\t\tinstallCmd: 'pnpm install --frozen-lockfile --offline',\n\t\t\tcacheTarget: '/root/.local/share/pnpm/store',\n\t\t\tcacheId: 'pnpm',\n\t\t\trun: 'pnpm',\n\t\t\texec: 'pnpm exec',\n\t\t\tdlx: 'pnpm dlx',\n\t\t\taddGlobal: 'pnpm add -g',\n\t\t},\n\t\tnpm: {\n\t\t\tinstall: '', // npm comes with node\n\t\t\tlockfile: 'package-lock.json',\n\t\t\tfetch: '', // npm doesn't have fetch\n\t\t\tinstallCmd: 'npm ci',\n\t\t\tcacheTarget: '/root/.npm',\n\t\t\tcacheId: 'npm',\n\t\t\trun: 'npm run',\n\t\t\texec: 'npx',\n\t\t\tdlx: 'npx',\n\t\t\taddGlobal: 'npm install -g',\n\t\t},\n\t\tyarn: {\n\t\t\tinstall: 'corepack enable && corepack prepare yarn@stable --activate',\n\t\t\tlockfile: 'yarn.lock',\n\t\t\tfetch: '', // yarn doesn't have fetch\n\t\t\tinstallCmd: 'yarn install --frozen-lockfile',\n\t\t\tcacheTarget: '/root/.yarn/cache',\n\t\t\tcacheId: 'yarn',\n\t\t\trun: 'yarn',\n\t\t\texec: 'yarn exec',\n\t\t\tdlx: 'yarn dlx',\n\t\t\taddGlobal: 'yarn global add',\n\t\t},\n\t\tbun: {\n\t\t\tinstall: 'npm install -g bun',\n\t\t\tlockfile: 'bun.lockb',\n\t\t\tfetch: '', // bun doesn't have fetch\n\t\t\tinstallCmd: 'bun install --frozen-lockfile',\n\t\t\tcacheTarget: '/root/.bun/install/cache',\n\t\t\tcacheId: 'bun',\n\t\t\trun: 'bun run',\n\t\t\texec: 'bunx',\n\t\t\tdlx: 'bunx',\n\t\t\taddGlobal: 'bun add -g',\n\t\t},\n\t};\n\treturn configs[pm];\n}\n\n/**\n * Generate a multi-stage Dockerfile for building from source\n * Optimized for build speed with:\n * - BuildKit cache mounts for package manager store\n * - pnpm fetch for better layer caching (when using pnpm)\n * - Optional turbo prune for monorepos\n */\nexport function generateMultiStageDockerfile(\n\toptions: MultiStageDockerfileOptions,\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\thealthCheckPath,\n\t\tturbo,\n\t\tturboPackage,\n\t\tpackageManager,\n\t} = options;\n\n\tif (turbo) {\n\t\treturn generateTurboDockerfile({\n\t\t\t...options,\n\t\t\tturboPackage: turboPackage ?? 'api',\n\t\t});\n\t}\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install\n\t\t? `\\n# Install ${packageManager}\\nRUN ${pm.install}\\n`\n\t\t: '';\n\tconst hasFetch = packageManager === 'pnpm';\n\n\t// pnpm has fetch which allows better caching\n\tconst depsStage = hasFetch\n\t\t? `# Copy lockfile first for better caching\nCOPY ${pm.lockfile} ./\n\n# Fetch dependencies (downloads to virtual store, cached separately)\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${pm.fetch}\n\n# Copy package.json after fetch\nCOPY package.json ./\n\n# Install from cache (fast - no network needed)\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${pm.installCmd}`\n\t\t: `# Copy package files\nCOPY package.json ${pm.lockfile} ./\n\n# Install dependencies with cache\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${pm.installCmd}`;\n\n\treturn `# syntax=docker/dockerfile:1\n# Stage 1: Dependencies\nFROM ${baseImage} AS deps\n\nWORKDIR /app\n${installPm}\n${depsStage}\n\n# Stage 2: Build\nFROM deps AS builder\n\nWORKDIR /app\n\n# Copy source (deps already installed)\nCOPY . .\n\n# Build production server using gkm\nRUN ${pm.exec} gkm build --provider server --production\n\n# Stage 3: Production\nFROM ${baseImage} AS runner\n\nWORKDIR /app\n\n# Install tini for proper signal handling as PID 1\nRUN apk add --no-cache tini\n\n# Create non-root user\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 hono\n\n# Copy bundled server\nCOPY --from=builder --chown=hono:nodejs /app/.gkm/server/dist/server.mjs ./\n\n# Environment\nENV NODE_ENV=production\nENV PORT=${port}\n\n# Health check\nHEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \\\\\n CMD wget -qO- http://localhost:${port}${healthCheckPath} > /dev/null 2>&1 || exit 1\n\n# Switch to non-root user\nUSER hono\n\nEXPOSE ${port}\n\n# Use tini as entrypoint to handle PID 1 responsibilities\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"server.mjs\"]\n`;\n}\n\n/**\n * Generate a Dockerfile optimized for Turbo monorepos\n * Uses turbo prune to create minimal Docker context\n */\nfunction generateTurboDockerfile(options: MultiStageDockerfileOptions): string {\n\tconst { baseImage, port, healthCheckPath, turboPackage, packageManager } =\n\t\toptions;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\n\t// For turbo builds, we can't use --frozen-lockfile because turbo prune\n\t// creates a subset that may not perfectly match. Use relaxed install.\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\n\t// Use pnpm dlx for pnpm (avoids global bin dir issues in Docker)\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\n\treturn `# syntax=docker/dockerfile:1\n# Stage 1: Prune monorepo\nFROM ${baseImage} AS pruner\n\nWORKDIR /app\n\n${installPm}\n\nCOPY . .\n\n# Prune to only include necessary packages\nRUN ${turboCmd} prune ${turboPackage} --docker\n\n# Stage 2: Install dependencies\nFROM ${baseImage} AS deps\n\nWORKDIR /app\n\n${installPm}\n\n# Copy pruned lockfile and package.jsons\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\n\n# Install dependencies (no frozen-lockfile since turbo prune creates a subset)\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\n# Stage 3: Build\nFROM deps AS builder\n\nWORKDIR /app\n\n# Copy pruned source\nCOPY --from=pruner /app/out/full/ ./\n\n# Build production server using gkm\nRUN ${pm.exec} gkm build --provider server --production\n\n# Stage 4: Production\nFROM ${baseImage} AS runner\n\nWORKDIR /app\n\nRUN apk add --no-cache tini\n\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 hono\n\nCOPY --from=builder --chown=hono:nodejs /app/.gkm/server/dist/server.mjs ./\n\nENV NODE_ENV=production\nENV PORT=${port}\n\nHEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \\\\\n CMD wget -qO- http://localhost:${port}${healthCheckPath} > /dev/null 2>&1 || exit 1\n\nUSER hono\n\nEXPOSE ${port}\n\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"server.mjs\"]\n`;\n}\n\n/**\n * Generate a slim Dockerfile for pre-built bundles\n */\nexport function generateSlimDockerfile(options: DockerTemplateOptions): string {\n\tconst { baseImage, port, healthCheckPath } = options;\n\n\treturn `# Slim Dockerfile for pre-built production bundle\nFROM ${baseImage}\n\nWORKDIR /app\n\n# Install tini for proper signal handling as PID 1\n# Handles SIGTERM propagation and zombie process reaping\nRUN apk add --no-cache tini\n\n# Create non-root user\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 hono\n\n# Copy pre-built bundle\nCOPY .gkm/server/dist/server.mjs ./\n\n# Environment\nENV NODE_ENV=production\nENV PORT=${port}\n\n# Health check\nHEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \\\\\n CMD wget -qO- http://localhost:${port}${healthCheckPath} > /dev/null 2>&1 || exit 1\n\n# Switch to non-root user\nUSER hono\n\nEXPOSE ${port}\n\n# Use tini as entrypoint to handle PID 1 responsibilities\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"server.mjs\"]\n`;\n}\n\n/**\n * Generate .dockerignore file\n */\nexport function generateDockerignore(): string {\n\treturn `# Dependencies\nnode_modules\n.pnpm-store\n\n# Build output (except what we need)\n.gkm/aws*\n.gkm/server/*.ts\n!.gkm/server/dist\n\n# IDE and editor\n.idea\n.vscode\n*.swp\n*.swo\n\n# Git\n.git\n.gitignore\n\n# Logs\n*.log\nnpm-debug.log*\npnpm-debug.log*\n\n# Test files\n**/*.test.ts\n**/*.spec.ts\n**/__tests__\ncoverage\n\n# Documentation\ndocs\n*.md\n!README.md\n\n# Environment files (handle secrets separately)\n.env\n.env.*\n!.env.example\n\n# Docker files (don't copy recursively)\nDockerfile*\ndocker-compose*\n.dockerignore\n`;\n}\n\n/**\n * Generate docker-entrypoint.sh for custom startup logic\n */\nexport function generateDockerEntrypoint(): string {\n\treturn `#!/bin/sh\nset -e\n\n# Run any custom startup scripts here\n# Example: wait for database\n# until nc -z $DB_HOST $DB_PORT; do\n# echo \"Waiting for database...\"\n# sleep 1\n# done\n\n# Execute the main command\nexec \"$@\"\n`;\n}\n\n/**\n * Resolve Docker configuration from GkmConfig with defaults\n */\nexport function resolveDockerConfig(\n\tconfig: GkmConfig,\n): Required<Omit<DockerConfig, 'compose'>> & Pick<DockerConfig, 'compose'> {\n\tconst docker = config.docker ?? {};\n\n\t// Try to get image name from package.json name\n\tlet defaultImageName = 'api';\n\ttry {\n\t\t// eslint-disable-next-line @typescript-eslint/no-require-imports\n\t\tconst pkg = require(`${process.cwd()}/package.json`);\n\t\tif (pkg.name) {\n\t\t\t// Remove scope and use just the package name\n\t\t\tdefaultImageName = pkg.name.replace(/^@[^/]+\\//, '');\n\t\t}\n\t} catch {\n\t\t// Ignore if package.json doesn't exist\n\t}\n\n\treturn {\n\t\tregistry: docker.registry ?? '',\n\t\timageName: docker.imageName ?? defaultImageName,\n\t\tbaseImage: docker.baseImage ?? 'node:22-alpine',\n\t\tport: docker.port ?? 3000,\n\t\tcompose: docker.compose,\n\t};\n}\n\n/**\n * Generate a Dockerfile for Next.js frontend apps using standalone output.\n * Uses turbo prune for monorepo optimization.\n * @internal Exported for testing\n */\nexport function generateNextjsDockerfile(\n\toptions: FrontendDockerfileOptions,\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\tappPath,\n\t\tturboPackage,\n\t\tpackageManager,\n\t\tpublicUrlArgs = ['NEXT_PUBLIC_API_URL', 'NEXT_PUBLIC_AUTH_URL'],\n\t} = options;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\n\t// For turbo builds, we can't use --frozen-lockfile because turbo prune\n\t// creates a subset that may not perfectly match. Use relaxed install.\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\n\t// Use pnpm dlx for pnpm (avoids global bin dir issues in Docker)\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\n\t// Generate ARG and ENV declarations for public URLs\n\tconst publicUrlArgDeclarations = publicUrlArgs\n\t\t.map((arg) => `ARG ${arg}=\"\"`)\n\t\t.join('\\n');\n\tconst publicUrlEnvDeclarations = publicUrlArgs\n\t\t.map((arg) => `ENV ${arg}=$${arg}`)\n\t\t.join('\\n');\n\n\treturn `# syntax=docker/dockerfile:1\n# Next.js standalone Dockerfile with turbo prune optimization\n\n# Stage 1: Prune monorepo\nFROM ${baseImage} AS pruner\n\nWORKDIR /app\n\n${installPm}\n\nCOPY . .\n\n# Prune to only include necessary packages\nRUN ${turboCmd} prune ${turboPackage} --docker\n\n# Stage 2: Install dependencies\nFROM ${baseImage} AS deps\n\nWORKDIR /app\n\n${installPm}\n\n# Copy pruned lockfile and package.jsons\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\n\n# Install dependencies\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\n# Stage 3: Build\nFROM deps AS builder\n\nWORKDIR /app\n\n# Build-time args for public API URLs (populated by gkm deploy)\n# These get baked into the Next.js build as public environment variables\n${publicUrlArgDeclarations}\n\n# Convert ARGs to ENVs for Next.js build\n${publicUrlEnvDeclarations}\n\n# Copy pruned source\nCOPY --from=pruner /app/out/full/ ./\n\n# Copy workspace root configs for turbo builds (turbo prune doesn't include root configs)\n# Using wildcard to make it optional for single-app projects\nCOPY --from=pruner /app/tsconfig.* ./\n\n# Ensure public directory exists (may be empty for scaffolded projects)\nRUN mkdir -p ${appPath}/public\n\n# Set Next.js to produce standalone output\nENV NEXT_TELEMETRY_DISABLED=1\n\n# Build the application\nRUN ${turboCmd} run build --filter=${turboPackage}\n\n# Stage 4: Production\nFROM ${baseImage} AS runner\n\nWORKDIR /app\n\n# Install tini for proper signal handling\nRUN apk add --no-cache tini\n\n# Create non-root user\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 nextjs\n\n# Set environment\nENV NODE_ENV=production\nENV NEXT_TELEMETRY_DISABLED=1\nENV PORT=${port}\nENV HOSTNAME=\"0.0.0.0\"\n\n# Copy static files and standalone output\nCOPY --from=builder --chown=nextjs:nodejs /app/${appPath}/.next/standalone ./\nCOPY --from=builder --chown=nextjs:nodejs /app/${appPath}/.next/static ./${appPath}/.next/static\nCOPY --from=builder --chown=nextjs:nodejs /app/${appPath}/public ./${appPath}/public\n\nUSER nextjs\n\nEXPOSE ${port}\n\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"${appPath}/server.js\"]\n`;\n}\n\n/**\n * Generate a Dockerfile for backend apps in a workspace.\n * Uses turbo prune for monorepo optimization.\n * @internal Exported for testing\n */\nexport function generateBackendDockerfile(\n\toptions: FrontendDockerfileOptions & { healthCheckPath?: string },\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\tappPath,\n\t\tturboPackage,\n\t\tpackageManager,\n\t\thealthCheckPath = '/health',\n\t} = options;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\n\treturn `# syntax=docker/dockerfile:1\n# Backend Dockerfile with turbo prune optimization\n\n# Stage 1: Prune monorepo\nFROM ${baseImage} AS pruner\n\nWORKDIR /app\n\n${installPm}\n\nCOPY . .\n\n# Prune to only include necessary packages\nRUN ${turboCmd} prune ${turboPackage} --docker\n\n# Stage 2: Install dependencies\nFROM ${baseImage} AS deps\n\nWORKDIR /app\n\n${installPm}\n\n# Copy pruned lockfile and package.jsons\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\n\n# Install dependencies\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\n# Stage 3: Build\nFROM deps AS builder\n\nWORKDIR /app\n\n# Build-time args for encrypted secrets\nARG GKM_ENCRYPTED_CREDENTIALS=\"\"\nARG GKM_CREDENTIALS_IV=\"\"\n\n# Copy pruned source\nCOPY --from=pruner /app/out/full/ ./\n\n# Copy workspace root configs for turbo builds (turbo prune doesn't include root configs)\n# Using wildcard to make it optional for single-app projects\nCOPY --from=pruner /app/gkm.config.* ./\nCOPY --from=pruner /app/tsconfig.* ./\n\n# Write encrypted credentials for gkm build to embed\nRUN if [ -n \"$GKM_ENCRYPTED_CREDENTIALS\" ]; then \\\n mkdir -p ${appPath}/.gkm && \\\n echo \"$GKM_ENCRYPTED_CREDENTIALS\" > ${appPath}/.gkm/credentials.enc && \\\n echo \"$GKM_CREDENTIALS_IV\" > ${appPath}/.gkm/credentials.iv; \\\n fi\n\n# Build production server using gkm\nRUN cd ${appPath} && ${pm.exec} gkm build --provider server --production\n\n# Stage 4: Production\nFROM ${baseImage} AS runner\n\nWORKDIR /app\n\nRUN apk add --no-cache tini\n\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 hono\n\n# Copy bundled server\nCOPY --from=builder --chown=hono:nodejs /app/${appPath}/.gkm/server/dist/server.mjs ./\n\nENV NODE_ENV=production\nENV PORT=${port}\n\nHEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \\\\\n CMD wget -qO- http://localhost:${port}${healthCheckPath} > /dev/null 2>&1 || exit 1\n\nUSER hono\n\nEXPOSE ${port}\n\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"server.mjs\"]\n`;\n}\n\n/**\n * Options for entry-based Dockerfile generation.\n */\nexport interface EntryDockerfileOptions {\n\timageName: string;\n\tbaseImage: string;\n\tport: number;\n\t/** App path relative to workspace root */\n\tappPath: string;\n\t/** Entry file path relative to app path (e.g., './src/index.ts') */\n\tentry: string;\n\t/** Package name for turbo prune */\n\tturboPackage: string;\n\t/** Detected package manager */\n\tpackageManager: PackageManager;\n\t/** Health check path (default: '/health') */\n\thealthCheckPath?: string;\n}\n\n/**\n * Generate a Dockerfile for apps with a custom entry point.\n * Uses esbuild to bundle the entry point into dist/index.mjs with all dependencies.\n * This is used for apps that don't use gkm routes (e.g., Better Auth servers).\n * @internal Exported for testing\n */\nexport function generateEntryDockerfile(\n\toptions: EntryDockerfileOptions,\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\tappPath,\n\t\tentry,\n\t\tturboPackage,\n\t\tpackageManager,\n\t\thealthCheckPath = '/health',\n\t} = options;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\n\treturn `# syntax=docker/dockerfile:1\n# Entry-based Dockerfile with turbo prune + tsdown bundling\n\n# Stage 1: Prune monorepo\nFROM ${baseImage} AS pruner\n\nWORKDIR /app\n\n${installPm}\n\nCOPY . .\n\n# Prune to only include necessary packages\nRUN ${turboCmd} prune ${turboPackage} --docker\n\n# Stage 2: Install dependencies\nFROM ${baseImage} AS deps\n\nWORKDIR /app\n\n${installPm}\n\n# Copy pruned lockfile and package.jsons\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\n\n# Install dependencies\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\n# Stage 3: Build with tsdown\nFROM deps AS builder\n\nWORKDIR /app\n\n# Build-time args for encrypted secrets\nARG GKM_ENCRYPTED_CREDENTIALS=\"\"\nARG GKM_CREDENTIALS_IV=\"\"\n\n# Copy pruned source\nCOPY --from=pruner /app/out/full/ ./\n\n# Copy workspace root configs for turbo builds (turbo prune doesn't include root configs)\n# Using wildcard to make it optional for single-app projects\nCOPY --from=pruner /app/tsconfig.* ./\n\n# Write encrypted credentials for tsdown to embed via define\nRUN if [ -n \"$GKM_ENCRYPTED_CREDENTIALS\" ]; then \\\n mkdir -p ${appPath}/.gkm && \\\n echo \"$GKM_ENCRYPTED_CREDENTIALS\" > ${appPath}/.gkm/credentials.enc && \\\n echo \"$GKM_CREDENTIALS_IV\" > ${appPath}/.gkm/credentials.iv; \\\n fi\n\n# Bundle entry point with esbuild (outputs to dist/index.mjs)\n# Creates a fully standalone bundle with all dependencies included\n# Use define to embed credentials if present\nRUN cd ${appPath} && \\\n if [ -f .gkm/credentials.enc ]; then \\\n CREDS=$(cat .gkm/credentials.enc) && \\\n IV=$(cat .gkm/credentials.iv) && \\\n npx esbuild ${entry} --bundle --platform=node --target=node22 --format=esm \\\n --outfile=dist/index.mjs --packages=bundle \\\n --banner:js='import { createRequire } from \"module\"; const require = createRequire(import.meta.url);' \\\n --define:__GKM_ENCRYPTED_CREDENTIALS__=\"'\\\\\"$CREDS\\\\\"'\" \\\n --define:__GKM_CREDENTIALS_IV__=\"'\\\\\"$IV\\\\\"'\"; \\\n else \\\n npx esbuild ${entry} --bundle --platform=node --target=node22 --format=esm \\\n --outfile=dist/index.mjs --packages=bundle \\\n --banner:js='import { createRequire } from \"module\"; const require = createRequire(import.meta.url);'; \\\n fi\n\n# Stage 4: Production\nFROM ${baseImage} AS runner\n\nWORKDIR /app\n\nRUN apk add --no-cache tini\n\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 app\n\n# Copy bundled output only (no node_modules needed - fully bundled)\nCOPY --from=builder --chown=app:nodejs /app/${appPath}/dist/index.mjs ./\n\nENV NODE_ENV=production\nENV PORT=${port}\n\nHEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \\\\\n CMD wget -qO- http://localhost:${port}${healthCheckPath} > /dev/null 2>&1 || exit 1\n\nUSER app\n\nEXPOSE ${port}\n\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"index.mjs\"]\n`;\n}\n\n/**\n * Generate a Dockerfile for Node-based SSR web frameworks (TanStack Start,\n * Remix). Builds with turbo, then runs the framework's production start\n * script via `npm start`.\n *\n * Public URL build args are declared so framework bundlers (Vite, Vinxi)\n * can inline them at build time. Per-framework prefix is the caller's job.\n * @internal Exported for testing\n */\nexport function generateNodeWebDockerfile(\n\toptions: FrontendDockerfileOptions,\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\tappPath,\n\t\tturboPackage,\n\t\tpackageManager,\n\t\tpublicUrlArgs = [],\n\t} = options;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\tconst startCmd =\n\t\tpackageManager === 'pnpm'\n\t\t\t? 'pnpm start'\n\t\t\t: `${pm.lockfile.startsWith('package-lock') ? 'npm' : packageManager} start`;\n\n\tconst argDecls = publicUrlArgs.map((a) => `ARG ${a}=\"\"`).join('\\n');\n\tconst envDecls = publicUrlArgs.map((a) => `ENV ${a}=$${a}`).join('\\n');\n\n\treturn `# syntax=docker/dockerfile:1\n# Node SSR web Dockerfile (TanStack Start / Remix) with turbo prune\n\nFROM ${baseImage} AS pruner\nWORKDIR /app\n${installPm}\nCOPY . .\nRUN ${turboCmd} prune ${turboPackage} --docker\n\nFROM ${baseImage} AS deps\nWORKDIR /app\n${installPm}\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\nFROM deps AS builder\nWORKDIR /app\n${argDecls}\n${envDecls}\nCOPY --from=pruner /app/out/full/ ./\nCOPY --from=pruner /app/tsconfig.* ./\nRUN ${turboCmd} run build --filter=${turboPackage}\n\nFROM ${baseImage} AS runner\nWORKDIR /app\nRUN apk add --no-cache tini\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 app\n\n# Copy the whole built workspace — frameworks differ on output paths\nCOPY --from=builder --chown=app:nodejs /app/ ./\n\nENV NODE_ENV=production\nENV PORT=${port}\nENV HOSTNAME=\"0.0.0.0\"\n\nUSER app\nEXPOSE ${port}\n\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"sh\", \"-c\", \"cd ${appPath} && ${startCmd}\"]\n`;\n}\n\n/**\n * Generate a Dockerfile for Vite SPA apps. Builds the static bundle with\n * turbo, then serves it via nginx.\n * @internal Exported for testing\n */\nexport function generateViteStaticDockerfile(\n\toptions: FrontendDockerfileOptions,\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\tappPath,\n\t\tturboPackage,\n\t\tpackageManager,\n\t\tpublicUrlArgs = [],\n\t} = options;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\n\tconst argDecls = publicUrlArgs.map((a) => `ARG ${a}=\"\"`).join('\\n');\n\tconst envDecls = publicUrlArgs.map((a) => `ENV ${a}=$${a}`).join('\\n');\n\n\treturn `# syntax=docker/dockerfile:1\n# Vite SPA Dockerfile — builds static assets, serves via nginx\n\nFROM ${baseImage} AS pruner\nWORKDIR /app\n${installPm}\nCOPY . .\nRUN ${turboCmd} prune ${turboPackage} --docker\n\nFROM ${baseImage} AS deps\nWORKDIR /app\n${installPm}\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\nFROM deps AS builder\nWORKDIR /app\n${argDecls}\n${envDecls}\nCOPY --from=pruner /app/out/full/ ./\nCOPY --from=pruner /app/tsconfig.* ./\nRUN ${turboCmd} run build --filter=${turboPackage}\n\nFROM nginx:alpine AS runner\nCOPY --from=builder /app/${appPath}/dist /usr/share/nginx/html\nRUN printf 'server {\\\\n listen ${port};\\\\n root /usr/share/nginx/html;\\\\n index index.html;\\\\n location / { try_files $uri $uri/ /index.html; }\\\\n}\\\\n' \\\\\n > /etc/nginx/conf.d/default.conf\nEXPOSE ${port}\nCMD [\"nginx\", \"-g\", \"daemon off;\"]\n`;\n}\n","import { execSync } from 'node:child_process';\nimport { copyFileSync, existsSync, readFileSync, unlinkSync } from 'node:fs';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { basename, join } from 'node:path';\nimport { loadConfig, loadWorkspaceConfig } from '../config';\nimport { getPublicUrlArgNames } from '../deploy/domain.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport {\n\tgenerateDockerCompose,\n\tgenerateMinimalDockerCompose,\n\tgenerateWorkspaceCompose,\n} from './compose';\nimport {\n\tdetectPackageManager,\n\tfindLockfilePath,\n\tgenerateBackendDockerfile,\n\tgenerateDockerEntrypoint,\n\tgenerateDockerignore,\n\tgenerateEntryDockerfile,\n\tgenerateMultiStageDockerfile,\n\tgenerateNextjsDockerfile,\n\tgenerateNodeWebDockerfile,\n\tgenerateSlimDockerfile,\n\tgenerateViteStaticDockerfile,\n\thasTurboConfig,\n\tisMonorepo,\n\tresolveDockerConfig,\n} from './templates';\n\nexport {\n\tdetectPackageManager,\n\tfindLockfilePath,\n\thasTurboConfig,\n\tisMonorepo,\n} from './templates';\n\nconst logger = console;\n\nexport interface DockerOptions {\n\t/** Build Docker image after generating files */\n\tbuild?: boolean;\n\t/** Push image to registry after building */\n\tpush?: boolean;\n\t/** Image tag (default: 'latest') */\n\ttag?: string;\n\t/** Container registry URL */\n\tregistry?: string;\n\t/** Use slim Dockerfile (requires pre-built bundle from `gkm build --production`) */\n\tslim?: boolean;\n\t/** Enable turbo prune for monorepo optimization */\n\tturbo?: boolean;\n\t/** Package name for turbo prune (defaults to package.json name) */\n\tturboPackage?: string;\n}\n\nexport interface DockerGeneratedFiles {\n\tdockerfile: string;\n\tdockerCompose: string;\n\tdockerignore: string;\n\tentrypoint: string;\n}\n\n/**\n * Docker command implementation\n * Generates Dockerfile, docker-compose.yml, and related files\n *\n * Default: Multi-stage Dockerfile that builds from source inside Docker\n * --slim: Slim Dockerfile that copies pre-built bundle (requires prior build)\n */\nexport async function dockerCommand(\n\toptions: DockerOptions,\n): Promise<DockerGeneratedFiles | WorkspaceDockerResult> {\n\t// Load config with workspace detection\n\tconst loadedConfig = await loadWorkspaceConfig();\n\n\t// Route to workspace docker mode for multi-app workspaces\n\tif (loadedConfig.type === 'workspace') {\n\t\tlogger.log('📦 Detected workspace configuration');\n\t\treturn workspaceDockerCommand(loadedConfig.workspace, options);\n\t}\n\n\t// Single-app mode - use existing logic\n\tconst config = await loadConfig();\n\tconst dockerConfig = resolveDockerConfig(config);\n\n\t// Get health check path from production config\n\tconst serverConfig =\n\t\ttypeof config.providers?.server === 'object'\n\t\t\t? config.providers.server\n\t\t\t: undefined;\n\tconst healthCheckPath = serverConfig?.production?.healthCheck ?? '/health';\n\n\t// Determine Dockerfile type\n\t// Default: Multi-stage (builds inside Docker for reproducibility)\n\t// --slim: Requires pre-built bundle\n\tconst useSlim = options.slim === true;\n\n\tif (useSlim) {\n\t\t// Verify pre-built bundle exists for slim mode\n\t\tconst distDir = join(process.cwd(), '.gkm', 'server', 'dist');\n\t\tconst hasBuild = existsSync(join(distDir, 'server.mjs'));\n\n\t\tif (!hasBuild) {\n\t\t\tthrow new Error(\n\t\t\t\t'Slim Dockerfile requires a pre-built bundle. Run `gkm build --provider server --production` first, or omit --slim to use multi-stage build.',\n\t\t\t);\n\t\t}\n\t}\n\n\t// Generate Docker files\n\tconst dockerDir = join(process.cwd(), '.gkm', 'docker');\n\tawait mkdir(dockerDir, { recursive: true });\n\n\t// Detect package manager from lockfiles\n\tconst packageManager = detectPackageManager();\n\tconst inMonorepo = isMonorepo();\n\tconst hasTurbo = hasTurboConfig();\n\n\t// Auto-enable turbo for monorepos with turbo.json\n\tlet useTurbo = options.turbo ?? false;\n\tif (inMonorepo && !useSlim) {\n\t\tif (hasTurbo) {\n\t\t\tuseTurbo = true;\n\t\t\tlogger.log(' Detected monorepo with turbo.json - using turbo prune');\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t'Monorepo detected but turbo.json not found.\\n\\n' +\n\t\t\t\t\t'Docker builds in monorepos require Turborepo for proper dependency isolation.\\n\\n' +\n\t\t\t\t\t'To fix this:\\n' +\n\t\t\t\t\t' 1. Install turbo: pnpm add -Dw turbo\\n' +\n\t\t\t\t\t' 2. Create turbo.json in your monorepo root\\n' +\n\t\t\t\t\t' 3. Run this command again\\n\\n' +\n\t\t\t\t\t'See: https://turbo.build/repo/docs/guides/tools/docker',\n\t\t\t);\n\t\t}\n\t}\n\n\t// Get the actual package name from package.json for turbo prune\n\tlet turboPackage = options.turboPackage ?? dockerConfig.imageName;\n\tif (useTurbo && !options.turboPackage) {\n\t\ttry {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-require-imports\n\t\t\tconst pkg = require(`${process.cwd()}/package.json`);\n\t\t\tif (pkg.name) {\n\t\t\t\tturboPackage = pkg.name;\n\t\t\t\tlogger.log(` Turbo package: ${turboPackage}`);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Fall back to imageName\n\t\t}\n\t}\n\n\tconst templateOptions = {\n\t\timageName: dockerConfig.imageName,\n\t\tbaseImage: dockerConfig.baseImage,\n\t\tport: dockerConfig.port,\n\t\thealthCheckPath,\n\t\tprebuilt: useSlim,\n\t\tturbo: useTurbo,\n\t\tturboPackage,\n\t\tpackageManager,\n\t};\n\n\t// Generate Dockerfile\n\tconst dockerfile = useSlim\n\t\t? generateSlimDockerfile(templateOptions)\n\t\t: generateMultiStageDockerfile(templateOptions);\n\n\tconst dockerMode = useSlim ? 'slim' : useTurbo ? 'turbo' : 'multi-stage';\n\n\tconst dockerfilePath = join(dockerDir, 'Dockerfile');\n\tawait writeFile(dockerfilePath, dockerfile);\n\tlogger.log(\n\t\t`Generated: .gkm/docker/Dockerfile (${dockerMode}, ${packageManager})`,\n\t);\n\n\t// Generate docker-compose.yml\n\tconst composeOptions = {\n\t\timageName: dockerConfig.imageName,\n\t\tregistry: options.registry ?? dockerConfig.registry,\n\t\tport: dockerConfig.port,\n\t\thealthCheckPath,\n\t\tservices: dockerConfig.compose?.services ?? {},\n\t};\n\n\t// Check if there are any services configured\n\tconst hasServices = Array.isArray(composeOptions.services)\n\t\t? composeOptions.services.length > 0\n\t\t: Object.keys(composeOptions.services).length > 0;\n\n\tconst dockerCompose = hasServices\n\t\t? generateDockerCompose(composeOptions)\n\t\t: generateMinimalDockerCompose(composeOptions);\n\n\tconst composePath = join(dockerDir, 'docker-compose.yml');\n\tawait writeFile(composePath, dockerCompose);\n\tlogger.log('Generated: .gkm/docker/docker-compose.yml');\n\n\t// Generate .dockerignore in project root (Docker looks for it there)\n\tconst dockerignore = generateDockerignore();\n\tconst dockerignorePath = join(process.cwd(), '.dockerignore');\n\tawait writeFile(dockerignorePath, dockerignore);\n\tlogger.log('Generated: .dockerignore (project root)');\n\n\t// Generate docker-entrypoint.sh\n\tconst entrypoint = generateDockerEntrypoint();\n\tconst entrypointPath = join(dockerDir, 'docker-entrypoint.sh');\n\tawait writeFile(entrypointPath, entrypoint);\n\tlogger.log('Generated: .gkm/docker/docker-entrypoint.sh');\n\n\tconst result: DockerGeneratedFiles = {\n\t\tdockerfile: dockerfilePath,\n\t\tdockerCompose: composePath,\n\t\tdockerignore: dockerignorePath,\n\t\tentrypoint: entrypointPath,\n\t};\n\n\t// Build Docker image if requested\n\tif (options.build) {\n\t\tawait buildDockerImage(dockerConfig.imageName, options);\n\t}\n\n\t// Push Docker image if requested\n\tif (options.push) {\n\t\tawait pushDockerImage(dockerConfig.imageName, options);\n\t}\n\n\treturn result;\n}\n\n/**\n * Ensure lockfile exists in the build context\n * For monorepos, copies from workspace root if needed\n * Returns cleanup function if file was copied\n */\nfunction ensureLockfile(cwd: string): (() => void) | null {\n\tconst lockfilePath = findLockfilePath(cwd);\n\n\tif (!lockfilePath) {\n\t\tlogger.warn(\n\t\t\t'\\n⚠️ No lockfile found. Docker build may fail or use stale dependencies.',\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst lockfileName = basename(lockfilePath);\n\tconst localLockfile = join(cwd, lockfileName);\n\n\t// If lockfile exists locally (same directory), nothing to do\n\tif (lockfilePath === localLockfile) {\n\t\treturn null;\n\t}\n\n\tlogger.log(` Copying ${lockfileName} from monorepo root...`);\n\tcopyFileSync(lockfilePath, localLockfile);\n\n\t// Return cleanup function\n\treturn () => {\n\t\ttry {\n\t\t\tunlinkSync(localLockfile);\n\t\t} catch {\n\t\t\t// Ignore cleanup errors\n\t\t}\n\t};\n}\n\n/**\n * Build Docker image\n * Uses BuildKit for cache mount support\n */\nasync function buildDockerImage(\n\timageName: string,\n\toptions: DockerOptions,\n): Promise<void> {\n\tconst tag = options.tag ?? 'latest';\n\tconst registry = options.registry;\n\n\tconst fullImageName = registry\n\t\t? `${registry}/${imageName}:${tag}`\n\t\t: `${imageName}:${tag}`;\n\n\tlogger.log(`\\n🐳 Building Docker image: ${fullImageName}`);\n\n\tconst cwd = process.cwd();\n\n\t// Ensure lockfile exists (copy from monorepo root if needed)\n\tconst cleanup = ensureLockfile(cwd);\n\n\ttry {\n\t\t// Use BuildKit for cache mount support (required for --mount=type=cache)\n\t\texecSync(\n\t\t\t`DOCKER_BUILDKIT=1 docker build -f .gkm/docker/Dockerfile -t ${fullImageName} .`,\n\t\t\t{\n\t\t\t\tcwd,\n\t\t\t\tstdio: 'inherit',\n\t\t\t\tenv: { ...process.env, DOCKER_BUILDKIT: '1' },\n\t\t\t},\n\t\t);\n\t\tlogger.log(`✅ Docker image built: ${fullImageName}`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to build Docker image: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t} finally {\n\t\t// Clean up copied lockfile\n\t\tcleanup?.();\n\t}\n}\n\n/**\n * Push Docker image to registry\n */\nasync function pushDockerImage(\n\timageName: string,\n\toptions: DockerOptions,\n): Promise<void> {\n\tconst tag = options.tag ?? 'latest';\n\tconst registry = options.registry;\n\n\tif (!registry) {\n\t\tthrow new Error(\n\t\t\t'Registry is required to push Docker image. Use --registry or configure docker.registry in gkm.config.ts',\n\t\t);\n\t}\n\n\tconst fullImageName = `${registry}/${imageName}:${tag}`;\n\n\tlogger.log(`\\n🚀 Pushing Docker image: ${fullImageName}`);\n\n\ttry {\n\t\texecSync(`docker push ${fullImageName}`, {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t});\n\t\tlogger.log(`✅ Docker image pushed: ${fullImageName}`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to push Docker image: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n}\n\n/**\n * Result of generating Docker files for a single app in a workspace.\n */\nexport interface AppDockerResult {\n\tappName: string;\n\ttype: 'backend' | 'web' | 'mobile';\n\tdockerfile: string;\n\timageName: string;\n}\n\n/**\n * Result of workspace docker command.\n */\nexport interface WorkspaceDockerResult {\n\tapps: AppDockerResult[];\n\tdockerCompose: string;\n\tdockerignore: string;\n}\n\n/**\n * Get the package name from package.json in an app directory.\n */\nfunction getAppPackageName(appPath: string): string | undefined {\n\ttry {\n\t\tconst pkgPath = join(appPath, 'package.json');\n\t\tif (!existsSync(pkgPath)) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst content = readFileSync(pkgPath, 'utf-8');\n\t\tconst pkg = JSON.parse(content);\n\t\treturn pkg.name;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Generate Dockerfiles for all apps in a workspace.\n * @internal Exported for testing\n */\nexport async function workspaceDockerCommand(\n\tworkspace: NormalizedWorkspace,\n\toptions: DockerOptions,\n): Promise<WorkspaceDockerResult> {\n\tconst results: AppDockerResult[] = [];\n\tconst apps = Object.entries(workspace.apps);\n\n\tlogger.log(`\\n🐳 Generating Dockerfiles for workspace: ${workspace.name}`);\n\n\t// Create docker output directory\n\tconst dockerDir = join(workspace.root, '.gkm', 'docker');\n\tawait mkdir(dockerDir, { recursive: true });\n\n\t// Detect package manager\n\tconst packageManager = detectPackageManager(workspace.root);\n\tlogger.log(` Package manager: ${packageManager}`);\n\n\t// Generate Dockerfile for each app\n\tfor (const [appName, app] of apps) {\n\t\t// Mobile apps deploy via their own toolchain (e.g. EAS Build for Expo)\n\t\t// — no Docker image is produced.\n\t\tif (app.type === 'mobile') {\n\t\t\tlogger.log(\n\t\t\t\t`\\n 📱 Skipping Docker for ${appName} (mobile app — deploy via framework toolchain)`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst appPath = app.path;\n\t\tconst fullAppPath = join(workspace.root, appPath);\n\n\t\t// Get package name for turbo prune (use package.json name or app name)\n\t\tconst turboPackage = getAppPackageName(fullAppPath) ?? appName;\n\n\t\t// Determine image name\n\t\tconst imageName = appName;\n\n\t\tconst hasEntry = !!app.entry;\n\t\tconst buildType = hasEntry ? 'entry' : app.type;\n\t\tlogger.log(`\\n 📄 Generating Dockerfile for ${appName} (${buildType})`);\n\n\t\tlet dockerfile: string;\n\n\t\tif (app.type === 'web') {\n\t\t\tconst publicUrlArgs = getPublicUrlArgNames(app);\n\t\t\tconst webOpts = {\n\t\t\t\timageName,\n\t\t\t\tbaseImage: 'node:22-alpine',\n\t\t\t\tport: app.port,\n\t\t\t\tappPath,\n\t\t\t\tturboPackage,\n\t\t\t\tpackageManager,\n\t\t\t\tpublicUrlArgs,\n\t\t\t};\n\n\t\t\tswitch (app.framework) {\n\t\t\t\tcase 'vite':\n\t\t\t\t\tdockerfile = generateViteStaticDockerfile(webOpts);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'tanstack-start':\n\t\t\t\tcase 'remix':\n\t\t\t\t\tdockerfile = generateNodeWebDockerfile(webOpts);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t// nextjs (and any unspecified web framework — schema requires\n\t\t\t\t\t// a valid framework, so this is just a default).\n\t\t\t\t\tdockerfile = generateNextjsDockerfile(webOpts);\n\t\t\t}\n\t\t} else if (app.entry) {\n\t\t\t// Backend with custom entry point - use tsdown bundling\n\t\t\tdockerfile = generateEntryDockerfile({\n\t\t\t\timageName,\n\t\t\t\tbaseImage: 'node:22-alpine',\n\t\t\t\tport: app.port,\n\t\t\t\tappPath,\n\t\t\t\tentry: app.entry,\n\t\t\t\tturboPackage,\n\t\t\t\tpackageManager,\n\t\t\t\thealthCheckPath: '/health',\n\t\t\t});\n\t\t} else {\n\t\t\t// Backend with gkm routes - use gkm build\n\t\t\tdockerfile = generateBackendDockerfile({\n\t\t\t\timageName,\n\t\t\t\tbaseImage: 'node:22-alpine',\n\t\t\t\tport: app.port,\n\t\t\t\tappPath,\n\t\t\t\tturboPackage,\n\t\t\t\tpackageManager,\n\t\t\t\thealthCheckPath: '/health',\n\t\t\t});\n\t\t}\n\n\t\t// Write Dockerfile with app-specific name\n\t\tconst dockerfilePath = join(dockerDir, `Dockerfile.${appName}`);\n\t\tawait writeFile(dockerfilePath, dockerfile);\n\t\tlogger.log(` Generated: .gkm/docker/Dockerfile.${appName}`);\n\n\t\tresults.push({\n\t\t\tappName,\n\t\t\ttype: app.type,\n\t\t\tdockerfile: dockerfilePath,\n\t\t\timageName,\n\t\t});\n\t}\n\n\t// Generate shared .dockerignore\n\tconst dockerignore = generateDockerignore();\n\tconst dockerignorePath = join(workspace.root, '.dockerignore');\n\tawait writeFile(dockerignorePath, dockerignore);\n\tlogger.log(`\\n Generated: .dockerignore (workspace root)`);\n\n\t// Generate docker-compose.yml for workspace\n\tconst dockerCompose = generateWorkspaceCompose(workspace, {\n\t\tregistry: options.registry,\n\t});\n\tconst composePath = join(dockerDir, 'docker-compose.yml');\n\tawait writeFile(composePath, dockerCompose);\n\tlogger.log(` Generated: .gkm/docker/docker-compose.yml`);\n\n\t// Summary\n\tlogger.log(\n\t\t`\\n✅ Generated ${results.length} Dockerfile(s) + docker-compose.yml`,\n\t);\n\tlogger.log('\\n📋 Build commands:');\n\tfor (const result of results) {\n\t\tconst icon =\n\t\t\tresult.type === 'backend' ? '⚙️' : result.type === 'mobile' ? '📱' : '🌐';\n\t\tlogger.log(\n\t\t\t` ${icon} docker build -f .gkm/docker/Dockerfile.${result.appName} -t ${result.imageName} .`,\n\t\t);\n\t}\n\tlogger.log('\\n📋 Run all services:');\n\tlogger.log(' docker compose -f .gkm/docker/docker-compose.yml up --build');\n\n\treturn {\n\t\tapps: results,\n\t\tdockerCompose: composePath,\n\t\tdockerignore: dockerignorePath,\n\t};\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { GkmConfig } from '../config';\nimport { dockerCommand, findLockfilePath } from '../docker';\nimport type { DeployResult, DockerDeployConfig } from './types';\n\n/**\n * Get app name from package.json in the current working directory\n * Used for Dokploy app/project naming\n */\nexport function getAppNameFromCwd(): string | undefined {\n\tconst packageJsonPath = join(process.cwd(), 'package.json');\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn undefined;\n\t}\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n\t\tif (pkg.name) {\n\t\t\t// Strip org scope if present (e.g., @myorg/app -> app)\n\t\t\treturn pkg.name.replace(/^@[^/]+\\//, '');\n\t\t}\n\t} catch {\n\t\t// Ignore parse errors\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Get app name from package.json adjacent to the lockfile (project root)\n * Used for Docker image naming\n */\nexport function getAppNameFromPackageJson(): string | undefined {\n\tconst cwd = process.cwd();\n\n\t// Find the lockfile to determine the project root\n\tconst lockfilePath = findLockfilePath(cwd);\n\tif (!lockfilePath) {\n\t\treturn undefined;\n\t}\n\n\t// Use the package.json adjacent to the lockfile\n\tconst projectRoot = dirname(lockfilePath);\n\tconst packageJsonPath = join(projectRoot, 'package.json');\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn undefined;\n\t}\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n\t\tif (pkg.name) {\n\t\t\t// Strip org scope if present (e.g., @myorg/app -> app)\n\t\t\treturn pkg.name.replace(/^@[^/]+\\//, '');\n\t\t}\n\t} catch {\n\t\t// Ignore parse errors\n\t}\n\n\treturn undefined;\n}\n\nconst logger = console;\n\nexport interface DockerDeployOptions {\n\t/** Deployment stage */\n\tstage: string;\n\t/** Image tag */\n\ttag: string;\n\t/** Skip pushing to registry */\n\tskipPush?: boolean;\n\t/** Master key from build */\n\tmasterKey?: string;\n\t/** Docker config from gkm.config */\n\tconfig: DockerDeployConfig;\n\t/**\n\t * Build arguments to pass to docker build.\n\t * Format: ['KEY=value', 'KEY2=value2']\n\t */\n\tbuildArgs?: string[];\n\t/**\n\t * Public URL argument names for frontend Dockerfile generation.\n\t * Used to ensure the Dockerfile declares these as ARG/ENV.\n\t */\n\tpublicUrlArgs?: string[];\n}\n\n/**\n * Get the full image reference\n */\nexport function getImageRef(\n\tregistry: string | undefined,\n\timageName: string,\n\ttag: string,\n): string {\n\tif (registry) {\n\t\treturn `${registry}/${imageName}:${tag}`;\n\t}\n\treturn `${imageName}:${tag}`;\n}\n\n/**\n * Build Docker image\n * @param imageRef - Full image reference (registry/name:tag)\n * @param appName - Name of the app (used for Dockerfile.{appName} in workspaces)\n * @param buildArgs - Build arguments to pass to docker build\n */\nasync function buildImage(\n\timageRef: string,\n\tappName?: string,\n\tbuildArgs?: string[],\n): Promise<void> {\n\tlogger.log(`\\n🔨 Building Docker image: ${imageRef}`);\n\n\tconst cwd = process.cwd();\n\tconst lockfilePath = findLockfilePath(cwd);\n\tconst lockfileDir = lockfilePath ? dirname(lockfilePath) : cwd;\n\tconst inMonorepo = lockfileDir !== cwd;\n\n\t// Generate appropriate Dockerfile\n\tif (appName || inMonorepo) {\n\t\tlogger.log(' Generating Dockerfile for monorepo (turbo prune)...');\n\t} else {\n\t\tlogger.log(' Generating Dockerfile...');\n\t}\n\tawait dockerCommand({});\n\n\t// Determine build context and Dockerfile path\n\t// For workspaces with multiple apps, use per-app Dockerfile (Dockerfile.api, etc.)\n\tconst dockerfileSuffix = appName ? `.${appName}` : '';\n\tconst dockerfilePath = `.gkm/docker/Dockerfile${dockerfileSuffix}`;\n\n\t// Build from workspace/monorepo root when we have a lockfile elsewhere or appName is provided\n\tconst buildCwd = lockfilePath && (inMonorepo || appName) ? lockfileDir : cwd;\n\tif (buildCwd !== cwd) {\n\t\tlogger.log(` Building from workspace root: ${buildCwd}`);\n\t}\n\n\t// Build the build args string\n\tconst buildArgsString =\n\t\tbuildArgs && buildArgs.length > 0\n\t\t\t? buildArgs.map((arg) => `--build-arg \"${arg}\"`).join(' ')\n\t\t\t: '';\n\n\ttry {\n\t\t// Build for linux/amd64 to ensure compatibility with most cloud servers\n\t\tconst cmd = [\n\t\t\t'DOCKER_BUILDKIT=1 docker build',\n\t\t\t'--platform linux/amd64',\n\t\t\t`-f ${dockerfilePath}`,\n\t\t\t`-t ${imageRef}`,\n\t\t\tbuildArgsString,\n\t\t\t'.',\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(' ');\n\n\t\texecSync(cmd, {\n\t\t\tcwd: buildCwd,\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, DOCKER_BUILDKIT: '1' },\n\t\t});\n\t\tlogger.log(`✅ Image built: ${imageRef}`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to build Docker image: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n}\n\n/**\n * Push Docker image to registry\n */\nasync function pushImage(imageRef: string): Promise<void> {\n\tlogger.log(`\\n☁️ Pushing image: ${imageRef}`);\n\n\ttry {\n\t\texecSync(`docker push ${imageRef}`, {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t});\n\t\tlogger.log(`✅ Image pushed: ${imageRef}`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to push Docker image: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n}\n\n/**\n * Deploy using Docker (build and optionally push image)\n */\nexport async function deployDocker(\n\toptions: DockerDeployOptions,\n): Promise<DeployResult> {\n\tconst { stage, tag, skipPush, masterKey, config, buildArgs } = options;\n\n\t// imageName should always be set by resolveDockerConfig\n\tconst imageName = config.imageName!;\n\tconst imageRef = getImageRef(config.registry, imageName, tag);\n\n\t// Build image (pass appName for workspace Dockerfile selection)\n\tawait buildImage(imageRef, config.appName, buildArgs);\n\n\t// Push to registry if not skipped\n\tif (!skipPush) {\n\t\tif (!config.registry) {\n\t\t\tlogger.warn(\n\t\t\t\t'\\n⚠️ No registry configured. Use --skip-push or configure docker.registry in gkm.config.ts',\n\t\t\t);\n\t\t} else {\n\t\t\tawait pushImage(imageRef);\n\t\t}\n\t}\n\n\t// Output deployment info\n\tlogger.log('\\n✅ Docker deployment ready!');\n\tlogger.log(`\\n📋 Deployment details:`);\n\tlogger.log(` Image: ${imageRef}`);\n\tlogger.log(` Stage: ${stage}`);\n\n\tif (masterKey) {\n\t\tlogger.log(`\\n🔐 Deploy with this environment variable:`);\n\t\tlogger.log(` GKM_MASTER_KEY=${masterKey}`);\n\t\tlogger.log('\\n Example docker run:');\n\t\tlogger.log(` docker run -e GKM_MASTER_KEY=${masterKey} ${imageRef}`);\n\t}\n\n\treturn {\n\t\timageRef,\n\t\tmasterKey,\n\t};\n}\n\n/**\n * Resolve Docker deploy config from gkm config\n * - imageName: from config, or cwd package.json, or 'app' (for Docker image)\n * - projectName: from root package.json, or 'app' (for Dokploy project)\n * - appName: from cwd package.json, or projectName (for Dokploy app within project)\n */\nexport function resolveDockerConfig(config: GkmConfig): DockerDeployConfig {\n\t// projectName comes from root package.json (monorepo name)\n\tconst projectName = getAppNameFromPackageJson() ?? 'app';\n\n\t// appName comes from cwd package.json (the app being deployed)\n\tconst appName = getAppNameFromCwd() ?? projectName;\n\n\t// imageName defaults to appName (cwd package.json)\n\tconst imageName = config.docker?.imageName ?? appName;\n\n\treturn {\n\t\tregistry: config.docker?.registry,\n\t\timageName,\n\t\tprojectName,\n\t\tappName,\n\t};\n}\n","import { getDokployRegistryId, getDokployToken } from '../auth';\nimport { DokployApi } from './dokploy-api';\nimport type { DeployResult, DokployDeployConfig } from './types';\n\nconst logger = console;\n\nexport interface DokployDeployOptions {\n\t/** Deployment stage */\n\tstage: string;\n\t/** Image tag */\n\ttag: string;\n\t/** Image reference */\n\timageRef: string;\n\t/** Master key from build */\n\tmasterKey?: string;\n\t/** Dokploy config from gkm.config */\n\tconfig: DokployDeployConfig;\n}\n\n/**\n * Get the Dokploy API token from stored credentials or environment\n */\nasync function getApiToken(): Promise<string> {\n\tconst token = await getDokployToken();\n\tif (!token) {\n\t\tthrow new Error(\n\t\t\t'Dokploy credentials not found.\\n' +\n\t\t\t\t'Run \"gkm login --service dokploy\" to authenticate, or set DOKPLOY_API_TOKEN.',\n\t\t);\n\t}\n\treturn token;\n}\n\n/**\n * Create a Dokploy API client\n */\nasync function createApi(endpoint: string): Promise<DokployApi> {\n\tconst token = await getApiToken();\n\treturn new DokployApi({ baseUrl: endpoint, token });\n}\n\n/**\n * Deploy to Dokploy\n */\nexport async function deployDokploy(\n\toptions: DokployDeployOptions,\n): Promise<DeployResult> {\n\tconst { stage, imageRef, masterKey, config } = options;\n\n\tlogger.log(`\\n🎯 Deploying to Dokploy...`);\n\tlogger.log(` Endpoint: ${config.endpoint}`);\n\tlogger.log(` Application: ${config.applicationId}`);\n\n\tconst api = await createApi(config.endpoint);\n\n\t// Configure Docker provider with the image\n\tlogger.log(` Configuring Docker image: ${imageRef}`);\n\n\t// Determine registry credentials\n\tconst registryOptions: {\n\t\tregistryId?: string;\n\t\tusername?: string;\n\t\tpassword?: string;\n\t\tregistryUrl?: string;\n\t} = {};\n\n\tif (config.registryId) {\n\t\t// Use registry ID from config\n\t\tregistryOptions.registryId = config.registryId;\n\t\tlogger.log(` Using Dokploy registry: ${config.registryId}`);\n\t} else {\n\t\t// Try stored registry ID from credentials\n\t\tconst storedRegistryId = await getDokployRegistryId();\n\t\tif (storedRegistryId) {\n\t\t\tregistryOptions.registryId = storedRegistryId;\n\t\t\tlogger.log(` Using stored Dokploy registry: ${storedRegistryId}`);\n\t\t} else if (config.registryCredentials) {\n\t\t\t// Use explicit credentials from config\n\t\t\tregistryOptions.username = config.registryCredentials.username;\n\t\t\tregistryOptions.password = config.registryCredentials.password;\n\t\t\tregistryOptions.registryUrl = config.registryCredentials.registryUrl;\n\t\t\tlogger.log(\n\t\t\t\t` Using registry credentials for: ${config.registryCredentials.registryUrl}`,\n\t\t\t);\n\t\t} else {\n\t\t\t// Try environment variables\n\t\t\tconst username = process.env.DOCKER_REGISTRY_USERNAME;\n\t\t\tconst password = process.env.DOCKER_REGISTRY_PASSWORD;\n\t\t\tconst registryUrl = process.env.DOCKER_REGISTRY_URL || config.registry;\n\n\t\t\tif (username && password && registryUrl) {\n\t\t\t\tregistryOptions.username = username;\n\t\t\t\tregistryOptions.password = password;\n\t\t\t\tregistryOptions.registryUrl = registryUrl;\n\t\t\t\tlogger.log(` Using registry credentials from environment`);\n\t\t\t}\n\t\t}\n\t}\n\n\tawait api.saveDockerProvider(config.applicationId, imageRef, registryOptions);\n\tlogger.log(' ✓ Docker provider configured');\n\n\t// Prepare environment variables\n\tconst envVars: Record<string, string> = {};\n\n\tif (masterKey) {\n\t\tenvVars.GKM_MASTER_KEY = masterKey;\n\t}\n\n\t// Update environment if we have variables to set\n\tif (Object.keys(envVars).length > 0) {\n\t\tlogger.log(' Updating environment variables...');\n\n\t\t// Convert env vars to the format Dokploy expects (KEY=VALUE per line)\n\t\tconst envString = Object.entries(envVars)\n\t\t\t.map(([key, value]) => `${key}=${value}`)\n\t\t\t.join('\\n');\n\n\t\tawait api.saveApplicationEnv(config.applicationId, envString);\n\t\tlogger.log(' ✓ Environment variables updated');\n\t}\n\n\t// Trigger deployment\n\tlogger.log(' Triggering deployment...');\n\tawait api.deployApplication(config.applicationId);\n\tlogger.log(' ✓ Deployment triggered');\n\n\tlogger.log('\\n✅ Dokploy deployment initiated!');\n\tlogger.log(`\\n📋 Deployment details:`);\n\tlogger.log(` Image: ${imageRef}`);\n\tlogger.log(` Stage: ${stage}`);\n\tlogger.log(` Application ID: ${config.applicationId}`);\n\n\tif (masterKey) {\n\t\tlogger.log(`\\n🔐 GKM_MASTER_KEY has been set in Dokploy environment`);\n\t}\n\n\t// Construct the probable deployment URL\n\tconst deploymentUrl = `${config.endpoint}/project/${config.projectId}`;\n\tlogger.log(`\\n🔗 View deployment: ${deploymentUrl}`);\n\n\treturn {\n\t\timageRef,\n\t\tmasterKey,\n\t\turl: deploymentUrl,\n\t};\n}\n\n/**\n * Validate Dokploy configuration\n */\nexport function validateDokployConfig(\n\tconfig: Partial<DokployDeployConfig> | undefined,\n): config is DokployDeployConfig {\n\tif (!config) {\n\t\treturn false;\n\t}\n\n\tconst required = ['endpoint', 'projectId', 'applicationId'] as const;\n\tconst missing = required.filter((key) => !config[key]);\n\n\tif (missing.length > 0) {\n\t\tthrow new Error(\n\t\t\t`Missing Dokploy configuration: ${missing.join(', ')}\\n` +\n\t\t\t\t'Configure in gkm.config.ts:\\n' +\n\t\t\t\t' providers: {\\n' +\n\t\t\t\t' dokploy: {\\n' +\n\t\t\t\t\" endpoint: 'https://dokploy.example.com',\\n\" +\n\t\t\t\t\" projectId: 'proj_xxx',\\n\" +\n\t\t\t\t\" applicationId: 'app_xxx',\\n\" +\n\t\t\t\t' },\\n' +\n\t\t\t\t' }',\n\t\t);\n\t}\n\n\treturn true;\n}\n","/**\n * Environment Variable Resolution for Dokploy Deployments\n *\n * Resolves sniffed environment variables to actual values during deployment.\n * Auto-supports common variables like DATABASE_URL, REDIS_URL, BETTER_AUTH_*,\n * and falls back to user-provided secrets.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport type { StageSecrets } from '../secrets/types';\nimport { stripPublicPrefix } from '../workspace/publicEnv';\nimport type { NormalizedAppConfig } from '../workspace/types';\nimport {\n\ttype AppDbCredentials,\n\ttype DokployStageState,\n\tgetGeneratedSecret,\n\tsetGeneratedSecret,\n} from './state';\n\n/**\n * Context needed for environment variable resolution\n */\nexport interface EnvResolverContext {\n\t/** The app being deployed */\n\tapp: NormalizedAppConfig;\n\t/** The app name */\n\tappName: string;\n\t/** Deployment stage (production, staging, development) */\n\tstage: string;\n\t/** Deploy state (for persisting generated secrets) */\n\tstate: DokployStageState;\n\t/** Per-app database credentials (if postgres is enabled) */\n\tappCredentials?: AppDbCredentials;\n\t/** Postgres connection info (internal hostname) */\n\tpostgres?: {\n\t\thost: string;\n\t\tport: number;\n\t\tdatabase: string;\n\t};\n\t/** Redis connection info (internal hostname) */\n\tredis?: {\n\t\thost: string;\n\t\tport: number;\n\t\tpassword?: string;\n\t};\n\t/** Public hostname for this app */\n\tappHostname: string;\n\t/** All frontend app URLs (for BETTER_AUTH_TRUSTED_ORIGINS) */\n\tfrontendUrls: string[];\n\t/** User-provided secrets from secrets store */\n\tuserSecrets?: StageSecrets;\n\t/** Master key for runtime decryption (optional) */\n\tmasterKey?: string;\n\t/** URLs of deployed dependency apps (e.g., { auth: 'https://auth.example.com' }) */\n\tdependencyUrls?: Record<string, string>;\n}\n\n/**\n * Result of environment variable resolution\n */\nexport interface EnvResolutionResult {\n\t/** Successfully resolved environment variables */\n\tresolved: Record<string, string>;\n\t/** Environment variable names that could not be resolved */\n\tmissing: string[];\n}\n\n/**\n * Auto-supported environment variable names\n */\nexport const AUTO_SUPPORTED_VARS = [\n\t'PORT',\n\t'NODE_ENV',\n\t'STAGE',\n\t'DATABASE_URL',\n\t'REDIS_URL',\n\t'BETTER_AUTH_URL',\n\t'BETTER_AUTH_SECRET',\n\t'BETTER_AUTH_TRUSTED_ORIGINS',\n\t'GKM_MASTER_KEY',\n] as const;\n\nexport type AutoSupportedVar = (typeof AUTO_SUPPORTED_VARS)[number];\n\n/**\n * Check if a variable name is auto-supported\n */\nexport function isAutoSupportedVar(\n\tvarName: string,\n): varName is AutoSupportedVar {\n\treturn AUTO_SUPPORTED_VARS.includes(varName as AutoSupportedVar);\n}\n\n/**\n * Generate a secure random secret (64 hex characters = 32 bytes)\n */\nexport function generateSecret(): string {\n\treturn randomBytes(32).toString('hex');\n}\n\n/**\n * Get or generate a secret for an app.\n * If the secret already exists in state, returns it.\n * Otherwise generates a new one and stores it.\n */\nexport function getOrGenerateSecret(\n\tstate: DokployStageState,\n\tappName: string,\n\tsecretName: string,\n): string {\n\t// Check if already generated\n\tconst existing = getGeneratedSecret(state, appName, secretName);\n\tif (existing) {\n\t\treturn existing;\n\t}\n\n\t// Generate new secret\n\tconst generated = generateSecret();\n\n\t// Store in state for persistence\n\tsetGeneratedSecret(state, appName, secretName, generated);\n\n\treturn generated;\n}\n\n/**\n * Build a DATABASE_URL for an app with per-app credentials\n */\nexport function buildDatabaseUrl(\n\tcredentials: AppDbCredentials,\n\tpostgres: { host: string; port: number; database: string },\n): string {\n\tconst { dbUser, dbPassword } = credentials;\n\tconst { host, port, database } = postgres;\n\treturn `postgresql://${encodeURIComponent(dbUser)}:${encodeURIComponent(dbPassword)}@${host}:${port}/${database}`;\n}\n\n/**\n * Build a REDIS_URL\n */\nexport function buildRedisUrl(redis: {\n\thost: string;\n\tport: number;\n\tpassword?: string;\n}): string {\n\tconst { host, port, password } = redis;\n\tif (password) {\n\t\treturn `redis://:${encodeURIComponent(password)}@${host}:${port}`;\n\t}\n\treturn `redis://${host}:${port}`;\n}\n\n/**\n * Resolve a single environment variable\n */\nexport function resolveEnvVar(\n\tvarName: string,\n\tcontext: EnvResolverContext,\n): string | undefined {\n\t// Auto-supported variables\n\tswitch (varName) {\n\t\tcase 'PORT':\n\t\t\treturn String(context.app.port);\n\n\t\tcase 'NODE_ENV':\n\t\t\t// Always 'production' for deployed apps (gkm dev handles development mode)\n\t\t\treturn 'production';\n\n\t\tcase 'STAGE':\n\t\t\treturn context.stage;\n\n\t\tcase 'DATABASE_URL':\n\t\t\tif (context.appCredentials && context.postgres) {\n\t\t\t\treturn buildDatabaseUrl(context.appCredentials, context.postgres);\n\t\t\t}\n\t\t\t// Fall through to check user secrets\n\t\t\tbreak;\n\n\t\tcase 'REDIS_URL':\n\t\t\tif (context.redis) {\n\t\t\t\treturn buildRedisUrl(context.redis);\n\t\t\t}\n\t\t\t// Fall through to check user secrets\n\t\t\tbreak;\n\n\t\tcase 'BETTER_AUTH_URL':\n\t\t\treturn `https://${context.appHostname}`;\n\n\t\tcase 'BETTER_AUTH_SECRET':\n\t\t\treturn getOrGenerateSecret(\n\t\t\t\tcontext.state,\n\t\t\t\tcontext.appName,\n\t\t\t\t'BETTER_AUTH_SECRET',\n\t\t\t);\n\n\t\tcase 'BETTER_AUTH_TRUSTED_ORIGINS':\n\t\t\tif (context.frontendUrls.length > 0) {\n\t\t\t\treturn context.frontendUrls.join(',');\n\t\t\t}\n\t\t\t// Fall through to check user secrets\n\t\t\tbreak;\n\n\t\tcase 'GKM_MASTER_KEY':\n\t\t\tif (context.masterKey) {\n\t\t\t\treturn context.masterKey;\n\t\t\t}\n\t\t\t// Fall through to check user secrets\n\t\t\tbreak;\n\t}\n\n\t// Check dependency URLs (e.g., AUTH_URL -> dependencyUrls.auth)\n\t// Also supports any known public prefix (NEXT_PUBLIC_, VITE_, ...) so\n\t// VITE_AUTH_URL and NEXT_PUBLIC_AUTH_URL both resolve to dependencyUrls.auth.\n\tif (context.dependencyUrls && varName.endsWith('_URL')) {\n\t\tconst stripped = stripPublicPrefix(varName) ?? varName;\n\t\tconst depName = stripped.slice(0, -4).toLowerCase();\n\n\t\tif (context.dependencyUrls[depName]) {\n\t\t\treturn context.dependencyUrls[depName];\n\t\t}\n\t}\n\n\t// Check user-provided secrets\n\tif (context.userSecrets) {\n\t\t// Check custom secrets first\n\t\tif (context.userSecrets.custom[varName]) {\n\t\t\treturn context.userSecrets.custom[varName];\n\t\t}\n\n\t\t// Check URLs (DATABASE_URL, REDIS_URL, RABBITMQ_URL)\n\t\tif (varName in context.userSecrets.urls) {\n\t\t\treturn context.userSecrets.urls[\n\t\t\t\tvarName as keyof typeof context.userSecrets.urls\n\t\t\t];\n\t\t}\n\n\t\t// Check service-specific vars\n\t\tif (\n\t\t\tvarName === 'POSTGRES_PASSWORD' &&\n\t\t\tcontext.userSecrets.services.postgres\n\t\t) {\n\t\t\treturn context.userSecrets.services.postgres.password;\n\t\t}\n\t\tif (varName === 'REDIS_PASSWORD' && context.userSecrets.services.redis) {\n\t\t\treturn context.userSecrets.services.redis.password;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Resolve all environment variables for an app\n */\nexport function resolveEnvVars(\n\trequiredVars: string[],\n\tcontext: EnvResolverContext,\n): EnvResolutionResult {\n\tconst resolved: Record<string, string> = {};\n\tconst missing: string[] = [];\n\n\tfor (const varName of requiredVars) {\n\t\tconst value = resolveEnvVar(varName, context);\n\t\tif (value !== undefined) {\n\t\t\tresolved[varName] = value;\n\t\t} else {\n\t\t\tmissing.push(varName);\n\t\t}\n\t}\n\n\treturn { resolved, missing };\n}\n\n/**\n * Format missing variables error message\n */\nexport function formatMissingVarsError(\n\tappName: string,\n\tmissing: string[],\n\tstage: string,\n): string {\n\tconst varList = missing.map((v) => ` - ${v}`).join('\\n');\n\treturn (\n\t\t`Deployment failed: ${appName} is missing required environment variables:\\n` +\n\t\t`${varList}\\n\\n` +\n\t\t`Add them with:\\n` +\n\t\t` gkm secrets:set <VAR_NAME> <value> --stage ${stage}\\n\\n` +\n\t\t`Or add them to the app's requiredEnv in gkm.config.ts to have them auto-resolved.`\n\t);\n}\n\n/**\n * Validate that all required environment variables can be resolved\n */\nexport function validateEnvVars(\n\trequiredVars: string[],\n\tcontext: EnvResolverContext,\n): { valid: boolean; missing: string[]; resolved: Record<string, string> } {\n\tconst { resolved, missing } = resolveEnvVars(requiredVars, context);\n\treturn {\n\t\tvalid: missing.length === 0,\n\t\tmissing,\n\t\tresolved,\n\t};\n}\n","import { existsSync } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tgetDokployCredentials,\n\tgetDokployRegistryId,\n\tgetDokployToken,\n\tstoreDokployRegistryId,\n} from '../auth';\nimport { DokployApi } from './dokploy-api';\nimport type { DokployDeployConfig } from './types';\n\nconst logger = console;\n\nexport interface DeployInitOptions {\n\t/** Dokploy endpoint URL (optional if logged in) */\n\tendpoint?: string;\n\t/** Project name (creates new or uses existing) */\n\tprojectName: string;\n\t/** Application name */\n\tappName: string;\n\t/** Use existing project ID instead of creating/finding */\n\tprojectId?: string;\n\t/** Registry ID in Dokploy (optional, uses stored if available) */\n\tregistryId?: string;\n}\n\nexport interface RegistrySetupOptions {\n\t/** Dokploy endpoint URL (optional if logged in) */\n\tendpoint?: string;\n\t/** Registry name (for display in Dokploy) */\n\tregistryName: string;\n\t/** Registry URL (e.g., ghcr.io, docker.io) */\n\tregistryUrl: string;\n\t/** Registry username */\n\tusername: string;\n\t/** Registry password or token */\n\tpassword: string;\n\t/** Image prefix (optional, e.g., org-name) */\n\timagePrefix?: string;\n}\n\n/**\n * Get the Dokploy API token from stored credentials or environment\n */\nasync function getApiToken(): Promise<string> {\n\tconst token = await getDokployToken();\n\tif (!token) {\n\t\tthrow new Error(\n\t\t\t'Dokploy credentials not found.\\n' +\n\t\t\t\t'Run \"gkm login --service dokploy\" to authenticate, or set DOKPLOY_API_TOKEN.',\n\t\t);\n\t}\n\treturn token;\n}\n\n/**\n * Get Dokploy endpoint from options or stored credentials\n */\nasync function getEndpoint(providedEndpoint?: string): Promise<string> {\n\tif (providedEndpoint) {\n\t\treturn providedEndpoint;\n\t}\n\n\tconst stored = await getDokployCredentials();\n\tif (stored) {\n\t\treturn stored.endpoint;\n\t}\n\n\tthrow new Error(\n\t\t'Dokploy endpoint not specified.\\n' +\n\t\t\t'Either run \"gkm login --service dokploy\" first, or provide --endpoint.',\n\t);\n}\n\n/**\n * Create a Dokploy API client\n */\nasync function createApi(endpoint: string): Promise<DokployApi> {\n\tconst token = await getApiToken();\n\treturn new DokployApi({ baseUrl: endpoint, token });\n}\n\n/**\n * Update gkm.config.ts with Dokploy configuration\n */\nexport async function updateConfig(\n\tconfig: DokployDeployConfig,\n\tcwd: string = process.cwd(),\n): Promise<void> {\n\tconst configPath = join(cwd, 'gkm.config.ts');\n\n\tif (!existsSync(configPath)) {\n\t\tlogger.warn(\n\t\t\t'\\n gkm.config.ts not found. Add this configuration manually:\\n',\n\t\t);\n\t\tlogger.log(` providers: {`);\n\t\tlogger.log(` dokploy: {`);\n\t\tlogger.log(` endpoint: '${config.endpoint}',`);\n\t\tlogger.log(` projectId: '${config.projectId}',`);\n\t\tlogger.log(` applicationId: '${config.applicationId}',`);\n\t\tlogger.log(` },`);\n\t\tlogger.log(` },`);\n\t\treturn;\n\t}\n\n\tconst content = await readFile(configPath, 'utf-8');\n\n\t// Check if providers.dokploy already exists\n\tif (content.includes('dokploy:') && content.includes('applicationId:')) {\n\t\tlogger.log('\\n Dokploy config already exists in gkm.config.ts');\n\t\tlogger.log(' Updating with new values...');\n\t}\n\n\t// Build the dokploy config string\n\tconst registryLine = config.registryId\n\t\t? `\\n\\t\\t\\tregistryId: '${config.registryId}',`\n\t\t: '';\n\tconst dokployConfigStr = `dokploy: {\n\t\t\tendpoint: '${config.endpoint}',\n\t\t\tprojectId: '${config.projectId}',\n\t\t\tapplicationId: '${config.applicationId}',${registryLine}\n\t\t}`;\n\n\t// Try to add or update the dokploy config\n\tlet newContent: string;\n\n\tif (content.includes('providers:')) {\n\t\t// Add dokploy to existing providers\n\t\tif (content.includes('dokploy:')) {\n\t\t\t// Update existing dokploy config (handle multi-line with registryId)\n\t\t\tnewContent = content.replace(/dokploy:\\s*\\{[^}]*\\}/s, dokployConfigStr);\n\t\t} else {\n\t\t\t// Add dokploy to providers\n\t\t\tnewContent = content.replace(\n\t\t\t\t/providers:\\s*\\{/,\n\t\t\t\t`providers: {\\n\\t\\t${dokployConfigStr},`,\n\t\t\t);\n\t\t}\n\t} else {\n\t\t// Add providers section before the closing of defineConfig\n\t\tnewContent = content.replace(\n\t\t\t/}\\s*\\)\\s*;?\\s*$/,\n\t\t\t`\n\tproviders: {\n\t\t${dokployConfigStr},\n\t},\n});`,\n\t\t);\n\t}\n\n\tawait writeFile(configPath, newContent);\n\tlogger.log('\\n ✓ Updated gkm.config.ts with Dokploy configuration');\n}\n\n/**\n * Initialize Dokploy deployment configuration\n */\nexport async function deployInitCommand(\n\toptions: DeployInitOptions,\n): Promise<DokployDeployConfig> {\n\tconst {\n\t\tprojectName,\n\t\tappName,\n\t\tprojectId: existingProjectId,\n\t\tregistryId,\n\t} = options;\n\n\tconst endpoint = await getEndpoint(options.endpoint);\n\tconst api = await createApi(endpoint);\n\n\tlogger.log(`\\n🚀 Initializing Dokploy deployment...`);\n\tlogger.log(` Endpoint: ${endpoint}`);\n\n\t// Step 1: Find or create project\n\tlet projectId: string;\n\n\tif (existingProjectId) {\n\t\tprojectId = existingProjectId;\n\t\tlogger.log(`\\n📁 Using existing project: ${projectId}`);\n\t} else {\n\t\tlogger.log(`\\n📁 Looking for project: ${projectName}`);\n\n\t\tconst projects = await api.listProjects();\n\t\tconst existingProject = projects.find(\n\t\t\t(p) => p.name.toLowerCase() === projectName.toLowerCase(),\n\t\t);\n\n\t\tif (existingProject) {\n\t\t\tprojectId = existingProject.projectId;\n\t\t\tlogger.log(` Found existing project: ${projectId}`);\n\t\t} else {\n\t\t\tlogger.log(` Creating new project...`);\n\t\t\tconst result = await api.createProject(projectName);\n\t\t\tprojectId = result.project.projectId;\n\t\t\tlogger.log(` ✓ Created project: ${projectId}`);\n\t\t}\n\t}\n\n\t// Step 2: Get project to find environment\n\tconst project = await api.getProject(projectId);\n\tlet environmentId: string;\n\n\tconst firstEnv = project.environments?.[0];\n\tif (firstEnv) {\n\t\tenvironmentId = firstEnv.environmentId;\n\t} else {\n\t\t// Create a default environment\n\t\tlogger.log(` Creating production environment...`);\n\t\tconst env = await api.createEnvironment(projectId, 'production');\n\t\tenvironmentId = env.environmentId;\n\t}\n\n\t// Step 3: Create application\n\tlogger.log(`\\n📦 Creating application: ${appName}`);\n\tconst application = await api.createApplication(\n\t\tappName,\n\t\tprojectId,\n\t\tenvironmentId,\n\t);\n\tlogger.log(` ✓ Created application: ${application.applicationId}`);\n\n\t// Step 4: Configure registry if provided\n\tif (registryId) {\n\t\tlogger.log(`\\n🔧 Configuring registry: ${registryId}`);\n\t\tawait api.updateApplication(application.applicationId, { registryId });\n\t\tlogger.log(` ✓ Registry configured`);\n\t} else {\n\t\t// List available registries\n\t\ttry {\n\t\t\tconst registries = await api.listRegistries();\n\t\t\tif (registries.length > 0) {\n\t\t\t\tlogger.log(`\\n📋 Available registries:`);\n\t\t\t\tfor (const reg of registries) {\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` - ${reg.registryName}: ${reg.registryUrl} (${reg.registryId})`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlogger.log(`\\n To use a registry, run with --registry-id <id>`);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore registry listing errors\n\t\t}\n\t}\n\n\t// Step 5: Build config\n\tconst config: DokployDeployConfig = {\n\t\tendpoint,\n\t\tprojectId,\n\t\tapplicationId: application.applicationId,\n\t};\n\n\t// Step 6: Update gkm.config.ts\n\tawait updateConfig(config);\n\n\tlogger.log(`\\n✅ Dokploy deployment initialized!`);\n\tlogger.log(`\\n📋 Configuration:`);\n\tlogger.log(` Project ID: ${projectId}`);\n\tlogger.log(` Application ID: ${application.applicationId}`);\n\tlogger.log(`\\n🔗 View in Dokploy: ${endpoint}/project/${projectId}`);\n\tlogger.log(`\\n📝 Next steps:`);\n\tlogger.log(` 1. Initialize secrets: gkm secrets:init --stage production`);\n\tlogger.log(` 2. Deploy: gkm deploy --provider dokploy --stage production`);\n\n\treturn config;\n}\n\n/**\n * List available Dokploy resources\n */\nexport async function deployListCommand(options: {\n\tendpoint?: string;\n\tresource: 'projects' | 'registries';\n}): Promise<void> {\n\tconst endpoint = await getEndpoint(options.endpoint);\n\tconst api = await createApi(endpoint);\n\n\tconst { resource } = options;\n\n\tif (resource === 'projects') {\n\t\tlogger.log(`\\n📁 Projects in ${endpoint}:`);\n\t\tconst projects = await api.listProjects();\n\n\t\tif (projects.length === 0) {\n\t\t\tlogger.log(' No projects found');\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const project of projects) {\n\t\t\tlogger.log(`\\n ${project.name} (${project.projectId})`);\n\t\t\tif (project.description) {\n\t\t\t\tlogger.log(` ${project.description}`);\n\t\t\t}\n\t\t}\n\t} else if (resource === 'registries') {\n\t\tlogger.log(`\\n🐳 Registries in ${endpoint}:`);\n\t\tconst registries = await api.listRegistries();\n\n\t\tif (registries.length === 0) {\n\t\t\tlogger.log(' No registries configured');\n\t\t\tlogger.log(' Run \"gkm registry:setup\" to configure a registry');\n\t\t\treturn;\n\t\t}\n\n\t\tconst storedRegistryId = await getDokployRegistryId();\n\n\t\tfor (const registry of registries) {\n\t\t\tconst isDefault = registry.registryId === storedRegistryId;\n\t\t\tconst marker = isDefault ? ' (default)' : '';\n\t\t\tlogger.log(\n\t\t\t\t`\\n ${registry.registryName}${marker} (${registry.registryId})`,\n\t\t\t);\n\t\t\tlogger.log(` URL: ${registry.registryUrl}`);\n\t\t\tlogger.log(` Username: ${registry.username}`);\n\t\t\tif (registry.imagePrefix) {\n\t\t\t\tlogger.log(` Prefix: ${registry.imagePrefix}`);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Setup a Docker registry in Dokploy\n */\nexport async function registrySetupCommand(\n\toptions: RegistrySetupOptions,\n): Promise<string> {\n\tconst { registryName, registryUrl, username, password, imagePrefix } =\n\t\toptions;\n\n\tconst endpoint = await getEndpoint(options.endpoint);\n\tconst api = await createApi(endpoint);\n\n\tlogger.log(`\\n🐳 Setting up Docker registry in Dokploy...`);\n\tlogger.log(` Endpoint: ${endpoint}`);\n\n\t// Check if registry with same URL already exists\n\tconst existingRegistries = await api.listRegistries();\n\tconst existing = existingRegistries.find(\n\t\t(r) =>\n\t\t\tr.registryUrl === registryUrl ||\n\t\t\tr.registryName.toLowerCase() === registryName.toLowerCase(),\n\t);\n\n\tlet registryId: string;\n\n\tif (existing) {\n\t\tlogger.log(`\\n📋 Found existing registry: ${existing.registryName}`);\n\t\tlogger.log(` Updating credentials...`);\n\n\t\tawait api.updateRegistry(existing.registryId, {\n\t\t\tregistryName,\n\t\t\tusername,\n\t\t\tpassword,\n\t\t\timagePrefix,\n\t\t});\n\n\t\tregistryId = existing.registryId;\n\t\tlogger.log(` ✓ Registry updated: ${registryId}`);\n\t} else {\n\t\tlogger.log(`\\n📦 Creating registry: ${registryName}`);\n\n\t\tconst registry = await api.createRegistry(\n\t\t\tregistryName,\n\t\t\tregistryUrl,\n\t\t\tusername,\n\t\t\tpassword,\n\t\t\t{ imagePrefix },\n\t\t);\n\n\t\tregistryId = registry.registryId;\n\t\tlogger.log(` ✓ Registry created: ${registryId}`);\n\t}\n\n\t// Store registry ID in credentials\n\tawait storeDokployRegistryId(registryId);\n\tlogger.log(`\\n💾 Saved registry ID to ~/.gkm/credentials.json`);\n\n\tlogger.log(`\\n✅ Registry setup complete!`);\n\tlogger.log(`\\n📋 Registry Details:`);\n\tlogger.log(` ID: ${registryId}`);\n\tlogger.log(` Name: ${registryName}`);\n\tlogger.log(` URL: ${registryUrl}`);\n\tlogger.log(` Username: ${username}`);\n\tif (imagePrefix) {\n\t\tlogger.log(` Prefix: ${imagePrefix}`);\n\t}\n\n\tlogger.log(\n\t\t`\\n📝 The registry ID is now stored and will be used automatically`,\n\t);\n\tlogger.log(` when deploying with \"gkm deploy --provider dokploy\"`);\n\n\treturn registryId;\n}\n\n/**\n * Use an existing registry (set as default)\n */\nexport async function registryUseCommand(options: {\n\tendpoint?: string;\n\tregistryId: string;\n}): Promise<void> {\n\tconst { registryId } = options;\n\n\tconst endpoint = await getEndpoint(options.endpoint);\n\tconst api = await createApi(endpoint);\n\n\tlogger.log(`\\n🔧 Setting default registry...`);\n\n\t// Verify the registry exists\n\ttry {\n\t\tconst registry = await api.getRegistry(registryId);\n\t\tlogger.log(` Found registry: ${registry.registryName}`);\n\t} catch {\n\t\tthrow new Error(\n\t\t\t`Registry not found: ${registryId}\\n` +\n\t\t\t\t'Run \"gkm deploy:list registries\" to see available registries.',\n\t\t);\n\t}\n\n\t// Store registry ID in credentials\n\tawait storeDokployRegistryId(registryId);\n\n\tlogger.log(`\\n✅ Default registry set: ${registryId}`);\n\tlogger.log(` This registry will be used for future deployments.`);\n}\n","/**\n * State Provider Interface\n *\n * Abstracts the storage backend for deployment state.\n * Built-in providers: LocalStateProvider, SSMStateProvider\n * Users can also supply custom implementations.\n */\n\nimport type { DokployStageState } from './state';\n\n/**\n * Interface for deployment state storage providers.\n *\n * Implementations must handle:\n * - Reading state for a stage (returns null if not found)\n * - Writing state for a stage (creates or updates)\n */\nexport interface StateProvider {\n\t/**\n\t * Read deployment state for a stage.\n\t *\n\t * @param stage - The deployment stage (e.g., 'development', 'production')\n\t * @returns The state object or null if not found\n\t */\n\tread(stage: string): Promise<DokployStageState | null>;\n\n\t/**\n\t * Write deployment state for a stage.\n\t *\n\t * @param stage - The deployment stage\n\t * @param state - The state object to persist\n\t */\n\twrite(stage: string, state: DokployStageState): Promise<void>;\n}\n\n/**\n * Valid AWS regions.\n */\nexport type AwsRegion =\n\t| 'us-east-1'\n\t| 'us-east-2'\n\t| 'us-west-1'\n\t| 'us-west-2'\n\t| 'af-south-1'\n\t| 'ap-east-1'\n\t| 'ap-south-1'\n\t| 'ap-south-2'\n\t| 'ap-southeast-1'\n\t| 'ap-southeast-2'\n\t| 'ap-southeast-3'\n\t| 'ap-southeast-4'\n\t| 'ap-northeast-1'\n\t| 'ap-northeast-2'\n\t| 'ap-northeast-3'\n\t| 'ca-central-1'\n\t| 'eu-central-1'\n\t| 'eu-central-2'\n\t| 'eu-west-1'\n\t| 'eu-west-2'\n\t| 'eu-west-3'\n\t| 'eu-south-1'\n\t| 'eu-south-2'\n\t| 'eu-north-1'\n\t| 'me-south-1'\n\t| 'me-central-1'\n\t| 'sa-east-1';\n\n/**\n * Local state provider config.\n */\nexport interface LocalStateConfig {\n\tprovider: 'local';\n}\n\n/**\n * SSM state provider config (requires region).\n */\nexport interface SSMStateConfig {\n\tprovider: 'ssm';\n\t/** AWS region (required for SSM provider) */\n\tregion: AwsRegion;\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile?: string;\n}\n\n/**\n * Custom state provider config.\n */\nexport interface CustomStateConfig {\n\t/** Custom StateProvider implementation */\n\tprovider: StateProvider;\n}\n\n/**\n * State configuration types.\n */\nexport type StateConfig = LocalStateConfig | SSMStateConfig | CustomStateConfig;\n\n/**\n * Check if value is a StateProvider implementation.\n */\nexport function isStateProvider(value: unknown): value is StateProvider {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\ttypeof (value as StateProvider).read === 'function' &&\n\t\ttypeof (value as StateProvider).write === 'function'\n\t);\n}\n\nexport interface CreateStateProviderOptions {\n\t/** State config from workspace */\n\tconfig?: StateConfig;\n\t/** Workspace root directory (for local provider) */\n\tworkspaceRoot: string;\n\t/** Workspace name (for SSM parameter path) */\n\tworkspaceName: string;\n}\n\n/**\n * Create a state provider based on configuration.\n *\n * - 'local': LocalStateProvider (default)\n * - 'ssm': CachedStateProvider with SSM as source of truth\n * - Custom: Use provided StateProvider implementation\n */\nexport async function createStateProvider(\n\toptions: CreateStateProviderOptions,\n): Promise<StateProvider> {\n\tconst { config, workspaceRoot, workspaceName } = options;\n\n\t// Default to local provider if no config\n\tif (!config) {\n\t\tconst { LocalStateProvider } = await import('./LocalStateProvider');\n\t\treturn new LocalStateProvider(workspaceRoot);\n\t}\n\n\t// Custom provider implementation\n\tif (isStateProvider(config.provider)) {\n\t\treturn config.provider;\n\t}\n\n\t// Built-in providers (discriminated by provider string)\n\tconst provider = config.provider;\n\n\tif (provider === 'local') {\n\t\tconst { LocalStateProvider } = await import('./LocalStateProvider');\n\t\treturn new LocalStateProvider(workspaceRoot);\n\t}\n\n\tif (provider === 'ssm') {\n\t\tif (!workspaceName) {\n\t\t\tthrow new Error(\n\t\t\t\t'Workspace name is required for SSM state provider. Set \"name\" in gkm.config.ts.',\n\t\t\t);\n\t\t}\n\n\t\tconst { LocalStateProvider } = await import('./LocalStateProvider');\n\t\tconst { SSMStateProvider } = await import('./SSMStateProvider');\n\t\tconst { CachedStateProvider } = await import('./CachedStateProvider');\n\n\t\tconst ssmConfig = config as SSMStateConfig;\n\t\tconst local = new LocalStateProvider(workspaceRoot);\n\t\tconst ssm = SSMStateProvider.create({\n\t\t\tworkspaceName,\n\t\t\tregion: ssmConfig.region,\n\t\t\tprofile: ssmConfig.profile,\n\t\t});\n\n\t\treturn new CachedStateProvider(ssm, local);\n\t}\n\n\t// Should never reach here - custom providers handled above\n\tthrow new Error(`Unknown state provider: ${JSON.stringify(config)}`);\n}\n","import { encryptSecrets } from '../secrets/encryption.js';\nimport { toEmbeddableSecrets } from '../secrets/storage.js';\nimport type {\n\tEmbeddableSecrets,\n\tEncryptedPayload,\n\tStageSecrets,\n} from '../secrets/types.js';\nimport type { SniffedEnvironment } from './sniffer.js';\n\n/**\n * Result of filtering secrets for an app.\n */\nexport interface FilteredAppSecrets {\n\tappName: string;\n\t/** Secrets filtered to only include what the app needs */\n\tsecrets: EmbeddableSecrets;\n\t/** List of required env vars that were found in secrets */\n\tfound: string[];\n\t/** List of required env vars that were NOT found in secrets */\n\tmissing: string[];\n}\n\n/**\n * Filter secrets to only include the env vars that an app requires.\n *\n * @param stageSecrets - All secrets for the stage\n * @param sniffedEnv - The sniffed environment requirements for the app\n * @returns Filtered secrets with found/missing tracking\n */\nexport function filterSecretsForApp(\n\tstageSecrets: StageSecrets,\n\tsniffedEnv: SniffedEnvironment,\n): FilteredAppSecrets {\n\t// Convert stage secrets to flat embeddable format\n\tconst allSecrets = toEmbeddableSecrets(stageSecrets);\n\tconst filtered: EmbeddableSecrets = {};\n\tconst found: string[] = [];\n\tconst missing: string[] = [];\n\n\t// Filter to only required env vars\n\tfor (const key of sniffedEnv.requiredEnvVars) {\n\t\tif (key in allSecrets) {\n\t\t\tfiltered[key] = allSecrets[key]!;\n\t\t\tfound.push(key);\n\t\t} else {\n\t\t\tmissing.push(key);\n\t\t}\n\t}\n\n\treturn {\n\t\tappName: sniffedEnv.appName,\n\t\tsecrets: filtered,\n\t\tfound: found.sort(),\n\t\tmissing: missing.sort(),\n\t};\n}\n\n/**\n * Result of encrypting secrets for an app.\n */\nexport interface EncryptedAppSecrets {\n\tappName: string;\n\t/** Encrypted payload with credentials and IV */\n\tpayload: EncryptedPayload;\n\t/** Master key for runtime decryption (hex encoded) */\n\tmasterKey: string;\n\t/** Number of secrets encrypted */\n\tsecretCount: number;\n\t/** List of required env vars that were NOT found in secrets */\n\tmissingSecrets: string[];\n}\n\n/**\n * Encrypt filtered secrets for an app.\n * Generates an ephemeral master key that should be injected into Dokploy.\n *\n * @param filteredSecrets - The filtered secrets for the app\n * @returns Encrypted payload with master key\n */\nexport function encryptSecretsForApp(\n\tfilteredSecrets: FilteredAppSecrets,\n): EncryptedAppSecrets {\n\tconst payload = encryptSecrets(filteredSecrets.secrets);\n\n\treturn {\n\t\tappName: filteredSecrets.appName,\n\t\tpayload,\n\t\tmasterKey: payload.masterKey,\n\t\tsecretCount: Object.keys(filteredSecrets.secrets).length,\n\t\tmissingSecrets: filteredSecrets.missing,\n\t};\n}\n\n/**\n * Filter and encrypt secrets for an app in one step.\n *\n * @param stageSecrets - All secrets for the stage\n * @param sniffedEnv - The sniffed environment requirements for the app\n * @returns Encrypted secrets with master key\n */\nexport function prepareSecretsForApp(\n\tstageSecrets: StageSecrets,\n\tsniffedEnv: SniffedEnvironment,\n): EncryptedAppSecrets {\n\tconst filtered = filterSecretsForApp(stageSecrets, sniffedEnv);\n\treturn encryptSecretsForApp(filtered);\n}\n\n/**\n * Prepare secrets for multiple apps.\n *\n * @param stageSecrets - All secrets for the stage\n * @param sniffedApps - Map of app name to sniffed environment\n * @returns Map of app name to encrypted secrets\n */\nexport function prepareSecretsForAllApps(\n\tstageSecrets: StageSecrets,\n\tsniffedApps: Map<string, SniffedEnvironment>,\n): Map<string, EncryptedAppSecrets> {\n\tconst results = new Map<string, EncryptedAppSecrets>();\n\n\tfor (const [appName, sniffedEnv] of sniffedApps) {\n\t\t// Only prepare secrets for apps that have required env vars\n\t\tif (sniffedEnv.requiredEnvVars.length > 0) {\n\t\t\tconst encrypted = prepareSecretsForApp(stageSecrets, sniffedEnv);\n\t\t\tresults.set(appName, encrypted);\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Report on secrets preparation status for all apps.\n */\nexport interface SecretsReport {\n\t/** Total number of apps processed */\n\ttotalApps: number;\n\t/** Apps with encrypted secrets */\n\tappsWithSecrets: string[];\n\t/** Apps without secrets (frontends or no env requirements) */\n\tappsWithoutSecrets: string[];\n\t/** Apps with missing secrets (warnings) */\n\tappsWithMissingSecrets: Array<{\n\t\tappName: string;\n\t\tmissing: string[];\n\t}>;\n}\n\n/**\n * Generate a report on secrets preparation.\n */\nexport function generateSecretsReport(\n\tencryptedApps: Map<string, EncryptedAppSecrets>,\n\tsniffedApps: Map<string, SniffedEnvironment>,\n): SecretsReport {\n\tconst appsWithSecrets: string[] = [];\n\tconst appsWithoutSecrets: string[] = [];\n\tconst appsWithMissingSecrets: Array<{ appName: string; missing: string[] }> =\n\t\t[];\n\n\tfor (const [appName, sniffedEnv] of sniffedApps) {\n\t\tif (sniffedEnv.requiredEnvVars.length === 0) {\n\t\t\tappsWithoutSecrets.push(appName);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst encrypted = encryptedApps.get(appName);\n\t\tif (encrypted) {\n\t\t\tappsWithSecrets.push(appName);\n\n\t\t\tif (encrypted.missingSecrets.length > 0) {\n\t\t\t\tappsWithMissingSecrets.push({\n\t\t\t\t\tappName,\n\t\t\t\t\tmissing: encrypted.missingSecrets,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\ttotalApps: sniffedApps.size,\n\t\tappsWithSecrets: appsWithSecrets.sort(),\n\t\tappsWithoutSecrets: appsWithoutSecrets.sort(),\n\t\tappsWithMissingSecrets,\n\t};\n}\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport type { SniffResult } from '@geekmidas/envkit/sniffer';\nimport { normalizeRoutes } from '../workspace/client-generator.js';\nimport { getPublicEnvPrefix } from '../workspace/publicEnv.js';\nimport type { NormalizedAppConfig } from '../workspace/types.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Resolve the tsx package path from the CLI package's dependencies.\n * This ensures tsx is available regardless of whether the target project has it installed.\n */\nfunction resolveTsxPath(): string {\n\tconst require = createRequire(import.meta.url);\n\treturn require.resolve('tsx');\n}\n\n/**\n * Resolve the path to a sniffer helper file.\n * Handles both dev (.ts with tsx) and production (.mjs from dist).\n *\n * In production: sniffer.ts is bundled into dist/index.mjs, but sniffer helper\n * files are output to dist/deploy/ as standalone modules for subprocess loading.\n *\n * In development: All files are in src/deploy/ and loaded via tsx.\n */\nfunction resolveSnifferFile(baseName: string): string {\n\t// Try deploy/ subdirectory first (production: bundled code is at dist/index.mjs,\n\t// but sniffer files are at dist/deploy/)\n\tconst deployMjsPath = resolve(__dirname, 'deploy', `${baseName}.mjs`);\n\tif (existsSync(deployMjsPath)) {\n\t\treturn deployMjsPath;\n\t}\n\n\t// Try same directory .mjs (production: if running from dist/deploy/ directly)\n\tconst mjsPath = resolve(__dirname, `${baseName}.mjs`);\n\tif (existsSync(mjsPath)) {\n\t\treturn mjsPath;\n\t}\n\n\t// Try same directory .ts (development with tsx: all files in src/deploy/)\n\tconst tsPath = resolve(__dirname, `${baseName}.ts`);\n\tif (existsSync(tsPath)) {\n\t\treturn tsPath;\n\t}\n\n\t// Fallback to .ts (will error if neither exists)\n\treturn tsPath;\n}\n\n// Re-export SniffResult for consumers\nexport type { SniffResult } from '@geekmidas/envkit/sniffer';\n\n/**\n * Result of sniffing an app's environment requirements.\n */\nexport interface SniffedEnvironment {\n\tappName: string;\n\t/**\n\t * All required environment variable names.\n\t * When `SniffAppOptions.markOptional` is true, optional variables are\n\t * suffixed with `?` (e.g. `PORT?`).\n\t */\n\trequiredEnvVars: string[];\n\t/**\n\t * Subset of variables that are optional (accessed via `.optional()` or\n\t * `.default()`). Always populated regardless of `markOptional`.\n\t */\n\toptionalEnvVars: string[];\n}\n\n/**\n * Options for sniffing an app's environment.\n */\nexport interface SniffAppOptions {\n\t/** Whether to log warnings for errors encountered during sniffing. Defaults to true. */\n\tlogWarnings?: boolean;\n\t/**\n\t * When true, optional variables (those with `.optional()` or `.default()`)\n\t * are suffixed with `?` in `requiredEnvVars` (e.g. `PORT?`).\n\t * Defaults to false.\n\t */\n\tmarkOptional?: boolean;\n}\n\n/**\n * Get required environment variables for an app.\n *\n * Detection strategy (in order):\n * 1. Frontend apps: Returns empty (no server secrets)\n * 2. Apps with `requiredEnv`: Uses explicit list from config\n * 3. Entry apps: Imports entry file in subprocess to capture config.parse() calls\n * 4. Route-based apps: Loads route files and calls getEnvironment() on each construct\n * 5. Apps with `envParser` (no routes): Runs SnifferEnvironmentParser to detect usage\n * 6. Apps with neither: Returns empty\n *\n * This function handles \"fire and forget\" async operations gracefully,\n * capturing errors and unhandled rejections without failing the build.\n *\n * @param app - The normalized app configuration\n * @param appName - The name of the app\n * @param workspacePath - Absolute path to the workspace root\n * @param options - Optional configuration for sniffing behavior\n * @returns The sniffed environment with required variables\n */\nexport async function sniffAppEnvironment(\n\tapp: NormalizedAppConfig,\n\tappName: string,\n\tworkspacePath: string,\n\toptions: SniffAppOptions = {},\n): Promise<SniffedEnvironment> {\n\tconst { logWarnings = true, markOptional = false } = options;\n\n\t// 1. Frontend apps - handle dependencies and config sniffing\n\tif (app.type === 'web' || app.type === 'mobile') {\n\t\t// Auto-generate {prefix}{DEP}_URL from dependencies, where prefix matches\n\t\t// the framework's public-var convention (NEXT_PUBLIC_, VITE_, ...).\n\t\t// For frameworks without a prefix (e.g. Remix), no dep var is required.\n\t\tconst publicPrefix = getPublicEnvPrefix(app.framework);\n\t\tconst depVars = publicPrefix\n\t\t\t? (app.dependencies ?? []).map(\n\t\t\t\t\t(dep) => `${publicPrefix}${dep.toUpperCase()}_URL`,\n\t\t\t\t)\n\t\t\t: [];\n\n\t\t// If config specified, sniff by importing the file(s)\n\t\t// The file calls .parse() at module load, which triggers sniffer to capture vars\n\t\tif (app.config) {\n\t\t\tconst sniffedVars: string[] = [];\n\t\t\tconst sniffedOptional: string[] = [];\n\n\t\t\t// Collect config paths to sniff\n\t\t\tconst configPaths: string[] = [];\n\t\t\tif (app.config.client) configPaths.push(app.config.client);\n\t\t\tif (app.config.server) configPaths.push(app.config.server);\n\n\t\t\t// Sniff each config file\n\t\t\tfor (const configPath of configPaths) {\n\t\t\t\tconst result = await sniffEntryFile(\n\t\t\t\t\tconfigPath,\n\t\t\t\t\tapp.path,\n\t\t\t\t\tworkspacePath,\n\t\t\t\t);\n\n\t\t\t\tif (logWarnings && result.error) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[sniffer] ${appName}: Config file \"${configPath}\" threw error during sniffing (env vars still captured): ${result.error.message}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tsniffedVars.push(...result.envVars);\n\t\t\t\tsniffedOptional.push(...result.optionalEnvVars);\n\t\t\t}\n\n\t\t\tconst optionalEnvVars = [...new Set(sniffedOptional)];\n\t\t\t// Combine: dependency vars + sniffed vars (deduplicated)\n\t\t\tconst allVars = applyMarkOptional(\n\t\t\t\t[...new Set([...depVars, ...sniffedVars])],\n\t\t\t\toptionalEnvVars,\n\t\t\t\tmarkOptional,\n\t\t\t);\n\t\t\treturn { appName, requiredEnvVars: allVars, optionalEnvVars };\n\t\t}\n\n\t\treturn { appName, requiredEnvVars: depVars, optionalEnvVars: [] };\n\t}\n\n\t// 2. Entry apps - import entry file in subprocess to trigger config.parse()\n\tif (app.entry) {\n\t\tconst result = await sniffEntryFile(app.entry, app.path, workspacePath);\n\n\t\tif (logWarnings && result.error) {\n\t\t\tconsole.warn(\n\t\t\t\t`[sniffer] ${appName}: Entry file threw error during sniffing (env vars still captured): ${result.error.message}`,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tappName,\n\t\t\trequiredEnvVars: applyMarkOptional(\n\t\t\t\tresult.envVars,\n\t\t\t\tresult.optionalEnvVars,\n\t\t\t\tmarkOptional,\n\t\t\t),\n\t\t\toptionalEnvVars: result.optionalEnvVars,\n\t\t};\n\t}\n\n\t// 4. Route-based apps - load routes and call getEnvironment() on each construct\n\tif (app.routes) {\n\t\tconst result = await sniffRouteFiles(\n\t\t\tnormalizeRoutes(app.routes),\n\t\t\tapp.path,\n\t\t\tworkspacePath,\n\t\t);\n\n\t\tif (logWarnings && result.error) {\n\t\t\tconsole.warn(\n\t\t\t\t`[sniffer] ${appName}: Route sniffing threw error (env vars still captured): ${result.error.message}`,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tappName,\n\t\t\trequiredEnvVars: applyMarkOptional(\n\t\t\t\tresult.envVars,\n\t\t\t\tresult.optionalEnvVars,\n\t\t\t\tmarkOptional,\n\t\t\t),\n\t\t\toptionalEnvVars: result.optionalEnvVars,\n\t\t};\n\t}\n\n\t// 5. Apps with envParser but no routes - run sniffer to detect env var usage\n\tif (app.envParser) {\n\t\tconst result = await sniffEnvParser(app.envParser, app.path, workspacePath);\n\n\t\t// Log any issues for debugging\n\t\tif (logWarnings) {\n\t\t\tif (result.error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[sniffer] ${appName}: envParser threw error during sniffing (env vars still captured): ${result.error.message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (result.unhandledRejections.length > 0) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[sniffer] ${appName}: Fire-and-forget rejections during sniffing (suppressed): ${result.unhandledRejections.map((e) => e.message).join(', ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tappName,\n\t\t\trequiredEnvVars: applyMarkOptional(\n\t\t\t\tresult.envVars,\n\t\t\t\tresult.optionalEnvVars,\n\t\t\t\tmarkOptional,\n\t\t\t),\n\t\t\toptionalEnvVars: result.optionalEnvVars,\n\t\t};\n\t}\n\n\t// No env detection method available\n\treturn { appName, requiredEnvVars: [], optionalEnvVars: [] };\n}\n\n/**\n * Apply the `?` suffix to optional variables in an env var list.\n * Only modifies the list when `markOptional` is true and there are optional vars.\n */\nfunction applyMarkOptional(\n\tenvVars: string[],\n\toptionalEnvVars: string[],\n\tmarkOptional: boolean,\n): string[] {\n\tif (!markOptional || optionalEnvVars.length === 0) return envVars;\n\tconst optionalSet = new Set(optionalEnvVars);\n\treturn envVars.map((v) => (optionalSet.has(v) ? `${v}?` : v));\n}\n\n/**\n * Result from sniffing an entry file.\n */\ninterface EntrySniffResult {\n\tenvVars: string[];\n\toptionalEnvVars: string[];\n\terror?: Error;\n}\n\n/**\n * Sniff an entry file by importing it in a subprocess.\n *\n * Entry apps call `config.parse()` at module load time. To capture which\n * env vars are accessed, we:\n * 1. Spawn a subprocess with a module loader hook\n * 2. The loader intercepts `@geekmidas/envkit` and replaces EnvironmentParser\n * with SnifferEnvironmentParser\n * 3. Import the entry file (triggers config.parse())\n * 4. Capture and return the accessed env var names\n *\n * This approach provides process isolation - each app is sniffed in its own\n * subprocess, preventing module cache pollution.\n *\n * @param entryPath - Relative path to the entry file (e.g., './src/index.ts')\n * @param appPath - The app's path relative to workspace (e.g., 'apps/auth')\n * @param workspacePath - Absolute path to workspace root\n * @returns EntrySniffResult with env vars and optional error\n */\nasync function sniffEntryFile(\n\tentryPath: string,\n\tappPath: string,\n\tworkspacePath: string,\n): Promise<EntrySniffResult> {\n\tconst fullEntryPath = resolve(workspacePath, appPath, entryPath);\n\tconst loaderPath = resolveSnifferFile('sniffer-loader');\n\tconst workerPath = resolveSnifferFile('sniffer-worker');\n\n\treturn new Promise((resolvePromise) => {\n\t\tconst child = spawn(\n\t\t\t'node',\n\t\t\t['--import', loaderPath, workerPath, fullEntryPath],\n\t\t\t{\n\t\t\t\tcwd: resolve(workspacePath, appPath),\n\t\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t\t// Ensure tsx is available for TypeScript entry files\n\t\t\t\t\tNODE_OPTIONS: '--import=tsx',\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tlet stdout = '';\n\t\tlet stderr = '';\n\n\t\tchild.stdout.on('data', (data) => {\n\t\t\tstdout += data.toString();\n\t\t});\n\n\t\tchild.stderr.on('data', (data) => {\n\t\t\tstderr += data.toString();\n\t\t});\n\n\t\tchild.on('close', (code) => {\n\t\t\t// Try to parse the JSON output from the worker\n\t\t\ttry {\n\t\t\t\t// Find the last JSON object in stdout (worker may emit other output)\n\t\t\t\tconst jsonMatch = stdout.match(/\\{[^{}]*\"envVars\"[^{}]*\\}[^{]*$/);\n\t\t\t\tif (jsonMatch) {\n\t\t\t\t\tconst result = JSON.parse(jsonMatch[0]);\n\t\t\t\t\tresolvePromise({\n\t\t\t\t\t\tenvVars: result.envVars || [],\n\t\t\t\t\t\toptionalEnvVars: result.optionalEnvVars || [],\n\t\t\t\t\t\terror: result.error ? new Error(result.error) : undefined,\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// JSON parse failed\n\t\t\t}\n\n\t\t\t// If we couldn't parse the output, return empty with error info\n\t\t\tresolvePromise({\n\t\t\t\tenvVars: [],\n\t\t\t\toptionalEnvVars: [],\n\t\t\t\terror: new Error(\n\t\t\t\t\t`Failed to sniff entry file (exit code ${code}): ${stderr || stdout || 'No output'}`,\n\t\t\t\t),\n\t\t\t});\n\t\t});\n\n\t\tchild.on('error', (err) => {\n\t\t\tresolvePromise({\n\t\t\t\tenvVars: [],\n\t\t\t\toptionalEnvVars: [],\n\t\t\t\terror: err,\n\t\t\t});\n\t\t});\n\t});\n}\n\n/**\n * Sniff route files by loading constructs and calling getEnvironment().\n *\n * Route-based apps have endpoints, functions, crons, and subscribers that\n * use services. Each service's register() method accesses environment variables.\n *\n * This runs in a subprocess with tsx loader to properly handle TypeScript\n * compilation and path alias resolution (e.g., `src/...` imports).\n *\n * @param routes - Glob pattern(s) for route files\n * @param appPath - The app's path relative to workspace (e.g., 'apps/api')\n * @param workspacePath - Absolute path to workspace root\n * @returns EntrySniffResult with env vars and optional error\n */\nasync function sniffRouteFiles(\n\troutes: string | string[],\n\tappPath: string,\n\tworkspacePath: string,\n): Promise<EntrySniffResult> {\n\tconst fullAppPath = resolve(workspacePath, appPath);\n\tconst workerPath = resolveSnifferFile('sniffer-routes-worker');\n\tconst tsxPath = resolveTsxPath();\n\n\t// Convert array of patterns to first pattern (worker handles glob internally)\n\tconst routesArray = Array.isArray(routes) ? routes : [routes];\n\tconst pattern = routesArray[0];\n\tif (!pattern) {\n\t\treturn {\n\t\t\tenvVars: [],\n\t\t\toptionalEnvVars: [],\n\t\t\terror: new Error('No route patterns provided'),\n\t\t};\n\t}\n\n\treturn new Promise((resolvePromise) => {\n\t\tconst child = spawn(\n\t\t\t'node',\n\t\t\t['--import', tsxPath, workerPath, fullAppPath, pattern],\n\t\t\t{\n\t\t\t\tcwd: fullAppPath,\n\t\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tlet stdout = '';\n\t\tlet stderr = '';\n\n\t\tchild.stdout.on('data', (data) => {\n\t\t\tstdout += data.toString();\n\t\t});\n\n\t\tchild.stderr.on('data', (data) => {\n\t\t\tstderr += data.toString();\n\t\t});\n\n\t\tchild.on('close', (code) => {\n\t\t\t// Log any stderr output (import errors, etc.)\n\t\t\tif (stderr) {\n\t\t\t\tstderr\n\t\t\t\t\t.split('\\n')\n\t\t\t\t\t.filter((line) => line.trim())\n\t\t\t\t\t.forEach((line) => console.warn(line));\n\t\t\t}\n\n\t\t\t// Try to parse the JSON output from the worker\n\t\t\ttry {\n\t\t\t\t// Find the last JSON object in stdout (worker may emit other output)\n\t\t\t\tconst jsonMatch = stdout.match(/\\{[^{}]*\"envVars\"[^{}]*\\}[^{]*$/);\n\t\t\t\tif (jsonMatch) {\n\t\t\t\t\tconst result = JSON.parse(jsonMatch[0]);\n\t\t\t\t\tresolvePromise({\n\t\t\t\t\t\tenvVars: result.envVars || [],\n\t\t\t\t\t\t// Routes worker uses getEnvironment() which doesn't track\n\t\t\t\t\t\t// optionality yet — populated when service register() is sniffed\n\t\t\t\t\t\toptionalEnvVars: result.optionalEnvVars || [],\n\t\t\t\t\t\terror: result.error ? new Error(result.error) : undefined,\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// JSON parse failed\n\t\t\t}\n\n\t\t\t// If we couldn't parse the output, return empty with error info\n\t\t\tresolvePromise({\n\t\t\t\tenvVars: [],\n\t\t\t\toptionalEnvVars: [],\n\t\t\t\terror: new Error(\n\t\t\t\t\t`Failed to sniff route files (exit code ${code}): ${stderr || stdout || 'No output'}`,\n\t\t\t\t),\n\t\t\t});\n\t\t});\n\n\t\tchild.on('error', (err) => {\n\t\t\tresolvePromise({\n\t\t\t\tenvVars: [],\n\t\t\t\toptionalEnvVars: [],\n\t\t\t\terror: err,\n\t\t\t});\n\t\t});\n\t});\n}\n\n/**\n * Run the SnifferEnvironmentParser on an envParser module to detect\n * which environment variables it accesses.\n *\n * This function handles \"fire and forget\" async operations by using\n * the shared sniffWithFireAndForget utility from @geekmidas/envkit.\n *\n * @param envParserPath - The envParser config (e.g., './src/config/env#envParser')\n * @param appPath - The app's path relative to workspace\n * @param workspacePath - Absolute path to workspace root\n * @returns SniffResult with env vars and any errors encountered\n */\nasync function sniffEnvParser(\n\tenvParserPath: string,\n\tappPath: string,\n\tworkspacePath: string,\n): Promise<SniffResult> {\n\t// Parse the envParser path: './src/config/env#envParser' or './src/config/env'\n\tconst [modulePath, exportName = 'default'] = envParserPath.split('#');\n\tif (!modulePath) {\n\t\treturn { envVars: [], optionalEnvVars: [], unhandledRejections: [] };\n\t}\n\n\t// Resolve the full path to the module\n\tconst fullPath = resolve(workspacePath, appPath, modulePath);\n\n\t// Dynamically import the sniffer utilities\n\tlet SnifferEnvironmentParser: any;\n\tlet sniffWithFireAndForget: any;\n\ttry {\n\t\tconst envkitModule = await import('@geekmidas/envkit/sniffer');\n\t\tSnifferEnvironmentParser = envkitModule.SnifferEnvironmentParser;\n\t\tsniffWithFireAndForget = envkitModule.sniffWithFireAndForget;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.warn(\n\t\t\t`[sniffer] Failed to import SnifferEnvironmentParser: ${message}`,\n\t\t);\n\t\treturn { envVars: [], optionalEnvVars: [], unhandledRejections: [] };\n\t}\n\n\tconst sniffer = new SnifferEnvironmentParser();\n\n\treturn sniffWithFireAndForget(sniffer, async () => {\n\t\t// Import the envParser module\n\t\tconst moduleUrl = pathToFileURL(fullPath).href;\n\t\tconst module = await import(moduleUrl);\n\n\t\t// Get the envParser function\n\t\tconst envParser = module[exportName];\n\t\tif (typeof envParser !== 'function') {\n\t\t\tconsole.warn(\n\t\t\t\t`[sniffer] Export \"${exportName}\" from \"${modulePath}\" is not a function`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// The envParser function typically creates and configures an EnvironmentParser.\n\t\t// We pass our sniffer which implements the same interface.\n\t\tconst result = envParser(sniffer);\n\n\t\t// If the result is a ConfigParser, call parse() to trigger env var access\n\t\tif (result && typeof result.parse === 'function') {\n\t\t\ttry {\n\t\t\t\tresult.parse();\n\t\t\t} catch {\n\t\t\t\t// Parsing may fail due to mock values, that's expected\n\t\t\t}\n\t\t}\n\t});\n}\n\n/**\n * Sniff environment requirements for multiple apps.\n *\n * @param apps - Map of app name to app config\n * @param workspacePath - Absolute path to workspace root\n * @param options - Optional configuration for sniffing behavior\n * @returns Map of app name to sniffed environment\n */\nexport async function sniffAllApps(\n\tapps: Record<string, NormalizedAppConfig>,\n\tworkspacePath: string,\n\toptions: SniffAppOptions = {},\n): Promise<Map<string, SniffedEnvironment>> {\n\tconst results = new Map<string, SniffedEnvironment>();\n\n\tfor (const [appName, app] of Object.entries(apps)) {\n\t\tconst sniffed = await sniffAppEnvironment(\n\t\t\tapp,\n\t\t\tappName,\n\t\t\tworkspacePath,\n\t\t\toptions,\n\t\t);\n\t\tresults.set(appName, sniffed);\n\t}\n\n\treturn results;\n}\n\n// Export for testing\nexport {\n\tsniffEnvParser as _sniffEnvParser,\n\tsniffEntryFile as _sniffEntryFile,\n\tsniffRouteFiles as _sniffRouteFiles,\n};\n","/**\n * Deploy Module\n *\n * Handles deployment of GKM workspaces to various providers (Docker, Dokploy).\n *\n * ## Per-App Database Credentials\n *\n * When deploying to Dokploy with Postgres, this module creates per-app database\n * users with isolated schemas. This follows the same pattern as local dev mode\n * (docker/postgres/init.sh).\n *\n * ### How It Works\n *\n * 1. **Provisioning**: Creates Postgres service with master credentials\n * 2. **User Creation**: For each backend app that needs DATABASE_URL:\n * - Generates a unique password (stored in deploy state)\n * - Creates a database user with that password\n * - Assigns schema permissions based on app name\n * 3. **Schema Assignment**:\n * - `api` app: Uses `public` schema (shared tables)\n * - Other apps (e.g., `auth`): Get their own schema with `search_path` set\n * 4. **Environment Injection**: Each app receives its own DATABASE_URL\n *\n * ### Security\n *\n * - External Postgres port is enabled only during user creation, then disabled\n * - Each app can only access its own schema\n * - Credentials are stored in `.gkm/deploy-{stage}.json` (gitignored)\n * - Subsequent deploys reuse existing credentials from state\n *\n * ### Example Flow\n *\n * ```\n * gkm deploy --stage production\n * ├─ Create Postgres (user: postgres, db: myproject)\n * ├─ Enable external port temporarily\n * ├─ Create user \"api\" → public schema\n * ├─ Create user \"auth\" → auth schema (search_path=auth)\n * ├─ Disable external port\n * ├─ Deploy \"api\" with DATABASE_URL=postgresql://api:xxx@postgres:5432/myproject\n * └─ Deploy \"auth\" with DATABASE_URL=postgresql://auth:yyy@postgres:5432/myproject\n * ```\n *\n * @module deploy\n */\n\nimport { randomBytes } from 'node:crypto';\nimport { stdin as input, stdout as output } from 'node:process';\nimport * as readline from 'node:readline/promises';\nimport { Client as PgClient } from 'pg';\nimport {\n\tgetDokployCredentials,\n\tgetDokployRegistryId,\n\tstoreDokployCredentials,\n\tvalidateDokployToken,\n} from '../auth';\nimport { storeDokployRegistryId } from '../auth/credentials';\nimport { buildCommand } from '../build/index';\nimport { type GkmConfig, loadConfig, loadWorkspaceConfig } from '../config';\nimport { readStageSecrets } from '../secrets/storage.js';\nimport {\n\tgetAppBuildOrder,\n\tgetDeployTargetError,\n\tgetPublicEnvPrefix,\n\tisDeployTargetSupported,\n} from '../workspace/index.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { orchestrateDns, verifyDnsRecords } from './dns/index.js';\nimport { deployDocker, resolveDockerConfig } from './docker';\nimport { deployDokploy } from './dokploy';\nimport {\n\tDokployApi,\n\ttype DokployApplication,\n\ttype DokployPostgres,\n\ttype DokployRedis,\n} from './dokploy-api';\nimport { isMainFrontendApp, resolveHost } from './domain.js';\nimport {\n\ttype EnvResolverContext,\n\tformatMissingVarsError,\n\tvalidateEnvVars,\n} from './env-resolver.js';\nimport { updateConfig } from './init';\nimport { createStateProvider } from './StateProvider.js';\nimport { generateSecretsReport, prepareSecretsForAllApps } from './secrets.js';\nimport { sniffAllApps } from './sniffer.js';\nimport {\n\ttype AppDbCredentials,\n\tcreateEmptyState,\n\tgetAllAppCredentials,\n\tgetApplicationId,\n\tgetBackupState,\n\tgetPostgresId,\n\tgetRedisId,\n\tsetAppCredentials,\n\tsetApplicationId,\n\tsetBackupState,\n\tsetPostgresBackupId,\n\tsetPostgresId,\n\tsetRedisId,\n} from './state.js';\nimport type {\n\tAppDeployResult,\n\tDeployOptions,\n\tDeployProvider,\n\tDeployResult,\n\tDockerDeployConfig,\n\tDokployDeployConfig,\n\tWorkspaceDeployResult,\n} from './types';\n\nconst logger = console;\n\n/**\n * Prompt for input\n */\nasync function prompt(message: string, hidden = false): Promise<string> {\n\tif (!process.stdin.isTTY) {\n\t\tthrow new Error('Interactive input required. Please configure manually.');\n\t}\n\n\tif (hidden) {\n\t\tprocess.stdout.write(message);\n\t\treturn new Promise((resolve) => {\n\t\t\tlet value = '';\n\t\t\tconst onData = (char: Buffer) => {\n\t\t\t\tconst c = char.toString();\n\t\t\t\tif (c === '\\n' || c === '\\r') {\n\t\t\t\t\tprocess.stdin.setRawMode(false);\n\t\t\t\t\tprocess.stdin.pause();\n\t\t\t\t\tprocess.stdin.removeListener('data', onData);\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\tresolve(value);\n\t\t\t\t} else if (c === '\\u0003') {\n\t\t\t\t\tprocess.stdin.setRawMode(false);\n\t\t\t\t\tprocess.stdin.pause();\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t} else if (c === '\\u007F' || c === '\\b') {\n\t\t\t\t\tif (value.length > 0) value = value.slice(0, -1);\n\t\t\t\t} else {\n\t\t\t\t\tvalue += c;\n\t\t\t\t}\n\t\t\t};\n\t\t\tprocess.stdin.setRawMode(true);\n\t\t\tprocess.stdin.resume();\n\t\t\tprocess.stdin.on('data', onData);\n\t\t});\n\t}\n\n\tconst rl = readline.createInterface({ input, output });\n\ttry {\n\t\treturn await rl.question(message);\n\t} finally {\n\t\trl.close();\n\t}\n}\n\n/**\n * Docker compose services that can be provisioned\n */\ninterface DockerComposeServices {\n\tpostgres?: boolean;\n\tredis?: boolean;\n\trabbitmq?: boolean;\n}\n\n/**\n * Service URLs including both connection URLs and individual parameters\n */\ninterface ServiceUrls {\n\tDATABASE_URL?: string;\n\tDATABASE_HOST?: string;\n\tDATABASE_PORT?: string;\n\tDATABASE_NAME?: string;\n\tDATABASE_USER?: string;\n\tDATABASE_PASSWORD?: string;\n\tREDIS_URL?: string;\n\tREDIS_HOST?: string;\n\tREDIS_PORT?: string;\n\tREDIS_PASSWORD?: string;\n}\n\n/**\n * Result of Dokploy setup including provisioned service URLs\n */\ninterface DokploySetupResult {\n\tconfig: DokployDeployConfig;\n\tserviceUrls?: ServiceUrls;\n}\n\n/**\n * Result from provisioning services\n */\nexport interface ProvisionServicesResult {\n\tserviceUrls: ServiceUrls;\n\tserviceIds: {\n\t\tpostgresId?: string;\n\t\tredisId?: string;\n\t};\n}\n\n/**\n * Configuration for a database user to create during Dokploy deployment.\n *\n * @property name - The database username (typically matches the app name)\n * @property password - The generated password for this user\n * @property usePublicSchema - If true, user gets access to public schema (for api app).\n * If false, user gets their own schema with search_path set.\n */\ninterface DbUserConfig {\n\tname: string;\n\tpassword: string;\n\tusePublicSchema: boolean;\n}\n\n/**\n * Wait for Postgres to be ready to accept connections.\n *\n * Polls the Postgres server until it accepts a connection or max retries reached.\n * Used after enabling the external port to ensure the database is accessible\n * before creating users.\n *\n * @param host - The Postgres server hostname\n * @param port - The external port (typically 5432)\n * @param user - Master database user (postgres)\n * @param password - Master database password\n * @param database - Database name to connect to\n * @param maxRetries - Maximum number of connection attempts (default: 30)\n * @param retryIntervalMs - Milliseconds between retries (default: 2000)\n * @throws Error if Postgres is not ready after maxRetries\n */\nasync function waitForPostgres(\n\thost: string,\n\tport: number,\n\tuser: string,\n\tpassword: string,\n\tdatabase: string,\n\tmaxRetries = 30,\n\tretryIntervalMs = 2000,\n): Promise<void> {\n\tfor (let i = 0; i < maxRetries; i++) {\n\t\ttry {\n\t\t\tconst client = new PgClient({ host, port, user, password, database });\n\t\t\tawait client.connect();\n\t\t\tawait client.end();\n\t\t\treturn;\n\t\t} catch {\n\t\t\tif (i < maxRetries - 1) {\n\t\t\t\tlogger.log(` Waiting for Postgres... (${i + 1}/${maxRetries})`);\n\t\t\t\tawait new Promise((r) => setTimeout(r, retryIntervalMs));\n\t\t\t}\n\t\t}\n\t}\n\tthrow new Error(`Postgres not ready after ${maxRetries} retries`);\n}\n\n/**\n * Initialize Postgres with per-app users and schemas.\n *\n * This function implements the same user/schema isolation pattern used in local\n * dev mode (see docker/postgres/init.sh). It:\n *\n * 1. Temporarily enables the external Postgres port\n * 2. Connects using master credentials\n * 3. Creates each user with appropriate schema permissions\n * 4. Disables the external port for security\n *\n * Schema assignment follows this pattern:\n * - `api` app: Uses `public` schema (shared tables, migrations run here)\n * - Other apps: Get their own schema with `search_path` configured\n *\n * @param api - The Dokploy API client\n * @param postgres - The provisioned Postgres service details\n * @param serverHostname - The Dokploy server hostname (for external connection)\n * @param users - Array of users to create with their schema configuration\n *\n * @example\n * ```ts\n * await initializePostgresUsers(api, postgres, 'dokploy.example.com', [\n * { name: 'api', password: 'xxx', usePublicSchema: true },\n * { name: 'auth', password: 'yyy', usePublicSchema: false },\n * ]);\n * ```\n */\nasync function initializePostgresUsers(\n\tapi: DokployApi,\n\tpostgres: DokployPostgres,\n\tserverHostname: string,\n\tusers: DbUserConfig[],\n): Promise<void> {\n\tlogger.log('\\n🔧 Initializing database users...');\n\n\t// Enable external port temporarily\n\tconst externalPort = 5432;\n\tlogger.log(` Enabling external port ${externalPort}...`);\n\tawait api.savePostgresExternalPort(postgres.postgresId, externalPort);\n\n\t// Redeploy to apply external port change\n\tawait api.deployPostgres(postgres.postgresId);\n\n\t// Wait for Postgres to be ready with external port\n\tlogger.log(\n\t\t` Waiting for Postgres to be accessible at ${serverHostname}:${externalPort}...`,\n\t);\n\tawait waitForPostgres(\n\t\tserverHostname,\n\t\texternalPort,\n\t\tpostgres.databaseUser,\n\t\tpostgres.databasePassword,\n\t\tpostgres.databaseName,\n\t);\n\n\t// Connect and create users\n\tconst client = new PgClient({\n\t\thost: serverHostname,\n\t\tport: externalPort,\n\t\tuser: postgres.databaseUser,\n\t\tpassword: postgres.databasePassword,\n\t\tdatabase: postgres.databaseName,\n\t});\n\n\ttry {\n\t\tawait client.connect();\n\n\t\tfor (const user of users) {\n\t\t\tconst schemaName = user.usePublicSchema ? 'public' : user.name;\n\t\t\tlogger.log(\n\t\t\t\t` Creating user \"${user.name}\" with schema \"${schemaName}\"...`,\n\t\t\t);\n\n\t\t\t// Create or update user with all settings in one DO block\n\t\t\t// This avoids \"tuple already updated by self\" errors from multiple ALTER USER calls\n\t\t\tif (user.usePublicSchema) {\n\t\t\t\t// API uses public schema\n\t\t\t\tawait client.query(`\n\t\t\t\t\tDO $$ BEGIN\n\t\t\t\t\t\tIF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${user.name}') THEN\n\t\t\t\t\t\t\tCREATE USER \"${user.name}\" WITH PASSWORD '${user.password}';\n\t\t\t\t\t\tELSE\n\t\t\t\t\t\t\tALTER USER \"${user.name}\" WITH PASSWORD '${user.password}';\n\t\t\t\t\t\tEND IF;\n\t\t\t\t\tEND $$;\n\t\t\t\t`);\n\t\t\t\tawait client.query(`\n\t\t\t\t\tGRANT ALL ON SCHEMA public TO \"${user.name}\";\n\t\t\t\t\tALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO \"${user.name}\";\n\t\t\t\t\tALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO \"${user.name}\";\n\t\t\t\t`);\n\t\t\t} else {\n\t\t\t\t// Other apps get their own schema - combine user creation and search_path in one block\n\t\t\t\tawait client.query(`\n\t\t\t\t\tDO $$ BEGIN\n\t\t\t\t\t\tIF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${user.name}') THEN\n\t\t\t\t\t\t\tCREATE USER \"${user.name}\" WITH PASSWORD '${user.password}';\n\t\t\t\t\t\tELSE\n\t\t\t\t\t\t\tALTER USER \"${user.name}\" WITH PASSWORD '${user.password}';\n\t\t\t\t\t\tEND IF;\n\t\t\t\t\t\t-- Set search_path in same transaction to avoid tuple conflict\n\t\t\t\t\t\tALTER USER \"${user.name}\" SET search_path TO \"${schemaName}\";\n\t\t\t\t\tEND $$;\n\t\t\t\t`);\n\t\t\t\tawait client.query(`\n\t\t\t\t\tCREATE SCHEMA IF NOT EXISTS \"${schemaName}\" AUTHORIZATION \"${user.name}\";\n\t\t\t\t\tGRANT USAGE ON SCHEMA \"${schemaName}\" TO \"${user.name}\";\n\t\t\t\t\tGRANT ALL ON ALL TABLES IN SCHEMA \"${schemaName}\" TO \"${user.name}\";\n\t\t\t\t\tALTER DEFAULT PRIVILEGES IN SCHEMA \"${schemaName}\" GRANT ALL ON TABLES TO \"${user.name}\";\n\t\t\t\t`);\n\t\t\t}\n\n\t\t\tlogger.log(` ✓ User \"${user.name}\" configured`);\n\t\t}\n\t} finally {\n\t\tawait client.end();\n\t}\n\n\t// Disable external port for security\n\tlogger.log(' Disabling external port...');\n\tawait api.savePostgresExternalPort(postgres.postgresId, null);\n\tawait api.deployPostgres(postgres.postgresId);\n\n\tlogger.log(' ✓ Database users initialized');\n}\n\n/**\n * Get the server hostname from the Dokploy endpoint URL\n */\nfunction getServerHostname(endpoint: string): string {\n\tconst url = new URL(endpoint);\n\treturn url.hostname;\n}\n\n/**\n * Build per-app DATABASE_URL for internal Docker network communication.\n *\n * The URL uses the Postgres container name (postgresAppName) as the host,\n * which resolves via Docker's internal DNS when apps are in the same network.\n *\n * @param appName - The database username (matches the app name)\n * @param appPassword - The app's database password\n * @param postgresAppName - The Postgres container/service name in Dokploy\n * @param databaseName - The database name (typically the project name)\n * @returns A properly encoded PostgreSQL connection URL\n *\n * @example\n * ```ts\n * const url = buildPerAppDatabaseUrl('api', 'secret123', 'postgres-abc', 'myproject');\n * // Returns: postgresql://api:secret123@postgres-abc:5432/myproject\n * ```\n */\nfunction _buildPerAppDatabaseUrl(\n\tappName: string,\n\tappPassword: string,\n\tpostgresAppName: string,\n\tdatabaseName: string,\n): string {\n\treturn `postgresql://${appName}:${encodeURIComponent(appPassword)}@${postgresAppName}:5432/${databaseName}`;\n}\n\n/**\n * Provision docker compose services in Dokploy\n * @internal Exported for testing\n */\nexport async function provisionServices(\n\tapi: DokployApi,\n\tprojectId: string,\n\tenvironmentId: string | undefined,\n\tprojectName: string,\n\tservices?: DockerComposeServices,\n\texistingServiceIds?: { postgresId?: string; redisId?: string },\n): Promise<ProvisionServicesResult | undefined> {\n\tlogger.log(\n\t\t`\\n🔍 provisionServices called: services=${JSON.stringify(services)}, envId=${environmentId}`,\n\t);\n\tif (!services || !environmentId) {\n\t\tlogger.log(' Skipping: no services or no environmentId');\n\t\treturn undefined;\n\t}\n\n\tconst serviceUrls: ServiceUrls = {};\n\tconst serviceIds: { postgresId?: string; redisId?: string } = {};\n\n\tif (services.postgres) {\n\t\tlogger.log('\\n🐘 Checking PostgreSQL...');\n\t\tconst postgresName = 'db';\n\n\t\ttry {\n\t\t\tlet postgres: DokployPostgres | null = null;\n\t\t\tlet created = false;\n\n\t\t\t// Check if we have an existing ID from state\n\t\t\tif (existingServiceIds?.postgresId) {\n\t\t\t\tlogger.log(` Using cached ID: ${existingServiceIds.postgresId}`);\n\t\t\t\tpostgres = await api.getPostgres(existingServiceIds.postgresId);\n\t\t\t\tif (postgres) {\n\t\t\t\t\tlogger.log(` ✓ PostgreSQL found: ${postgres.postgresId}`);\n\t\t\t\t} else {\n\t\t\t\t\tlogger.log(` ⚠ Cached ID invalid, will create new`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If not found by ID, use findOrCreate\n\t\t\tif (!postgres) {\n\t\t\t\tconst databasePassword = randomBytes(16).toString('hex');\n\t\t\t\t// Use project name as database name (replace hyphens with underscores for PostgreSQL)\n\t\t\t\tconst databaseName = projectName.replace(/-/g, '_');\n\n\t\t\t\tconst result = await api.findOrCreatePostgres(\n\t\t\t\t\tpostgresName,\n\t\t\t\t\tprojectId,\n\t\t\t\t\tenvironmentId,\n\t\t\t\t\t{ databaseName, databasePassword },\n\t\t\t\t);\n\t\t\t\tpostgres = result.postgres;\n\t\t\t\tcreated = result.created;\n\n\t\t\t\tif (created) {\n\t\t\t\t\tlogger.log(` ✓ Created PostgreSQL: ${postgres.postgresId}`);\n\n\t\t\t\t\t// Deploy the database (only for new instances)\n\t\t\t\t\tawait api.deployPostgres(postgres.postgresId);\n\t\t\t\t\tlogger.log(' ✓ PostgreSQL deployed');\n\t\t\t\t} else {\n\t\t\t\t\tlogger.log(` ✓ PostgreSQL already exists: ${postgres.postgresId}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Store the ID for state\n\t\t\tserviceIds.postgresId = postgres.postgresId;\n\n\t\t\t// Store individual connection parameters\n\t\t\tserviceUrls.DATABASE_HOST = postgres.appName;\n\t\t\tserviceUrls.DATABASE_PORT = '5432';\n\t\t\tserviceUrls.DATABASE_NAME = postgres.databaseName;\n\t\t\tserviceUrls.DATABASE_USER = postgres.databaseUser;\n\t\t\tserviceUrls.DATABASE_PASSWORD = postgres.databasePassword;\n\n\t\t\t// Construct connection URL using internal docker network hostname\n\t\t\tserviceUrls.DATABASE_URL = `postgresql://${postgres.databaseUser}:${postgres.databasePassword}@${postgres.appName}:5432/${postgres.databaseName}`;\n\t\t\tlogger.log(` ✓ Database credentials configured`);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : 'Unknown error';\n\t\t\tlogger.log(` ⚠ Failed to provision PostgreSQL: ${message}`);\n\t\t}\n\t}\n\n\tif (services.redis) {\n\t\tlogger.log('\\n🔴 Checking Redis...');\n\t\tconst redisName = 'cache';\n\n\t\ttry {\n\t\t\tlet redis: DokployRedis | null = null;\n\t\t\tlet created = false;\n\n\t\t\t// Check if we have an existing ID from state\n\t\t\tif (existingServiceIds?.redisId) {\n\t\t\t\tlogger.log(` Using cached ID: ${existingServiceIds.redisId}`);\n\t\t\t\tredis = await api.getRedis(existingServiceIds.redisId);\n\t\t\t\tif (redis) {\n\t\t\t\t\tlogger.log(` ✓ Redis found: ${redis.redisId}`);\n\t\t\t\t} else {\n\t\t\t\t\tlogger.log(` ⚠ Cached ID invalid, will create new`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If not found by ID, use findOrCreate\n\t\t\tif (!redis) {\n\t\t\t\tconst { randomBytes } = await import('node:crypto');\n\t\t\t\tconst databasePassword = randomBytes(16).toString('hex');\n\n\t\t\t\tconst result = await api.findOrCreateRedis(\n\t\t\t\t\tredisName,\n\t\t\t\t\tprojectId,\n\t\t\t\t\tenvironmentId,\n\t\t\t\t\t{ databasePassword },\n\t\t\t\t);\n\t\t\t\tredis = result.redis;\n\t\t\t\tcreated = result.created;\n\n\t\t\t\tif (created) {\n\t\t\t\t\tlogger.log(` ✓ Created Redis: ${redis.redisId}`);\n\n\t\t\t\t\t// Deploy the redis instance (only for new instances)\n\t\t\t\t\tawait api.deployRedis(redis.redisId);\n\t\t\t\t\tlogger.log(' ✓ Redis deployed');\n\t\t\t\t} else {\n\t\t\t\t\tlogger.log(` ✓ Redis already exists: ${redis.redisId}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Store the ID for state\n\t\t\tserviceIds.redisId = redis.redisId;\n\n\t\t\t// Store individual connection parameters\n\t\t\tserviceUrls.REDIS_HOST = redis.appName;\n\t\t\tserviceUrls.REDIS_PORT = '6379';\n\t\t\tif (redis.databasePassword) {\n\t\t\t\tserviceUrls.REDIS_PASSWORD = redis.databasePassword;\n\t\t\t}\n\n\t\t\t// Construct connection URL\n\t\t\tconst password = redis.databasePassword\n\t\t\t\t? `:${redis.databasePassword}@`\n\t\t\t\t: '';\n\t\t\tserviceUrls.REDIS_URL = `redis://${password}${redis.appName}:6379`;\n\t\t\tlogger.log(` ✓ Redis credentials configured`);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : 'Unknown error';\n\t\t\tlogger.log(` ⚠ Failed to provision Redis: ${message}`);\n\t\t}\n\t}\n\n\treturn Object.keys(serviceUrls).length > 0\n\t\t? { serviceUrls, serviceIds }\n\t\t: undefined;\n}\n\n/**\n * Ensure Dokploy is fully configured, recovering/creating resources as needed\n */\nasync function ensureDokploySetup(\n\tconfig: GkmConfig,\n\tdockerConfig: DockerDeployConfig,\n\tstage: string,\n\tservices?: DockerComposeServices,\n): Promise<DokploySetupResult> {\n\tlogger.log('\\n🔧 Checking Dokploy setup...');\n\n\t// Step 1: Ensure we have Dokploy credentials\n\tlet creds = await getDokployCredentials();\n\n\tif (!creds) {\n\t\tlogger.log(\"\\n📋 Dokploy credentials not found. Let's set them up.\");\n\t\tconst endpoint = await prompt(\n\t\t\t'Dokploy URL (e.g., https://dokploy.example.com): ',\n\t\t);\n\t\tconst normalizedEndpoint = endpoint.replace(/\\/$/, '');\n\n\t\ttry {\n\t\t\tnew URL(normalizedEndpoint);\n\t\t} catch {\n\t\t\tthrow new Error('Invalid URL format');\n\t\t}\n\n\t\tlogger.log(\n\t\t\t`\\nGenerate a token at: ${normalizedEndpoint}/settings/profile\\n`,\n\t\t);\n\t\tconst token = await prompt('API Token: ', true);\n\n\t\tlogger.log('\\nValidating credentials...');\n\t\tconst isValid = await validateDokployToken(normalizedEndpoint, token);\n\t\tif (!isValid) {\n\t\t\tthrow new Error('Invalid credentials. Please check your token.');\n\t\t}\n\n\t\tawait storeDokployCredentials(token, normalizedEndpoint);\n\t\tcreds = { token, endpoint: normalizedEndpoint };\n\t\tlogger.log('✓ Credentials saved');\n\t}\n\n\tconst api = new DokployApi({ baseUrl: creds.endpoint, token: creds.token });\n\n\t// Step 2: Check if we have config in gkm.config.ts\n\tconst existingConfig = config.providers?.dokploy;\n\tif (\n\t\texistingConfig &&\n\t\ttypeof existingConfig !== 'boolean' &&\n\t\texistingConfig.applicationId &&\n\t\texistingConfig.projectId\n\t) {\n\t\tlogger.log('✓ Dokploy config found in gkm.config.ts');\n\n\t\t// Verify the application still exists\n\t\ttry {\n\t\t\tconst projectDetails = await api.getProject(existingConfig.projectId);\n\t\t\tlogger.log('✓ Project verified');\n\n\t\t\t// Get registry ID from config first, then from local storage\n\t\t\tconst storedRegistryId =\n\t\t\t\texistingConfig.registryId ?? (await getDokployRegistryId());\n\n\t\t\t// Get environment ID for service provisioning (match by stage name)\n\t\t\tconst environments = projectDetails.environments ?? [];\n\t\t\tlet environment = environments.find(\n\t\t\t\t(e) => e.name.toLowerCase() === stage.toLowerCase(),\n\t\t\t);\n\n\t\t\t// Create environment if it doesn't exist for this stage\n\t\t\tif (!environment) {\n\t\t\t\tlogger.log(` Creating \"${stage}\" environment...`);\n\t\t\t\tenvironment = await api.createEnvironment(\n\t\t\t\t\texistingConfig.projectId,\n\t\t\t\t\tstage,\n\t\t\t\t);\n\t\t\t\tlogger.log(` ✓ Created environment: ${environment.environmentId}`);\n\t\t\t}\n\n\t\t\tconst environmentId = environment.environmentId;\n\n\t\t\t// Provision services if configured\n\t\t\tlogger.log(\n\t\t\t\t` Services config: ${JSON.stringify(services)}, envId: ${environmentId}`,\n\t\t\t);\n\t\t\t// For single-app mode, we don't have state persistence yet, so pass undefined\n\t\t\tconst provisionResult = await provisionServices(\n\t\t\t\tapi,\n\t\t\t\texistingConfig.projectId,\n\t\t\t\tenvironmentId,\n\t\t\t\tdockerConfig.appName!,\n\t\t\t\tservices,\n\t\t\t\tundefined, // No state in single-app mode\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tconfig: {\n\t\t\t\t\tendpoint: existingConfig.endpoint,\n\t\t\t\t\tprojectId: existingConfig.projectId,\n\t\t\t\t\tapplicationId: existingConfig.applicationId,\n\t\t\t\t\tregistry: existingConfig.registry,\n\t\t\t\t\tregistryId: storedRegistryId ?? undefined,\n\t\t\t\t},\n\t\t\t\tserviceUrls: provisionResult?.serviceUrls,\n\t\t\t};\n\t\t} catch {\n\t\t\tlogger.log('⚠ Project not found, will recover...');\n\t\t}\n\t}\n\n\t// Step 3: Find or create project\n\tlogger.log('\\n📁 Looking for project...');\n\tconst projectName = dockerConfig.projectName!;\n\tconst projects = await api.listProjects();\n\tlet project = projects.find(\n\t\t(p) => p.name.toLowerCase() === projectName.toLowerCase(),\n\t);\n\n\tlet environmentId: string;\n\n\tif (project) {\n\t\tlogger.log(\n\t\t\t` Found existing project: ${project.name} (${project.projectId})`,\n\t\t);\n\n\t\t// Step 4: Get or create environment for existing project (match by stage)\n\t\tconst projectDetails = await api.getProject(project.projectId);\n\t\tconst environments = projectDetails.environments ?? [];\n\t\tconst matchingEnv = environments.find(\n\t\t\t(e) => e.name.toLowerCase() === stage.toLowerCase(),\n\t\t);\n\t\tif (matchingEnv) {\n\t\t\tenvironmentId = matchingEnv.environmentId;\n\t\t\tlogger.log(` Using environment: ${matchingEnv.name}`);\n\t\t} else {\n\t\t\tlogger.log(` Creating \"${stage}\" environment...`);\n\t\t\tconst env = await api.createEnvironment(project.projectId, stage);\n\t\t\tenvironmentId = env.environmentId;\n\t\t\tlogger.log(` ✓ Created environment: ${stage}`);\n\t\t}\n\t} else {\n\t\tlogger.log(` Creating project: ${projectName}`);\n\t\tconst result = await api.createProject(projectName);\n\t\tproject = result.project;\n\t\t// Rename the default environment to match stage if different\n\t\tif (result.environment.name.toLowerCase() !== stage.toLowerCase()) {\n\t\t\tlogger.log(` Creating \"${stage}\" environment...`);\n\t\t\tconst env = await api.createEnvironment(project.projectId, stage);\n\t\t\tenvironmentId = env.environmentId;\n\t\t} else {\n\t\t\tenvironmentId = result.environment.environmentId;\n\t\t}\n\t\tlogger.log(` ✓ Created project: ${project.projectId}`);\n\t\tlogger.log(` ✓ Using environment: ${stage}`);\n\t}\n\n\t// Step 5: Find or create application\n\tlogger.log('\\n📦 Looking for application...');\n\tconst appName = dockerConfig.appName!;\n\n\tlet applicationId: string;\n\n\t// Try to find existing app from config\n\tif (\n\t\texistingConfig &&\n\t\ttypeof existingConfig !== 'boolean' &&\n\t\texistingConfig.applicationId\n\t) {\n\t\tapplicationId = existingConfig.applicationId;\n\t\tlogger.log(` Using application from config: ${applicationId}`);\n\t} else {\n\t\t// Create new application\n\t\tlogger.log(` Creating application: ${appName}`);\n\t\tconst app = await api.createApplication(\n\t\t\tappName,\n\t\t\tproject.projectId,\n\t\t\tenvironmentId,\n\t\t);\n\t\tapplicationId = app.applicationId;\n\t\tlogger.log(` ✓ Created application: ${applicationId}`);\n\t}\n\n\t// Step 6: Ensure registry is set up\n\tlogger.log('\\n🐳 Checking registry...');\n\tlet registryId = await getDokployRegistryId();\n\n\tif (registryId) {\n\t\t// Verify stored registry still exists\n\t\ttry {\n\t\t\tconst registry = await api.getRegistry(registryId);\n\t\t\tlogger.log(` Using registry: ${registry.registryName}`);\n\t\t} catch {\n\t\t\tlogger.log(' ⚠ Stored registry not found, clearing...');\n\t\t\tregistryId = undefined;\n\t\t\tawait storeDokployRegistryId('');\n\t\t}\n\t}\n\n\tif (!registryId) {\n\t\tconst registries = await api.listRegistries();\n\n\t\tif (registries.length === 0) {\n\t\t\t// No registries exist\n\t\t\tif (dockerConfig.registry) {\n\t\t\t\tlogger.log(\" No registries found in Dokploy. Let's create one.\");\n\t\t\t\tlogger.log(` Registry URL: ${dockerConfig.registry}`);\n\n\t\t\t\tconst username = await prompt('Registry username: ');\n\t\t\t\tconst password = await prompt('Registry password/token: ', true);\n\n\t\t\t\tconst registry = await api.createRegistry(\n\t\t\t\t\t'Default Registry',\n\t\t\t\t\tdockerConfig.registry,\n\t\t\t\t\tusername,\n\t\t\t\t\tpassword,\n\t\t\t\t);\n\t\t\t\tregistryId = registry.registryId;\n\t\t\t\tawait storeDokployRegistryId(registryId);\n\t\t\t\tlogger.log(` ✓ Registry created: ${registryId}`);\n\t\t\t} else {\n\t\t\t\tlogger.log(\n\t\t\t\t\t' ⚠ No registry configured. Set docker.registry in gkm.config.ts',\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\t// Show available registries and let user select or create new\n\t\t\tlogger.log(' Available registries:');\n\t\t\tregistries.forEach((reg, i) => {\n\t\t\t\tlogger.log(` ${i + 1}. ${reg.registryName} (${reg.registryUrl})`);\n\t\t\t});\n\t\t\tif (dockerConfig.registry) {\n\t\t\t\tlogger.log(` ${registries.length + 1}. Create new registry`);\n\t\t\t}\n\n\t\t\tconst maxOption = dockerConfig.registry\n\t\t\t\t? registries.length + 1\n\t\t\t\t: registries.length;\n\t\t\tconst selection = await prompt(` Select registry (1-${maxOption}): `);\n\t\t\tconst index = parseInt(selection, 10) - 1;\n\n\t\t\tif (index >= 0 && index < registries.length) {\n\t\t\t\t// Selected existing registry\n\t\t\t\tregistryId = registries[index]!.registryId;\n\t\t\t\tawait storeDokployRegistryId(registryId);\n\t\t\t\tlogger.log(` ✓ Selected: ${registries[index]!.registryName}`);\n\t\t\t} else if (dockerConfig.registry && index === registries.length) {\n\t\t\t\t// Create new registry\n\t\t\t\tlogger.log(`\\n Creating new registry...`);\n\t\t\t\tlogger.log(` Registry URL: ${dockerConfig.registry}`);\n\n\t\t\t\tconst username = await prompt(' Registry username: ');\n\t\t\t\tconst password = await prompt(' Registry password/token: ', true);\n\n\t\t\t\tconst registry = await api.createRegistry(\n\t\t\t\t\tdockerConfig.registry.replace(/^https?:\\/\\//, ''),\n\t\t\t\t\tdockerConfig.registry,\n\t\t\t\t\tusername,\n\t\t\t\t\tpassword,\n\t\t\t\t);\n\t\t\t\tregistryId = registry.registryId;\n\t\t\t\tawait storeDokployRegistryId(registryId);\n\t\t\t\tlogger.log(` ✓ Registry created: ${registryId}`);\n\t\t\t} else {\n\t\t\t\tlogger.log(' ⚠ Invalid selection, skipping registry setup');\n\t\t\t}\n\t\t}\n\t}\n\n\t// Step 7: Build and save config\n\tconst dokployConfig: DokployDeployConfig = {\n\t\tendpoint: creds.endpoint,\n\t\tprojectId: project.projectId,\n\t\tapplicationId,\n\t\tregistryId: registryId ?? undefined,\n\t};\n\n\t// Update gkm.config.ts\n\tawait updateConfig(dokployConfig);\n\n\tlogger.log('\\n✅ Dokploy setup complete!');\n\tlogger.log(` Project: ${project.projectId}`);\n\tlogger.log(` Application: ${applicationId}`);\n\tif (registryId) {\n\t\tlogger.log(` Registry: ${registryId}`);\n\t}\n\n\t// Step 8: Provision docker compose services if configured\n\t// For single-app mode, we don't have state persistence yet, so pass undefined\n\tconst provisionResult = await provisionServices(\n\t\tapi,\n\t\tproject.projectId,\n\t\tenvironmentId,\n\t\tdockerConfig.appName!,\n\t\tservices,\n\t\tundefined, // No state in single-app mode\n\t);\n\n\treturn {\n\t\tconfig: dokployConfig,\n\t\tserviceUrls: provisionResult?.serviceUrls,\n\t};\n}\n\n/**\n * Generate image tag from stage and timestamp\n */\nexport function generateTag(stage: string): string {\n\tconst timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);\n\treturn `${stage}-${timestamp}`;\n}\n\n/**\n * Deploy all apps in a workspace to Dokploy.\n *\n * Two-phase orchestration:\n * - PHASE 1: Deploy backend apps (with encrypted secrets)\n * - PHASE 2: Deploy frontend apps (with public URLs from backends)\n *\n * Security model:\n * - Backend apps get encrypted secrets embedded at build time\n * - Only GKM_MASTER_KEY is injected as Dokploy env var\n * - Frontend apps get public URLs baked in at build time (no secrets)\n *\n * @internal Exported for testing\n */\nexport async function workspaceDeployCommand(\n\tworkspace: NormalizedWorkspace,\n\toptions: DeployOptions,\n): Promise<WorkspaceDeployResult> {\n\tconst { provider, stage, tag, apps: selectedApps } = options;\n\n\tif (provider !== 'dokploy') {\n\t\tthrow new Error(\n\t\t\t`Workspace deployment only supports Dokploy. Got: ${provider}`,\n\t\t);\n\t}\n\n\tlogger.log(`\\n🚀 Deploying workspace \"${workspace.name}\" to Dokploy...`);\n\tlogger.log(` Stage: ${stage}`);\n\n\t// Generate tag if not provided\n\tconst imageTag = tag ?? generateTag(stage);\n\tlogger.log(` Tag: ${imageTag}`);\n\n\t// Get apps to deploy in dependency order\n\tconst buildOrder = getAppBuildOrder(workspace);\n\n\t// Filter to selected apps if specified\n\tlet appsToDeployNames = buildOrder;\n\tif (selectedApps && selectedApps.length > 0) {\n\t\t// Validate selected apps exist\n\t\tconst invalidApps = selectedApps.filter((name) => !workspace.apps[name]);\n\t\tif (invalidApps.length > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Unknown apps: ${invalidApps.join(', ')}\\n` +\n\t\t\t\t\t`Available apps: ${Object.keys(workspace.apps).join(', ')}`,\n\t\t\t);\n\t\t}\n\t\t// Keep only selected apps, but maintain dependency order\n\t\tappsToDeployNames = buildOrder.filter((name) =>\n\t\t\tselectedApps.includes(name),\n\t\t);\n\t\tlogger.log(` Deploying apps: ${appsToDeployNames.join(', ')}`);\n\t} else {\n\t\tlogger.log(` Deploying all apps: ${appsToDeployNames.join(', ')}`);\n\t}\n\n\t// Filter apps by deploy target\n\tconst dokployApps = appsToDeployNames.filter((name) => {\n\t\tconst app = workspace.apps[name]!;\n\t\tconst target = app.resolvedDeployTarget;\n\t\tif (!isDeployTargetSupported(target)) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠️ Skipping ${name}: ${getDeployTargetError(target, name)}`,\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t});\n\n\tif (dokployApps.length === 0) {\n\t\tthrow new Error(\n\t\t\t'No apps to deploy. All selected apps have unsupported deploy targets.',\n\t\t);\n\t}\n\n\tappsToDeployNames = dokployApps;\n\n\t// ==================================================================\n\t// PREFLIGHT: Load secrets and sniff environment requirements\n\t// ==================================================================\n\tlogger.log('\\n🔐 Loading secrets and analyzing environment requirements...');\n\n\t// Load secrets for this stage\n\tconst stageSecrets = await readStageSecrets(stage, workspace.root);\n\tif (!stageSecrets) {\n\t\tlogger.log(` ⚠️ No secrets found for stage \"${stage}\"`);\n\t\tlogger.log(\n\t\t\t` Run \"gkm secrets:init --stage ${stage}\" to create secrets`,\n\t\t);\n\t}\n\n\t// Sniff environment variables for all apps\n\tconst sniffedApps = await sniffAllApps(workspace.apps, workspace.root);\n\n\t// Prepare encrypted secrets for backend apps\n\tconst encryptedSecrets = stageSecrets\n\t\t? prepareSecretsForAllApps(stageSecrets, sniffedApps)\n\t\t: new Map();\n\n\t// Report on secrets preparation\n\tif (stageSecrets) {\n\t\tconst report = generateSecretsReport(encryptedSecrets, sniffedApps);\n\t\tif (report.appsWithSecrets.length > 0) {\n\t\t\tlogger.log(\n\t\t\t\t` ✓ Encrypted secrets for: ${report.appsWithSecrets.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (report.appsWithMissingSecrets.length > 0) {\n\t\t\tfor (const { appName, missing } of report.appsWithMissingSecrets) {\n\t\t\t\tlogger.log(` ⚠️ ${appName}: Missing secrets: ${missing.join(', ')}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// SETUP: Credentials, Project, Registry\n\t// ==================================================================\n\tlet creds = await getDokployCredentials();\n\tif (!creds) {\n\t\tlogger.log(\"\\n📋 Dokploy credentials not found. Let's set them up.\");\n\t\tconst endpoint = await prompt(\n\t\t\t'Dokploy URL (e.g., https://dokploy.example.com): ',\n\t\t);\n\t\tconst normalizedEndpoint = endpoint.replace(/\\/$/, '');\n\n\t\ttry {\n\t\t\tnew URL(normalizedEndpoint);\n\t\t} catch {\n\t\t\tthrow new Error('Invalid URL format');\n\t\t}\n\n\t\tlogger.log(\n\t\t\t`\\nGenerate a token at: ${normalizedEndpoint}/settings/profile\\n`,\n\t\t);\n\t\tconst token = await prompt('API Token: ', true);\n\n\t\tlogger.log('\\nValidating credentials...');\n\t\tconst isValid = await validateDokployToken(normalizedEndpoint, token);\n\t\tif (!isValid) {\n\t\t\tthrow new Error('Invalid credentials. Please check your token.');\n\t\t}\n\n\t\tawait storeDokployCredentials(token, normalizedEndpoint);\n\t\tcreds = { token, endpoint: normalizedEndpoint };\n\t\tlogger.log('✓ Credentials saved');\n\t}\n\n\tconst api = new DokployApi({ baseUrl: creds.endpoint, token: creds.token });\n\n\t// Find or create project for the workspace\n\tlogger.log('\\n📁 Setting up Dokploy project...');\n\tconst projectName = workspace.name;\n\tconst projects = await api.listProjects();\n\tlet project = projects.find(\n\t\t(p) => p.name.toLowerCase() === projectName.toLowerCase(),\n\t);\n\n\tlet environmentId: string;\n\n\tif (project) {\n\t\tlogger.log(` Found existing project: ${project.name}`);\n\t\tconst projectDetails = await api.getProject(project.projectId);\n\t\tconst environments = projectDetails.environments ?? [];\n\t\tconst matchingEnv = environments.find(\n\t\t\t(e) => e.name.toLowerCase() === stage.toLowerCase(),\n\t\t);\n\t\tif (matchingEnv) {\n\t\t\tenvironmentId = matchingEnv.environmentId;\n\t\t\tlogger.log(` Using environment: ${matchingEnv.name}`);\n\t\t} else {\n\t\t\tlogger.log(` Creating \"${stage}\" environment...`);\n\t\t\tconst env = await api.createEnvironment(project.projectId, stage);\n\t\t\tenvironmentId = env.environmentId;\n\t\t\tlogger.log(` ✓ Created environment: ${stage}`);\n\t\t}\n\t} else {\n\t\tlogger.log(` Creating project: ${projectName}`);\n\t\tconst result = await api.createProject(projectName);\n\t\tproject = result.project;\n\t\tif (result.environment.name.toLowerCase() !== stage.toLowerCase()) {\n\t\t\tlogger.log(` Creating \"${stage}\" environment...`);\n\t\t\tconst env = await api.createEnvironment(project.projectId, stage);\n\t\t\tenvironmentId = env.environmentId;\n\t\t} else {\n\t\t\tenvironmentId = result.environment.environmentId;\n\t\t}\n\t\tlogger.log(` ✓ Created project: ${project.projectId}`);\n\t}\n\n\t// ==================================================================\n\t// STATE: Create state provider and load deploy state\n\t// ==================================================================\n\tlogger.log('\\n📋 Loading deploy state...');\n\n\t// Create state provider based on workspace config\n\tconst stateProvider = await createStateProvider({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\n\tlet state = await stateProvider.read(stage);\n\n\tif (state) {\n\t\tlogger.log(` Found existing state for stage \"${stage}\"`);\n\t\t// Verify project ID matches (in case of recreation)\n\t\tif (state.projectId !== project.projectId) {\n\t\t\tlogger.log(` ⚠ Project ID changed, updating state`);\n\t\t\tstate.projectId = project.projectId;\n\t\t}\n\t\t// Verify environment ID matches (in case of recreation)\n\t\tif (state.environmentId !== environmentId) {\n\t\t\tlogger.log(` ⚠ Environment ID changed, updating state`);\n\t\t\tstate.environmentId = environmentId;\n\t\t}\n\t} else {\n\t\tlogger.log(` Creating new state for stage \"${stage}\"`);\n\t\tstate = createEmptyState(stage, project.projectId, environmentId);\n\t}\n\n\t// Get or set up registry\n\tlogger.log('\\n🐳 Checking registry...');\n\tlet registryId = await getDokployRegistryId();\n\tconst registry = workspace.deploy.dokploy?.registry;\n\n\tif (registryId) {\n\t\ttry {\n\t\t\tconst reg = await api.getRegistry(registryId);\n\t\t\tlogger.log(` Using registry: ${reg.registryName}`);\n\t\t} catch {\n\t\t\tlogger.log(' ⚠ Stored registry not found, clearing...');\n\t\t\tregistryId = undefined;\n\t\t\tawait storeDokployRegistryId('');\n\t\t}\n\t}\n\n\tif (!registryId) {\n\t\tconst registries = await api.listRegistries();\n\t\tif (registries.length > 0) {\n\t\t\tregistryId = registries[0]!.registryId;\n\t\t\tawait storeDokployRegistryId(registryId);\n\t\t\tlogger.log(` Using registry: ${registries[0]!.registryName}`);\n\t\t} else if (registry) {\n\t\t\tlogger.log(\" No registries found in Dokploy. Let's create one.\");\n\t\t\tlogger.log(` Registry URL: ${registry}`);\n\n\t\t\tconst username = await prompt('Registry username: ');\n\t\t\tconst password = await prompt('Registry password/token: ', true);\n\n\t\t\tconst reg = await api.createRegistry(\n\t\t\t\t'Default Registry',\n\t\t\t\tregistry,\n\t\t\t\tusername,\n\t\t\t\tpassword,\n\t\t\t);\n\t\t\tregistryId = reg.registryId;\n\t\t\tawait storeDokployRegistryId(registryId);\n\t\t\tlogger.log(` ✓ Registry created: ${registryId}`);\n\t\t} else {\n\t\t\tlogger.log(\n\t\t\t\t' ⚠ No registry configured. Set deploy.dokploy.registry in workspace config',\n\t\t\t);\n\t\t}\n\t}\n\n\t// Provision infrastructure services if configured\n\tconst services = workspace.services;\n\tconst dockerServices = {\n\t\tpostgres: services.db !== undefined && services.db !== false,\n\t\tredis: services.cache !== undefined && services.cache !== false,\n\t};\n\n\t// Track provisioned postgres info for per-app DATABASE_URL\n\tlet provisionedPostgres: DokployPostgres | null = null;\n\tlet provisionedRedis: DokployRedis | null = null;\n\n\tif (dockerServices.postgres || dockerServices.redis) {\n\t\tlogger.log('\\n🔧 Provisioning infrastructure services...');\n\t\t// Pass existing service IDs from state (prefer state over URL sniffing)\n\t\tconst existingServiceIds = {\n\t\t\tpostgresId: getPostgresId(state),\n\t\t\tredisId: getRedisId(state),\n\t\t};\n\n\t\tconst provisionResult = await provisionServices(\n\t\t\tapi,\n\t\t\tproject.projectId,\n\t\t\tenvironmentId,\n\t\t\tworkspace.name,\n\t\t\tdockerServices,\n\t\t\texistingServiceIds,\n\t\t);\n\n\t\t// Update state with returned service IDs\n\t\tif (provisionResult?.serviceIds) {\n\t\t\tif (provisionResult.serviceIds.postgresId) {\n\t\t\t\tsetPostgresId(state, provisionResult.serviceIds.postgresId);\n\t\t\t\t// Fetch full postgres info for later use\n\t\t\t\tprovisionedPostgres = await api.getPostgres(\n\t\t\t\t\tprovisionResult.serviceIds.postgresId,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (provisionResult.serviceIds.redisId) {\n\t\t\t\tsetRedisId(state, provisionResult.serviceIds.redisId);\n\t\t\t\t// Fetch full redis info for later use\n\t\t\t\tprovisionedRedis = await api.getRedis(\n\t\t\t\t\tprovisionResult.serviceIds.redisId,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// Separate apps by type for two-phase deployment\n\t// ==================================================================\n\t// Mobile apps deploy via their own toolchain (e.g. EAS Build for Expo)\n\t// — skip them in the Dokploy deploy phases.\n\tconst skippedMobileApps = appsToDeployNames.filter(\n\t\t(name) => workspace.apps[name]!.type === 'mobile',\n\t);\n\tif (skippedMobileApps.length > 0) {\n\t\tlogger.log(\n\t\t\t`\\n📱 Skipping ${skippedMobileApps.length} mobile app(s) — deploy via framework toolchain: ${skippedMobileApps.join(', ')}`,\n\t\t);\n\t}\n\n\tconst backendApps = appsToDeployNames.filter(\n\t\t(name) => workspace.apps[name]!.type === 'backend',\n\t);\n\tconst frontendApps = appsToDeployNames.filter(\n\t\t(name) => workspace.apps[name]!.type === 'web',\n\t);\n\n\t// ==================================================================\n\t// Initialize per-app database users if Postgres is provisioned\n\t// ==================================================================\n\tconst perAppDbCredentials = new Map<string, AppDbCredentials>();\n\n\tif (provisionedPostgres && backendApps.length > 0) {\n\t\t// Determine which backend apps need DATABASE_URL\n\t\tconst appsNeedingDb = backendApps.filter((appName) => {\n\t\t\tconst requirements = sniffedApps.get(appName);\n\t\t\treturn requirements?.requiredEnvVars.includes('DATABASE_URL');\n\t\t});\n\n\t\tif (appsNeedingDb.length > 0) {\n\t\t\tlogger.log(`\\n🔐 Setting up per-app database credentials...`);\n\t\t\tlogger.log(` Apps needing DATABASE_URL: ${appsNeedingDb.join(', ')}`);\n\n\t\t\t// Get or generate credentials for each app\n\t\t\tconst existingCredentials = getAllAppCredentials(state);\n\t\t\tconst usersToCreate: DbUserConfig[] = [];\n\n\t\t\tfor (const appName of appsNeedingDb) {\n\t\t\t\tlet credentials = existingCredentials[appName];\n\n\t\t\t\tif (credentials) {\n\t\t\t\t\tlogger.log(` ${appName}: Using existing credentials from state`);\n\t\t\t\t} else {\n\t\t\t\t\t// Generate new credentials\n\t\t\t\t\tconst password = randomBytes(16).toString('hex');\n\t\t\t\t\tcredentials = { dbUser: appName, dbPassword: password };\n\t\t\t\t\tsetAppCredentials(state, appName, credentials);\n\t\t\t\t\tlogger.log(` ${appName}: Generated new credentials`);\n\t\t\t\t}\n\n\t\t\t\tperAppDbCredentials.set(appName, credentials);\n\n\t\t\t\t// Always add to users to create (idempotent - will update if exists)\n\t\t\t\tusersToCreate.push({\n\t\t\t\t\tname: appName,\n\t\t\t\t\tpassword: credentials.dbPassword,\n\t\t\t\t\tusePublicSchema: appName === 'api', // API uses public schema, others get their own\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Initialize database users\n\t\t\tconst serverHostname = getServerHostname(creds.endpoint);\n\t\t\tawait initializePostgresUsers(\n\t\t\t\tapi,\n\t\t\t\tprovisionedPostgres,\n\t\t\t\tserverHostname,\n\t\t\t\tusersToCreate,\n\t\t\t);\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// Provision backup destination if configured\n\t// ==================================================================\n\tif (workspace.deploy?.backups && provisionedPostgres) {\n\t\tlogger.log('\\n💾 Provisioning backup destination...');\n\n\t\tconst { provisionBackupDestination } = await import(\n\t\t\t'./backup-provisioner.js'\n\t\t);\n\n\t\tconst backupState = await provisionBackupDestination({\n\t\t\tapi,\n\t\t\tprojectId: project.projectId,\n\t\t\tprojectName: workspace.name,\n\t\t\tstage,\n\t\t\tconfig: workspace.deploy.backups,\n\t\t\texistingState: getBackupState(state),\n\t\t\tlogger,\n\t\t});\n\n\t\t// Save backup state\n\t\tsetBackupState(state, backupState);\n\n\t\t// Create backup schedule for postgres if not already configured\n\t\tif (!backupState.postgresBackupId) {\n\t\t\tconst backupSchedule = workspace.deploy.backups.schedule ?? '0 2 * * *';\n\t\t\tconst backupRetention = workspace.deploy.backups.retention ?? 30;\n\n\t\t\tlogger.log(' Creating postgres backup schedule...');\n\t\t\tconst backup = await api.createPostgresBackup({\n\t\t\t\tschedule: backupSchedule,\n\t\t\t\tprefix: `${stage}/postgres`,\n\t\t\t\tdestinationId: backupState.destinationId,\n\t\t\t\tdatabase: provisionedPostgres.databaseName,\n\t\t\t\tpostgresId: provisionedPostgres.postgresId,\n\t\t\t\tenabled: true,\n\t\t\t\tkeepLatestCount: backupRetention,\n\t\t\t});\n\t\t\tsetPostgresBackupId(state, backup.backupId);\n\t\t\tlogger.log(` ✓ Postgres backup schedule created (${backupSchedule})`);\n\t\t} else {\n\t\t\tlogger.log(' ✓ Using existing postgres backup schedule');\n\t\t}\n\t}\n\n\t// Track deployed app public URLs for frontend builds\n\tconst publicUrls: Record<string, string> = {};\n\tconst results: AppDeployResult[] = [];\n\tconst dokployConfig = workspace.deploy.dokploy;\n\n\t// Track domain IDs and hostnames for DNS orchestration\n\tconst appHostnames = new Map<string, string>(); // appName -> hostname\n\tconst appDomainIds = new Map<string, string>(); // appName -> domainId\n\n\t// ==================================================================\n\t// PRE-COMPUTE: Frontend URLs for BETTER_AUTH_TRUSTED_ORIGINS\n\t// ==================================================================\n\tconst frontendUrls: string[] = [];\n\tfor (const appName of frontendApps) {\n\t\tconst app = workspace.apps[appName]!;\n\t\tconst isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);\n\t\tconst hostname = resolveHost(\n\t\t\tappName,\n\t\t\tapp,\n\t\t\tstage,\n\t\t\tdokployConfig,\n\t\t\tisMainFrontend,\n\t\t);\n\t\tfrontendUrls.push(`https://${hostname}`);\n\t}\n\n\t// ==================================================================\n\t// PHASE 1: Deploy backend apps (with encrypted secrets)\n\t// ==================================================================\n\tif (backendApps.length > 0) {\n\t\tlogger.log('\\n📦 PHASE 1: Deploying backend applications...');\n\n\t\tfor (const appName of backendApps) {\n\t\t\tconst app = workspace.apps[appName]!;\n\n\t\t\tlogger.log(`\\n ⚙️ Deploying ${appName}...`);\n\n\t\t\ttry {\n\t\t\t\t// Use simple app name - project already provides namespace\n\t\t\t\tconst dokployAppName = appName;\n\n\t\t\t\t// Check state for cached application ID\n\t\t\t\tlet application: DokployApplication | null = null;\n\t\t\t\tconst cachedAppId = getApplicationId(state, appName);\n\n\t\t\t\tif (cachedAppId) {\n\t\t\t\t\tlogger.log(` Using cached ID: ${cachedAppId}`);\n\t\t\t\t\tapplication = await api.getApplication(cachedAppId);\n\t\t\t\t\tif (application) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` ✓ Application found: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.log(` ⚠ Cached ID invalid, will create new`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If not found by ID, use findOrCreate\n\t\t\t\tif (!application) {\n\t\t\t\t\tconst result = await api.findOrCreateApplication(\n\t\t\t\t\t\tdokployAppName,\n\t\t\t\t\t\tproject.projectId,\n\t\t\t\t\t\tenvironmentId,\n\t\t\t\t\t);\n\t\t\t\t\tapplication = result.application;\n\n\t\t\t\t\tif (result.created) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` Created application: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` Found existing application: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Store application ID in state\n\t\t\t\tsetApplicationId(state, appName, application.applicationId);\n\n\t\t\t\t// Get encrypted secrets for this app\n\t\t\t\tconst appSecrets = encryptedSecrets.get(appName);\n\t\t\t\tconst buildArgs: string[] = [];\n\n\t\t\t\tif (appSecrets && appSecrets.secretCount > 0) {\n\t\t\t\t\tbuildArgs.push(\n\t\t\t\t\t\t`GKM_ENCRYPTED_CREDENTIALS=${appSecrets.payload.encrypted}`,\n\t\t\t\t\t);\n\t\t\t\t\tbuildArgs.push(`GKM_CREDENTIALS_IV=${appSecrets.payload.iv}`);\n\t\t\t\t\tlogger.log(` Encrypted ${appSecrets.secretCount} secrets`);\n\t\t\t\t}\n\n\t\t\t\t// Build Docker image with encrypted secrets\n\t\t\t\tconst imageName = `${workspace.name}-${appName}`;\n\t\t\t\tconst imageRef = registry\n\t\t\t\t\t? `${registry}/${imageName}:${imageTag}`\n\t\t\t\t\t: `${imageName}:${imageTag}`;\n\n\t\t\t\tlogger.log(` Building Docker image: ${imageRef}`);\n\n\t\t\t\tawait deployDocker({\n\t\t\t\t\tstage,\n\t\t\t\t\ttag: imageTag,\n\t\t\t\t\tskipPush: false,\n\t\t\t\t\tconfig: {\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\timageName,\n\t\t\t\t\t\tappName,\n\t\t\t\t\t},\n\t\t\t\t\tbuildArgs,\n\t\t\t\t});\n\n\t\t\t\t// Compute hostname first (needed for BETTER_AUTH_URL)\n\t\t\t\tconst backendHost = resolveHost(\n\t\t\t\t\tappName,\n\t\t\t\t\tapp,\n\t\t\t\t\tstage,\n\t\t\t\t\tdokployConfig,\n\t\t\t\t\tfalse, // Backend apps are not main frontend\n\t\t\t\t);\n\n\t\t\t\t// Build dependency URLs from already-deployed apps\n\t\t\t\tconst dependencyUrls: Record<string, string> = {};\n\t\t\t\tif (app.dependencies) {\n\t\t\t\t\tfor (const dep of app.dependencies) {\n\t\t\t\t\t\tif (publicUrls[dep]) {\n\t\t\t\t\t\t\tdependencyUrls[dep] = publicUrls[dep];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Build env resolver context\n\t\t\t\tconst envContext: EnvResolverContext = {\n\t\t\t\t\tapp,\n\t\t\t\t\tappName,\n\t\t\t\t\tstage,\n\t\t\t\t\tstate,\n\t\t\t\t\tappCredentials: perAppDbCredentials.get(appName),\n\t\t\t\t\tpostgres: provisionedPostgres\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\thost: provisionedPostgres.appName,\n\t\t\t\t\t\t\t\tport: 5432,\n\t\t\t\t\t\t\t\tdatabase: provisionedPostgres.databaseName,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tredis: provisionedRedis\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\thost: provisionedRedis.appName,\n\t\t\t\t\t\t\t\tport: 6379,\n\t\t\t\t\t\t\t\tpassword: provisionedRedis.databasePassword,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tappHostname: backendHost,\n\t\t\t\t\tfrontendUrls,\n\t\t\t\t\tuserSecrets: stageSecrets ?? undefined,\n\t\t\t\t\tmasterKey: appSecrets?.masterKey,\n\t\t\t\t\tdependencyUrls,\n\t\t\t\t};\n\n\t\t\t\t// Resolve all required environment variables\n\t\t\t\t// Always include PORT, NODE_ENV, STAGE even if not explicitly required\n\t\t\t\tconst appRequirements = sniffedApps.get(appName);\n\t\t\t\tconst sniffedVars = appRequirements?.requiredEnvVars ?? [];\n\t\t\t\tconst requiredVars = [\n\t\t\t\t\t...new Set(['PORT', 'NODE_ENV', 'STAGE', ...sniffedVars]),\n\t\t\t\t];\n\t\t\t\tconst { valid, missing, resolved } = validateEnvVars(\n\t\t\t\t\trequiredVars,\n\t\t\t\t\tenvContext,\n\t\t\t\t);\n\n\t\t\t\tif (!valid) {\n\t\t\t\t\tthrow new Error(formatMissingVarsError(appName, missing, stage));\n\t\t\t\t}\n\n\t\t\t\t// Build env vars string for Dokploy\n\t\t\t\tconst envVars: string[] = Object.entries(resolved).map(\n\t\t\t\t\t([key, value]) => `${key}=${value}`,\n\t\t\t\t);\n\n\t\t\t\tif (Object.keys(resolved).length > 0) {\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` Resolved ${Object.keys(resolved).length} env vars: ${Object.keys(resolved).join(', ')}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Configure and deploy application in Dokploy\n\t\t\t\tawait api.saveDockerProvider(application.applicationId, imageRef, {\n\t\t\t\t\tregistryId,\n\t\t\t\t});\n\n\t\t\t\tawait api.saveApplicationEnv(\n\t\t\t\t\tapplication.applicationId,\n\t\t\t\t\tenvVars.join('\\n'),\n\t\t\t\t);\n\n\t\t\t\tlogger.log(` Deploying to Dokploy...`);\n\t\t\t\tawait api.deployApplication(application.applicationId);\n\n\t\t\t\t// Check if domain already exists (backendHost computed above)\n\t\t\t\tconst existingDomains = await api.getDomainsByApplicationId(\n\t\t\t\t\tapplication.applicationId,\n\t\t\t\t);\n\t\t\t\tconst existingDomain = existingDomains.find(\n\t\t\t\t\t(d) => d.host === backendHost,\n\t\t\t\t);\n\n\t\t\t\tif (existingDomain) {\n\t\t\t\t\t// Domain already exists\n\t\t\t\t\tappHostnames.set(appName, backendHost);\n\t\t\t\t\tappDomainIds.set(appName, existingDomain.domainId);\n\t\t\t\t\tpublicUrls[appName] = `https://${backendHost}`;\n\t\t\t\t\tlogger.log(` ✓ Domain: https://${backendHost} (existing)`);\n\t\t\t\t} else {\n\t\t\t\t\t// Create new domain\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst domain = await api.createDomain({\n\t\t\t\t\t\t\thost: backendHost,\n\t\t\t\t\t\t\tport: app.port,\n\t\t\t\t\t\t\thttps: true,\n\t\t\t\t\t\t\tcertificateType: 'letsencrypt',\n\t\t\t\t\t\t\tapplicationId: application.applicationId,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tappHostnames.set(appName, backendHost);\n\t\t\t\t\t\tappDomainIds.set(appName, domain.domainId);\n\t\t\t\t\t\tpublicUrls[appName] = `https://${backendHost}`;\n\t\t\t\t\t\tlogger.log(` ✓ Domain: https://${backendHost} (created)`);\n\t\t\t\t\t} catch (domainError) {\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\tdomainError instanceof Error\n\t\t\t\t\t\t\t\t? domainError.message\n\t\t\t\t\t\t\t\t: 'Unknown error';\n\t\t\t\t\t\tlogger.log(` ⚠ Domain creation failed: ${message}`);\n\t\t\t\t\t\tappHostnames.set(appName, backendHost);\n\t\t\t\t\t\tpublicUrls[appName] = `https://${backendHost}`;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tresults.push({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: app.type,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tapplicationId: application.applicationId,\n\t\t\t\t\timageRef,\n\t\t\t\t});\n\n\t\t\t\tlogger.log(` ✓ ${appName} deployed successfully`);\n\t\t\t} catch (error) {\n\t\t\t\tconst message =\n\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error';\n\t\t\t\tlogger.log(` ✗ Failed to deploy ${appName}: ${message}`);\n\n\t\t\t\tresults.push({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: app.type,\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: message,\n\t\t\t\t});\n\n\t\t\t\t// Abort on backend failure to prevent incomplete deployment\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Backend deployment failed for ${appName}. Aborting to prevent partial deployment.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// PHASE 2: Deploy frontend apps (with public URLs from backends)\n\t// ==================================================================\n\tif (frontendApps.length > 0) {\n\t\tlogger.log('\\n🌐 PHASE 2: Deploying frontend applications...');\n\n\t\tfor (const appName of frontendApps) {\n\t\t\tconst app = workspace.apps[appName]!;\n\n\t\t\tlogger.log(`\\n 🌐 Deploying ${appName}...`);\n\n\t\t\ttry {\n\t\t\t\t// Use simple app name - project already provides namespace\n\t\t\t\tconst dokployAppName = appName;\n\n\t\t\t\t// Check state for cached application ID\n\t\t\t\tlet application: DokployApplication | null = null;\n\t\t\t\tconst cachedAppId = getApplicationId(state, appName);\n\n\t\t\t\tif (cachedAppId) {\n\t\t\t\t\tlogger.log(` Using cached ID: ${cachedAppId}`);\n\t\t\t\t\tapplication = await api.getApplication(cachedAppId);\n\t\t\t\t\tif (application) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` ✓ Application found: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.log(` ⚠ Cached ID invalid, will create new`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If not found by ID, use findOrCreate\n\t\t\t\tif (!application) {\n\t\t\t\t\tconst result = await api.findOrCreateApplication(\n\t\t\t\t\t\tdokployAppName,\n\t\t\t\t\t\tproject.projectId,\n\t\t\t\t\t\tenvironmentId,\n\t\t\t\t\t);\n\t\t\t\t\tapplication = result.application;\n\n\t\t\t\t\tif (result.created) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` Created application: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` Found existing application: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Store application ID in state\n\t\t\t\tsetApplicationId(state, appName, application.applicationId);\n\n\t\t\t\t// Build dependency URLs for frontend (same pattern as backend)\n\t\t\t\tconst dependencyUrls: Record<string, string> = {};\n\t\t\t\tif (app.dependencies) {\n\t\t\t\t\tfor (const dep of app.dependencies) {\n\t\t\t\t\t\tif (publicUrls[dep]) {\n\t\t\t\t\t\t\tdependencyUrls[dep] = publicUrls[dep];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Compute hostname for this frontend app\n\t\t\t\tconst isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);\n\t\t\t\tconst frontendHost = resolveHost(\n\t\t\t\t\tappName,\n\t\t\t\t\tapp,\n\t\t\t\t\tstage,\n\t\t\t\t\tdokployConfig,\n\t\t\t\t\tisMainFrontend,\n\t\t\t\t);\n\n\t\t\t\t// Build env context for frontend\n\t\t\t\tconst envContext: EnvResolverContext = {\n\t\t\t\t\tapp,\n\t\t\t\t\tappName,\n\t\t\t\t\tstage,\n\t\t\t\t\tstate,\n\t\t\t\t\tappHostname: frontendHost,\n\t\t\t\t\tfrontendUrls: [],\n\t\t\t\t\tuserSecrets: stageSecrets ?? undefined,\n\t\t\t\t\tdependencyUrls,\n\t\t\t\t};\n\n\t\t\t\t// Resolve all env vars BEFORE Docker build (public-prefixed vars\n\t\t\t\t// must be present at bundler build time so they get inlined).\n\t\t\t\tconst sniffedVars = sniffedApps.get(appName)?.requiredEnvVars ?? [];\n\t\t\t\tconst { valid, missing, resolved } = validateEnvVars(\n\t\t\t\t\tsniffedVars,\n\t\t\t\t\tenvContext,\n\t\t\t\t);\n\n\t\t\t\tif (!valid) {\n\t\t\t\t\tthrow new Error(formatMissingVarsError(appName, missing, stage));\n\t\t\t\t}\n\n\t\t\t\tif (Object.keys(resolved).length > 0) {\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` Resolved ${Object.keys(resolved).length} env vars: ${Object.keys(resolved).join(', ')}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Build args: only the framework's public-prefixed vars get baked\n\t\t\t\t// into the bundle. Server-only vars stay as runtime env.\n\t\t\t\tconst publicPrefix = getPublicEnvPrefix(app.framework);\n\t\t\t\tconst buildArgs: string[] = [];\n\t\t\t\tconst publicUrlArgNames: string[] = [];\n\n\t\t\t\tif (publicPrefix) {\n\t\t\t\t\tfor (const [key, value] of Object.entries(resolved)) {\n\t\t\t\t\t\tif (key.startsWith(publicPrefix)) {\n\t\t\t\t\t\t\tbuildArgs.push(`${key}=${value}`);\n\t\t\t\t\t\t\tpublicUrlArgNames.push(key);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (buildArgs.length > 0) {\n\t\t\t\t\tlogger.log(` Build args: ${publicUrlArgNames.join(', ')}`);\n\t\t\t\t}\n\n\t\t\t\t// Build Docker image with public-prefixed vars as build args\n\t\t\t\tconst imageName = `${workspace.name}-${appName}`;\n\t\t\t\tconst imageRef = registry\n\t\t\t\t\t? `${registry}/${imageName}:${imageTag}`\n\t\t\t\t\t: `${imageName}:${imageTag}`;\n\n\t\t\t\tlogger.log(` Building Docker image: ${imageRef}`);\n\n\t\t\t\tawait deployDocker({\n\t\t\t\t\tstage,\n\t\t\t\t\ttag: imageTag,\n\t\t\t\t\tskipPush: false,\n\t\t\t\t\tconfig: {\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\timageName,\n\t\t\t\t\t\tappName,\n\t\t\t\t\t},\n\t\t\t\t\tbuildArgs,\n\t\t\t\t\t// Pass arg names for Dockerfile ARG generation\n\t\t\t\t\tpublicUrlArgs: publicUrlArgNames,\n\t\t\t\t});\n\n\t\t\t\t// Prepare runtime environment variables\n\t\t\t\tconst envVars: string[] = [\n\t\t\t\t\t`NODE_ENV=production`,\n\t\t\t\t\t`PORT=${app.port}`,\n\t\t\t\t\t`STAGE=${stage}`,\n\t\t\t\t];\n\n\t\t\t\t// Add all resolved vars as runtime env (for SSR and server components)\n\t\t\t\tfor (const [key, value] of Object.entries(resolved)) {\n\t\t\t\t\tenvVars.push(`${key}=${value}`);\n\t\t\t\t}\n\n\t\t\t\t// Configure and deploy application in Dokploy\n\t\t\t\tawait api.saveDockerProvider(application.applicationId, imageRef, {\n\t\t\t\t\tregistryId,\n\t\t\t\t});\n\n\t\t\t\tawait api.saveApplicationEnv(\n\t\t\t\t\tapplication.applicationId,\n\t\t\t\t\tenvVars.join('\\n'),\n\t\t\t\t);\n\n\t\t\t\tlogger.log(` Deploying to Dokploy...`);\n\t\t\t\tawait api.deployApplication(application.applicationId);\n\n\t\t\t\t// Check if domain already exists (frontendHost computed earlier for env context)\n\t\t\t\tconst existingFrontendDomains = await api.getDomainsByApplicationId(\n\t\t\t\t\tapplication.applicationId,\n\t\t\t\t);\n\t\t\t\tconst existingFrontendDomain = existingFrontendDomains.find(\n\t\t\t\t\t(d) => d.host === frontendHost,\n\t\t\t\t);\n\n\t\t\t\tif (existingFrontendDomain) {\n\t\t\t\t\t// Domain already exists\n\t\t\t\t\tappHostnames.set(appName, frontendHost);\n\t\t\t\t\tappDomainIds.set(appName, existingFrontendDomain.domainId);\n\t\t\t\t\tpublicUrls[appName] = `https://${frontendHost}`;\n\t\t\t\t\tlogger.log(` ✓ Domain: https://${frontendHost} (existing)`);\n\t\t\t\t} else {\n\t\t\t\t\t// Create new domain\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst domain = await api.createDomain({\n\t\t\t\t\t\t\thost: frontendHost,\n\t\t\t\t\t\t\tport: app.port,\n\t\t\t\t\t\t\thttps: true,\n\t\t\t\t\t\t\tcertificateType: 'letsencrypt',\n\t\t\t\t\t\t\tapplicationId: application.applicationId,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tappHostnames.set(appName, frontendHost);\n\t\t\t\t\t\tappDomainIds.set(appName, domain.domainId);\n\t\t\t\t\t\tpublicUrls[appName] = `https://${frontendHost}`;\n\t\t\t\t\t\tlogger.log(` ✓ Domain: https://${frontendHost} (created)`);\n\t\t\t\t\t} catch (domainError) {\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\tdomainError instanceof Error\n\t\t\t\t\t\t\t\t? domainError.message\n\t\t\t\t\t\t\t\t: 'Unknown error';\n\t\t\t\t\t\tlogger.log(` ⚠ Domain creation failed: ${message}`);\n\t\t\t\t\t\tappHostnames.set(appName, frontendHost);\n\t\t\t\t\t\tpublicUrls[appName] = `https://${frontendHost}`;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tresults.push({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: app.type,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tapplicationId: application.applicationId,\n\t\t\t\t\timageRef,\n\t\t\t\t});\n\n\t\t\t\tlogger.log(` ✓ ${appName} deployed successfully`);\n\t\t\t} catch (error) {\n\t\t\t\tconst message =\n\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error';\n\t\t\t\tlogger.log(` ✗ Failed to deploy ${appName}: ${message}`);\n\n\t\t\t\tresults.push({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: app.type,\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: message,\n\t\t\t\t});\n\t\t\t\t// Don't abort on frontend failures - continue with other frontends\n\t\t\t}\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// STATE: Save deploy state\n\t// ==================================================================\n\tlogger.log('\\n📋 Saving deploy state...');\n\tawait stateProvider.write(stage, state);\n\tlogger.log(' ✓ State saved');\n\n\t// ==================================================================\n\t// DNS: Create DNS records, verify propagation, and validate for SSL\n\t// ==================================================================\n\tconst dnsConfig = workspace.deploy.dns;\n\tif (dnsConfig && appHostnames.size > 0) {\n\t\tconst dnsResult = await orchestrateDns(\n\t\t\tappHostnames,\n\t\t\tdnsConfig,\n\t\t\tcreds.endpoint,\n\t\t);\n\n\t\t// Verify DNS records resolve correctly (with state caching)\n\t\tif (dnsResult?.serverIp && appHostnames.size > 0) {\n\t\t\tawait verifyDnsRecords(appHostnames, dnsResult.serverIp, state);\n\n\t\t\t// Save state again to persist DNS verification results\n\t\t\tawait stateProvider.write(stage, state);\n\t\t}\n\n\t\t// Validate domains to trigger SSL certificate generation\n\t\tif (dnsResult?.success && appHostnames.size > 0) {\n\t\t\tlogger.log('\\n🔒 Validating domains for SSL certificates...');\n\t\t\tfor (const [appName, hostname] of appHostnames) {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await api.validateDomain(hostname);\n\t\t\t\t\tif (result.isValid) {\n\t\t\t\t\t\tlogger.log(` ✓ ${appName}: ${hostname} → ${result.resolvedIp}`);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.log(` ⚠ ${appName}: ${hostname} not valid`);\n\t\t\t\t\t}\n\t\t\t\t} catch (validationError) {\n\t\t\t\t\tconst message =\n\t\t\t\t\t\tvalidationError instanceof Error\n\t\t\t\t\t\t\t? validationError.message\n\t\t\t\t\t\t\t: 'Unknown error';\n\t\t\t\t\tlogger.log(` ⚠ ${appName}: validation failed - ${message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// Summary\n\t// ==================================================================\n\tconst successCount = results.filter((r) => r.success).length;\n\tconst failedCount = results.filter((r) => !r.success).length;\n\n\tlogger.log(`\\n${'─'.repeat(50)}`);\n\tlogger.log(`\\n✅ Workspace deployment complete!`);\n\tlogger.log(` Project: ${project.projectId}`);\n\tlogger.log(` Successful: ${successCount}`);\n\tif (failedCount > 0) {\n\t\tlogger.log(` Failed: ${failedCount}`);\n\t}\n\n\t// Print deployed URLs\n\tif (Object.keys(publicUrls).length > 0) {\n\t\tlogger.log('\\n 📡 Deployed URLs:');\n\t\tfor (const [name, url] of Object.entries(publicUrls)) {\n\t\t\tlogger.log(` ${name}: ${url}`);\n\t\t}\n\t}\n\n\treturn {\n\t\tapps: results,\n\t\tprojectId: project.projectId,\n\t\tsuccessCount,\n\t\tfailedCount,\n\t};\n}\n\n/**\n * Main deploy command\n */\nexport async function deployCommand(\n\toptions: DeployOptions,\n): Promise<DeployResult | WorkspaceDeployResult> {\n\tconst { provider, stage, tag, skipPush, skipBuild } = options;\n\n\t// Load config with workspace detection\n\tconst loadedConfig = await loadWorkspaceConfig();\n\n\t// Route to workspace deploy mode for multi-app workspaces\n\tif (loadedConfig.type === 'workspace') {\n\t\tlogger.log('📦 Detected workspace configuration');\n\t\treturn workspaceDeployCommand(loadedConfig.workspace, options);\n\t}\n\n\tlogger.log(`\\n🚀 Deploying to ${provider}...`);\n\tlogger.log(` Stage: ${stage}`);\n\n\t// Single-app mode - use existing logic\n\tconst config = await loadConfig();\n\n\t// Generate tag if not provided\n\tconst imageTag = tag ?? generateTag(stage);\n\tlogger.log(` Tag: ${imageTag}`);\n\n\t// Resolve docker config for image reference\n\tconst dockerConfig = resolveDockerConfig(config);\n\tconst imageName = dockerConfig.imageName!;\n\tconst registry = dockerConfig.registry;\n\tconst imageRef = registry\n\t\t? `${registry}/${imageName}:${imageTag}`\n\t\t: `${imageName}:${imageTag}`;\n\n\t// For Dokploy, set up services BEFORE build so URLs are available\n\tlet dokployConfig: DokployDeployConfig | undefined;\n\tlet finalRegistry = registry;\n\n\tif (provider === 'dokploy') {\n\t\t// Extract docker compose services config\n\t\tconst composeServices = config.docker?.compose?.services;\n\t\tlogger.log(\n\t\t\t`\\n🔍 Docker compose config: ${JSON.stringify(config.docker?.compose)}`,\n\t\t);\n\t\tconst dockerServices: DockerComposeServices | undefined = composeServices\n\t\t\t? Array.isArray(composeServices)\n\t\t\t\t? {\n\t\t\t\t\t\tpostgres: composeServices.includes('postgres'),\n\t\t\t\t\t\tredis: composeServices.includes('redis'),\n\t\t\t\t\t\trabbitmq: composeServices.includes('rabbitmq'),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\tpostgres: Boolean(composeServices.postgres),\n\t\t\t\t\t\tredis: Boolean(composeServices.redis),\n\t\t\t\t\t\trabbitmq: Boolean(composeServices.rabbitmq),\n\t\t\t\t\t}\n\t\t\t: undefined;\n\n\t\t// Ensure Dokploy is fully set up (credentials, project, app, registry, services)\n\t\tconst setupResult = await ensureDokploySetup(\n\t\t\tconfig,\n\t\t\tdockerConfig,\n\t\t\tstage,\n\t\t\tdockerServices,\n\t\t);\n\t\tdokployConfig = setupResult.config;\n\t\tfinalRegistry = dokployConfig.registry ?? dockerConfig.registry;\n\n\t\t// Save provisioned service URLs to secrets before build\n\t\tif (setupResult.serviceUrls) {\n\t\t\tconst { readStageSecrets, writeStageSecrets, initStageSecrets } =\n\t\t\t\tawait import('../secrets/storage');\n\t\t\tlet secrets = await readStageSecrets(stage);\n\n\t\t\t// Create secrets file if it doesn't exist\n\t\t\tif (!secrets) {\n\t\t\t\tlogger.log(` Creating secrets file for stage \"${stage}\"...`);\n\t\t\t\tsecrets = initStageSecrets(stage);\n\t\t\t}\n\n\t\t\tlet updated = false;\n\t\t\t// URL fields go to secrets.urls, individual params go to secrets.custom\n\t\t\tconst urlFields = ['DATABASE_URL', 'REDIS_URL', 'RABBITMQ_URL'] as const;\n\n\t\t\tfor (const [key, value] of Object.entries(setupResult.serviceUrls)) {\n\t\t\t\tif (!value) continue;\n\n\t\t\t\tif (urlFields.includes(key as (typeof urlFields)[number])) {\n\t\t\t\t\t// URL fields\n\t\t\t\t\tconst urlKey = key as keyof typeof secrets.urls;\n\t\t\t\t\tif (!secrets.urls[urlKey]) {\n\t\t\t\t\t\tsecrets.urls[urlKey] = value;\n\t\t\t\t\t\tlogger.log(` Saved ${key} to secrets.urls`);\n\t\t\t\t\t\tupdated = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Individual parameters (HOST, PORT, NAME, USER, PASSWORD)\n\t\t\t\t\tif (!secrets.custom[key]) {\n\t\t\t\t\t\tsecrets.custom[key] = value;\n\t\t\t\t\t\tlogger.log(` Saved ${key} to secrets.custom`);\n\t\t\t\t\t\tupdated = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (updated) {\n\t\t\t\tawait writeStageSecrets(secrets);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Build for production with secrets injection (unless skipped)\n\tlet masterKey: string | undefined;\n\tif (!skipBuild) {\n\t\tlogger.log(`\\n📦 Building for production...`);\n\t\tconst buildResult = await buildCommand({\n\t\t\tprovider: 'server',\n\t\t\tproduction: true,\n\t\t\tstage,\n\t\t});\n\t\tmasterKey = buildResult.masterKey;\n\t} else {\n\t\tlogger.log(`\\n⏭️ Skipping build (--skip-build)`);\n\t}\n\n\t// Deploy based on provider\n\tlet result: DeployResult;\n\n\tswitch (provider) {\n\t\tcase 'docker': {\n\t\t\tresult = await deployDocker({\n\t\t\t\tstage,\n\t\t\t\ttag: imageTag,\n\t\t\t\tskipPush,\n\t\t\t\tmasterKey,\n\t\t\t\tconfig: dockerConfig,\n\t\t\t});\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 'dokploy': {\n\t\t\tif (!dokployConfig) {\n\t\t\t\tthrow new Error('Dokploy config not initialized');\n\t\t\t}\n\t\t\tconst finalImageRef = finalRegistry\n\t\t\t\t? `${finalRegistry}/${imageName}:${imageTag}`\n\t\t\t\t: `${imageName}:${imageTag}`;\n\n\t\t\t// First build and push the Docker image\n\t\t\tawait deployDocker({\n\t\t\t\tstage,\n\t\t\t\ttag: imageTag,\n\t\t\t\tskipPush: false, // Dokploy needs the image in registry\n\t\t\t\tmasterKey,\n\t\t\t\tconfig: {\n\t\t\t\t\tregistry: finalRegistry,\n\t\t\t\t\timageName: dockerConfig.imageName,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\t// Then trigger Dokploy deployment\n\t\t\tresult = await deployDokploy({\n\t\t\t\tstage,\n\t\t\t\ttag: imageTag,\n\t\t\t\timageRef: finalImageRef,\n\t\t\t\tmasterKey,\n\t\t\t\tconfig: dokployConfig,\n\t\t\t});\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 'aws-lambda': {\n\t\t\tlogger.log('\\n⚠️ AWS Lambda deployment is not yet implemented.');\n\t\t\tlogger.log(' Use SST or AWS CDK for Lambda deployments.');\n\t\t\tresult = { imageRef, masterKey };\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault: {\n\t\t\tthrow new Error(\n\t\t\t\t`Unknown deploy provider: ${provider}\\n` +\n\t\t\t\t\t'Supported providers: docker, dokploy, aws-lambda',\n\t\t\t);\n\t\t}\n\t}\n\n\tlogger.log('\\n✅ Deployment complete!');\n\n\treturn result;\n}\n\nexport type { DeployOptions, DeployProvider, DeployResult };\n","/**\n * State Management CLI Commands\n *\n * Commands for managing deployment state across local and remote providers.\n */\n\nimport { loadWorkspaceConfig } from '../config';\nimport { CachedStateProvider } from './CachedStateProvider';\nimport { createStateProvider } from './StateProvider';\nimport type { DokployStageState } from './state';\n\nexport interface StateCommandOptions {\n\tstage: string;\n}\n\n/**\n * Pull state from remote to local.\n * `gkm state:pull --stage=<stage>`\n */\nexport async function statePullCommand(\n\toptions: StateCommandOptions,\n): Promise<void> {\n\tconst { workspace } = await loadWorkspaceConfig();\n\n\tif (!workspace.state || workspace.state.provider === 'local') {\n\t\tconsole.error('No remote state provider configured.');\n\t\tconsole.error('Add a remote provider in gkm.config.ts:');\n\t\tconsole.error(' state: { provider: \"ssm\", region: \"us-east-1\" }');\n\t\tprocess.exit(1);\n\t}\n\n\tconst provider = await createStateProvider({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\n\tif (!(provider instanceof CachedStateProvider)) {\n\t\tconsole.error('State provider does not support pull operation.');\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(`Pulling state for stage: ${options.stage}...`);\n\tconst state = await provider.pull(options.stage);\n\n\tif (state) {\n\t\tconsole.log('State pulled successfully.');\n\t\tprintStateSummary(state);\n\t} else {\n\t\tconsole.log('No remote state found for this stage.');\n\t}\n}\n\n/**\n * Push local state to remote.\n * `gkm state:push --stage=<stage>`\n */\nexport async function statePushCommand(\n\toptions: StateCommandOptions,\n): Promise<void> {\n\tconst { workspace } = await loadWorkspaceConfig();\n\n\tif (!workspace.state || workspace.state.provider === 'local') {\n\t\tconsole.error('No remote state provider configured.');\n\t\tconsole.error('Add a remote provider in gkm.config.ts:');\n\t\tconsole.error(' state: { provider: \"ssm\", region: \"us-east-1\" }');\n\t\tprocess.exit(1);\n\t}\n\n\tconst provider = await createStateProvider({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\n\tif (!(provider instanceof CachedStateProvider)) {\n\t\tconsole.error('State provider does not support push operation.');\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(`Pushing state for stage: ${options.stage}...`);\n\tconst state = await provider.push(options.stage);\n\n\tif (state) {\n\t\tconsole.log('State pushed successfully.');\n\t\tprintStateSummary(state);\n\t} else {\n\t\tconsole.log('No local state found for this stage.');\n\t}\n}\n\n/**\n * Show current state.\n * `gkm state:show --stage=<stage>`\n */\nexport async function stateShowCommand(\n\toptions: StateCommandOptions & { json?: boolean },\n): Promise<void> {\n\tconst { workspace } = await loadWorkspaceConfig();\n\n\tconst provider = await createStateProvider({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\n\tconst state = await provider.read(options.stage);\n\n\tif (!state) {\n\t\tconsole.log(`No state found for stage: ${options.stage}`);\n\t\treturn;\n\t}\n\n\tif (options.json) {\n\t\tconsole.log(JSON.stringify(state, null, 2));\n\t} else {\n\t\tprintStateDetails(state);\n\t}\n}\n\n/**\n * Compare local and remote state.\n * `gkm state:diff --stage=<stage>`\n */\nexport async function stateDiffCommand(\n\toptions: StateCommandOptions,\n): Promise<void> {\n\tconst { workspace } = await loadWorkspaceConfig();\n\n\tif (!workspace.state || workspace.state.provider === 'local') {\n\t\tconsole.error('No remote state provider configured.');\n\t\tconsole.error('Diff requires a remote provider to compare against.');\n\t\tprocess.exit(1);\n\t}\n\n\tconst provider = await createStateProvider({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\n\tif (!(provider instanceof CachedStateProvider)) {\n\t\tconsole.error('State provider does not support diff operation.');\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(`Comparing state for stage: ${options.stage}...\\n`);\n\tconst { local, remote } = await provider.diff(options.stage);\n\n\tif (!local && !remote) {\n\t\tconsole.log('No state found (local or remote).');\n\t\treturn;\n\t}\n\n\tif (!local) {\n\t\tconsole.log('Local: (none)');\n\t} else {\n\t\tconsole.log(`Local: Last deployed ${local.lastDeployedAt}`);\n\t}\n\n\tif (!remote) {\n\t\tconsole.log('Remote: (none)');\n\t} else {\n\t\tconsole.log(`Remote: Last deployed ${remote.lastDeployedAt}`);\n\t}\n\n\tconsole.log('');\n\n\t// Compare applications\n\tconst localApps = local?.applications ?? {};\n\tconst remoteApps = remote?.applications ?? {};\n\tconst allApps = new Set([\n\t\t...Object.keys(localApps),\n\t\t...Object.keys(remoteApps),\n\t]);\n\n\tif (allApps.size > 0) {\n\t\tconsole.log('Applications:');\n\t\tfor (const app of allApps) {\n\t\t\tconst localId = localApps[app];\n\t\t\tconst remoteId = remoteApps[app];\n\n\t\t\tif (localId === remoteId) {\n\t\t\t\tconsole.log(` ${app}: ${localId ?? '(none)'}`);\n\t\t\t} else if (!localId) {\n\t\t\t\tconsole.log(` ${app}: (none) -> ${remoteId} [REMOTE ONLY]`);\n\t\t\t} else if (!remoteId) {\n\t\t\t\tconsole.log(` ${app}: ${localId} -> (none) [LOCAL ONLY]`);\n\t\t\t} else {\n\t\t\t\tconsole.log(\n\t\t\t\t\t` ${app}: ${localId} (local) != ${remoteId} (remote) [MISMATCH]`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Compare services\n\tconst localServices = local?.services ?? {};\n\tconst remoteServices = remote?.services ?? {};\n\n\tif (\n\t\tObject.keys(localServices).length > 0 ||\n\t\tObject.keys(remoteServices).length > 0\n\t) {\n\t\tconsole.log('\\nServices:');\n\t\tconst serviceKeys = new Set([\n\t\t\t...Object.keys(localServices),\n\t\t\t...Object.keys(remoteServices),\n\t\t]);\n\n\t\tfor (const key of serviceKeys) {\n\t\t\tconst localVal = localServices[key as keyof typeof localServices];\n\t\t\tconst remoteVal = remoteServices[key as keyof typeof remoteServices];\n\n\t\t\tif (localVal === remoteVal) {\n\t\t\t\tconsole.log(` ${key}: ${localVal ?? '(none)'}`);\n\t\t\t} else {\n\t\t\t\tconsole.log(\n\t\t\t\t\t` ${key}: ${localVal ?? '(none)'} (local) != ${remoteVal ?? '(none)'} (remote)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction printStateSummary(state: DokployStageState): void {\n\tconst appCount = Object.keys(state.applications).length;\n\tconst hasPostgres = !!state.services.postgresId;\n\tconst hasRedis = !!state.services.redisId;\n\n\tconsole.log(` Stage: ${state.stage}`);\n\tconsole.log(` Applications: ${appCount}`);\n\tconsole.log(` Postgres: ${hasPostgres ? 'configured' : 'none'}`);\n\tconsole.log(` Redis: ${hasRedis ? 'configured' : 'none'}`);\n\tconsole.log(` Last deployed: ${state.lastDeployedAt}`);\n}\n\nfunction printStateDetails(state: DokployStageState): void {\n\tconsole.log(`Stage: ${state.stage}`);\n\tconsole.log(`Environment ID: ${state.environmentId}`);\n\tconsole.log(`Last Deployed: ${state.lastDeployedAt}`);\n\tconsole.log('');\n\n\tconsole.log('Applications:');\n\tconst apps = Object.entries(state.applications);\n\tif (apps.length === 0) {\n\t\tconsole.log(' (none)');\n\t} else {\n\t\tfor (const [name, id] of apps) {\n\t\t\tconsole.log(` ${name}: ${id}`);\n\t\t}\n\t}\n\tconsole.log('');\n\n\tconsole.log('Services:');\n\tif (!state.services.postgresId && !state.services.redisId) {\n\t\tconsole.log(' (none)');\n\t} else {\n\t\tif (state.services.postgresId) {\n\t\t\tconsole.log(` Postgres: ${state.services.postgresId}`);\n\t\t}\n\t\tif (state.services.redisId) {\n\t\t\tconsole.log(` Redis: ${state.services.redisId}`);\n\t\t}\n\t}\n\n\tif (state.dnsVerified && Object.keys(state.dnsVerified).length > 0) {\n\t\tconsole.log('');\n\t\tconsole.log('DNS Verified:');\n\t\tfor (const [hostname, info] of Object.entries(state.dnsVerified)) {\n\t\t\tconsole.log(` ${hostname}: ${info.serverIp} (${info.verifiedAt})`);\n\t\t}\n\t}\n}\n","import { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\n// Load package.json - handles both bundled (flat dist/) and source (nested src/init/)\nfunction loadPackageJson(): { version: string } {\n\ttry {\n\t\t// Try flat dist path first (../package.json from dist/)\n\t\treturn require('../package.json');\n\t} catch {\n\t\t// Fall back to nested source path (../../package.json from src/init/)\n\t\treturn require('../../package.json');\n\t}\n}\n\nconst pkg = loadPackageJson();\n\n/**\n * CLI version resolved from package.json at runtime\n */\nexport const CLI_VERSION = `~${pkg.version}`;\n\n/**\n * Package versions for @geekmidas packages\n *\n * AUTO-GENERATED (except CLI) - Do not edit manually\n * Run: pnpm --filter @geekmidas/cli sync-versions\n */\nexport const GEEKMIDAS_VERSIONS = {\n\t'@geekmidas/audit': '~2.2.0',\n\t'@geekmidas/auth': '~2.0.1',\n\t'@geekmidas/cache': '~1.1.1',\n\t'@geekmidas/client': '~9.0.0',\n\t'@geekmidas/cloud': '~1.1.0',\n\t'@geekmidas/constructs': '~7.0.0',\n\t'@geekmidas/db': '~1.1.0',\n\t'@geekmidas/emailkit': '~1.0.1',\n\t'@geekmidas/envkit': '~1.1.0',\n\t'@geekmidas/errors': '~1.0.1',\n\t'@geekmidas/events': '~1.1.5',\n\t'@geekmidas/logger': '~1.0.2',\n\t'@geekmidas/rate-limit': '~4.0.0',\n\t'@geekmidas/schema': '~1.0.3',\n\t'@geekmidas/services': '~2.0.0',\n\t'@geekmidas/storage': '~2.0.3',\n\t'@geekmidas/studio': '~2.0.0',\n\t'@geekmidas/telescope': '~1.1.0',\n\t'@geekmidas/testkit': '~3.1.0',\n\t'@geekmidas/cli': CLI_VERSION,\n} as const;\n\nexport type GeekmidasPackage = keyof typeof GEEKMIDAS_VERSIONS;\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate auth app files for fullstack template\n * Uses better-auth with magic link authentication\n */\nexport function generateAuthAppFiles(\n\toptions: TemplateOptions,\n): GeneratedFile[] {\n\tif (!options.monorepo || options.template !== 'fullstack') {\n\t\treturn [];\n\t}\n\n\tconst packageName = `@${options.name}/auth`;\n\tconst modelsPackage = `@${options.name}/models`;\n\n\t// package.json for auth app\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\tscripts: {\n\t\t\tdev: 'gkm dev --entry ./src/index.ts',\n\t\t\tbuild: 'tsc',\n\t\t\tstart: 'node dist/index.js',\n\t\t\ttypecheck: 'tsc --noEmit',\n\t\t\t'db:migrate': 'gkm exec -- npx @better-auth/cli migrate',\n\t\t\t'db:generate': 'gkm exec -- npx @better-auth/cli generate',\n\t\t},\n\t\tdependencies: {\n\t\t\t[modelsPackage]: 'workspace:*',\n\t\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t\t'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],\n\t\t\t'@hono/node-server': '~1.13.0',\n\t\t\t'better-auth': '~1.2.0',\n\t\t\thono: '~4.8.0',\n\t\t\tkysely: '~0.27.0',\n\t\t\tpg: '~8.13.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t\t'@types/node': '~22.0.0',\n\t\t\t'@types/pg': '~8.11.0',\n\t\t\ttsx: '~4.20.0',\n\t\t\ttypescript: '~5.8.2',\n\t\t},\n\t};\n\n\t// tsconfig.json for auth app\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\tbaseUrl: '.',\n\t\t\tpaths: {\n\t\t\t\t'~/*': ['./src/*'],\n\t\t\t\t[`@${options.name}/*`]: ['../../packages/*/src'],\n\t\t\t},\n\t\t},\n\t\tinclude: ['src/**/*.ts'],\n\t\texclude: ['node_modules', 'dist'],\n\t};\n\n\t// src/config/env.ts\n\tconst envTs = `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { EnvironmentParser } from '@geekmidas/envkit';\n\nexport const envParser = new EnvironmentParser({ ...process.env, ...Credentials });\n\n// Global config - only minimal shared values\n// Service-specific config should be parsed where needed\nexport const config = envParser\n .create((get) => ({\n nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),\n stage: get('STAGE').enum(['development', 'staging', 'production']).default('development'),\n }))\n .parse();\n`;\n\n\t// src/config/logger.ts\n\tconst loggerTs = `import { createLogger } from '@geekmidas/logger/${options.loggerType}';\n\nexport const logger = createLogger();\n`;\n\n\t// src/auth.ts - better-auth instance with magic link\n\tconst authTs = `import { betterAuth } from 'better-auth';\nimport { magicLink } from 'better-auth/plugins';\nimport pg from 'pg';\nimport { envParser } from './config/env.ts';\nimport { logger } from './config/logger.ts';\n\n// Parse auth-specific config (no defaults - values from secrets)\nconst authConfig = envParser\n .create((get) => ({\n databaseUrl: get('DATABASE_URL').string(),\n baseUrl: get('BETTER_AUTH_URL').string(),\n trustedOrigins: get('BETTER_AUTH_TRUSTED_ORIGINS').string(),\n secret: get('BETTER_AUTH_SECRET').string(),\n }))\n .parse();\n\nexport const auth = betterAuth({\n database: new pg.Pool({\n connectionString: authConfig.databaseUrl,\n }),\n baseURL: authConfig.baseUrl,\n trustedOrigins: authConfig.trustedOrigins.split(','),\n secret: authConfig.secret,\n plugins: [\n magicLink({\n sendMagicLink: async ({ email, url }) => {\n // TODO: Implement email sending using @geekmidas/emailkit\n // For development, log the magic link\n logger.info({ email, url }, 'Magic link generated');\n console.log('\\\\n================================');\n console.log('MAGIC LINK FOR:', email);\n console.log(url);\n console.log('================================\\\\n');\n },\n expiresIn: 300, // 5 minutes\n }),\n ],\n emailAndPassword: {\n enabled: false, // Only magic link for now\n },\n});\n\nexport type Auth = typeof auth;\n`;\n\n\t// src/index.ts - Hono app entry point\n\tconst indexTs = `import { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { serve } from '@hono/node-server';\nimport { auth } from './auth.ts';\nimport { envParser } from './config/env.ts';\nimport { logger } from './config/logger.ts';\n\n// Parse server config (no defaults - values from secrets)\nconst serverConfig = envParser\n .create((get) => ({\n port: get('PORT').string().transform(Number),\n trustedOrigins: get('BETTER_AUTH_TRUSTED_ORIGINS').string(),\n }))\n .parse();\n\nconst app = new Hono();\n\n// CORS must be registered before routes\napp.use(\n '/api/auth/*',\n cors({\n origin: serverConfig.trustedOrigins.split(','),\n allowHeaders: ['Content-Type', 'Authorization'],\n allowMethods: ['POST', 'GET', 'OPTIONS'],\n credentials: true,\n }),\n);\n\n// Health check endpoint\napp.get('/health', (c) => {\n return c.json({\n status: 'ok',\n service: 'auth',\n timestamp: new Date().toISOString(),\n });\n});\n\n// Mount better-auth handler\napp.on(['POST', 'GET'], '/api/auth/*', (c) => {\n return auth.handler(c.req.raw);\n});\n\nlogger.info({ port: serverConfig.port }, 'Starting auth server');\n\nserve({\n fetch: app.fetch,\n port: serverConfig.port,\n}, (info) => {\n logger.info({ port: info.port }, 'Auth server running');\n});\n`;\n\n\t// .gitignore for auth app\n\tconst gitignore = `node_modules/\ndist/\n.env.local\n*.log\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'apps/auth/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/src/config/env.ts',\n\t\t\tcontent: envTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/src/config/logger.ts',\n\t\t\tcontent: loggerTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/src/auth.ts',\n\t\t\tcontent: authTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/src/index.ts',\n\t\t\tcontent: indexTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/.gitignore',\n\t\t\tcontent: gitignore,\n\t\t},\n\t];\n}\n","import type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\n\n/**\n * Vitest config content with globalSetup for database-enabled apps\n */\nconst vitestConfigContent = `import { defineConfig } from 'vitest/config';\nimport tsconfigPaths from 'vite-tsconfig-paths';\n\nexport default defineConfig({\n plugins: [tsconfigPaths()],\n test: {\n environment: 'node',\n globalSetup: './test/globalSetup.ts',\n },\n});\n`;\n\n/**\n * Generate configuration files (gkm.config.ts, tsconfig.json, biome.json, turbo.json)\n */\nexport function generateConfigFiles(\n\toptions: TemplateOptions,\n\ttemplate: TemplateConfig,\n): GeneratedFile[] {\n\tconst { telescope, studio, routesStructure } = options;\n\tconst isServerless = template.name === 'serverless';\n\tconst hasWorker = template.name === 'worker';\n\tconst isFullstack = options.template === 'fullstack';\n\n\t// Get routes glob pattern based on structure\n\tconst getRoutesGlob = () => {\n\t\tswitch (routesStructure) {\n\t\t\tcase 'centralized-endpoints':\n\t\t\t\treturn './src/endpoints/**/*.ts';\n\t\t\tcase 'centralized-routes':\n\t\t\t\treturn './src/routes/**/*.ts';\n\t\t\tcase 'domain-based':\n\t\t\t\treturn './src/**/routes/*.ts';\n\t\t}\n\t};\n\n\t// For fullstack template, generate workspace config at root\n\t// Single app config is still generated for non-fullstack monorepo setups\n\tif (isFullstack) {\n\t\t// Workspace config is generated in monorepo.ts for fullstack\n\t\treturn generateSingleAppConfigFiles(options, template, {\n\t\t\ttelescope,\n\t\t\tstudio,\n\t\t\troutesStructure,\n\t\t\tisServerless,\n\t\t\thasWorker,\n\t\t\tgetRoutesGlob,\n\t\t});\n\t}\n\n\t// Build gkm.config.ts for single-app\n\tlet gkmConfig = `import { defineConfig } from '@geekmidas/cli/config';\n\nexport default defineConfig({\n routes: '${getRoutesGlob()}',\n envParser: './src/config/env#envParser',\n logger: './src/config/logger#logger',`;\n\n\tif (isServerless || hasWorker) {\n\t\tgkmConfig += `\n functions: './src/functions/**/*.ts',`;\n\t}\n\n\tif (hasWorker) {\n\t\tgkmConfig += `\n crons: './src/crons/**/*.ts',\n subscribers: './src/subscribers/**/*.ts',`;\n\t}\n\n\tif (telescope) {\n\t\tgkmConfig += `\n telescope: {\n enabled: true,\n path: '/__telescope',\n },`;\n\t}\n\n\tif (studio) {\n\t\tgkmConfig += `\n studio: './src/config/studio#studio',`;\n\t}\n\n\t// Always add openapi config (output path is fixed to .gkm/openapi.ts)\n\tgkmConfig += `\n openapi: {\n enabled: true,\n },`;\n\n\tgkmConfig += `\n});\n`;\n\n\t// Build tsconfig.json - extends root for monorepo, standalone for non-monorepo\n\t// Using noEmit: true since typecheck is done via turbo\n\tconst tsConfig = options.monorepo\n\t\t? {\n\t\t\t\textends: '../../tsconfig.json',\n\t\t\t\tcompilerOptions: {\n\t\t\t\t\tnoEmit: true,\n\t\t\t\t\tallowImportingTsExtensions: true,\n\t\t\t\t\tbaseUrl: '.',\n\t\t\t\t\tpaths: {\n\t\t\t\t\t\t'~/*': ['./src/*'],\n\t\t\t\t\t\t[`@${options.name}/*`]: ['../../packages/*/src'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tinclude: ['src/**/*.ts'],\n\t\t\t\texclude: ['node_modules', 'dist'],\n\t\t\t}\n\t\t: {\n\t\t\t\tcompilerOptions: {\n\t\t\t\t\ttarget: 'ES2022',\n\t\t\t\t\tmodule: 'NodeNext',\n\t\t\t\t\tmoduleResolution: 'NodeNext',\n\t\t\t\t\tlib: ['ES2022'],\n\t\t\t\t\tstrict: true,\n\t\t\t\t\tesModuleInterop: true,\n\t\t\t\t\tskipLibCheck: true,\n\t\t\t\t\tforceConsistentCasingInFileNames: true,\n\t\t\t\t\tresolveJsonModule: true,\n\t\t\t\t\tnoEmit: true,\n\t\t\t\t\tallowImportingTsExtensions: true,\n\t\t\t\t},\n\t\t\t\tinclude: ['src/**/*.ts'],\n\t\t\t\texclude: ['node_modules', 'dist'],\n\t\t\t};\n\n\t// Skip biome.json and turbo.json for monorepo (they're at root)\n\tif (options.monorepo) {\n\t\tconst files: GeneratedFile[] = [\n\t\t\t{\n\t\t\t\tpath: 'gkm.config.ts',\n\t\t\t\tcontent: gkmConfig,\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: 'tsconfig.json',\n\t\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t\t},\n\t\t];\n\n\t\tif (options.database) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'vitest.config.ts',\n\t\t\t\tcontent: vitestConfigContent,\n\t\t\t});\n\t\t}\n\n\t\treturn files;\n\t}\n\n\t// Build biome.json\n\tconst biomeConfig = {\n\t\t$schema: 'https://biomejs.dev/schemas/2.3.0/schema.json',\n\t\tvcs: {\n\t\t\tenabled: true,\n\t\t\tclientKind: 'git',\n\t\t\tuseIgnoreFile: true,\n\t\t},\n\t\torganizeImports: {\n\t\t\tenabled: true,\n\t\t},\n\t\tformatter: {\n\t\t\tenabled: true,\n\t\t\tindentStyle: 'space',\n\t\t\tindentWidth: 2,\n\t\t\tlineWidth: 80,\n\t\t},\n\t\tjavascript: {\n\t\t\tformatter: {\n\t\t\t\tquoteStyle: 'single',\n\t\t\t\ttrailingCommas: 'all',\n\t\t\t\tsemicolons: 'always',\n\t\t\t\tarrowParentheses: 'always',\n\t\t\t},\n\t\t},\n\t\tlinter: {\n\t\t\tenabled: true,\n\t\t\trules: {\n\t\t\t\trecommended: true,\n\t\t\t\tcorrectness: {\n\t\t\t\t\tnoUnusedImports: 'error',\n\t\t\t\t\tnoUnusedVariables: 'error',\n\t\t\t\t},\n\t\t\t\tstyle: {\n\t\t\t\t\tnoNonNullAssertion: 'off',\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tfiles: {\n\t\t\tignore: ['node_modules', 'dist', '.gkm', 'coverage'],\n\t\t},\n\t};\n\n\t// Build turbo.json\n\tconst turboConfig = {\n\t\t$schema: 'https://turbo.build/schema.json',\n\t\ttasks: {\n\t\t\tbuild: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: ['dist/**'],\n\t\t\t},\n\t\t\tdev: {\n\t\t\t\tcache: false,\n\t\t\t\tpersistent: true,\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\tcache: false,\n\t\t\t},\n\t\t\t'test:once': {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: ['coverage/**'],\n\t\t\t},\n\t\t\ttypecheck: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t\tlint: {\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t\tfmt: {\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t},\n\t};\n\n\tconst files: GeneratedFile[] = [\n\t\t{\n\t\t\tpath: 'gkm.config.ts',\n\t\t\tcontent: gkmConfig,\n\t\t},\n\t\t{\n\t\t\tpath: 'tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'biome.json',\n\t\t\tcontent: `${JSON.stringify(biomeConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'turbo.json',\n\t\t\tcontent: `${JSON.stringify(turboConfig, null, 2)}\\n`,\n\t\t},\n\t];\n\n\tif (options.database) {\n\t\tfiles.push({\n\t\t\tpath: 'vitest.config.ts',\n\t\t\tcontent: vitestConfigContent,\n\t\t});\n\t}\n\n\treturn files;\n}\n\n/**\n * Helper to generate config files for API app in fullstack template\n * (workspace config is at root, so no gkm.config.ts for app)\n */\ninterface ConfigHelperOptions {\n\ttelescope: boolean;\n\tstudio: boolean;\n\troutesStructure: string;\n\tisServerless: boolean;\n\thasWorker: boolean;\n\tgetRoutesGlob: () => string;\n}\n\nfunction generateSingleAppConfigFiles(\n\toptions: TemplateOptions,\n\t_template: TemplateConfig,\n\t_helpers: ConfigHelperOptions,\n): GeneratedFile[] {\n\t// For fullstack, only generate tsconfig.json for the API app\n\t// The workspace gkm.config.ts is generated in monorepo.ts\n\t// Using noEmit: true since typecheck is done via turbo\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\tbaseUrl: '.',\n\t\t\tpaths: {\n\t\t\t\t'~/*': ['./src/*'],\n\t\t\t\t[`@${options.name}/*`]: ['../../packages/*/src'],\n\t\t\t},\n\t\t},\n\t\tinclude: ['src/**/*.ts'],\n\t\texclude: ['node_modules', 'dist'],\n\t};\n\n\tconst files: GeneratedFile[] = [\n\t\t{\n\t\t\tpath: 'tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t];\n\n\tif (options.database) {\n\t\tfiles.push({\n\t\t\tpath: 'vitest.config.ts',\n\t\t\tcontent: vitestConfigContent,\n\t\t});\n\t}\n\n\treturn files;\n}\n","import type { EventsBackend } from '../../types.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\n\nexport interface DatabaseAppConfig {\n\tname: string;\n\tpassword: string;\n}\n\n/**\n * Generate docker-compose.yml based on template and options\n */\nexport function generateDockerFiles(\n\toptions: TemplateOptions,\n\ttemplate: TemplateConfig,\n\tdbApps?: DatabaseAppConfig[],\n): GeneratedFile[] {\n\tconst { database } = options;\n\tconst isServerless = template.name === 'serverless';\n\tconst hasWorker = template.name === 'worker';\n\tconst isFullstack = options.template === 'fullstack';\n\n\tconst services: string[] = [];\n\tconst volumes: string[] = [];\n\tconst files: GeneratedFile[] = [];\n\n\t// PostgreSQL database\n\tif (database) {\n\t\tconst initVolume =\n\t\t\tisFullstack && dbApps?.length\n\t\t\t\t? `\n - ./docker/postgres/init.sh:/docker-entrypoint-initdb.d/init.sh:ro`\n\t\t\t\t: '';\n\n\t\tconst envFile =\n\t\t\tisFullstack && dbApps?.length\n\t\t\t\t? `\n env_file:\n - ./docker/.env`\n\t\t\t\t: '';\n\n\t\tservices.push(` postgres:\n image: postgres:18-alpine\n container_name: ${options.name}-postgres\n restart: unless-stopped${envFile}\n environment:\n POSTGRES_USER: \\${POSTGRES_USER:-postgres}\n POSTGRES_PASSWORD: \\${POSTGRES_PASSWORD:-postgres}\n POSTGRES_DB: \\${POSTGRES_DB:-${options.name.replace(/-/g, '_')}_dev}\n ports:\n - '\\${POSTGRES_HOST_PORT:-5432}:5432'\n volumes:\n - dbdata:/var/lib/postgresql/18/data${initVolume}\n healthcheck:\n test: ['CMD-SHELL', 'pg_isready -U $$POSTGRES_USER']\n interval: 5s\n timeout: 5s\n retries: 5`);\n\t\tvolumes.push(' dbdata:');\n\n\t\t// Generate PostgreSQL init script and .env for fullstack template\n\t\tif (isFullstack && dbApps?.length) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'docker/postgres/init.sh',\n\t\t\t\tcontent: generatePostgresInitScript(dbApps, options.services?.events),\n\t\t\t});\n\n\t\t\t// Generate .env file for docker-compose (contains db passwords)\n\t\t\tfiles.push({\n\t\t\t\tpath: 'docker/.env',\n\t\t\t\tcontent: generateDockerEnv(dbApps, options.services?.events),\n\t\t\t});\n\t\t}\n\t}\n\n\t// Redis - different setup for serverless vs standard\n\tif (isServerless) {\n\t\t// Use serverless-redis-http for Lambda compatibility\n\t\tservices.push(` redis:\n image: redis:7-alpine\n container_name: ${options.name}-redis\n restart: unless-stopped\n ports:\n - '\\${REDIS_HOST_PORT:-6379}:6379'\n volumes:\n - redis_data:/data\n healthcheck:\n test: ['CMD', 'redis-cli', 'ping']\n interval: 5s\n timeout: 5s\n retries: 5\n\n serverless-redis:\n image: hiett/serverless-redis-http:latest\n container_name: ${options.name}-serverless-redis\n restart: unless-stopped\n ports:\n - '\\${SRH_HOST_PORT:-8079}:80'\n environment:\n SRH_MODE: env\n SRH_TOKEN: local_dev_token\n SRH_CONNECTION_STRING: redis://redis:6379\n depends_on:\n redis:\n condition: service_healthy`);\n\t\tvolumes.push(' redis_data:');\n\t} else {\n\t\t// Standard Redis for non-serverless templates\n\t\tservices.push(` redis:\n image: redis:7-alpine\n container_name: ${options.name}-redis\n restart: unless-stopped\n ports:\n - '\\${REDIS_HOST_PORT:-6379}:6379'\n volumes:\n - redis_data:/data\n healthcheck:\n test: ['CMD', 'redis-cli', 'ping']\n interval: 5s\n timeout: 5s\n retries: 5`);\n\t\tvolumes.push(' redis_data:');\n\t}\n\n\t// RabbitMQ for worker template\n\tif (hasWorker) {\n\t\tservices.push(` rabbitmq:\n image: rabbitmq:3-management-alpine\n container_name: ${options.name}-rabbitmq\n restart: unless-stopped\n ports:\n - '\\${RABBITMQ_HOST_PORT:-5672}:5672'\n - '\\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672'\n environment:\n RABBITMQ_DEFAULT_USER: guest\n RABBITMQ_DEFAULT_PASS: guest\n volumes:\n - rabbitmq_data:/var/lib/rabbitmq\n healthcheck:\n test: ['CMD', 'rabbitmq-diagnostics', 'check_running']\n interval: 10s\n timeout: 5s\n retries: 5`);\n\t\tvolumes.push(' rabbitmq_data:');\n\t}\n\n\t// Mailpit for email testing\n\tif (options.services?.mail) {\n\t\tservices.push(` mailpit:\n image: axllent/mailpit:latest\n container_name: ${options.name}-mailpit\n restart: unless-stopped\n ports:\n - '\\${SMTP_PORT:-1025}:1025'\n - '\\${MAILPIT_PORT:-8025}:8025'\n environment:\n MP_SMTP_AUTH: \\${SMTP_USER:-${options.name}}:\\${SMTP_PASS:-${options.name}}`);\n\t}\n\n\t// MinIO for S3-compatible object storage\n\tif (options.services?.storage) {\n\t\tservices.push(` minio:\n image: minio/minio:latest\n container_name: ${options.name}-minio\n restart: unless-stopped\n entrypoint: sh\n command: -c 'mkdir -p /data/\\${STORAGE_BUCKET:-${options.name}} && /usr/bin/docker-entrypoint.sh server --console-address \":9001\" /data'\n environment:\n MINIO_ROOT_USER: \\${STORAGE_ACCESS_KEY_ID:-${options.name}}\n MINIO_ROOT_PASSWORD: \\${STORAGE_SECRET_ACCESS_KEY:-${options.name}}\n ports:\n - '\\${MINIO_API_HOST_PORT:-9000}:9000'\n - '\\${MINIO_CONSOLE_HOST_PORT:-9001}:9001'\n volumes:\n - minio_data:/data\n healthcheck:\n test: ['CMD', 'mc', 'ready', 'local']\n interval: 10s\n timeout: 5s\n retries: 5`);\n\t\tvolumes.push(' minio_data:');\n\t}\n\n\t// LocalStack for SNS events\n\tif (options.services?.events === 'sns') {\n\t\tservices.push(` localstack:\n image: localstack/localstack:latest\n container_name: ${options.name}-localstack\n restart: unless-stopped\n environment:\n SERVICES: sns,sqs\n AWS_DEFAULT_REGION: \\${AWS_REGION:-us-east-1}\n AWS_ACCESS_KEY_ID: \\${AWS_ACCESS_KEY_ID:-localstack}\n AWS_SECRET_ACCESS_KEY: \\${AWS_SECRET_ACCESS_KEY:-localstack}\n ports:\n - '\\${LOCALSTACK_PORT:-4566}:4566'\n volumes:\n - localstack_data:/var/lib/localstack\n healthcheck:\n test: ['CMD', 'curl', '-f', 'http://localhost:4566/_localstack/health']\n interval: 10s\n timeout: 5s\n retries: 5`);\n\t\tvolumes.push(' localstack_data:');\n\t}\n\n\t// RabbitMQ for rabbitmq events (when not already added by worker template)\n\tif (options.services?.events === 'rabbitmq' && !hasWorker) {\n\t\tservices.push(` rabbitmq:\n image: rabbitmq:3-management-alpine\n container_name: ${options.name}-rabbitmq\n restart: unless-stopped\n ports:\n - '\\${RABBITMQ_HOST_PORT:-5672}:5672'\n - '\\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672'\n environment:\n RABBITMQ_DEFAULT_USER: guest\n RABBITMQ_DEFAULT_PASS: guest\n volumes:\n - rabbitmq_data:/var/lib/rabbitmq\n healthcheck:\n test: ['CMD', 'rabbitmq-diagnostics', 'check_running']\n interval: 10s\n timeout: 5s\n retries: 5`);\n\t\tif (!volumes.includes(' rabbitmq_data:')) {\n\t\t\tvolumes.push(' rabbitmq_data:');\n\t\t}\n\t}\n\n\t// Build docker-compose.yml\n\tlet dockerCompose = `# Use \"gkm dev\" or \"gkm test\" to start services.\n# Running \"docker compose up\" directly will not inject secrets or resolve ports.\nservices:\n${services.join('\\n\\n')}\n`;\n\n\tif (volumes.length > 0) {\n\t\tdockerCompose += `\nvolumes:\n${volumes.join('\\n')}\n`;\n\t}\n\n\t// Add docker-compose.yml to files\n\tfiles.push({\n\t\tpath: 'docker-compose.yml',\n\t\tcontent: dockerCompose,\n\t});\n\n\treturn files;\n}\n\n/**\n * Generate .env file for docker-compose with database passwords\n */\nfunction generateDockerEnv(\n\tapps: DatabaseAppConfig[],\n\t_eventsBackend?: EventsBackend,\n): string {\n\tconst envVars = apps.map((app) => {\n\t\tconst envVar = `${app.name.toUpperCase()}_DB_PASSWORD`;\n\t\treturn `${envVar}=${app.password}`;\n\t});\n\n\t// Always include pgboss password for the dedicated pgboss user\n\tenvVars.push(`PGBOSS_DB_PASSWORD=pgboss-dev-password`);\n\n\treturn `# Auto-generated docker environment file\n# Contains database passwords for docker-compose postgres init\n# This file is gitignored - do not commit to version control\n${envVars.join('\\n')}\n`;\n}\n\n/**\n * Generate PostgreSQL init shell script that creates per-app users with separate schemas.\n * Uses idempotent DO blocks so the script can be re-run safely.\n * - api user: uses public schema\n * - auth user: uses auth schema with search_path=auth\n * - pgboss user: uses pgboss schema (when events === 'pgboss')\n */\nfunction generatePostgresInitScript(\n\tapps: DatabaseAppConfig[],\n\t_eventsBackend?: EventsBackend,\n): string {\n\tconst userCreations = apps.map((app) => {\n\t\tconst userName = app.name.replace(/-/g, '_');\n\t\tconst envVar = `${app.name.toUpperCase()}_DB_PASSWORD`;\n\t\tconst isApi = app.name === 'api';\n\t\tconst schemaName = isApi ? 'public' : userName;\n\n\t\tif (isApi) {\n\t\t\treturn `\n# Create ${app.name} user (uses public schema) - idempotent\necho \"Creating user ${userName}...\"\npsql -v ON_ERROR_STOP=1 --username \"$POSTGRES_USER\" --dbname \"$POSTGRES_DB\" <<-EOSQL\n DO \\\\$\\\\$\n BEGIN\n IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${userName}') THEN\n CREATE USER ${userName} WITH PASSWORD '$${envVar}';\n ELSE\n ALTER USER ${userName} WITH PASSWORD '$${envVar}';\n END IF;\n END\n \\\\$\\\\$;\n GRANT ALL ON SCHEMA public TO ${userName};\n ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${userName};\n ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ${userName};\nEOSQL\n`;\n\t\t}\n\t\treturn `\n# Create ${app.name} user with dedicated schema - idempotent\necho \"Creating user ${userName} with schema ${schemaName}...\"\npsql -v ON_ERROR_STOP=1 --username \"$POSTGRES_USER\" --dbname \"$POSTGRES_DB\" <<-EOSQL\n DO \\\\$\\\\$\n BEGIN\n IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${userName}') THEN\n CREATE USER ${userName} WITH PASSWORD '$${envVar}';\n ELSE\n ALTER USER ${userName} WITH PASSWORD '$${envVar}';\n END IF;\n END\n \\\\$\\\\$;\n CREATE SCHEMA IF NOT EXISTS ${schemaName} AUTHORIZATION ${userName};\n ALTER USER ${userName} SET search_path TO ${schemaName};\n GRANT USAGE ON SCHEMA ${schemaName} TO ${userName};\n GRANT ALL ON ALL TABLES IN SCHEMA ${schemaName} TO ${userName};\n GRANT ALL ON ALL SEQUENCES IN SCHEMA ${schemaName} TO ${userName};\n ALTER DEFAULT PRIVILEGES IN SCHEMA ${schemaName} GRANT ALL ON TABLES TO ${userName};\n ALTER DEFAULT PRIVILEGES IN SCHEMA ${schemaName} GRANT ALL ON SEQUENCES TO ${userName};\nEOSQL\n`;\n\t});\n\n\t// Always add pgboss user and schema.\n\t// pgboss reuses the same database with a dedicated user/schema,\n\t// so subscribers work out of the box without explicit events config.\n\tconst pgbossBlock = `\n# Create pgboss user with dedicated schema - idempotent\necho \"Creating pgboss user and schema...\"\npsql -v ON_ERROR_STOP=1 --username \"$POSTGRES_USER\" --dbname \"$POSTGRES_DB\" <<-EOSQL\n DO \\\\$\\\\$\n BEGIN\n IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'pgboss') THEN\n CREATE USER pgboss WITH PASSWORD '$PGBOSS_DB_PASSWORD';\n ELSE\n ALTER USER pgboss WITH PASSWORD '$PGBOSS_DB_PASSWORD';\n END IF;\n END\n \\\\$\\\\$;\n CREATE SCHEMA IF NOT EXISTS pgboss AUTHORIZATION pgboss;\n ALTER USER pgboss SET search_path TO pgboss;\n GRANT USAGE ON SCHEMA pgboss TO pgboss;\n GRANT ALL ON ALL TABLES IN SCHEMA pgboss TO pgboss;\n GRANT ALL ON ALL SEQUENCES IN SCHEMA pgboss TO pgboss;\n ALTER DEFAULT PRIVILEGES IN SCHEMA pgboss GRANT ALL ON TABLES TO pgboss;\n ALTER DEFAULT PRIVILEGES IN SCHEMA pgboss GRANT ALL ON SEQUENCES TO pgboss;\nEOSQL\n`;\n\n\t// Add extensions\n\tconst extensions = `\n# Create extensions\necho \"Creating extensions...\"\npsql -v ON_ERROR_STOP=1 --username \"$POSTGRES_USER\" --dbname \"$POSTGRES_DB\" <<-EOSQL\n CREATE EXTENSION IF NOT EXISTS pg_trgm;\n CREATE EXTENSION IF NOT EXISTS citext;\nEOSQL\n`;\n\n\treturn `#!/bin/bash\nset -e\n\n# Auto-generated PostgreSQL init script (idempotent - safe to re-run)\n# Creates per-app users with separate schemas in a single database\n# - api: uses public schema\n# - auth: uses auth schema (search_path=auth)\n# - pgboss: uses pgboss schema for event processing\n${extensions}\n${userCreations.join('\\n')}${pgbossBlock}\necho \"Database initialization complete!\"\n`;\n}\n","import type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\n\n/**\n * Generate environment-related files (.gitignore only).\n * Note: .env files are no longer generated. Use `gkm secrets:init` to initialize\n * encrypted secrets stored in `.gkm/secrets/{stage}.json` with keys stored at\n * `~/.gkm/{project-name}/{stage}.key`.\n */\nexport function generateEnvFiles(\n\toptions: TemplateOptions,\n\t_template: TemplateConfig,\n): GeneratedFile[] {\n\tconst files: GeneratedFile[] = [];\n\n\t// Only add .gitignore for non-monorepo (monorepo has it at root)\n\tif (!options.monorepo) {\n\t\tconst gitignore = `# Dependencies\nnode_modules/\n\n# Build output\ndist/\n.gkm/\n\n# Environment (legacy - use gkm secrets instead)\n.env\n.env.local\n.env.*.local\n\n# IDE\n.idea/\n.vscode/\n*.swp\n*.swo\n\n# OS\n.DS_Store\nThumbs.db\n\n# Logs\n*.log\nnpm-debug.log*\nyarn-debug.log*\npnpm-debug.log*\n\n# Test coverage\ncoverage/\n\n# TypeScript cache\n*.tsbuildinfo\n`;\n\t\tfiles.push({\n\t\t\tpath: '.gitignore',\n\t\t\tcontent: gitignore,\n\t\t});\n\t}\n\n\treturn files;\n}\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate an Expo mobile app for the fullstack template.\n *\n * Mirrors the structure of rezgo/apps/app: NativeWind for styling,\n * expo-router for navigation, better-auth + magic link, React Query\n * for data, all wired into the toolbox's typed API client.\n */\nexport function generateExpoAppFiles(\n\toptions: TemplateOptions,\n): GeneratedFile[] {\n\tif (!options.monorepo || options.template !== 'fullstack') {\n\t\treturn [];\n\t}\n\n\tconst packageName = `@${options.name}/app`;\n\tconst apiPackage = `@${options.name}/api`;\n\tconst modelsPackage = `@${options.name}/models`;\n\t// Expo bundle id / scheme — kept simple; the user can rename later.\n\tconst slug = options.name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\tconst scheme = slug.replace(/-/g, '');\n\tconst bundleId = `com.${scheme}.app`;\n\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tmain: 'expo-router/entry',\n\t\tprivate: true,\n\t\tscripts: {\n\t\t\tdev: 'gkm exec -- expo start -c',\n\t\t\tios: 'expo start --ios',\n\t\t\tandroid: 'expo start --android',\n\t\t\tweb: 'expo start --web',\n\t\t\ttypecheck: 'tsc --noEmit',\n\t\t},\n\t\tdependencies: {\n\t\t\t[apiPackage]: 'workspace:*',\n\t\t\t[modelsPackage]: 'workspace:*',\n\t\t\t'@better-auth/expo': '~1.2.0',\n\t\t\t'@geekmidas/client': GEEKMIDAS_VERSIONS['@geekmidas/client'],\n\t\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t\t'@react-navigation/native': '^7.1.0',\n\t\t\t'@tanstack/react-query': '~5.80.0',\n\t\t\t'better-auth': '~1.2.0',\n\t\t\texpo: '~55.0.0',\n\t\t\t'expo-constants': '~55.0.0',\n\t\t\t'expo-dev-client': '~55.0.0',\n\t\t\t'expo-linking': '~55.0.0',\n\t\t\t'expo-router': '~55.0.0',\n\t\t\t'expo-secure-store': '~14.2.0',\n\t\t\t'expo-splash-screen': '~55.0.0',\n\t\t\t'expo-status-bar': '~55.0.0',\n\t\t\tnativewind: '~4.2.0',\n\t\t\treact: '19.2.0',\n\t\t\t'react-dom': '19.2.0',\n\t\t\t'react-native': '0.83.6',\n\t\t\t'react-native-gesture-handler': '~2.30.0',\n\t\t\t'react-native-reanimated': '~4.2.0',\n\t\t\t'react-native-safe-area-context': '~5.6.0',\n\t\t\t'react-native-screens': '~4.23.0',\n\t\t\t'react-native-web': '~0.21.0',\n\t\t\ttailwindcss: '~3.4.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@babel/core': '^7.25.0',\n\t\t\t'@types/react': '~19.0.0',\n\t\t\ttypescript: '~5.8.2',\n\t\t},\n\t};\n\n\tconst appConfig = `import type { ConfigContext, ExpoConfig } from '@expo/config';\n\n/**\n * Expo runtime config. Public env vars (\\`EXPO_PUBLIC_*\\`) are inlined\n * at build time; everything else is read at runtime from \\`extra\\`.\n */\nexport default function config(_context: ConfigContext): ExpoConfig {\n return {\n name: '${options.name}',\n slug: '${slug}',\n version: '0.0.1',\n orientation: 'portrait',\n icon: './assets/icon.png',\n scheme: '${scheme}',\n userInterfaceStyle: 'automatic',\n newArchEnabled: true,\n ios: {\n supportsTablet: true,\n bundleIdentifier: '${bundleId}',\n },\n android: {\n package: '${bundleId}',\n adaptiveIcon: {\n foregroundImage: './assets/adaptive-icon.png',\n backgroundColor: '#ffffff',\n },\n edgeToEdgeEnabled: true,\n },\n web: {\n output: 'static',\n },\n plugins: [\n 'expo-router',\n 'expo-secure-store',\n [\n 'expo-splash-screen',\n {\n image: './assets/splash.png',\n imageWidth: 200,\n resizeMode: 'contain',\n backgroundColor: '#ffffff',\n },\n ],\n ],\n experiments: {\n typedRoutes: true,\n },\n };\n}\n`;\n\n\tconst easJson = {\n\t\tcli: {\n\t\t\tversion: '>= 10.2.1',\n\t\t\tappVersionSource: 'remote',\n\t\t},\n\t\tbuild: {\n\t\t\tdev: {\n\t\t\t\tdevelopmentClient: true,\n\t\t\t\tdistribution: 'internal',\n\t\t\t\tenvironment: 'development',\n\t\t\t\tenv: {\n\t\t\t\t\tEXPO_PUBLIC_API_URL: 'http://localhost:3000',\n\t\t\t\t\tEXPO_PUBLIC_AUTH_URL: 'http://localhost:3002',\n\t\t\t\t},\n\t\t\t},\n\t\t\tpreview: {\n\t\t\t\tdistribution: 'internal',\n\t\t\t\tenvironment: 'preview',\n\t\t\t\tenv: {\n\t\t\t\t\tEXPO_PUBLIC_API_URL: 'https://api.example.com',\n\t\t\t\t\tEXPO_PUBLIC_AUTH_URL: 'https://auth.example.com',\n\t\t\t\t},\n\t\t\t},\n\t\t\tproduction: {\n\t\t\t\tautoIncrement: true,\n\t\t\t\tenvironment: 'production',\n\t\t\t\tenv: {\n\t\t\t\t\tEXPO_PUBLIC_API_URL: 'https://api.example.com',\n\t\t\t\t\tEXPO_PUBLIC_AUTH_URL: 'https://auth.example.com',\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n\n\tconst babelConfig = `module.exports = function (api) {\n api.cache(true);\n return {\n presets: [\n ['babel-preset-expo', { jsxImportSource: 'nativewind' }],\n 'nativewind/babel',\n ],\n };\n};\n`;\n\n\tconst metroConfig = `const { getDefaultConfig } = require('expo/metro-config');\nconst { withNativeWind } = require('nativewind/metro');\n\nconst config = getDefaultConfig(__dirname);\n\nmodule.exports = withNativeWind(config, { input: './global.css' });\n`;\n\n\tconst tsConfig = {\n\t\textends: 'expo/tsconfig.base',\n\t\tcompilerOptions: {\n\t\t\tstrict: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\ttypes: ['nativewind/types'],\n\t\t\tpaths: {\n\t\t\t\t'@/*': ['./*'],\n\t\t\t\t[modelsPackage]: ['../../packages/models/src'],\n\t\t\t\t[`${modelsPackage}/*`]: ['../../packages/models/src/*'],\n\t\t\t\t[`${apiPackage}/client`]: ['../../apps/api/.gkm/openapi.ts'],\n\t\t\t},\n\t\t},\n\t\tinclude: [\n\t\t\t'**/*.ts',\n\t\t\t'**/*.tsx',\n\t\t\t'.expo/types/**/*.ts',\n\t\t\t'expo-env.d.ts',\n\t\t\t'nativewind-env.d.ts',\n\t\t],\n\t};\n\n\tconst tailwindConfig = `import type { Config } from 'tailwindcss';\n\nexport default {\n content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],\n presets: [require('nativewind/preset')],\n theme: {\n extend: {},\n },\n plugins: [],\n} satisfies Config;\n`;\n\n\tconst globalCss = `@tailwind base;\n@tailwind components;\n@tailwind utilities;\n`;\n\n\tconst expoEnvDts = `/// <reference types=\"expo/types\" />\\n`;\n\n\tconst nativewindEnvDts = `/// <reference types=\"nativewind/types\" />\\n`;\n\n\tconst configTs = `import { EnvironmentParser } from '@geekmidas/envkit';\n\n/**\n * Public app config. EXPO_PUBLIC_* vars are inlined into the bundle at\n * build time, so they're safe to read in client code.\n */\nconst envParser = new EnvironmentParser({\n EXPO_PUBLIC_API_URL: process.env.EXPO_PUBLIC_API_URL,\n EXPO_PUBLIC_AUTH_URL: process.env.EXPO_PUBLIC_AUTH_URL,\n});\n\nexport const config = envParser\n .create((get) => ({\n apiUrl: get('EXPO_PUBLIC_API_URL').string(),\n authUrl: get('EXPO_PUBLIC_AUTH_URL').string(),\n }))\n .parse();\n`;\n\n\tconst queryClientTs = `import { QueryClient } from '@tanstack/react-query';\n\nexport const queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 60 * 1000,\n },\n },\n});\n`;\n\n\tconst authClientTs = `import { expoClient } from '@better-auth/expo/client';\nimport { magicLinkClient } from 'better-auth/client/plugins';\nimport { createAuthClient } from 'better-auth/react';\nimport * as SecureStore from 'expo-secure-store';\n\nimport { config } from '@/config.ts';\n\nexport const STORAGE_PREFIX = '${scheme}';\nexport const COOKIE_STORE_KEY = \\`\\${STORAGE_PREFIX}_cookie\\`;\n\nexport const authClient = createAuthClient({\n baseURL: config.authUrl,\n plugins: [\n expoClient({\n scheme: '${scheme}',\n storagePrefix: STORAGE_PREFIX,\n storage: SecureStore,\n }),\n magicLinkClient(),\n ],\n});\n\nexport const { signIn, useSession } = authClient;\n\nexport async function signOut() {\n try {\n await authClient.signOut();\n } finally {\n await SecureStore.deleteItemAsync(COOKIE_STORE_KEY);\n }\n}\n`;\n\n\tconst apiTs = `import { createApi } from '${apiPackage}/client';\n\nimport { config } from '@/config.ts';\nimport { authClient } from './auth-client.ts';\nimport { queryClient } from './query-client.ts';\n\nexport function createAppApi(options?: { headers?: Record<string, string> }) {\n return createApi({\n baseURL: config.apiUrl,\n queryClient,\n headers: options?.headers,\n onRequest: (cfg) => {\n const cookie = authClient.getCookie();\n const next = { ...cfg, credentials: 'omit' as const };\n if (cookie) {\n return {\n ...next,\n headers: { ...(next.headers ?? {}), Cookie: cookie },\n };\n }\n return next;\n },\n });\n}\n`;\n\n\tconst apiContextTsx = `import { createContext, type ReactNode, use } from 'react';\nimport type { createAppApi } from './api.ts';\n\nexport type Api = ReturnType<typeof createAppApi>;\n\nconst ApiContext = createContext<Api | null>(null);\n\nexport function ApiProvider({\n api,\n children,\n}: {\n api: Api;\n children: ReactNode;\n}) {\n return <ApiContext.Provider value={api}>{children}</ApiContext.Provider>;\n}\n\nexport function useApi(): Api {\n const api = use(ApiContext);\n if (!api) {\n throw new Error('useApi must be used within an ApiProvider');\n }\n return api;\n}\n`;\n\n\tconst layoutTsx = `import '../global.css';\n\nimport { QueryClientProvider } from '@tanstack/react-query';\nimport { Stack, useRouter, useSegments } from 'expo-router';\nimport { StatusBar } from 'expo-status-bar';\nimport { useEffect, useMemo } from 'react';\nimport 'react-native-reanimated';\n\nimport { authClient } from '@/lib/auth-client.ts';\nimport { ApiProvider } from '@/lib/api-context.tsx';\nimport { createAppApi } from '@/lib/api.ts';\nimport { queryClient } from '@/lib/query-client.ts';\n\nfunction AuthGate({ children }: { children: React.ReactNode }) {\n const router = useRouter();\n const segments = useSegments();\n const { data: session, isPending } = authClient.useSession();\n\n useEffect(() => {\n if (isPending) return;\n const onAuthRoute = segments[0] === 'login';\n if (!session && !onAuthRoute) {\n router.replace('/login');\n } else if (session && onAuthRoute) {\n router.replace('/');\n }\n }, [isPending, session, segments, router]);\n\n return <>{children}</>;\n}\n\nexport default function RootLayout() {\n const api = useMemo(() => createAppApi(), []);\n\n return (\n <QueryClientProvider client={queryClient}>\n <ApiProvider api={api}>\n <AuthGate>\n <Stack screenOptions={{ headerShown: false }}>\n <Stack.Screen name=\"index\" />\n <Stack.Screen name=\"login\" />\n </Stack>\n </AuthGate>\n <StatusBar style=\"auto\" />\n </ApiProvider>\n </QueryClientProvider>\n );\n}\n`;\n\n\tconst indexTsx = `import { Pressable, Text, View } from 'react-native';\nimport { SafeAreaView } from 'react-native-safe-area-context';\n\nimport { signOut, useSession } from '@/lib/auth-client.ts';\nimport { useApi } from '@/lib/api-context.tsx';\n\nexport default function Home() {\n const { data: session } = useSession();\n const api = useApi();\n const { data: health } = api.useQuery('GET /health', {});\n\n return (\n <SafeAreaView className=\"flex-1 bg-white\">\n <View className=\"flex-1 items-center justify-center gap-4 px-6\">\n <Text className=\"text-3xl font-bold text-slate-900\">\n Welcome to ${options.name}\n </Text>\n {session ? (\n <Text className=\"text-sm text-slate-500\">\n Signed in as {session.user.email}\n </Text>\n ) : null}\n {health ? (\n <Text className=\"text-xs text-slate-400\">\n API: {JSON.stringify(health)}\n </Text>\n ) : null}\n <Pressable\n onPress={signOut}\n className=\"mt-4 rounded-lg bg-slate-900 px-6 py-3\"\n >\n <Text className=\"font-semibold text-white\">Sign out</Text>\n </Pressable>\n </View>\n </SafeAreaView>\n );\n}\n`;\n\n\tconst loginTsx = `import * as Linking from 'expo-linking';\nimport { useState } from 'react';\nimport {\n KeyboardAvoidingView,\n Platform,\n Pressable,\n Text,\n TextInput,\n View,\n} from 'react-native';\nimport { SafeAreaView } from 'react-native-safe-area-context';\n\nimport { authClient } from '@/lib/auth-client.ts';\n\nconst EMAIL_RE = /^[^\\\\s@]+@[^\\\\s@]+\\\\.[^\\\\s@]+$/;\n\nexport default function LoginScreen() {\n const [email, setEmail] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [sent, setSent] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n async function handleSubmit() {\n if (!EMAIL_RE.test(email.trim())) {\n setError('Enter a valid email address');\n return;\n }\n setError(null);\n setSubmitting(true);\n\n const callbackURL = Linking.createURL('/').replace(/^(\\\\w+):\\\\/\\\\/\\\\//, '$1://');\n try {\n const result = await authClient.signIn.magicLink({\n email: email.trim(),\n callbackURL,\n });\n if (result.error) {\n setError(result.error.message ?? 'Could not send magic link');\n return;\n }\n setSent(true);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Something went wrong');\n } finally {\n setSubmitting(false);\n }\n }\n\n if (sent) {\n return (\n <SafeAreaView className=\"flex-1 bg-white\">\n <View className=\"flex-1 items-center justify-center px-6\">\n <Text className=\"text-2xl font-bold text-slate-900\">\n Check your inbox\n </Text>\n <Text className=\"mt-3 text-center text-slate-500\">\n We sent a sign-in link to {email.trim()}\n </Text>\n </View>\n </SafeAreaView>\n );\n }\n\n return (\n <SafeAreaView className=\"flex-1 bg-white\">\n <KeyboardAvoidingView\n behavior={Platform.OS === 'ios' ? 'padding' : undefined}\n className=\"flex-1\"\n >\n <View className=\"flex-1 justify-center px-6\">\n <Text className=\"text-3xl font-bold text-slate-900\">Sign in</Text>\n <Text className=\"mt-1 text-sm text-slate-500\">\n We'll email you a one-time sign-in link.\n </Text>\n\n <Text className=\"mt-6 text-xs font-semibold uppercase text-slate-500\">\n Email\n </Text>\n <TextInput\n value={email}\n onChangeText={(v) => {\n setEmail(v);\n if (error) setError(null);\n }}\n autoCapitalize=\"none\"\n autoCorrect={false}\n keyboardType=\"email-address\"\n placeholder=\"you@example.com\"\n placeholderTextColor=\"#94a3b8\"\n editable={!submitting}\n className=\"mt-2 rounded-lg border border-slate-300 px-4 py-3\"\n />\n {error ? (\n <Text className=\"mt-2 text-sm text-red-600\">{error}</Text>\n ) : null}\n\n <Pressable\n onPress={handleSubmit}\n disabled={submitting}\n className=\"mt-6 items-center rounded-lg bg-slate-900 py-4 disabled:opacity-60\"\n >\n <Text className=\"font-semibold text-white\">\n {submitting ? 'Sending…' : 'Email me a sign-in link'}\n </Text>\n </Pressable>\n </View>\n </KeyboardAvoidingView>\n </SafeAreaView>\n );\n}\n`;\n\n\tconst gitignore = `node_modules/\n.expo/\ndist/\n*.log\n.env*.local\nios/\nandroid/\n.eas/\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'apps/app/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{ path: 'apps/app/app.config.ts', content: appConfig },\n\t\t{\n\t\t\tpath: 'apps/app/eas.json',\n\t\t\tcontent: `${JSON.stringify(easJson, null, 2)}\\n`,\n\t\t},\n\t\t{ path: 'apps/app/babel.config.js', content: babelConfig },\n\t\t{ path: 'apps/app/metro.config.js', content: metroConfig },\n\t\t{\n\t\t\tpath: 'apps/app/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{ path: 'apps/app/tailwind.config.ts', content: tailwindConfig },\n\t\t{ path: 'apps/app/global.css', content: globalCss },\n\t\t{ path: 'apps/app/expo-env.d.ts', content: expoEnvDts },\n\t\t{ path: 'apps/app/nativewind-env.d.ts', content: nativewindEnvDts },\n\t\t{ path: 'apps/app/config.ts', content: configTs },\n\t\t{ path: 'apps/app/lib/query-client.ts', content: queryClientTs },\n\t\t{ path: 'apps/app/lib/auth-client.ts', content: authClientTs },\n\t\t{ path: 'apps/app/lib/api.ts', content: apiTs },\n\t\t{ path: 'apps/app/lib/api-context.tsx', content: apiContextTsx },\n\t\t{ path: 'apps/app/app/_layout.tsx', content: layoutTsx },\n\t\t{ path: 'apps/app/app/index.tsx', content: indexTsx },\n\t\t{ path: 'apps/app/app/login.tsx', content: loginTsx },\n\t\t{ path: 'apps/app/.gitignore', content: gitignore },\n\t];\n}\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\n\n/**\n * Generate packages/models for shared Zod schemas (monorepo only)\n */\nexport function generateModelsPackage(\n\toptions: TemplateOptions,\n): GeneratedFile[] {\n\tif (!options.monorepo) {\n\t\treturn [];\n\t}\n\n\t// Package name based on project name\n\tconst packageName = `@${options.name}/models`;\n\n\t// package.json for models\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\texports: {\n\t\t\t'./*': './src/*.ts',\n\t\t},\n\t\tscripts: {\n\t\t\ttypecheck: 'tsc --noEmit',\n\t\t},\n\t\tdependencies: {},\n\t\tdevDependencies: {\n\t\t\ttypescript: '~5.8.2',\n\t\t},\n\t};\n\n\t// tsconfig.json for models - extends root config\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t},\n\t\tinclude: ['src/**/*.ts'],\n\t\texclude: ['node_modules', 'dist'],\n\t};\n\n\t// common.ts - shared utility schemas\n\tconst commonTs = `import { z } from 'zod';\n\n// ============================================\n// Common Schemas\n// ============================================\n\nexport const IdSchema = z.uuid();\n\nexport const IdParamsSchema = z.object({\n id: IdSchema,\n});\n\nexport const TimestampsSchema = z.object({\n createdAt: z.coerce.date(),\n updatedAt: z.coerce.date(),\n});\n\nexport const PaginationSchema = z.object({\n page: z.coerce.number().int().positive().default(1),\n limit: z.coerce.number().int().positive().max(100).default(20),\n});\n\nexport const PaginatedResponseSchema = <T extends z.ZodTypeAny>(itemSchema: T) =>\n z.object({\n items: z.array(itemSchema),\n total: z.number(),\n page: z.number(),\n limit: z.number(),\n totalPages: z.number(),\n });\n\n// ============================================\n// Type Exports\n// ============================================\n\nexport type Id = z.infer<typeof IdSchema>;\nexport type IdParams = z.infer<typeof IdParamsSchema>;\nexport type Timestamps = z.infer<typeof TimestampsSchema>;\nexport type Pagination = z.infer<typeof PaginationSchema>;\n`;\n\n\t// user.ts - user-related schemas\n\tconst userTs = `import { z } from 'zod';\nimport { IdSchema, TimestampsSchema } from './common.ts';\n\n// ============================================\n// User Schemas\n// ============================================\n\nexport const UserSchema = z.object({\n id: IdSchema,\n email: z.string().email(),\n name: z.string().min(1).max(100),\n ...TimestampsSchema.shape,\n});\n\nexport const CreateUserSchema = UserSchema.omit({\n id: true,\n createdAt: true,\n updatedAt: true,\n});\n\nexport const UpdateUserSchema = CreateUserSchema.partial();\n\n// ============================================\n// Response Schemas\n// ============================================\n\nexport const UserResponseSchema = UserSchema.pick({\n id: true,\n name: true,\n email: true,\n});\n\nexport const ListUsersResponseSchema = z.object({\n users: z.array(UserSchema.pick({ id: true, name: true })),\n});\n\n// ============================================\n// Type Exports\n// ============================================\n\nexport type User = z.infer<typeof UserSchema>;\nexport type CreateUser = z.infer<typeof CreateUserSchema>;\nexport type UpdateUser = z.infer<typeof UpdateUserSchema>;\nexport type UserResponse = z.infer<typeof UserResponseSchema>;\nexport type ListUsersResponse = z.infer<typeof ListUsersResponseSchema>;\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'packages/models/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/models/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/models/src/common.ts',\n\t\t\tcontent: commonTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/models/src/user.ts',\n\t\t\tcontent: userTs,\n\t\t},\n\t];\n}\n","import type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate monorepo root files (pnpm-workspace.yaml, root package.json, etc.)\n */\nexport function generateMonorepoFiles(\n\toptions: TemplateOptions,\n\t_template: TemplateConfig,\n): GeneratedFile[] {\n\tif (!options.monorepo) {\n\t\treturn [];\n\t}\n\n\tconst isFullstack = options.template === 'fullstack';\n\n\t// Root package.json for monorepo\n\tconst rootPackageJson = {\n\t\tname: options.name,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\tpackageManager: 'pnpm@10.13.1',\n\t\tscripts: {\n\t\t\tdev: isFullstack ? 'gkm dev' : 'turbo dev',\n\t\t\tbuild: isFullstack ? 'gkm build' : 'turbo build',\n\t\t\ttest: isFullstack ? 'gkm test' : 'turbo test',\n\t\t\t'test:once': isFullstack ? 'gkm test --run' : 'turbo test:once',\n\t\t\ttypecheck: 'turbo typecheck',\n\t\t\tlint: 'biome lint .',\n\t\t\tfmt: 'biome format . --write',\n\t\t\t'fmt:check': 'biome format .',\n\t\t\t...(isFullstack\n\t\t\t\t? { storybook: 'pnpm --filter ./packages/ui storybook' }\n\t\t\t\t: {}),\n\t\t\t...(options.deployTarget === 'dokploy'\n\t\t\t\t? { deploy: 'gkm deploy --provider dokploy --stage production' }\n\t\t\t\t: {}),\n\t\t},\n\t\tdependencies: {\n\t\t\tzod: '~4.1.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@biomejs/biome': '~2.3.0',\n\t\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t\tesbuild: '~0.27.0',\n\t\t\ttsx: '~4.20.0',\n\t\t\tturbo: '~2.3.0',\n\t\t\ttypescript: '~5.8.2',\n\t\t\tvitest: '~4.0.0',\n\t\t},\n\t};\n\n\t// pnpm-workspace.yaml - detect folder structure from apiPath\n\tconst apiPathParts = options.apiPath.split('/');\n\tconst appsFolder = apiPathParts[0] || 'apps';\n\n\tconst pnpmWorkspace = `packages:\n - '${appsFolder}/*'\n - 'packages/*'\n`;\n\n\t// Root biome.json\n\tconst biomeConfig = {\n\t\t$schema: 'https://biomejs.dev/schemas/2.3.0/schema.json',\n\t\tvcs: {\n\t\t\tenabled: true,\n\t\t\tclientKind: 'git',\n\t\t\tuseIgnoreFile: true,\n\t\t},\n\t\torganizeImports: {\n\t\t\tenabled: true,\n\t\t},\n\t\tformatter: {\n\t\t\tenabled: true,\n\t\t\tindentStyle: 'space',\n\t\t\tindentWidth: 2,\n\t\t\tlineWidth: 80,\n\t\t},\n\t\tjavascript: {\n\t\t\tformatter: {\n\t\t\t\tquoteStyle: 'single',\n\t\t\t\ttrailingCommas: 'all',\n\t\t\t\tsemicolons: 'always',\n\t\t\t\tarrowParentheses: 'always',\n\t\t\t},\n\t\t},\n\t\tlinter: {\n\t\t\tenabled: true,\n\t\t\trules: {\n\t\t\t\trecommended: true,\n\t\t\t\tcorrectness: {\n\t\t\t\t\tnoUnusedImports: 'error',\n\t\t\t\t\tnoUnusedVariables: 'error',\n\t\t\t\t},\n\t\t\t\tstyle: {\n\t\t\t\t\tnoNonNullAssertion: 'off',\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tfiles: {\n\t\t\tignore: ['node_modules', 'dist', '.gkm', 'coverage'],\n\t\t},\n\t};\n\n\t// Root turbo.json\n\tconst turboConfig = {\n\t\t$schema: 'https://turbo.build/schema.json',\n\t\ttasks: {\n\t\t\tbuild: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: ['dist/**'],\n\t\t\t},\n\t\t\tdev: {\n\t\t\t\tcache: false,\n\t\t\t\tpersistent: true,\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\tcache: false,\n\t\t\t},\n\t\t\t'test:once': {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: ['coverage/**'],\n\t\t\t},\n\t\t\ttypecheck: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t\tlint: {\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t\tfmt: {\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t},\n\t};\n\n\t// Root .gitignore\n\tconst gitignore = `# Dependencies\nnode_modules/\n\n# Build output\ndist/\n.gkm/\n\n# Environment\n.env\n.env.local\n.env.*.local\ndocker/.env\n\n# IDE\n.idea/\n*.swp\n*.swo\n\n# OS\n.DS_Store\nThumbs.db\n\n# Logs\n*.log\nnpm-debug.log*\nyarn-debug.log*\npnpm-debug.log*\n\n# Test coverage\ncoverage/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Turbo\n.turbo/\n`;\n\n\t// Root tsconfig.json - base config for all packages\n\t// Using turbo typecheck to run tsc --noEmit in each app/package\n\tconst tsConfig = {\n\t\tcompilerOptions: {\n\t\t\ttarget: 'ES2022',\n\t\t\tmodule: 'NodeNext',\n\t\t\tmoduleResolution: 'NodeNext',\n\t\t\tlib: ['ES2022'],\n\t\t\tstrict: true,\n\t\t\tesModuleInterop: true,\n\t\t\tskipLibCheck: true,\n\t\t\tforceConsistentCasingInFileNames: true,\n\t\t\tresolveJsonModule: true,\n\t\t},\n\t\texclude: ['node_modules', 'dist'],\n\t};\n\n\t// Vitest config for workspace\n\tconst vitestConfig = `import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n test: {\n globals: true,\n environment: 'node',\n include: ['apps/**/*.{test,spec}.ts', 'packages/**/*.{test,spec}.ts'],\n exclude: ['**/node_modules/**', '**/dist/**'],\n coverage: {\n provider: 'v8',\n reporter: ['text', 'json', 'html'],\n exclude: ['**/node_modules/**', '**/dist/**', '**/*.d.ts'],\n },\n },\n});\n`;\n\n\t// VSCode settings for consistent development experience\n\tconst vscodeSettings = {\n\t\t'search.exclude': {\n\t\t\t'**/.sst': true,\n\t\t\t'**/.gkm': true,\n\t\t\t'**/.turbo': true,\n\t\t},\n\t\t'editor.formatOnSave': true,\n\t\t'editor.defaultFormatter': 'biomejs.biome',\n\t\t'editor.codeActionsOnSave': {\n\t\t\t'source.fixAll.biome': 'always',\n\t\t\t'source.organizeImports.biome': 'always',\n\t\t\t'source.organizeImports': 'always',\n\t\t},\n\t\t'[typescriptreact]': {\n\t\t\t'editor.defaultFormatter': 'biomejs.biome',\n\t\t},\n\t\t'[typescript]': {\n\t\t\t'editor.defaultFormatter': 'biomejs.biome',\n\t\t},\n\t\t'[javascript]': {\n\t\t\t'editor.defaultFormatter': 'biomejs.biome',\n\t\t},\n\t\t'[json]': {\n\t\t\t'editor.defaultFormatter': 'biomejs.biome',\n\t\t},\n\t\t'cSpell.words': [\n\t\t\t'betterauth',\n\t\t\t'dokploy',\n\t\t\t'envkit',\n\t\t\t'geekmidas',\n\t\t\t'healthcheck',\n\t\t\t'kysely',\n\t\t\t'testkit',\n\t\t\t'timestamptz',\n\t\t\t'turborepo',\n\t\t\toptions.name,\n\t\t],\n\t};\n\n\t// VSCode extensions recommendations\n\tconst vscodeExtensions = {\n\t\trecommendations: [\n\t\t\t'biomejs.biome',\n\t\t\t'streetsidesoftware.code-spell-checker',\n\t\t\t'dbaeumer.vscode-eslint',\n\t\t\t'ms-azuretools.vscode-docker',\n\t\t],\n\t};\n\n\tconst files: GeneratedFile[] = [\n\t\t{\n\t\t\tpath: 'package.json',\n\t\t\tcontent: `${JSON.stringify(rootPackageJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'pnpm-workspace.yaml',\n\t\t\tcontent: pnpmWorkspace,\n\t\t},\n\t\t{\n\t\t\tpath: 'tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'biome.json',\n\t\t\tcontent: `${JSON.stringify(biomeConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'turbo.json',\n\t\t\tcontent: `${JSON.stringify(turboConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'vitest.config.ts',\n\t\t\tcontent: vitestConfig,\n\t\t},\n\t\t{\n\t\t\tpath: '.gitignore',\n\t\t\tcontent: gitignore,\n\t\t},\n\t\t{\n\t\t\tpath: '.vscode/settings.json',\n\t\t\tcontent: `${JSON.stringify(vscodeSettings, null, '\\t')}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: '.vscode/extensions.json',\n\t\t\tcontent: `${JSON.stringify(vscodeExtensions, null, '\\t')}\\n`,\n\t\t},\n\t];\n\n\t// Add workspace config for fullstack template\n\tif (isFullstack) {\n\t\tfiles.push({\n\t\t\tpath: 'gkm.config.ts',\n\t\t\tcontent: generateWorkspaceConfig(options),\n\t\t});\n\t}\n\n\treturn files;\n}\n\n/**\n * Generate the frontend app entry for the workspace config based on the\n * selected `frontendFramework`. Defaults to Next.js for backwards compat.\n */\nfunction generateFrontendAppEntry(options: TemplateOptions): string {\n\tswitch (options.frontendFramework) {\n\t\tcase 'tanstack-start':\n\t\t\treturn `web: {\n type: 'web',\n framework: 'tanstack-start',\n path: 'apps/web',\n port: 3001,\n dependencies: ['api', 'auth'],\n config: {\n client: './src/config/client.ts',\n server: './src/config/server.ts',\n },\n },`;\n\t\tcase 'expo':\n\t\t\treturn `app: {\n type: 'mobile',\n framework: 'expo',\n path: 'apps/app',\n port: 8081,\n dependencies: ['api', 'auth'],\n },`;\n\t\tdefault:\n\t\t\treturn `web: {\n type: 'web',\n framework: 'nextjs',\n path: 'apps/web',\n port: 3001,\n dependencies: ['api', 'auth'],\n config: {\n client: './src/config/client.ts',\n server: './src/config/server.ts',\n },\n },`;\n\t}\n}\n\n/**\n * Generate gkm.config.ts with defineWorkspace for fullstack template\n */\nfunction generateWorkspaceConfig(options: TemplateOptions): string {\n\tconst { telescope, services, deployTarget, routesStructure } = options;\n\n\t// Get routes glob pattern\n\tconst getRoutesGlob = (): string => {\n\t\tswitch (routesStructure) {\n\t\t\tcase 'centralized-endpoints':\n\t\t\t\treturn './src/endpoints/**/*.ts';\n\t\t\tcase 'centralized-routes':\n\t\t\t\treturn './src/routes/**/*.ts';\n\t\t\tcase 'domain-based':\n\t\t\t\treturn './src/**/routes/*.ts';\n\t\t}\n\t};\n\n\tlet config = `import { defineWorkspace } from '@geekmidas/cli/config';\n\nexport default defineWorkspace({\n name: '${options.name}',\n apps: {\n api: {\n type: 'backend',\n path: 'apps/api',\n port: 3000,\n routes: '${getRoutesGlob()}',\n envParser: './src/config/env#envParser',\n logger: './src/config/logger#logger',\n dependencies: ['auth'],`;\n\n\tif (telescope) {\n\t\tconfig += `\n telescope: {\n enabled: true,\n path: '/__telescope',\n },`;\n\t}\n\n\tconfig += `\n openapi: {\n enabled: true,\n },\n },\n auth: {\n type: 'backend',\n path: 'apps/auth',\n port: 3002,\n entry: './src/index.ts',\n framework: 'better-auth',\n envParser: './src/config/env#envParser',\n logger: './src/config/logger#logger',\n },\n ${generateFrontendAppEntry(options)}\n },\n shared: {\n packages: ['packages/*'],\n models: {\n path: 'packages/models',\n schema: 'zod',\n },\n },`;\n\n\t// Add services if any are selected\n\tif (\n\t\tservices.db ||\n\t\tservices.cache ||\n\t\tservices.mail ||\n\t\tservices.storage ||\n\t\tservices.events\n\t) {\n\t\tconfig += `\n services: {`;\n\t\tif (services.db) {\n\t\t\tconfig += `\n db: true,`;\n\t\t}\n\t\tif (services.cache) {\n\t\t\tconfig += `\n cache: true,`;\n\t\t}\n\t\tif (services.mail) {\n\t\t\tconfig += `\n mail: true,`;\n\t\t}\n\t\tif (services.storage) {\n\t\t\tconfig += `\n storage: true,`;\n\t\t}\n\t\tif (services.events) {\n\t\t\tconfig += `\n events: '${services.events}',`;\n\t\t}\n\t\tconfig += `\n },`;\n\t}\n\n\t// Add deploy config if dokploy is selected\n\tif (deployTarget === 'dokploy') {\n\t\tconfig += `\n deploy: {\n default: 'dokploy',\n },`;\n\t}\n\n\t// Always enable secrets for workspace dev environment\n\tconfig += `\n secrets: {\n enabled: true,\n },`;\n\n\tconfig += `\n});\n`;\n\n\treturn config;\n}\n","import { GEEKMIDAS_VERSIONS } from '../versions.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from './index.js';\n\nexport const apiTemplate: TemplateConfig = {\n\tname: 'api',\n\tdescription: 'Full API with auth, database, services',\n\n\tdependencies: {\n\t\t'@geekmidas/audit': GEEKMIDAS_VERSIONS['@geekmidas/audit'],\n\t\t'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],\n\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t'@geekmidas/events': GEEKMIDAS_VERSIONS['@geekmidas/events'],\n\t\t'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],\n\t\t'@geekmidas/rate-limit': GEEKMIDAS_VERSIONS['@geekmidas/rate-limit'],\n\t\t'@geekmidas/schema': GEEKMIDAS_VERSIONS['@geekmidas/schema'],\n\t\t'@geekmidas/services': GEEKMIDAS_VERSIONS['@geekmidas/services'],\n\t\t'@geekmidas/errors': GEEKMIDAS_VERSIONS['@geekmidas/errors'],\n\t\t'@geekmidas/auth': GEEKMIDAS_VERSIONS['@geekmidas/auth'],\n\t\t'@hono/node-server': '~1.14.1',\n\t\thono: '~4.8.2',\n\t\tpino: '~9.6.0',\n\t\tzod: '~4.1.0',\n\t},\n\n\tdevDependencies: {\n\t\t'@biomejs/biome': '~2.3.0',\n\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t'@types/node': '~22.0.0',\n\t\tesbuild: '~0.27.0',\n\t\ttsx: '~4.20.0',\n\t\tturbo: '~2.3.0',\n\t\ttypescript: '~5.8.2',\n\t\tvitest: '~4.0.0',\n\t},\n\n\tscripts: {\n\t\tdev: 'gkm dev',\n\t\tbuild: 'gkm build',\n\t\ttest: 'vitest',\n\t\t'test:once': 'vitest run',\n\t\ttypecheck: 'tsc --noEmit',\n\t\tlint: 'biome lint .',\n\t\tfmt: 'biome format . --write',\n\t\t'fmt:check': 'biome format .',\n\t},\n\n\tfiles: (options: TemplateOptions): GeneratedFile[] => {\n\t\tconst { loggerType, routesStructure, monorepo, name } = options;\n\n\t\tconst loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';\n\nexport const logger = createLogger();\n`;\n\n\t\t// Models package import path for monorepo\n\t\tconst modelsImport = monorepo ? `@${name}/models` : null;\n\n\t\t// Get route path based on structure\n\t\tconst getRoutePath = (file: string) => {\n\t\t\tswitch (routesStructure) {\n\t\t\t\tcase 'centralized-endpoints':\n\t\t\t\t\treturn `src/endpoints/${file}`;\n\t\t\t\tcase 'centralized-routes':\n\t\t\t\t\treturn `src/routes/${file}`;\n\t\t\t\tcase 'domain-based': {\n\t\t\t\t\tconst parts = file.split('/');\n\t\t\t\t\tif (parts.length === 1) {\n\t\t\t\t\t\treturn `src/${file.replace('.ts', '')}/routes/index.ts`;\n\t\t\t\t\t}\n\t\t\t\t\treturn `src/${parts[0]}/routes/${parts.slice(1).join('/')}`;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tconst files: GeneratedFile[] = [\n\t\t\t// src/config/env.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/env.ts',\n\t\t\t\tcontent: `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { EnvironmentParser } from '@geekmidas/envkit';\n\nexport const envParser = new EnvironmentParser({ ...process.env, ...Credentials });\n\n// Global config - only minimal shared values\n// Service-specific config should be parsed in each service\nexport const config = envParser\n .create((get) => ({\n nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),\n stage: get('STAGE').enum(['development', 'staging', 'production']).default('development'),\n }))\n .parse();\n`,\n\t\t\t},\n\n\t\t\t// src/config/logger.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/logger.ts',\n\t\t\t\tcontent: loggerContent,\n\t\t\t},\n\n\t\t\t// health endpoint\n\t\t\t{\n\t\t\t\tpath: getRoutePath('health.ts'),\n\t\t\t\tcontent: monorepo\n\t\t\t\t\t? `import { z } from 'zod';\nimport { publicRouter } from '~/router.ts';\n\nexport const healthEndpoint = publicRouter\n .get('/health')\n .output(z.object({\n status: z.string(),\n timestamp: z.string(),\n }))\n .handle(async () => ({\n status: 'ok',\n timestamp: new Date().toISOString(),\n }));\n`\n\t\t\t\t\t: `import { e } from '@geekmidas/constructs/endpoints';\nimport { z } from 'zod';\n\nexport const healthEndpoint = e\n .get('/health')\n .output(z.object({\n status: z.string(),\n timestamp: z.string(),\n }))\n .handle(async () => ({\n status: 'ok',\n timestamp: new Date().toISOString(),\n }));\n`,\n\t\t\t},\n\n\t\t\t// users endpoints\n\t\t\t{\n\t\t\t\tpath: getRoutePath('users/list.ts'),\n\t\t\t\tcontent: modelsImport\n\t\t\t\t\t? `import { e } from '@geekmidas/constructs/endpoints';\nimport { ListUsersResponseSchema } from '${modelsImport}/user';\n\nexport const listUsersEndpoint = e\n .get('/users')\n .output(ListUsersResponseSchema)\n .handle(async () => ({\n users: [\n { id: '550e8400-e29b-41d4-a716-446655440001', name: 'Alice' },\n { id: '550e8400-e29b-41d4-a716-446655440002', name: 'Bob' },\n ],\n }));\n`\n\t\t\t\t\t: `import { e } from '@geekmidas/constructs/endpoints';\nimport { z } from 'zod';\n\nconst UserSchema = z.object({\n id: z.string(),\n name: z.string(),\n});\n\nexport const listUsersEndpoint = e\n .get('/users')\n .output(z.object({\n users: z.array(UserSchema),\n }))\n .handle(async () => ({\n users: [\n { id: '1', name: 'Alice' },\n { id: '2', name: 'Bob' },\n ],\n }));\n`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: getRoutePath('users/get.ts'),\n\t\t\t\tcontent: modelsImport\n\t\t\t\t\t? `import { e } from '@geekmidas/constructs/endpoints';\nimport { IdParamsSchema } from '${modelsImport}/common';\nimport { UserResponseSchema } from '${modelsImport}/user';\n\nexport const getUserEndpoint = e\n .get('/users/:id')\n .params(IdParamsSchema)\n .output(UserResponseSchema)\n .handle(async ({ params }) => ({\n id: params.id,\n name: 'Alice',\n email: 'alice@example.com',\n }));\n`\n\t\t\t\t\t: `import { e } from '@geekmidas/constructs/endpoints';\nimport { z } from 'zod';\n\nexport const getUserEndpoint = e\n .get('/users/:id')\n .params(z.object({ id: z.string() }))\n .output(z.object({\n id: z.string(),\n name: z.string(),\n email: z.string().email(),\n }))\n .handle(async ({ params }) => ({\n id: params.id,\n name: 'Alice',\n email: 'alice@example.com',\n }));\n`,\n\t\t\t},\n\t\t];\n\n\t\t// Add auth service for monorepo (calls auth app for session)\n\t\tif (options.monorepo) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/services/auth.ts',\n\t\t\t\tcontent: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';\n\nexport interface Session {\n user: {\n id: string;\n email: string;\n name: string;\n };\n}\n\nexport interface AuthClient {\n getSession: (cookie: string) => Promise<Session | null>;\n}\n\nexport const authService = {\n serviceName: 'auth' as const,\n async register({ envParser, context }: ServiceRegisterOptions) {\n const logger = context.getLogger();\n\n const config = envParser\n .create((get) => ({\n url: get('AUTH_URL').string(),\n }))\n .parse();\n\n logger.info({ authUrl: config.url }, 'Auth service configured');\n\n return {\n getSession: async (cookie: string): Promise<Session | null> => {\n const res = await fetch(\\`\\${config.url}/api/auth/get-session\\`, {\n headers: { cookie },\n });\n if (!res.ok) return null;\n return res.json();\n },\n };\n },\n} satisfies Service<'auth', AuthClient>;\n`,\n\t\t\t});\n\n\t\t\t// Add router with session\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/router.ts',\n\t\t\t\tcontent: `import { e } from '@geekmidas/constructs/endpoints';\nimport { UnauthorizedError } from '@geekmidas/errors';\nimport { authService, type Session } from './services/auth.ts';\nimport { logger } from './config/logger.ts';\n\n// Public router - no auth required\nexport const publicRouter = e.logger(logger);\n\n// Router with auth service available (but session not enforced)\nexport const r = publicRouter.services([authService]);\n\n// Session router - requires active session, throws if not authenticated\nexport const sessionRouter = r.session<Session>(async ({ services, header }) => {\n const cookie = header('cookie') || '';\n const session = await services.auth.getSession(cookie);\n\n if (!session?.user) {\n throw new UnauthorizedError('No active session');\n }\n\n return session;\n});\n`,\n\t\t\t});\n\n\t\t\t// Add protected endpoint example\n\t\t\tfiles.push({\n\t\t\t\tpath: getRoutePath('profile.ts'),\n\t\t\t\tcontent: `import { z } from 'zod';\nimport { sessionRouter } from '~/router.ts';\n\nexport const profileEndpoint = sessionRouter\n .get('/profile')\n .output(z.object({\n id: z.string(),\n email: z.string(),\n name: z.string(),\n }))\n .handle(async ({ session }) => session.user);\n`,\n\t\t\t});\n\t\t}\n\n\t\t// Add database service if enabled\n\t\tif (options.database) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/services/database.ts',\n\t\t\t\tcontent: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';\nimport { Kysely, PostgresDialect } from 'kysely';\nimport pg from 'pg';\n\n// Define your database schema\nexport interface Database {\n users: {\n id: string;\n name: string;\n email: string;\n created_at: Date;\n };\n}\n\nexport const databaseService = {\n serviceName: 'database' as const,\n async register({ envParser, context }: ServiceRegisterOptions) {\n const logger = context.getLogger();\n logger.info('Connecting to database');\n\n const config = envParser\n .create((get) => ({\n url: get('DATABASE_URL').string(),\n }))\n .parse();\n\n const db = new Kysely<Database>({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: config.url }),\n }),\n });\n\n logger.info('Database connection established');\n return db;\n },\n} satisfies Service<'database', Kysely<Database>>;\n`,\n\t\t\t});\n\t\t}\n\n\t\t// Add Telescope config if enabled\n\t\tif (options.telescope) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/telescope.ts',\n\t\t\t\tcontent: `import { Telescope } from '@geekmidas/telescope';\nimport { InMemoryStorage } from '@geekmidas/telescope/storage/memory';\n\nexport const telescope = new Telescope({\n storage: new InMemoryStorage({ maxEntries: 100 }),\n enabled: process.env.NODE_ENV === 'development',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\t// Add Studio config if enabled (requires database)\n\t\tif (options.studio && options.database) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/studio.ts',\n\t\t\t\tcontent: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';\nimport { Kysely, PostgresDialect } from 'kysely';\nimport pg from 'pg';\nimport type { Database } from '~/services/database.ts';\nimport { envParser } from '~/config/env.ts';\n\n// Parse database config for Studio\nconst studioConfig = envParser\n .create((get) => ({\n databaseUrl: get('DATABASE_URL').string(),\n }))\n .parse();\n\n// Create a Kysely instance for Studio\nconst db = new Kysely<Database>({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: studioConfig.databaseUrl }),\n }),\n});\n\nexport const studio = new Studio<Database>({\n monitoring: {\n storage: new InMemoryMonitoringStorage({ maxEntries: 100 }),\n },\n data: {\n db,\n cursor: { field: 'id', direction: Direction.Desc },\n },\n enabled: process.env.NODE_ENV === 'development',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\treturn files;\n\t},\n};\n","import { GEEKMIDAS_VERSIONS } from '../versions.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from './index.js';\n\nexport const minimalTemplate: TemplateConfig = {\n\tname: 'minimal',\n\tdescription: 'Basic health endpoint',\n\n\tdependencies: {\n\t\t'@geekmidas/audit': GEEKMIDAS_VERSIONS['@geekmidas/audit'],\n\t\t'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],\n\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],\n\t\t'@geekmidas/rate-limit': GEEKMIDAS_VERSIONS['@geekmidas/rate-limit'],\n\t\t'@geekmidas/schema': GEEKMIDAS_VERSIONS['@geekmidas/schema'],\n\t\t'@hono/node-server': '~1.14.1',\n\t\thono: '~4.8.2',\n\t\tpino: '~9.6.0',\n\t\tzod: '~4.1.0',\n\t},\n\n\tdevDependencies: {\n\t\t'@biomejs/biome': '~2.3.0',\n\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t'@types/node': '~22.0.0',\n\t\tesbuild: '~0.27.0',\n\t\ttsx: '~4.20.0',\n\t\tturbo: '~2.3.0',\n\t\ttypescript: '~5.8.2',\n\t\tvitest: '~4.0.0',\n\t},\n\n\tscripts: {\n\t\tdev: 'gkm dev',\n\t\tbuild: 'gkm build',\n\t\ttest: 'vitest',\n\t\t'test:once': 'vitest run',\n\t\ttypecheck: 'tsc --noEmit',\n\t\tlint: 'biome lint .',\n\t\tfmt: 'biome format . --write',\n\t\t'fmt:check': 'biome format .',\n\t},\n\n\tfiles: (options: TemplateOptions): GeneratedFile[] => {\n\t\tconst { loggerType, routesStructure } = options;\n\n\t\tconst loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';\n\nexport const logger = createLogger();\n`;\n\n\t\t// Get route path based on structure\n\t\tconst getRoutePath = (file: string) => {\n\t\t\tswitch (routesStructure) {\n\t\t\t\tcase 'centralized-endpoints':\n\t\t\t\t\treturn `src/endpoints/${file}`;\n\t\t\t\tcase 'centralized-routes':\n\t\t\t\t\treturn `src/routes/${file}`;\n\t\t\t\tcase 'domain-based':\n\t\t\t\t\treturn `src/${file.replace('.ts', '')}/routes/index.ts`;\n\t\t\t}\n\t\t};\n\n\t\tconst files: GeneratedFile[] = [\n\t\t\t// src/config/env.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/env.ts',\n\t\t\t\tcontent: `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { EnvironmentParser } from '@geekmidas/envkit';\n\nexport const envParser = new EnvironmentParser({ ...process.env, ...Credentials });\n\n// Global config - only minimal shared values\n// Service-specific config should be parsed in each service\nexport const config = envParser\n .create((get) => ({\n nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),\n stage: get('STAGE').enum(['development', 'staging', 'production']).default('development'),\n }))\n .parse();\n`,\n\t\t\t},\n\n\t\t\t// src/config/logger.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/logger.ts',\n\t\t\t\tcontent: loggerContent,\n\t\t\t},\n\n\t\t\t// health endpoint\n\t\t\t{\n\t\t\t\tpath: getRoutePath('health.ts'),\n\t\t\t\tcontent: `import { e } from '@geekmidas/constructs/endpoints';\nimport { z } from 'zod';\n\nexport const healthEndpoint = e\n .get('/health')\n .output(z.object({\n status: z.string(),\n timestamp: z.string(),\n }))\n .handle(async () => ({\n status: 'ok',\n timestamp: new Date().toISOString(),\n }));\n`,\n\t\t\t},\n\t\t];\n\n\t\t// Add database service if enabled\n\t\tif (options.database) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/services/database.ts',\n\t\t\t\tcontent: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';\nimport { Kysely, PostgresDialect } from 'kysely';\nimport pg from 'pg';\n\n// Define your database schema\nexport interface Database {\n // Add your tables here\n}\n\nexport const databaseService = {\n serviceName: 'database' as const,\n async register({ envParser, context }: ServiceRegisterOptions) {\n const logger = context.getLogger();\n logger.info('Connecting to database');\n\n const config = envParser\n .create((get) => ({\n url: get('DATABASE_URL').string(),\n }))\n .parse();\n\n const db = new Kysely<Database>({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: config.url }),\n }),\n });\n\n logger.info('Database connection established');\n return db;\n },\n} satisfies Service<'database', Kysely<Database>>;\n`,\n\t\t\t});\n\t\t}\n\n\t\t// Add Telescope config if enabled\n\t\tif (options.telescope) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/telescope.ts',\n\t\t\t\tcontent: `import { Telescope } from '@geekmidas/telescope';\nimport { InMemoryStorage } from '@geekmidas/telescope/storage/memory';\n\nexport const telescope = new Telescope({\n storage: new InMemoryStorage({ maxEntries: 100 }),\n enabled: process.env.NODE_ENV === 'development',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\t// Add Studio config if enabled (requires database)\n\t\tif (options.studio && options.database) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/studio.ts',\n\t\t\t\tcontent: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';\nimport { Kysely, PostgresDialect } from 'kysely';\nimport pg from 'pg';\nimport type { Database } from '~/services/database.ts';\nimport { envParser } from '~/config/env.ts';\n\n// Parse database config for Studio\nconst studioConfig = envParser\n .create((get) => ({\n databaseUrl: get('DATABASE_URL').string(),\n }))\n .parse();\n\n// Create a Kysely instance for Studio\nconst db = new Kysely<Database>({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: studioConfig.databaseUrl }),\n }),\n});\n\nexport const studio = new Studio<Database>({\n monitoring: {\n storage: new InMemoryMonitoringStorage({ maxEntries: 100 }),\n },\n data: {\n db,\n cursor: { field: 'id', direction: Direction.Desc },\n },\n enabled: process.env.NODE_ENV === 'development',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\treturn files;\n\t},\n};\n","import { GEEKMIDAS_VERSIONS } from '../versions.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from './index.js';\n\nexport const serverlessTemplate: TemplateConfig = {\n\tname: 'serverless',\n\tdescription: 'AWS Lambda handlers',\n\n\tdependencies: {\n\t\t'@geekmidas/audit': GEEKMIDAS_VERSIONS['@geekmidas/audit'],\n\t\t'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],\n\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],\n\t\t'@geekmidas/cloud': GEEKMIDAS_VERSIONS['@geekmidas/cloud'],\n\t\t'@geekmidas/rate-limit': GEEKMIDAS_VERSIONS['@geekmidas/rate-limit'],\n\t\t'@geekmidas/schema': GEEKMIDAS_VERSIONS['@geekmidas/schema'],\n\t\t'@hono/node-server': '~1.14.1',\n\t\thono: '~4.8.2',\n\t\tpino: '~9.6.0',\n\t},\n\n\tdevDependencies: {\n\t\t'@biomejs/biome': '~2.3.0',\n\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t'@types/aws-lambda': '~8.10.92',\n\t\t'@types/node': '~22.0.0',\n\t\ttsx: '~4.20.0',\n\t\tturbo: '~2.3.0',\n\t\ttypescript: '~5.8.2',\n\t\tvitest: '~4.0.0',\n\t},\n\n\tscripts: {\n\t\tdev: 'gkm dev',\n\t\tbuild: 'gkm build --provider aws-apigatewayv2',\n\t\ttest: 'vitest',\n\t\t'test:once': 'vitest run',\n\t\ttypecheck: 'tsc --noEmit',\n\t\tlint: 'biome lint .',\n\t\tfmt: 'biome format . --write',\n\t\t'fmt:check': 'biome format .',\n\t},\n\n\tfiles: (options: TemplateOptions): GeneratedFile[] => {\n\t\tconst { loggerType, routesStructure } = options;\n\n\t\tconst loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';\n\nexport const logger = createLogger();\n`;\n\n\t\t// Get route path based on structure\n\t\tconst getRoutePath = (file: string) => {\n\t\t\tswitch (routesStructure) {\n\t\t\t\tcase 'centralized-endpoints':\n\t\t\t\t\treturn `src/endpoints/${file}`;\n\t\t\t\tcase 'centralized-routes':\n\t\t\t\t\treturn `src/routes/${file}`;\n\t\t\t\tcase 'domain-based':\n\t\t\t\t\treturn `src/${file.replace('.ts', '')}/routes/index.ts`;\n\t\t\t}\n\t\t};\n\n\t\tconst files: GeneratedFile[] = [\n\t\t\t// src/config/env.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/env.ts',\n\t\t\t\tcontent: `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { EnvironmentParser } from '@geekmidas/envkit';\n\nexport const envParser = new EnvironmentParser({ ...process.env, ...Credentials });\n\n// Global config - only minimal shared values\n// Service-specific config should be parsed in each service\nexport const config = envParser\n .create((get) => ({\n nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),\n stage: get('STAGE').enum(['dev', 'staging', 'prod']).default('dev'),\n }))\n .parse();\n`,\n\t\t\t},\n\n\t\t\t// src/config/logger.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/logger.ts',\n\t\t\t\tcontent: loggerContent,\n\t\t\t},\n\n\t\t\t// health endpoint\n\t\t\t{\n\t\t\t\tpath: getRoutePath('health.ts'),\n\t\t\t\tcontent: `import { e } from '@geekmidas/constructs/endpoints';\nimport { z } from 'zod';\n\nexport const healthEndpoint = e\n .get('/health')\n .output(z.object({\n status: z.string(),\n timestamp: z.string(),\n region: z.string(),\n }))\n .handle(async () => ({\n status: 'ok',\n timestamp: new Date().toISOString(),\n region: process.env.AWS_REGION || 'local',\n }));\n`,\n\t\t\t},\n\n\t\t\t// src/functions/hello.ts\n\t\t\t{\n\t\t\t\tpath: 'src/functions/hello.ts',\n\t\t\t\tcontent: `import { f } from '@geekmidas/constructs/functions';\nimport { z } from 'zod';\n\nexport const helloFunction = f\n .input(z.object({ name: z.string() }))\n .output(z.object({ message: z.string() }))\n .handle(async ({ input }) => ({\n message: \\`Hello, \\${input.name}!\\`,\n }));\n`,\n\t\t\t},\n\t\t];\n\n\t\t// Add Telescope config if enabled\n\t\tif (options.telescope) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/telescope.ts',\n\t\t\t\tcontent: `import { Telescope } from '@geekmidas/telescope';\nimport { InMemoryStorage } from '@geekmidas/telescope/storage/memory';\n\n// Note: For production Lambda, consider using a persistent storage\nexport const telescope = new Telescope({\n storage: new InMemoryStorage({ maxEntries: 50 }),\n enabled: process.env.STAGE === 'dev',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\treturn files;\n\t},\n};\n","import { GEEKMIDAS_VERSIONS } from '../versions.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from './index.js';\n\nexport const workerTemplate: TemplateConfig = {\n\tname: 'worker',\n\tdescription: 'Background job processing',\n\n\tdependencies: {\n\t\t'@geekmidas/audit': GEEKMIDAS_VERSIONS['@geekmidas/audit'],\n\t\t'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],\n\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t'@geekmidas/events': GEEKMIDAS_VERSIONS['@geekmidas/events'],\n\t\t'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],\n\t\t'@geekmidas/rate-limit': GEEKMIDAS_VERSIONS['@geekmidas/rate-limit'],\n\t\t'@geekmidas/schema': GEEKMIDAS_VERSIONS['@geekmidas/schema'],\n\t\t'@hono/node-server': '~1.14.1',\n\t\thono: '~4.8.2',\n\t\tpino: '~9.6.0',\n\t},\n\n\tdevDependencies: {\n\t\t'@biomejs/biome': '~2.3.0',\n\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t'@types/node': '~22.0.0',\n\t\ttsx: '~4.20.0',\n\t\tturbo: '~2.3.0',\n\t\ttypescript: '~5.8.2',\n\t\tvitest: '~4.0.0',\n\t},\n\n\tscripts: {\n\t\tdev: 'gkm dev',\n\t\tbuild: 'gkm build',\n\t\ttest: 'vitest',\n\t\t'test:once': 'vitest run',\n\t\ttypecheck: 'tsc --noEmit',\n\t\tlint: 'biome lint .',\n\t\tfmt: 'biome format . --write',\n\t\t'fmt:check': 'biome format .',\n\t},\n\n\tfiles: (options: TemplateOptions): GeneratedFile[] => {\n\t\tconst { loggerType, routesStructure } = options;\n\n\t\tconst loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';\n\nexport const logger = createLogger();\n`;\n\n\t\t// Get route path based on structure\n\t\tconst getRoutePath = (file: string) => {\n\t\t\tswitch (routesStructure) {\n\t\t\t\tcase 'centralized-endpoints':\n\t\t\t\t\treturn `src/endpoints/${file}`;\n\t\t\t\tcase 'centralized-routes':\n\t\t\t\t\treturn `src/routes/${file}`;\n\t\t\t\tcase 'domain-based':\n\t\t\t\t\treturn `src/${file.replace('.ts', '')}/routes/index.ts`;\n\t\t\t}\n\t\t};\n\n\t\tconst files: GeneratedFile[] = [\n\t\t\t// src/config/env.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/env.ts',\n\t\t\t\tcontent: `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { EnvironmentParser } from '@geekmidas/envkit';\n\nexport const envParser = new EnvironmentParser({ ...process.env, ...Credentials });\n\n// Global config - only minimal shared values\n// Service-specific config should be parsed in each service\nexport const config = envParser\n .create((get) => ({\n nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),\n stage: get('STAGE').enum(['development', 'staging', 'production']).default('development'),\n }))\n .parse();\n`,\n\t\t\t},\n\n\t\t\t// src/config/logger.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/logger.ts',\n\t\t\t\tcontent: loggerContent,\n\t\t\t},\n\n\t\t\t// health endpoint\n\t\t\t{\n\t\t\t\tpath: getRoutePath('health.ts'),\n\t\t\t\tcontent: `import { e } from '@geekmidas/constructs/endpoints';\nimport { z } from 'zod';\n\nexport const healthEndpoint = e\n .get('/health')\n .output(z.object({\n status: z.string(),\n timestamp: z.string(),\n }))\n .handle(async () => ({\n status: 'ok',\n timestamp: new Date().toISOString(),\n }));\n`,\n\t\t\t},\n\n\t\t\t// src/events/types.ts\n\t\t\t{\n\t\t\t\tpath: 'src/events/types.ts',\n\t\t\t\tcontent: `import type { PublishableMessage } from '@geekmidas/events';\n\n// Define your event types here\nexport type AppEvents =\n | PublishableMessage<'user.created', { userId: string; email: string }>\n | PublishableMessage<'user.updated', { userId: string; changes: Record<string, unknown> }>\n | PublishableMessage<'order.placed', { orderId: string; userId: string; total: number }>;\n`,\n\t\t\t},\n\n\t\t\t// src/events/publisher.ts\n\t\t\t{\n\t\t\t\tpath: 'src/events/publisher.ts',\n\t\t\t\tcontent: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';\nimport { Publisher, type EventPublisher } from '@geekmidas/events';\nimport type { AppEvents } from './types.ts';\n\nexport const eventsPublisherService = {\n serviceName: 'events' as const,\n async register({ envParser, context }: ServiceRegisterOptions) {\n const logger = context.getLogger();\n logger.info('Connecting to message broker');\n\n const config = envParser\n .create((get) => ({\n url: get('RABBITMQ_URL').string().default('amqp://localhost:5672'),\n }))\n .parse();\n\n const publisher = await Publisher.fromConnectionString<AppEvents>(\n \\`rabbitmq://\\${config.url.replace('amqp://', '')}?exchange=events\\`\n );\n\n logger.info('Message broker connection established');\n return publisher;\n },\n} satisfies Service<'events', EventPublisher<AppEvents>>;\n`,\n\t\t\t},\n\n\t\t\t// src/subscribers/user-events.ts\n\t\t\t{\n\t\t\t\tpath: 'src/subscribers/user-events.ts',\n\t\t\t\tcontent: `import { s } from '@geekmidas/constructs/subscribers';\nimport { eventsPublisherService } from '~/events/publisher.ts';\n\nexport const userEventsSubscriber = s\n .publisher(eventsPublisherService)\n .subscribe(['user.created', 'user.updated'])\n .handle(async ({ event, logger }) => {\n logger.info({ type: event.type, payload: event.payload }, 'Processing user event');\n\n switch (event.type) {\n case 'user.created':\n // Handle user creation\n logger.info({ userId: event.payload.userId }, 'New user created');\n break;\n case 'user.updated':\n // Handle user update\n logger.info({ userId: event.payload.userId }, 'User updated');\n break;\n }\n });\n`,\n\t\t\t},\n\n\t\t\t// src/crons/cleanup.ts\n\t\t\t{\n\t\t\t\tpath: 'src/crons/cleanup.ts',\n\t\t\t\tcontent: `import { cron } from '@geekmidas/constructs/crons';\n\n// Run every day at midnight\nexport const cleanupCron = cron('0 0 * * *')\n .handle(async ({ logger }) => {\n logger.info('Running cleanup job');\n\n // Add your cleanup logic here\n // e.g., delete old sessions, clean up temp files, etc.\n\n logger.info('Cleanup job completed');\n });\n`,\n\t\t\t},\n\t\t];\n\n\t\t// Add Telescope config if enabled\n\t\tif (options.telescope) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/telescope.ts',\n\t\t\t\tcontent: `import { Telescope } from '@geekmidas/telescope';\nimport { InMemoryStorage } from '@geekmidas/telescope/storage/memory';\n\nexport const telescope = new Telescope({\n storage: new InMemoryStorage({ maxEntries: 100 }),\n enabled: process.env.NODE_ENV === 'development',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\treturn files;\n\t},\n};\n","import { apiTemplate } from './api.js';\nimport { minimalTemplate } from './minimal.js';\nimport { serverlessTemplate } from './serverless.js';\nimport { workerTemplate } from './worker.js';\n\n/**\n * OpenAPI output path (fixed, not configurable)\n */\nexport const OPENAPI_OUTPUT_PATH = './.gkm/openapi.ts';\n\n/**\n * Endpoints output path (generated by gkm dev/build)\n */\nexport const ENDPOINTS_OUTPUT_PATH = './.gkm/server/endpoints.ts';\n\n/**\n * Logger implementation type\n */\nexport type LoggerType = 'pino' | 'console';\n\n/**\n * Routes structure pattern\n */\nexport type RoutesStructure =\n\t| 'centralized-endpoints'\n\t| 'centralized-routes'\n\t| 'domain-based';\n\n/**\n * Package manager type\n */\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';\n\n/**\n * Deploy target type\n */\nexport type DeployTarget = 'dokploy' | 'none';\n\n/**\n * Services selection\n */\nexport interface ServicesSelection {\n\tdb: boolean;\n\tcache: boolean;\n\tmail: boolean;\n\tstorage: boolean;\n\tevents?: import('../../types.js').EventsBackend;\n}\n\n/**\n * Frontend framework for the fullstack template's client app.\n * Limited to the three with first-class scaffolds; the wider\n * `FrontendFramework` type also covers `vite` and `remix` which\n * users can configure manually after init.\n */\nexport type FullstackFrontendFramework = 'nextjs' | 'tanstack-start' | 'expo';\n\n/**\n * Options collected from user prompts\n */\nexport interface TemplateOptions {\n\tname: string;\n\ttemplate: TemplateName;\n\ttelescope: boolean;\n\tdatabase: boolean;\n\tstudio: boolean;\n\tloggerType: LoggerType;\n\troutesStructure: RoutesStructure;\n\tmonorepo: boolean;\n\t/** Path for the API app in monorepo (e.g., 'apps/api') */\n\tapiPath: string;\n\t/** Selected package manager */\n\tpackageManager: PackageManager;\n\t/** Deploy target */\n\tdeployTarget: DeployTarget;\n\t/** Services selection */\n\tservices: ServicesSelection;\n\t/** Frontend framework (fullstack template only) */\n\tfrontendFramework?: FullstackFrontendFramework;\n}\n\n/**\n * A file to be generated\n */\nexport interface GeneratedFile {\n\tpath: string;\n\tcontent: string;\n}\n\n/**\n * Template configuration\n */\nexport interface TemplateConfig {\n\tname: TemplateName;\n\tdescription: string;\n\tdependencies: Record<string, string>;\n\tdevDependencies: Record<string, string>;\n\tscripts: Record<string, string>;\n\tfiles: (options: TemplateOptions) => GeneratedFile[];\n}\n\nexport type TemplateName =\n\t| 'minimal'\n\t| 'api'\n\t| 'serverless'\n\t| 'worker'\n\t| 'fullstack';\n\n/**\n * All available templates\n */\nexport const templates: Record<\n\tExclude<TemplateName, 'fullstack'>,\n\tTemplateConfig\n> = {\n\tminimal: minimalTemplate,\n\tapi: apiTemplate,\n\tserverless: serverlessTemplate,\n\tworker: workerTemplate,\n};\n\n/**\n * Template choices for prompts (Story 1.11 simplified to api + fullstack)\n */\nexport const templateChoices = [\n\t{\n\t\ttitle: 'API',\n\t\tvalue: 'api' as TemplateName,\n\t\tdescription: 'Single backend API with endpoints',\n\t},\n\t{\n\t\ttitle: 'Fullstack',\n\t\tvalue: 'fullstack' as TemplateName,\n\t\tdescription: 'Monorepo with API + Next.js + shared models',\n\t},\n];\n\n/**\n * All template choices (includes advanced options)\n */\nexport const allTemplateChoices = [\n\t{\n\t\ttitle: 'Minimal',\n\t\tvalue: 'minimal' as TemplateName,\n\t\tdescription: 'Basic health endpoint',\n\t},\n\t{\n\t\ttitle: 'API',\n\t\tvalue: 'api' as TemplateName,\n\t\tdescription: 'Full API with auth, database, services',\n\t},\n\t{\n\t\ttitle: 'Fullstack',\n\t\tvalue: 'fullstack' as TemplateName,\n\t\tdescription: 'Monorepo with API + Next.js + shared models',\n\t},\n\t{\n\t\ttitle: 'Serverless',\n\t\tvalue: 'serverless' as TemplateName,\n\t\tdescription: 'AWS Lambda handlers',\n\t},\n\t{\n\t\ttitle: 'Worker',\n\t\tvalue: 'worker' as TemplateName,\n\t\tdescription: 'Background job processing',\n\t},\n];\n\n/**\n * Frontend framework choices for the fullstack template prompt.\n */\nexport const frontendFrameworkChoices = [\n\t{\n\t\ttitle: 'Next.js',\n\t\tvalue: 'nextjs' as FullstackFrontendFramework,\n\t\tdescription: 'React server components, app router (default)',\n\t},\n\t{\n\t\ttitle: 'TanStack Start',\n\t\tvalue: 'tanstack-start' as FullstackFrontendFramework,\n\t\tdescription: 'File-based router on Vite + Nitro',\n\t},\n\t{\n\t\ttitle: 'Expo',\n\t\tvalue: 'expo' as FullstackFrontendFramework,\n\t\tdescription: 'React Native mobile app (iOS + Android)',\n\t},\n];\n\n/**\n * Logger type choices for prompts\n */\nexport const loggerTypeChoices = [\n\t{\n\t\ttitle: 'Pino',\n\t\tvalue: 'pino' as LoggerType,\n\t\tdescription: 'Fast JSON logger for production (recommended)',\n\t},\n\t{\n\t\ttitle: 'Console',\n\t\tvalue: 'console' as LoggerType,\n\t\tdescription: 'Simple console logger for development',\n\t},\n];\n\n/**\n * Routes structure choices for prompts\n */\nexport const routesStructureChoices = [\n\t{\n\t\ttitle: 'Centralized (endpoints)',\n\t\tvalue: 'centralized-endpoints' as RoutesStructure,\n\t\tdescription: 'src/endpoints/**/*.ts',\n\t},\n\t{\n\t\ttitle: 'Centralized (routes)',\n\t\tvalue: 'centralized-routes' as RoutesStructure,\n\t\tdescription: 'src/routes/**/*.ts',\n\t},\n\t{\n\t\ttitle: 'Domain-based',\n\t\tvalue: 'domain-based' as RoutesStructure,\n\t\tdescription: 'src/**/routes/*.ts (e.g., src/users/routes/list.ts)',\n\t},\n];\n\n/**\n * Package manager choices for prompts\n */\nexport const packageManagerChoices = [\n\t{\n\t\ttitle: 'pnpm',\n\t\tvalue: 'pnpm' as PackageManager,\n\t\tdescription: 'Fast, disk space efficient (recommended)',\n\t},\n\t{\n\t\ttitle: 'npm',\n\t\tvalue: 'npm' as PackageManager,\n\t\tdescription: 'Node.js default package manager',\n\t},\n\t{\n\t\ttitle: 'yarn',\n\t\tvalue: 'yarn' as PackageManager,\n\t\tdescription: 'Yarn package manager',\n\t},\n\t{\n\t\ttitle: 'bun',\n\t\tvalue: 'bun' as PackageManager,\n\t\tdescription: 'Fast JavaScript runtime and package manager',\n\t},\n];\n\n/**\n * Deploy target choices for prompts\n */\nexport const deployTargetChoices = [\n\t{\n\t\ttitle: 'Dokploy',\n\t\tvalue: 'dokploy' as DeployTarget,\n\t\tdescription: 'Deploy to Dokploy (Docker-based hosting)',\n\t},\n\t{\n\t\ttitle: 'Configure later',\n\t\tvalue: 'none' as DeployTarget,\n\t\tdescription: 'Skip deployment setup for now',\n\t},\n];\n\n/**\n * Services choices for multi-select prompt\n */\nexport const servicesChoices = [\n\t{\n\t\ttitle: 'PostgreSQL',\n\t\tvalue: 'db',\n\t\tdescription: 'PostgreSQL database',\n\t},\n\t{\n\t\ttitle: 'Redis',\n\t\tvalue: 'cache',\n\t\tdescription: 'Redis cache',\n\t},\n\t{\n\t\ttitle: 'Mailpit',\n\t\tvalue: 'mail',\n\t\tdescription: 'Email testing service (dev only)',\n\t},\n\t{\n\t\ttitle: 'MinIO',\n\t\tvalue: 'storage',\n\t\tdescription: 'S3-compatible object storage (dev only)',\n\t},\n];\n\n/**\n * Event backend choices for prompts\n */\nexport const eventsBackendChoices = [\n\t{\n\t\ttitle: 'pg-boss',\n\t\tvalue: 'pgboss' as const,\n\t\tdescription:\n\t\t\t'PostgreSQL-based job queue (reuses postgres, no extra container)',\n\t},\n\t{\n\t\ttitle: 'SNS/SQS',\n\t\tvalue: 'sns' as const,\n\t\tdescription: 'AWS SNS+SQS via LocalStack for local dev',\n\t},\n\t{\n\t\ttitle: 'RabbitMQ',\n\t\tvalue: 'rabbitmq' as const,\n\t\tdescription: 'AMQP message broker',\n\t},\n\t{\n\t\ttitle: 'None',\n\t\tvalue: undefined,\n\t\tdescription: 'Skip event backend',\n\t},\n];\n\n/**\n * Get a template by name\n */\nexport function getTemplate(name: TemplateName): TemplateConfig | null {\n\tif (name === 'fullstack') {\n\t\t// Fullstack template is handled specially, uses api template as base\n\t\treturn templates.api;\n\t}\n\tconst template = templates[name];\n\tif (!template) {\n\t\tthrow new Error(`Unknown template: ${name}`);\n\t}\n\treturn template;\n}\n\n/**\n * Check if a template is the fullstack monorepo template\n */\nexport function isFullstackTemplate(name: TemplateName): boolean {\n\treturn name === 'fullstack';\n}\n","import {\n\tENDPOINTS_OUTPUT_PATH,\n\ttype GeneratedFile,\n\tOPENAPI_OUTPUT_PATH,\n\ttype TemplateConfig,\n\ttype TemplateOptions,\n} from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate package.json with dependencies based on template and options\n */\nexport function generatePackageJson(\n\toptions: TemplateOptions,\n\ttemplate: TemplateConfig,\n): GeneratedFile[] {\n\tconst { name, telescope, database, studio, monorepo } = options;\n\n\t// Start with template dependencies\n\tconst dependencies = { ...template.dependencies };\n\tconst devDependencies = { ...template.devDependencies };\n\tconst scripts = { ...template.scripts };\n\n\t// Add optional dependencies based on user choices\n\tif (telescope) {\n\t\tdependencies['@geekmidas/telescope'] =\n\t\t\tGEEKMIDAS_VERSIONS['@geekmidas/telescope'];\n\t}\n\n\tif (studio) {\n\t\tdependencies['@geekmidas/studio'] = GEEKMIDAS_VERSIONS['@geekmidas/studio'];\n\t}\n\n\tif (database) {\n\t\tdependencies['@geekmidas/db'] = GEEKMIDAS_VERSIONS['@geekmidas/db'];\n\t\tdependencies.kysely = '~0.28.2';\n\t\tdependencies.pg = '~8.16.0';\n\t\tdevDependencies['@types/pg'] = '~8.15.0';\n\t\tdevDependencies['@geekmidas/testkit'] =\n\t\t\tGEEKMIDAS_VERSIONS['@geekmidas/testkit'];\n\t\tdevDependencies['@faker-js/faker'] = '~9.8.0';\n\t\tdevDependencies['kysely-ctl'] = '~0.10.0';\n\t\tdevDependencies['vite-tsconfig-paths'] = '~5.1.0';\n\t}\n\n\t// For monorepo apps, remove biome/turbo/esbuild (they're at root) and lint/fmt scripts\n\t// zod is at root level for monorepos\n\tif (monorepo) {\n\t\tdelete devDependencies['@biomejs/biome'];\n\t\tdelete devDependencies.turbo;\n\t\tdelete devDependencies.esbuild;\n\t\tdelete dependencies.zod;\n\t\tdelete scripts.lint;\n\t\tdelete scripts.fmt;\n\t\tdelete scripts['fmt:check'];\n\n\t\t// Add models package as dependency\n\t\tdependencies[`@${name}/models`] = 'workspace:*';\n\t}\n\n\t// Sort dependencies alphabetically\n\tconst sortObject = (obj: Record<string, string>) =>\n\t\tObject.fromEntries(\n\t\t\tObject.entries(obj).sort(([a], [b]) => a.localeCompare(b)),\n\t\t);\n\n\t// For monorepo, derive package name from apiPath (e.g., apps/api -> @name/api)\n\tlet packageName = name;\n\tif (monorepo && options.apiPath) {\n\t\tconst pathParts = options.apiPath.split('/');\n\t\tconst appName = pathParts[pathParts.length - 1] || 'api';\n\t\tpackageName = `@${name}/${appName}`;\n\t}\n\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\texports: {\n\t\t\t'./client': OPENAPI_OUTPUT_PATH,\n\t\t\t'./endpoints': ENDPOINTS_OUTPUT_PATH,\n\t\t},\n\t\tscripts,\n\t\tdependencies: sortObject(dependencies),\n\t\tdevDependencies: sortObject(devDependencies),\n\t};\n\n\treturn [\n\t\t{\n\t\t\tpath: 'package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t];\n}\n","import type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\n\n/**\n * Generate source files from template\n */\nexport function generateSourceFiles(\n\toptions: TemplateOptions,\n\ttemplate: TemplateConfig,\n): GeneratedFile[] {\n\treturn template.files(options);\n}\n","import type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\n\n/**\n * Generate test infrastructure files when database is enabled.\n * Includes transaction-isolated test config, global setup with migrations,\n * factory system with builders/seeds, and an example spec.\n */\nexport function generateTestFiles(\n\toptions: TemplateOptions,\n\t_template: TemplateConfig,\n): GeneratedFile[] {\n\tif (!options.database) {\n\t\treturn [];\n\t}\n\n\treturn [\n\t\t// kysely.config.ts - Kysely CLI configuration for migrations\n\t\t{\n\t\t\tpath: 'kysely.config.ts',\n\t\t\tcontent: `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { PostgresDialect } from 'kysely';\nimport { defineConfig } from 'kysely-ctl';\nimport pg from 'pg';\n\nexport default defineConfig({\n dialect: new PostgresDialect({\n pool: new pg.Pool({\n password: Credentials.POSTGRES_PASSWORD,\n user: Credentials.POSTGRES_USER,\n database: Credentials.POSTGRES_DB,\n port: Number(Credentials.POSTGRES_PORT),\n host: Credentials.POSTGRES_HOST,\n }),\n }),\n migrations: {\n migrationFolder: './src/db/migrations',\n },\n});\n`,\n\t\t},\n\n\t\t// test/config.ts - Wraps vitest `it` with transaction auto-rollback\n\t\t{\n\t\t\tpath: 'test/config.ts',\n\t\t\tcontent: `import { it as itVitest } from 'vitest';\nimport { Kysely, PostgresDialect } from 'kysely';\nimport pg from 'pg';\nimport { wrapVitestKyselyTransaction } from '@geekmidas/testkit/kysely';\nimport type { Database } from '~/services/database.ts';\n\nconst connection = new Kysely<Database>({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: process.env.DATABASE_URL }),\n }),\n});\n\nexport const it = wrapVitestKyselyTransaction<Database>(itVitest, {\n connection,\n});\n`,\n\t\t},\n\n\t\t// test/globalSetup.ts - Creates test database, provisions users, runs migrations\n\t\t{\n\t\t\tpath: 'test/globalSetup.ts',\n\t\t\tcontent: `import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { Credentials } from '@geekmidas/envkit/credentials';\nimport { PostgresKyselyMigrator } from '@geekmidas/testkit/kysely';\nimport { runInitScript } from '@geekmidas/testkit/postgres';\nimport { Kysely, PostgresDialect } from 'kysely';\nimport { FileMigrationProvider } from 'kysely/migration';\nimport pg from 'pg';\n\nexport default async function globalSetup() {\n const databaseUrl = Credentials.DATABASE_URL!;\n const migrationFolder = path.resolve(import.meta.dirname, '../db/migrations');\n\n const db = new Kysely({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: databaseUrl }),\n }),\n });\n\n const migrator = new PostgresKyselyMigrator({\n uri: databaseUrl,\n db,\n afterCreate: async (uri) => {\n const initScriptPath = path.resolve(process.cwd(), 'docker/postgres/init.sh');\n const dbUrl = new URL(Credentials.DATABASE_URL!);\n const apiUrl = new URL(Credentials.API_DATABASE_URL!);\n const authUrl = new URL(Credentials.AUTH_DATABASE_URL!);\n\n const env = {\n POSTGRES_USER: dbUrl.username,\n POSTGRES_DB: dbUrl.pathname.slice(1),\n API_DB_PASSWORD: decodeURIComponent(apiUrl.password),\n AUTH_DB_PASSWORD: decodeURIComponent(authUrl.password),\n PGBOSS_DB_PASSWORD: Credentials.PGBOSS_DB_PASSWORD ?? 'pgboss-dev-password',\n };\n\n await runInitScript(initScriptPath, uri, env);\n },\n provider: new FileMigrationProvider({\n fs,\n path,\n migrationFolder,\n }),\n });\n\n const teardown = await migrator.start();\n return teardown;\n}\n`,\n\t\t},\n\n\t\t// test/factory/index.ts - Factory aggregator\n\t\t{\n\t\t\tpath: 'test/factory/index.ts',\n\t\t\tcontent: `import type { Kysely } from 'kysely';\nimport { KyselyFactory } from '@geekmidas/testkit/kysely';\nimport type { Database } from '~/services/database.ts';\nimport { usersBuilder } from './users.ts';\n\nconst builders = { users: usersBuilder };\nconst seeds = {};\n\nexport function createFactory(db: Kysely<Database>) {\n return new KyselyFactory<Database, typeof builders, typeof seeds>(\n builders,\n seeds,\n db,\n );\n}\n\nexport type Factory = ReturnType<typeof createFactory>;\n`,\n\t\t},\n\n\t\t// test/factory/users.ts - Example builder\n\t\t{\n\t\t\tpath: 'test/factory/users.ts',\n\t\t\tcontent: `import { KyselyFactory } from '@geekmidas/testkit/kysely';\nimport type { Database } from '~/services/database.ts';\n\nexport const usersBuilder = KyselyFactory.createBuilder<Database, 'users'>(\n 'users',\n ({ faker }) => ({\n id: faker.string.uuid(),\n name: faker.person.fullName(),\n email: faker.internet.email(),\n created_at: new Date(),\n }),\n);\n`,\n\t\t},\n\n\t\t// test/example.spec.ts - Example test showing usage\n\t\t{\n\t\t\tpath: 'test/example.spec.ts',\n\t\t\tcontent: `import { describe, expect } from 'vitest';\nimport { it } from './config.ts';\n\ndescribe('example', () => {\n it('should have a working test setup', async ({ db }) => {\n // db is a transaction-wrapped Kysely instance\n // All changes are automatically rolled back after the test\n expect(db).toBeDefined();\n });\n});\n`,\n\t\t},\n\t];\n}\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\n\n/**\n * Generate UI package files for fullstack template\n * Based on @geekmidas/ui with shadcn/ui, Tailwind CSS v4, and Storybook\n */\nexport function generateUiPackageFiles(\n\toptions: TemplateOptions,\n): GeneratedFile[] {\n\tif (!options.monorepo || options.template !== 'fullstack') {\n\t\treturn [];\n\t}\n\n\tconst packageName = `@${options.name}/ui`;\n\n\t// package.json for UI package\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\texports: {\n\t\t\t'.': './src/index.ts',\n\t\t\t'./components': './src/components/index.ts',\n\t\t\t'./lib/utils': './src/lib/utils.ts',\n\t\t\t'./styles': './src/styles/globals.css',\n\t\t},\n\t\tscripts: {\n\t\t\t'ts:check': 'tsc --noEmit',\n\t\t\tstorybook: 'storybook dev -p 6006',\n\t\t\t'build:storybook': 'storybook build -o dist/storybook',\n\t\t},\n\t\tdependencies: {\n\t\t\t'@radix-ui/react-dialog': '~1.1.4',\n\t\t\t'@radix-ui/react-label': '~2.1.2',\n\t\t\t'@radix-ui/react-separator': '~1.1.2',\n\t\t\t'@radix-ui/react-slot': '~1.2.4',\n\t\t\t'@radix-ui/react-tabs': '~1.1.2',\n\t\t\t'@radix-ui/react-tooltip': '~1.1.6',\n\t\t\t'class-variance-authority': '~0.7.1',\n\t\t\tclsx: '^2.1.1',\n\t\t\t'lucide-react': '~0.562.0',\n\t\t\t'tailwind-merge': '~3.4.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@storybook/addon-a11y': '^8.4.7',\n\t\t\t'@storybook/addon-essentials': '^8.4.7',\n\t\t\t'@storybook/addon-interactions': '^8.4.7',\n\t\t\t'@storybook/react': '^8.4.7',\n\t\t\t'@storybook/react-vite': '^8.4.7',\n\t\t\t'@tailwindcss/vite': '^4.0.0',\n\t\t\t'@types/react': '^19.0.0',\n\t\t\t'@types/react-dom': '^19.0.0',\n\t\t\treact: '^19.0.0',\n\t\t\t'react-dom': '^19.0.0',\n\t\t\tstorybook: '^8.4.7',\n\t\t\ttailwindcss: '^4.0.0',\n\t\t\ttypescript: '^5.8.2',\n\t\t\tvite: '^6.0.0',\n\t\t},\n\t\tpeerDependencies: {\n\t\t\treact: '>=18.0.0',\n\t\t\t'react-dom': '>=18.0.0',\n\t\t\ttailwindcss: '>=4.0.0',\n\t\t},\n\t};\n\n\t// tsconfig.json for UI package\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tjsx: 'react-jsx',\n\t\t\tlib: ['ES2023', 'DOM', 'DOM.Iterable'],\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\tbaseUrl: '.',\n\t\t\tpaths: {\n\t\t\t\t'~/*': ['./src/*'],\n\t\t\t},\n\t\t},\n\t\tinclude: ['src/**/*'],\n\t\texclude: ['node_modules', 'dist', '**/*.stories.tsx'],\n\t};\n\n\t// components.json (shadcn config)\n\tconst componentsJson = {\n\t\t$schema: 'https://ui.shadcn.com/schema.json',\n\t\tstyle: 'new-york',\n\t\trsc: false,\n\t\ttsx: true,\n\t\ttailwind: {\n\t\t\tconfig: '',\n\t\t\tcss: 'src/styles/globals.css',\n\t\t\tbaseColor: 'neutral',\n\t\t\tcssVariables: true,\n\t\t\tprefix: '',\n\t\t},\n\t\taliases: {\n\t\t\tcomponents: '~/components',\n\t\t\tutils: '~/lib/utils',\n\t\t\tui: '~/components/ui',\n\t\t\tlib: '~/lib',\n\t\t\thooks: '~/hooks',\n\t\t},\n\t\ticonLibrary: 'lucide',\n\t};\n\n\t// .storybook/main.ts\n\tconst storybookMain = `import type { StorybookConfig } from '@storybook/react-vite';\n\nconst config: StorybookConfig = {\n stories: ['../src/**/*.stories.@(ts|tsx)'],\n addons: [\n '@storybook/addon-essentials',\n '@storybook/addon-interactions',\n '@storybook/addon-a11y',\n ],\n framework: {\n name: '@storybook/react-vite',\n options: {},\n },\n docs: {\n autodocs: 'tag',\n },\n viteFinal: async (config) => {\n // Add Tailwind CSS v4 plugin\n const tailwindcss = await import('@tailwindcss/vite');\n config.plugins = config.plugins || [];\n config.plugins.push(tailwindcss.default());\n return config;\n },\n};\n\nexport default config;\n`;\n\n\t// .storybook/preview.ts\n\tconst storybookPreview = `import type { Preview } from '@storybook/react';\nimport '../src/styles/globals.css';\n\nconst preview: Preview = {\n parameters: {\n backgrounds: {\n default: 'dark',\n values: [\n { name: 'dark', value: '#171717' },\n { name: 'surface', value: '#1c1c1c' },\n { name: 'light', value: '#fafafa' },\n ],\n },\n controls: {\n matchers: {\n color: /(background|color)$/i,\n date: /Date$/i,\n },\n },\n layout: 'centered',\n },\n};\n\nexport default preview;\n`;\n\n\t// src/styles/globals.css (Tailwind v4 format)\n\tconst globalsCss = `@import \"tailwindcss\";\n\n@theme {\n --color-background: hsl(var(--background));\n --color-foreground: hsl(var(--foreground));\n --color-card: hsl(var(--card));\n --color-card-foreground: hsl(var(--card-foreground));\n --color-popover: hsl(var(--popover));\n --color-popover-foreground: hsl(var(--popover-foreground));\n --color-primary: hsl(var(--primary));\n --color-primary-foreground: hsl(var(--primary-foreground));\n --color-secondary: hsl(var(--secondary));\n --color-secondary-foreground: hsl(var(--secondary-foreground));\n --color-muted: hsl(var(--muted));\n --color-muted-foreground: hsl(var(--muted-foreground));\n --color-accent: hsl(var(--accent));\n --color-accent-foreground: hsl(var(--accent-foreground));\n --color-destructive: hsl(var(--destructive));\n --color-destructive-foreground: hsl(var(--destructive-foreground));\n --color-border: hsl(var(--border));\n --color-input: hsl(var(--input));\n --color-ring: hsl(var(--ring));\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n}\n\n@layer base {\n :root {\n --background: 0 0% 100%;\n --foreground: 0 0% 3.9%;\n --card: 0 0% 100%;\n --card-foreground: 0 0% 3.9%;\n --popover: 0 0% 100%;\n --popover-foreground: 0 0% 3.9%;\n --primary: 160 84% 39%;\n --primary-foreground: 0 0% 98%;\n --secondary: 0 0% 96.1%;\n --secondary-foreground: 0 0% 9%;\n --muted: 0 0% 96.1%;\n --muted-foreground: 0 0% 45.1%;\n --accent: 0 0% 96.1%;\n --accent-foreground: 0 0% 9%;\n --destructive: 0 84.2% 60.2%;\n --destructive-foreground: 0 0% 98%;\n --border: 0 0% 89.8%;\n --input: 0 0% 89.8%;\n --ring: 160 84% 39%;\n --radius: 0.5rem;\n }\n\n .dark {\n --background: 0 0% 9%;\n --foreground: 0 0% 98%;\n --card: 0 0% 11%;\n --card-foreground: 0 0% 98%;\n --popover: 0 0% 11%;\n --popover-foreground: 0 0% 98%;\n --primary: 160 84% 52%;\n --primary-foreground: 0 0% 9%;\n --secondary: 0 0% 15%;\n --secondary-foreground: 0 0% 98%;\n --muted: 0 0% 15%;\n --muted-foreground: 0 0% 64%;\n --accent: 0 0% 15%;\n --accent-foreground: 0 0% 98%;\n --destructive: 0 62.8% 50.6%;\n --destructive-foreground: 0 0% 98%;\n --border: 0 0% 18%;\n --input: 0 0% 18%;\n --ring: 160 84% 52%;\n }\n}\n\n@layer base {\n * {\n @apply border-border;\n }\n body {\n @apply bg-background text-foreground;\n }\n}\n`;\n\n\t// src/lib/utils.ts\n\tconst utilsTs = `import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n`;\n\n\t// src/components/ui/button.tsx\n\tconst buttonTsx = `import { Slot } from '@radix-ui/react-slot';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst buttonVariants = cva(\n 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',\n {\n variants: {\n variant: {\n default:\n 'bg-primary text-primary-foreground shadow hover:bg-primary/90',\n destructive:\n 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',\n outline:\n 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',\n secondary:\n 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',\n ghost: 'hover:bg-accent hover:text-accent-foreground',\n link: 'text-primary underline-offset-4 hover:underline',\n },\n size: {\n default: 'h-9 px-4 py-2',\n sm: 'h-8 rounded-md px-3 text-xs',\n lg: 'h-10 rounded-md px-8',\n icon: 'h-9 w-9',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : 'button';\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n );\n },\n);\nButton.displayName = 'Button';\n\nexport { Button, buttonVariants };\n`;\n\n\t// src/components/ui/button/button.stories.tsx\n\tconst buttonStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Button } from '~/components/ui/button';\n\nconst meta: Meta<typeof Button> = {\n title: 'Components/Button',\n component: Button,\n tags: ['autodocs'],\n argTypes: {\n variant: {\n control: 'select',\n options: ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'],\n },\n size: {\n control: 'select',\n options: ['default', 'sm', 'lg', 'icon'],\n },\n },\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Button>;\n\nexport const Default: Story = {\n args: {\n children: 'Button',\n variant: 'default',\n size: 'default',\n },\n};\n\nexport const Secondary: Story = {\n args: {\n children: 'Secondary',\n variant: 'secondary',\n },\n};\n\nexport const Destructive: Story = {\n args: {\n children: 'Destructive',\n variant: 'destructive',\n },\n};\n\nexport const Outline: Story = {\n args: {\n children: 'Outline',\n variant: 'outline',\n },\n};\n\nexport const Ghost: Story = {\n args: {\n children: 'Ghost',\n variant: 'ghost',\n },\n};\n\nexport const Link: Story = {\n args: {\n children: 'Link',\n variant: 'link',\n },\n};\n`;\n\n\t// src/components/ui/input.tsx\n\tconst inputTsx = `import * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(\n ({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n 'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',\n className,\n )}\n ref={ref}\n {...props}\n />\n );\n },\n);\nInput.displayName = 'Input';\n\nexport { Input };\n`;\n\n\t// src/components/ui/card.tsx\n\tconst cardTsx = `import * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst Card = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'rounded-xl border bg-card text-card-foreground shadow',\n className,\n )}\n {...props}\n />\n));\nCard.displayName = 'Card';\n\nconst CardHeader = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex flex-col space-y-1.5 p-6', className)}\n {...props}\n />\n));\nCardHeader.displayName = 'CardHeader';\n\nconst CardTitle = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('font-semibold leading-none tracking-tight', className)}\n {...props}\n />\n));\nCardTitle.displayName = 'CardTitle';\n\nconst CardDescription = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n));\nCardDescription.displayName = 'CardDescription';\n\nconst CardContent = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />\n));\nCardContent.displayName = 'CardContent';\n\nconst CardFooter = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center p-6 pt-0', className)}\n {...props}\n />\n));\nCardFooter.displayName = 'CardFooter';\n\nexport { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };\n`;\n\n\t// src/components/ui/input/input.stories.tsx\n\tconst inputStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Input } from '~/components/ui/input';\n\nconst meta: Meta<typeof Input> = {\n title: 'Components/Input',\n component: Input,\n tags: ['autodocs'],\n argTypes: {\n type: {\n control: 'select',\n options: ['text', 'email', 'password', 'number', 'search', 'tel', 'url'],\n },\n disabled: {\n control: 'boolean',\n },\n },\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Input>;\n\nexport const Default: Story = {\n args: {\n placeholder: 'Enter text...',\n },\n};\n\nexport const Email: Story = {\n args: {\n type: 'email',\n placeholder: 'Enter email...',\n },\n};\n\nexport const Password: Story = {\n args: {\n type: 'password',\n placeholder: 'Enter password...',\n },\n};\n\nexport const Disabled: Story = {\n args: {\n placeholder: 'Disabled input',\n disabled: true,\n },\n};\n\nexport const WithValue: Story = {\n args: {\n defaultValue: 'Hello World',\n },\n};\n`;\n\n\t// src/components/ui/card/card.stories.tsx\n\tconst cardStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Button } from '~/components/ui/button';\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from '~/components/ui/card';\nimport { Input } from '~/components/ui/input';\n\nconst meta: Meta<typeof Card> = {\n title: 'Components/Card',\n component: Card,\n tags: ['autodocs'],\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Card>;\n\nexport const Default: Story = {\n render: () => (\n <Card className=\"w-[350px]\">\n <CardHeader>\n <CardTitle>Card Title</CardTitle>\n <CardDescription>Card description goes here.</CardDescription>\n </CardHeader>\n <CardContent>\n <p>Card content goes here.</p>\n </CardContent>\n </Card>\n ),\n};\n\nexport const WithFooter: Story = {\n render: () => (\n <Card className=\"w-[350px]\">\n <CardHeader>\n <CardTitle>Create Account</CardTitle>\n <CardDescription>Enter your details below.</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <Input placeholder=\"Email\" type=\"email\" />\n <Input placeholder=\"Password\" type=\"password\" />\n </CardContent>\n <CardFooter className=\"flex justify-between\">\n <Button variant=\"outline\">Cancel</Button>\n <Button>Create</Button>\n </CardFooter>\n </Card>\n ),\n};\n\nexport const Simple: Story = {\n render: () => (\n <Card className=\"w-[350px] p-6\">\n <p>Simple card with just content.</p>\n </Card>\n ),\n};\n`;\n\n\t// src/components/ui/label/index.tsx\n\tconst labelTsx = `import * as LabelPrimitive from '@radix-ui/react-label';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst labelVariants = cva(\n 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',\n);\n\nconst Label = React.forwardRef<\n React.ElementRef<typeof LabelPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &\n VariantProps<typeof labelVariants>\n>(({ className, ...props }, ref) => (\n <LabelPrimitive.Root\n ref={ref}\n className={cn(labelVariants(), className)}\n {...props}\n />\n));\nLabel.displayName = LabelPrimitive.Root.displayName;\n\nexport { Label };\n`;\n\n\t// src/components/ui/label/label.stories.tsx\n\tconst labelStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Input } from '~/components/ui/input';\nimport { Label } from '~/components/ui/label';\n\nconst meta: Meta<typeof Label> = {\n title: 'Components/Label',\n component: Label,\n tags: ['autodocs'],\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Label>;\n\nexport const Default: Story = {\n args: {\n children: 'Label',\n },\n};\n\nexport const WithInput: Story = {\n render: () => (\n <div className=\"grid w-full max-w-sm items-center gap-1.5\">\n <Label htmlFor=\"email\">Email</Label>\n <Input type=\"email\" id=\"email\" placeholder=\"Email\" />\n </div>\n ),\n};\n\nexport const Disabled: Story = {\n render: () => (\n <div className=\"grid w-full max-w-sm items-center gap-1.5\">\n <Label htmlFor=\"disabled\" className=\"peer-disabled:cursor-not-allowed peer-disabled:opacity-70\">\n Disabled\n </Label>\n <Input type=\"text\" id=\"disabled\" placeholder=\"Disabled\" disabled className=\"peer\" />\n </div>\n ),\n};\n`;\n\n\t// src/components/ui/badge/index.tsx\n\tconst badgeTsx = `import { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst badgeVariants = cva(\n 'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',\n {\n variants: {\n variant: {\n default:\n 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',\n secondary:\n 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',\n destructive:\n 'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',\n outline: 'text-foreground',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n },\n);\n\nexport interface BadgeProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof badgeVariants> {}\n\nfunction Badge({ className, variant, ...props }: BadgeProps) {\n return (\n <div className={cn(badgeVariants({ variant }), className)} {...props} />\n );\n}\n\nexport { Badge, badgeVariants };\n`;\n\n\t// src/components/ui/badge/badge.stories.tsx\n\tconst badgeStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Badge } from '~/components/ui/badge';\n\nconst meta: Meta<typeof Badge> = {\n title: 'Components/Badge',\n component: Badge,\n tags: ['autodocs'],\n argTypes: {\n variant: {\n control: 'select',\n options: ['default', 'secondary', 'destructive', 'outline'],\n },\n },\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Badge>;\n\nexport const Default: Story = {\n args: {\n children: 'Badge',\n variant: 'default',\n },\n};\n\nexport const Secondary: Story = {\n args: {\n children: 'Secondary',\n variant: 'secondary',\n },\n};\n\nexport const Destructive: Story = {\n args: {\n children: 'Destructive',\n variant: 'destructive',\n },\n};\n\nexport const Outline: Story = {\n args: {\n children: 'Outline',\n variant: 'outline',\n },\n};\n`;\n\n\t// src/components/ui/separator/index.tsx\n\tconst separatorTsx = `import * as SeparatorPrimitive from '@radix-ui/react-separator';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst Separator = React.forwardRef<\n React.ElementRef<typeof SeparatorPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>\n>(\n (\n { className, orientation = 'horizontal', decorative = true, ...props },\n ref,\n ) => (\n <SeparatorPrimitive.Root\n ref={ref}\n decorative={decorative}\n orientation={orientation}\n className={cn(\n 'shrink-0 bg-border',\n orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',\n className,\n )}\n {...props}\n />\n ),\n);\nSeparator.displayName = SeparatorPrimitive.Root.displayName;\n\nexport { Separator };\n`;\n\n\t// src/components/ui/separator/separator.stories.tsx\n\tconst separatorStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Separator } from '~/components/ui/separator';\n\nconst meta: Meta<typeof Separator> = {\n title: 'Components/Separator',\n component: Separator,\n tags: ['autodocs'],\n argTypes: {\n orientation: {\n control: 'select',\n options: ['horizontal', 'vertical'],\n },\n },\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Separator>;\n\nexport const Horizontal: Story = {\n render: () => (\n <div className=\"w-[300px]\">\n <div className=\"space-y-1\">\n <h4 className=\"text-sm font-medium leading-none\">Radix Primitives</h4>\n <p className=\"text-sm text-muted-foreground\">\n An open-source UI component library.\n </p>\n </div>\n <Separator className=\"my-4\" />\n <div className=\"flex h-5 items-center space-x-4 text-sm\">\n <div>Blog</div>\n <Separator orientation=\"vertical\" />\n <div>Docs</div>\n <Separator orientation=\"vertical\" />\n <div>Source</div>\n </div>\n </div>\n ),\n};\n\nexport const Vertical: Story = {\n render: () => (\n <div className=\"flex h-5 items-center space-x-4 text-sm\">\n <div>Blog</div>\n <Separator orientation=\"vertical\" />\n <div>Docs</div>\n <Separator orientation=\"vertical\" />\n <div>Source</div>\n </div>\n ),\n};\n`;\n\n\t// src/components/ui/tabs/index.tsx\n\tconst tabsTsx = `import * as TabsPrimitive from '@radix-ui/react-tabs';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst Tabs = TabsPrimitive.Root;\n\nconst TabsList = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.List\n ref={ref}\n className={cn(\n 'inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground',\n className,\n )}\n {...props}\n />\n));\nTabsList.displayName = TabsPrimitive.List.displayName;\n\nconst TabsTrigger = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n 'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow',\n className,\n )}\n {...props}\n />\n));\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName;\n\nconst TabsContent = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Content\n ref={ref}\n className={cn(\n 'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n className,\n )}\n {...props}\n />\n));\nTabsContent.displayName = TabsPrimitive.Content.displayName;\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent };\n`;\n\n\t// src/components/ui/tabs/tabs.stories.tsx\n\tconst tabsStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/ui/tabs';\nimport { Button } from '~/components/ui/button';\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '~/components/ui/card';\nimport { Input } from '~/components/ui/input';\nimport { Label } from '~/components/ui/label';\n\nconst meta: Meta<typeof Tabs> = {\n title: 'Components/Tabs',\n component: Tabs,\n tags: ['autodocs'],\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Tabs>;\n\nexport const Default: Story = {\n render: () => (\n <Tabs defaultValue=\"account\" className=\"w-[400px]\">\n <TabsList>\n <TabsTrigger value=\"account\">Account</TabsTrigger>\n <TabsTrigger value=\"password\">Password</TabsTrigger>\n </TabsList>\n <TabsContent value=\"account\">\n <Card>\n <CardHeader>\n <CardTitle>Account</CardTitle>\n <CardDescription>\n Make changes to your account here. Click save when you're done.\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"name\">Name</Label>\n <Input id=\"name\" defaultValue=\"Pedro Duarte\" />\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor=\"username\">Username</Label>\n <Input id=\"username\" defaultValue=\"@peduarte\" />\n </div>\n </CardContent>\n <CardFooter>\n <Button>Save changes</Button>\n </CardFooter>\n </Card>\n </TabsContent>\n <TabsContent value=\"password\">\n <Card>\n <CardHeader>\n <CardTitle>Password</CardTitle>\n <CardDescription>\n Change your password here. After saving, you'll be logged out.\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"current\">Current password</Label>\n <Input id=\"current\" type=\"password\" />\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor=\"new\">New password</Label>\n <Input id=\"new\" type=\"password\" />\n </div>\n </CardContent>\n <CardFooter>\n <Button>Save password</Button>\n </CardFooter>\n </Card>\n </TabsContent>\n </Tabs>\n ),\n};\n`;\n\n\t// src/components/ui/tooltip/index.tsx\n\tconst tooltipTsx = `import * as TooltipPrimitive from '@radix-ui/react-tooltip';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst TooltipProvider = TooltipPrimitive.Provider;\n\nconst Tooltip = TooltipPrimitive.Root;\n\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n 'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n className,\n )}\n {...props}\n />\n </TooltipPrimitive.Portal>\n));\nTooltipContent.displayName = TooltipPrimitive.Content.displayName;\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n`;\n\n\t// src/components/ui/tooltip/tooltip.stories.tsx\n\tconst tooltipStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '~/components/ui/tooltip';\nimport { Button } from '~/components/ui/button';\n\nconst meta: Meta<typeof Tooltip> = {\n title: 'Components/Tooltip',\n component: Tooltip,\n tags: ['autodocs'],\n decorators: [\n (Story) => (\n <TooltipProvider>\n <Story />\n </TooltipProvider>\n ),\n ],\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Tooltip>;\n\nexport const Default: Story = {\n render: () => (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\">Hover me</Button>\n </TooltipTrigger>\n <TooltipContent>\n <p>Add to library</p>\n </TooltipContent>\n </Tooltip>\n ),\n};\n\nexport const Positions: Story = {\n render: () => (\n <div className=\"flex gap-4\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\">Top</Button>\n </TooltipTrigger>\n <TooltipContent side=\"top\">\n <p>Top tooltip</p>\n </TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\">Bottom</Button>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\">\n <p>Bottom tooltip</p>\n </TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\">Left</Button>\n </TooltipTrigger>\n <TooltipContent side=\"left\">\n <p>Left tooltip</p>\n </TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\">Right</Button>\n </TooltipTrigger>\n <TooltipContent side=\"right\">\n <p>Right tooltip</p>\n </TooltipContent>\n </Tooltip>\n </div>\n ),\n};\n`;\n\n\t// src/components/ui/dialog/index.tsx\n\tconst dialogTsx = `import * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { X } from 'lucide-react';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst Dialog = DialogPrimitive.Root;\n\nconst DialogTrigger = DialogPrimitive.Trigger;\n\nconst DialogPortal = DialogPrimitive.Portal;\n\nconst DialogClose = DialogPrimitive.Close;\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n 'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n className,\n )}\n {...props}\n />\n));\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName;\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',\n className,\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n));\nDialogContent.displayName = DialogPrimitive.Content.displayName;\n\nconst DialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n 'flex flex-col space-y-1.5 text-center sm:text-left',\n className,\n )}\n {...props}\n />\n);\nDialogHeader.displayName = 'DialogHeader';\n\nconst DialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',\n className,\n )}\n {...props}\n />\n);\nDialogFooter.displayName = 'DialogFooter';\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\n 'text-lg font-semibold leading-none tracking-tight',\n className,\n )}\n {...props}\n />\n));\nDialogTitle.displayName = DialogPrimitive.Title.displayName;\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n));\nDialogDescription.displayName = DialogPrimitive.Description.displayName;\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogTrigger,\n DialogClose,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n};\n`;\n\n\t// src/components/ui/dialog/dialog.stories.tsx\n\tconst dialogStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '~/components/ui/dialog';\nimport { Button } from '~/components/ui/button';\nimport { Input } from '~/components/ui/input';\nimport { Label } from '~/components/ui/label';\n\nconst meta: Meta<typeof Dialog> = {\n title: 'Components/Dialog',\n component: Dialog,\n tags: ['autodocs'],\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Dialog>;\n\nexport const Default: Story = {\n render: () => (\n <Dialog>\n <DialogTrigger asChild>\n <Button variant=\"outline\">Edit Profile</Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-[425px]\">\n <DialogHeader>\n <DialogTitle>Edit profile</DialogTitle>\n <DialogDescription>\n Make changes to your profile here. Click save when you're done.\n </DialogDescription>\n </DialogHeader>\n <div className=\"grid gap-4 py-4\">\n <div className=\"grid grid-cols-4 items-center gap-4\">\n <Label htmlFor=\"name\" className=\"text-right\">\n Name\n </Label>\n <Input id=\"name\" defaultValue=\"Pedro Duarte\" className=\"col-span-3\" />\n </div>\n <div className=\"grid grid-cols-4 items-center gap-4\">\n <Label htmlFor=\"username\" className=\"text-right\">\n Username\n </Label>\n <Input id=\"username\" defaultValue=\"@peduarte\" className=\"col-span-3\" />\n </div>\n </div>\n <DialogFooter>\n <Button type=\"submit\">Save changes</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n ),\n};\n\nexport const Alert: Story = {\n render: () => (\n <Dialog>\n <DialogTrigger asChild>\n <Button variant=\"destructive\">Delete Account</Button>\n </DialogTrigger>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Are you absolutely sure?</DialogTitle>\n <DialogDescription>\n This action cannot be undone. This will permanently delete your\n account and remove your data from our servers.\n </DialogDescription>\n </DialogHeader>\n <DialogFooter>\n <Button variant=\"outline\">Cancel</Button>\n <Button variant=\"destructive\">Delete</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n ),\n};\n`;\n\n\t// src/components/ui/index.ts\n\tconst componentsUiIndex = `export { Button, type ButtonProps, buttonVariants } from './button.tsx';\nexport { Input } from './input.tsx';\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardDescription,\n CardContent,\n} from './card.tsx';\nexport { Label } from './label.tsx';\nexport { Badge, type BadgeProps, badgeVariants } from './badge.tsx';\nexport { Separator } from './separator.tsx';\nexport { Tabs, TabsList, TabsTrigger, TabsContent } from './tabs.tsx';\nexport {\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n TooltipProvider,\n} from './tooltip.tsx';\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogTrigger,\n DialogClose,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n} from './dialog.tsx';\n`;\n\n\t// Rename component files to index.tsx (same content)\n\tconst buttonIndexTsx = buttonTsx;\n\tconst inputIndexTsx = inputTsx;\n\tconst cardIndexTsx = cardTsx;\n\n\t// src/components/index.ts\n\tconst componentsIndex = `export * from './ui/index.ts';\n`;\n\n\t// src/index.ts\n\tconst indexTs = `// @${options.name}/ui - Shared UI component library\n\n// shadcn/ui components\nexport * from './components/index.ts';\n\n// Utilities\nexport { cn } from './lib/utils.ts';\n`;\n\n\t// .gitignore for UI package\n\tconst gitignore = `node_modules/\ndist/\nstorybook-static/\n*.log\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'packages/ui/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/components.json',\n\t\t\tcontent: `${JSON.stringify(componentsJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/.storybook/main.ts',\n\t\t\tcontent: storybookMain,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/.storybook/preview.ts',\n\t\t\tcontent: storybookPreview,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/styles/globals.css',\n\t\t\tcontent: globalsCss,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/lib/utils.ts',\n\t\t\tcontent: utilsTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/button/index.tsx',\n\t\t\tcontent: buttonIndexTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/button/button.stories.tsx',\n\t\t\tcontent: buttonStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/input/index.tsx',\n\t\t\tcontent: inputIndexTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/input/input.stories.tsx',\n\t\t\tcontent: inputStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/card/index.tsx',\n\t\t\tcontent: cardIndexTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/card/card.stories.tsx',\n\t\t\tcontent: cardStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/label/index.tsx',\n\t\t\tcontent: labelTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/label/label.stories.tsx',\n\t\t\tcontent: labelStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/badge/index.tsx',\n\t\t\tcontent: badgeTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/badge/badge.stories.tsx',\n\t\t\tcontent: badgeStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/separator/index.tsx',\n\t\t\tcontent: separatorTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/separator/separator.stories.tsx',\n\t\t\tcontent: separatorStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/tabs/index.tsx',\n\t\t\tcontent: tabsTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/tabs/tabs.stories.tsx',\n\t\t\tcontent: tabsStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/tooltip/index.tsx',\n\t\t\tcontent: tooltipTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/tooltip/tooltip.stories.tsx',\n\t\t\tcontent: tooltipStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/dialog/index.tsx',\n\t\t\tcontent: dialogTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/dialog/dialog.stories.tsx',\n\t\t\tcontent: dialogStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/index.ts',\n\t\t\tcontent: componentsUiIndex,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/index.ts',\n\t\t\tcontent: componentsIndex,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/index.ts',\n\t\t\tcontent: indexTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/.gitignore',\n\t\t\tcontent: gitignore,\n\t\t},\n\t];\n}\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate Next.js web app files for fullstack template\n */\nexport function generateWebAppFiles(options: TemplateOptions): GeneratedFile[] {\n\tif (!options.monorepo || options.template !== 'fullstack') {\n\t\treturn [];\n\t}\n\n\tconst packageName = `@${options.name}/web`;\n\tconst apiPackage = `@${options.name}/api`;\n\tconst modelsPackage = `@${options.name}/models`;\n\tconst uiPackage = `@${options.name}/ui`;\n\n\t// package.json for web app\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\tscripts: {\n\t\t\tdev: 'gkm exec -- next dev --turbopack',\n\t\t\tbuild: 'gkm exec -- next build',\n\t\t\tstart: 'next start',\n\t\t\ttypecheck: 'tsc --noEmit',\n\t\t},\n\t\tdependencies: {\n\t\t\t[apiPackage]: 'workspace:*',\n\t\t\t[modelsPackage]: 'workspace:*',\n\t\t\t[uiPackage]: 'workspace:*',\n\t\t\t'@geekmidas/client': GEEKMIDAS_VERSIONS['@geekmidas/client'],\n\t\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t\t'@tanstack/react-query': '~5.80.0',\n\t\t\t'better-auth': '~1.2.0',\n\t\t\tnext: '~16.1.0',\n\t\t\treact: '~19.2.0',\n\t\t\t'react-dom': '~19.2.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t\t'@tailwindcss/postcss': '^4.0.0',\n\t\t\t'@types/node': '~22.0.0',\n\t\t\t'@types/react': '~19.0.0',\n\t\t\t'@types/react-dom': '~19.0.0',\n\t\t\ttailwindcss: '^4.0.0',\n\t\t\ttsx: '~4.20.0',\n\t\t\ttypescript: '~5.8.2',\n\t\t},\n\t};\n\n\t// next.config.ts\n\tconst nextConfig = `import type { NextConfig } from 'next';\n\nconst nextConfig: NextConfig = {\n output: 'standalone',\n reactStrictMode: true,\n transpilePackages: ['${apiPackage}', '${modelsPackage}', '${uiPackage}'],\n};\n\nexport default nextConfig;\n`;\n\n\t// postcss.config.mjs for Tailwind v4\n\tconst postcssConfig = `export default {\n plugins: {\n '@tailwindcss/postcss': {},\n },\n};\n`;\n\n\t// tsconfig.json for Next.js\n\t// Note: Next.js handles compilation, so noEmit: true\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tlib: ['dom', 'dom.iterable', 'ES2022'],\n\t\t\tallowJs: true,\n\t\t\tskipLibCheck: true,\n\t\t\tstrict: true,\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\tesModuleInterop: true,\n\t\t\tmodule: 'ESNext',\n\t\t\tmoduleResolution: 'bundler',\n\t\t\tresolveJsonModule: true,\n\t\t\tisolatedModules: true,\n\t\t\tjsx: 'preserve',\n\t\t\tincremental: true,\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'next',\n\t\t\t\t},\n\t\t\t],\n\t\t\tbaseUrl: '.',\n\t\t\tpaths: {\n\t\t\t\t'~/*': ['./src/*', '../../packages/ui/src/*'],\n\t\t\t\t[`${apiPackage}/client`]: ['../../apps/api/.gkm/openapi.ts'],\n\t\t\t\t[`${modelsPackage}`]: ['../../packages/models/src'],\n\t\t\t\t[`${modelsPackage}/*`]: ['../../packages/models/src/*'],\n\t\t\t\t[`${uiPackage}`]: ['../../packages/ui/src'],\n\t\t\t\t[`${uiPackage}/*`]: ['../../packages/ui/src/*'],\n\t\t\t},\n\t\t},\n\t\tinclude: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],\n\t\texclude: ['node_modules'],\n\t\treferences: [\n\t\t\t{ path: '../../apps/api' },\n\t\t\t{ path: '../../packages/ui' },\n\t\t\t{ path: '../../packages/models' },\n\t\t],\n\t};\n\n\t// Query client singleton for browser, fresh instance for server\n\tconst queryClientTs = `import { QueryClient } from '@tanstack/react-query';\n\nfunction makeQueryClient() {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 60 * 1000,\n },\n },\n });\n}\n\nlet browserQueryClient: QueryClient | undefined = undefined;\n\nexport function getQueryClient() {\n if (typeof window === 'undefined') {\n // Server: always make a new query client\n return makeQueryClient();\n }\n // Browser: reuse existing query client\n if (!browserQueryClient) browserQueryClient = makeQueryClient();\n return browserQueryClient;\n}\n`;\n\n\t// Client config - NEXT_PUBLIC_* vars (available in browser)\n\tconst clientConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';\n\n// Client config - only NEXT_PUBLIC_* vars (available in browser)\n// These values are inlined at build time by Next.js\nconst envParser = new EnvironmentParser({\n NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,\n NEXT_PUBLIC_AUTH_URL: process.env.NEXT_PUBLIC_AUTH_URL,\n});\n\nexport const clientConfig = envParser\n .create((get) => ({\n apiUrl: get('NEXT_PUBLIC_API_URL').string(),\n authUrl: get('NEXT_PUBLIC_AUTH_URL').string(),\n }))\n .parse();\n`;\n\n\t// Server config - server-only vars (not available in browser)\n\tconst serverConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';\n\n// Server config - all env vars (server-side only, not exposed to browser)\n// Access these only in Server Components, Route Handlers, or Server Actions\nconst envParser = new EnvironmentParser({ ...process.env });\n\nexport const serverConfig = envParser\n .create((get) => ({\n // Add server-only secrets here\n // Example: stripeSecretKey: get('STRIPE_SECRET_KEY').string(),\n }))\n .parse();\n`;\n\n\t// Auth client for better-auth\n\tconst authClientTs = `import { createAuthClient } from 'better-auth/react';\nimport { magicLinkClient } from 'better-auth/client/plugins';\nimport { clientConfig } from '~/config/client.ts';\n\nexport const authClient = createAuthClient({\n baseURL: clientConfig.authUrl,\n plugins: [magicLinkClient()],\n});\n\nexport const { signIn, signUp, signOut, useSession, magicLink } = authClient;\n`;\n\n\t// Providers using shared QueryClient\n\tconst providersTsx = `'use client';\n\nimport { QueryClientProvider } from '@tanstack/react-query';\nimport { getQueryClient } from '~/lib/query-client.ts';\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n const queryClient = getQueryClient();\n\n return (\n <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>\n );\n}\n`;\n\n\t// API client setup - imports directly from the API package export\n\tconst apiIndexTs = `import { createApi } from '${apiPackage}/client';\nimport { getQueryClient } from '~/lib/query-client.ts';\nimport { clientConfig } from '~/config/client.ts';\n\nexport const api = createApi({\n baseURL: clientConfig.apiUrl,\n queryClient: getQueryClient(),\n});\n`;\n\n\t// globals.css that imports UI package styles\n\tconst globalsCss = `@import '${uiPackage}/styles';\n`;\n\n\t// App layout\n\tconst layoutTsx = `import type { Metadata } from 'next';\nimport { Providers } from './providers.tsx';\nimport './globals.css';\n\nexport const metadata: Metadata = {\n title: '${options.name}',\n description: 'Created with gkm init',\n};\n\nexport default function RootLayout({\n children,\n}: {\n children: React.ReactNode;\n}) {\n return (\n <html lang=\"en\">\n <body>\n <Providers>{children}</Providers>\n </body>\n </html>\n );\n}\n`;\n\n\t// Home page with API example using UI components\n\tconst pageTsx = `import { api } from '~/api/index.ts';\nimport { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from '${uiPackage}/components';\n\nexport default async function Home() {\n // Type-safe API call using the generated client\n const health = await api('GET /health').catch(() => null);\n\n return (\n <main className=\"min-h-screen bg-background p-8\">\n <div className=\"mx-auto max-w-4xl space-y-8\">\n <div className=\"space-y-2\">\n <h1 className=\"text-4xl font-bold tracking-tight\">Welcome to ${options.name}</h1>\n <p className=\"text-muted-foreground\">Your fullstack application is ready.</p>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>API Status</CardTitle>\n <CardDescription>Connection to your backend API</CardDescription>\n </CardHeader>\n <CardContent>\n {health ? (\n <pre className=\"rounded-lg bg-muted p-4 text-sm\">\n {JSON.stringify(health, null, 2)}\n </pre>\n ) : (\n <p className=\"text-destructive\">Unable to connect to API</p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Next Steps</CardTitle>\n <CardDescription>Get started with your project</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <ul className=\"list-inside list-disc space-y-2 text-muted-foreground\">\n <li>Run <code className=\"rounded bg-muted px-1\">gkm openapi</code> to generate typed API client</li>\n <li>Edit <code className=\"rounded bg-muted px-1\">apps/web/src/app/page.tsx</code> to customize this page</li>\n <li>Add API routes in <code className=\"rounded bg-muted px-1\">apps/api/src/endpoints/</code></li>\n <li>Add UI components with <code className=\"rounded bg-muted px-1\">npx shadcn@latest add</code> in packages/ui</li>\n </ul>\n <div className=\"flex gap-4\">\n <Button>Get Started</Button>\n <Button variant=\"outline\">Documentation</Button>\n </div>\n </CardContent>\n </Card>\n </div>\n </main>\n );\n}\n`;\n\n\t// .gitignore for Next.js\n\tconst gitignore = `.next/\nnode_modules/\n.env.local\n*.log\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'apps/web/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/next.config.ts',\n\t\t\tcontent: nextConfig,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/postcss.config.mjs',\n\t\t\tcontent: postcssConfig,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/app/globals.css',\n\t\t\tcontent: globalsCss,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/app/layout.tsx',\n\t\t\tcontent: layoutTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/app/providers.tsx',\n\t\t\tcontent: providersTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/app/page.tsx',\n\t\t\tcontent: pageTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/config/client.ts',\n\t\t\tcontent: clientConfigTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/config/server.ts',\n\t\t\tcontent: serverConfigTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/lib/query-client.ts',\n\t\t\tcontent: queryClientTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/lib/auth-client.ts',\n\t\t\tcontent: authClientTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/api/index.ts',\n\t\t\tcontent: apiIndexTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/.gitignore',\n\t\t\tcontent: gitignore,\n\t\t},\n\t];\n}\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate TanStack Start web app files for the fullstack template.\n *\n * Mirrors the Next.js scaffold (apps/web) but on Vite + TanStack Start +\n * TanStack Router with the `VITE_` env-var convention.\n */\nexport function generateTanStackWebFiles(\n\toptions: TemplateOptions,\n): GeneratedFile[] {\n\tif (!options.monorepo || options.template !== 'fullstack') {\n\t\treturn [];\n\t}\n\n\tconst packageName = `@${options.name}/web`;\n\tconst apiPackage = `@${options.name}/api`;\n\tconst modelsPackage = `@${options.name}/models`;\n\tconst uiPackage = `@${options.name}/ui`;\n\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\tscripts: {\n\t\t\tdev: 'gkm exec -- vite dev',\n\t\t\tbuild: 'gkm exec -- vite build',\n\t\t\tstart: 'node .output/server/index.mjs',\n\t\t\ttypecheck: 'tsc --noEmit',\n\t\t},\n\t\tdependencies: {\n\t\t\t[apiPackage]: 'workspace:*',\n\t\t\t[modelsPackage]: 'workspace:*',\n\t\t\t[uiPackage]: 'workspace:*',\n\t\t\t'@geekmidas/client': GEEKMIDAS_VERSIONS['@geekmidas/client'],\n\t\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t\t'@tanstack/react-query': '~5.80.0',\n\t\t\t'@tanstack/react-router': '^1.87.0',\n\t\t\t'@tanstack/react-start': '^1.87.0',\n\t\t\t'better-auth': '~1.2.0',\n\t\t\treact: '~19.2.0',\n\t\t\t'react-dom': '~19.2.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t\t'@tailwindcss/vite': '^4.0.0',\n\t\t\t'@tanstack/router-plugin': '^1.87.0',\n\t\t\t'@types/node': '~22.0.0',\n\t\t\t'@types/react': '~19.0.0',\n\t\t\t'@types/react-dom': '~19.0.0',\n\t\t\t'@vitejs/plugin-react': '^4.3.4',\n\t\t\ttailwindcss: '^4.0.0',\n\t\t\ttsx: '~4.20.0',\n\t\t\ttypescript: '~5.8.2',\n\t\t\tvite: '^7.0.0',\n\t\t\t'vite-tsconfig-paths': '~5.1.0',\n\t\t},\n\t};\n\n\tconst viteConfig = `import { Credentials } from '@geekmidas/envkit/credentials';\nimport tailwindcss from '@tailwindcss/vite';\nimport { tanstackStart } from '@tanstack/react-start/plugin/vite';\nimport viteReact from '@vitejs/plugin-react';\nimport { defineConfig } from 'vite';\nimport tsconfigPaths from 'vite-tsconfig-paths';\n\nexport default defineConfig({\n server: {\n port: Number(Credentials.PORT ?? 3001),\n },\n // VITE_* vars are inlined automatically; no manual loadEnv needed.\n envPrefix: 'VITE_',\n plugins: [\n tsconfigPaths({ root: import.meta.dirname }),\n tailwindcss(),\n tanstackStart(),\n viteReact(),\n ],\n});\n`;\n\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tlib: ['dom', 'dom.iterable', 'ES2022'],\n\t\t\tallowJs: true,\n\t\t\tskipLibCheck: true,\n\t\t\tstrict: true,\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\tesModuleInterop: true,\n\t\t\tmodule: 'ESNext',\n\t\t\tmoduleResolution: 'bundler',\n\t\t\tresolveJsonModule: true,\n\t\t\tisolatedModules: true,\n\t\t\tjsx: 'preserve',\n\t\t\tincremental: true,\n\t\t\tbaseUrl: '.',\n\t\t\ttypes: ['vite/client'],\n\t\t\tpaths: {\n\t\t\t\t'~/*': ['./src/*', '../../packages/ui/src/*'],\n\t\t\t\t[modelsPackage]: ['../../packages/models/src'],\n\t\t\t\t[`${modelsPackage}/*`]: ['../../packages/models/src/*'],\n\t\t\t\t[uiPackage]: ['../../packages/ui/src'],\n\t\t\t\t[`${uiPackage}/*`]: ['../../packages/ui/src/*'],\n\t\t\t\t[`${apiPackage}/client`]: ['../../apps/api/.gkm/openapi.ts'],\n\t\t\t},\n\t\t},\n\t\tinclude: ['**/*.ts', '**/*.tsx', 'src/routeTree.gen.ts'],\n\t\texclude: ['node_modules', '.output', '.vinxi', 'dist'],\n\t};\n\n\tconst queryClientTs = `import { QueryClient } from '@tanstack/react-query';\n\nfunction makeQueryClient() {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 60 * 1000,\n },\n },\n });\n}\n\nlet browserQueryClient: QueryClient | undefined = undefined;\n\nexport function getQueryClient() {\n if (typeof window === 'undefined') {\n return makeQueryClient();\n }\n if (!browserQueryClient) browserQueryClient = makeQueryClient();\n return browserQueryClient;\n}\n`;\n\n\tconst clientConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';\n\n// Client config - VITE_* vars are inlined at build time.\nconst envParser = new EnvironmentParser({\n VITE_API_URL: import.meta.env.VITE_API_URL,\n VITE_AUTH_URL: import.meta.env.VITE_AUTH_URL,\n});\n\nexport const clientConfig = envParser\n .create((get) => ({\n apiUrl: get('VITE_API_URL').string(),\n authUrl: get('VITE_AUTH_URL').string(),\n }))\n .parse();\n`;\n\n\tconst serverConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';\n\n// Server config - all env vars (server-side only, not exposed to browser).\n// Access these only in TanStack Start server functions / server routes.\nconst envParser = new EnvironmentParser({ ...process.env });\n\nexport const serverConfig = envParser\n .create((_get) => ({\n // Add server-only secrets here\n // Example: stripeSecretKey: _get('STRIPE_SECRET_KEY').string(),\n }))\n .parse();\n`;\n\n\tconst authClientTs = `import { magicLinkClient } from 'better-auth/client/plugins';\nimport { createAuthClient } from 'better-auth/react';\nimport { clientConfig } from '~/config/client.ts';\n\nexport const authClient = createAuthClient({\n baseURL: clientConfig.authUrl,\n plugins: [magicLinkClient()],\n});\n\nexport const { signIn, signUp, signOut, useSession, magicLink } = authClient;\n`;\n\n\tconst apiIndexTs = `import { createApi } from '${apiPackage}/client';\nimport { clientConfig } from '~/config/client.ts';\nimport { getQueryClient } from '~/lib/query-client.ts';\n\nexport function createAppApi(options?: { headers?: Record<string, string> }) {\n return createApi({\n baseURL: clientConfig.apiUrl,\n queryClient: getQueryClient(),\n headers: options?.headers,\n onRequest: (config) => ({ ...config, credentials: 'include' }),\n });\n}\n\nexport const api = createAppApi();\n`;\n\n\tconst routerTs = `import { createRouter } from '@tanstack/react-router';\nimport { routeTree } from './routeTree.gen.ts';\n\nexport function getRouter() {\n return createRouter({\n routeTree,\n scrollRestoration: true,\n defaultPreload: 'intent',\n });\n}\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: ReturnType<typeof getRouter>;\n }\n}\n`;\n\n\t// Placeholder routeTree.gen — TanStack's plugin regenerates this on dev.\n\tconst routeTreeGen = `// This file is auto-generated by @tanstack/router-plugin.\n// It will be overwritten on the next \\`vite dev\\` run.\nimport { rootRouteWithContext } from '@tanstack/react-router';\n\nexport const routeTree = rootRouteWithContext()({\n // Routes are populated by the plugin from src/routes/**.tsx\n} as never);\n`;\n\n\tconst rootRouteTsx = `/// <reference types=\"vite/client\" />\nimport '~/styles/globals.css';\n\nimport { QueryClientProvider } from '@tanstack/react-query';\nimport {\n HeadContent,\n Outlet,\n Scripts,\n createRootRoute,\n} from '@tanstack/react-router';\nimport type { ReactNode } from 'react';\nimport { getQueryClient } from '~/lib/query-client.ts';\n\nexport const Route = createRootRoute({\n head: () => ({\n meta: [\n { charSet: 'utf-8' },\n { name: 'viewport', content: 'width=device-width, initial-scale=1' },\n { title: '${options.name}' },\n ],\n }),\n component: RootComponent,\n});\n\nfunction RootComponent() {\n const queryClient = getQueryClient();\n return (\n <RootDocument>\n <QueryClientProvider client={queryClient}>\n <Outlet />\n </QueryClientProvider>\n </RootDocument>\n );\n}\n\nfunction RootDocument({ children }: { children: ReactNode }) {\n return (\n <html lang=\"en\">\n <head>\n <HeadContent />\n </head>\n <body>\n {children}\n <Scripts />\n </body>\n </html>\n );\n}\n`;\n\n\tconst indexRouteTsx = `import { createFileRoute } from '@tanstack/react-router';\nimport { api } from '~/api/index.ts';\n\nexport const Route = createFileRoute('/')({\n component: HomeComponent,\n});\n\nfunction HomeComponent() {\n const { data: health } = api.useQuery('GET /health', {});\n\n return (\n <main className=\"min-h-screen bg-background p-8\">\n <div className=\"mx-auto max-w-4xl space-y-8\">\n <div className=\"space-y-2\">\n <h1 className=\"text-4xl font-bold tracking-tight\">\n Welcome to ${options.name}\n </h1>\n <p className=\"text-muted-foreground\">\n Your TanStack Start app is ready.\n </p>\n </div>\n\n <section className=\"rounded-lg border p-6\">\n <h2 className=\"text-xl font-semibold\">API Status</h2>\n {health ? (\n <pre className=\"mt-4 rounded bg-muted p-4 text-sm\">\n {JSON.stringify(health, null, 2)}\n </pre>\n ) : (\n <p className=\"mt-2 text-muted-foreground\">Connecting…</p>\n )}\n </section>\n </div>\n </main>\n );\n}\n`;\n\n\tconst globalsCss = `@import '${uiPackage}/styles';\n\n@source \"../..\";\n@source \"../../../../packages/ui/src\";\n`;\n\n\tconst gitignore = `.output/\n.vinxi/\nnode_modules/\n.env.local\n*.log\nsrc/routeTree.gen.ts\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'apps/web/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{ path: 'apps/web/vite.config.ts', content: viteConfig },\n\t\t{\n\t\t\tpath: 'apps/web/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{ path: 'apps/web/src/lib/query-client.ts', content: queryClientTs },\n\t\t{ path: 'apps/web/src/lib/auth-client.ts', content: authClientTs },\n\t\t{ path: 'apps/web/src/config/client.ts', content: clientConfigTs },\n\t\t{ path: 'apps/web/src/config/server.ts', content: serverConfigTs },\n\t\t{ path: 'apps/web/src/api/index.ts', content: apiIndexTs },\n\t\t{ path: 'apps/web/src/router.tsx', content: routerTs },\n\t\t{ path: 'apps/web/src/routeTree.gen.ts', content: routeTreeGen },\n\t\t{ path: 'apps/web/src/routes/__root.tsx', content: rootRouteTsx },\n\t\t{ path: 'apps/web/src/routes/index.tsx', content: indexRouteTsx },\n\t\t{ path: 'apps/web/src/styles/globals.css', content: globalsCss },\n\t\t{ path: 'apps/web/.gitignore', content: gitignore },\n\t];\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, parse } from 'node:path';\nimport fg from 'fast-glob';\nimport { parse as parseYaml } from 'yaml';\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';\n\n/**\n * Detect the package manager being used based on lockfiles or npm_config_user_agent\n */\nexport function detectPackageManager(\n\tcwd: string = process.cwd(),\n): PackageManager {\n\t// Check for lockfiles in cwd\n\tif (existsSync(join(cwd, 'pnpm-lock.yaml'))) return 'pnpm';\n\tif (existsSync(join(cwd, 'yarn.lock'))) return 'yarn';\n\tif (existsSync(join(cwd, 'bun.lockb'))) return 'bun';\n\tif (existsSync(join(cwd, 'package-lock.json'))) return 'npm';\n\n\t// Check npm_config_user_agent (set when running via npx/pnpm dlx/etc)\n\tconst userAgent = process.env.npm_config_user_agent || '';\n\tif (userAgent.includes('pnpm')) return 'pnpm';\n\tif (userAgent.includes('yarn')) return 'yarn';\n\tif (userAgent.includes('bun')) return 'bun';\n\n\treturn 'npm';\n}\n\n/**\n * Validate project name for npm package naming conventions\n */\nexport function validateProjectName(name: string): boolean | string {\n\tif (!name) {\n\t\treturn 'Project name is required';\n\t}\n\n\t// Check for valid npm package name characters\n\tif (!/^[a-z0-9-_@/.]+$/i.test(name)) {\n\t\treturn 'Project name can only contain letters, numbers, hyphens, underscores, @, /, and .';\n\t}\n\n\t// Check for reserved names\n\tconst reserved = ['node_modules', '.git', 'package.json', 'src'];\n\tif (reserved.includes(name.toLowerCase())) {\n\t\treturn `\"${name}\" is a reserved name`;\n\t}\n\n\treturn true;\n}\n\n/**\n * Check if a directory already exists at the target path\n */\nexport function checkDirectoryExists(\n\tname: string,\n\tcwd: string = process.cwd(),\n): boolean | string {\n\tconst targetPath = join(cwd, name);\n\tif (existsSync(targetPath)) {\n\t\treturn `Directory \"${name}\" already exists`;\n\t}\n\treturn true;\n}\n\n/**\n * Get the install command for a package manager\n */\nexport function getInstallCommand(pkgManager: PackageManager): string {\n\tswitch (pkgManager) {\n\t\tcase 'pnpm':\n\t\t\treturn 'pnpm install';\n\t\tcase 'yarn':\n\t\t\treturn 'yarn';\n\t\tcase 'bun':\n\t\t\treturn 'bun install';\n\t\tdefault:\n\t\t\treturn 'npm install';\n\t}\n}\n\n/**\n * Get the dev command for a package manager\n */\nexport function getRunCommand(\n\tpkgManager: PackageManager,\n\tscript: string,\n): string {\n\tswitch (pkgManager) {\n\t\tcase 'pnpm':\n\t\t\treturn `pnpm ${script}`;\n\t\tcase 'yarn':\n\t\t\treturn `yarn ${script}`;\n\t\tcase 'bun':\n\t\t\treturn `bun run ${script}`;\n\t\tdefault:\n\t\t\treturn `npm run ${script}`;\n\t}\n}\n\nconst lockfileByPm: Record<PackageManager, string> = {\n\tpnpm: 'pnpm-lock.yaml',\n\tyarn: 'yarn.lock',\n\tnpm: 'package-lock.json',\n\tbun: 'bun.lockb',\n};\n\n/**\n * Find the workspace/project root by walking up from cwd.\n * Checks for PM-specific workspace config, package.json#workspaces,\n * and lockfiles.\n */\nexport function findWorkspaceRoot(cwd: string, pm: PackageManager): string {\n\tlet dir = cwd;\n\tconst root = parse(dir).root;\n\tconst lockfile = lockfileByPm[pm];\n\n\twhile (dir !== root) {\n\t\tif (pm === 'pnpm' && existsSync(join(dir, 'pnpm-workspace.yaml'))) {\n\t\t\treturn dir;\n\t\t}\n\n\t\tconst pkgJsonPath = join(dir, 'package.json');\n\t\tif (existsSync(pkgJsonPath)) {\n\t\t\ttry {\n\t\t\t\tconst pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));\n\t\t\t\tif (pkg.workspaces) return dir;\n\t\t\t} catch {\n\t\t\t\t// ignore malformed package.json\n\t\t\t}\n\t\t}\n\n\t\tif (existsSync(join(dir, lockfile))) {\n\t\t\treturn dir;\n\t\t}\n\n\t\tdir = dirname(dir);\n\t}\n\n\treturn cwd;\n}\n\n/**\n * Get workspace package glob patterns from pnpm-workspace.yaml\n * or package.json#workspaces.\n */\nexport function getWorkspaceGlobs(root: string): string[] {\n\tconst pnpmWorkspacePath = join(root, 'pnpm-workspace.yaml');\n\tif (existsSync(pnpmWorkspacePath)) {\n\t\tconst content = readFileSync(pnpmWorkspacePath, 'utf-8');\n\t\tconst parsed = parseYaml(content);\n\t\treturn parsed?.packages ?? [];\n\t}\n\n\tconst rootPkgJsonPath = join(root, 'package.json');\n\tif (existsSync(rootPkgJsonPath)) {\n\t\tconst pkg = JSON.parse(readFileSync(rootPkgJsonPath, 'utf-8'));\n\t\tif (Array.isArray(pkg.workspaces)) {\n\t\t\treturn pkg.workspaces;\n\t\t}\n\t\tif (pkg.workspaces?.packages) {\n\t\t\treturn pkg.workspaces.packages;\n\t\t}\n\t}\n\n\treturn [];\n}\n\n/**\n * Find all package.json files across a workspace.\n * Returns the root package.json plus all workspace member package.json paths.\n */\nexport function findWorkspacePackages(\n\tcwd: string,\n\tpm: PackageManager,\n): string[] {\n\tconst workspaceRoot = findWorkspaceRoot(cwd, pm);\n\tconst results: string[] = [];\n\n\tconst rootPkgJson = join(workspaceRoot, 'package.json');\n\tif (existsSync(rootPkgJson)) {\n\t\tresults.push(rootPkgJson);\n\t}\n\n\tconst globs = getWorkspaceGlobs(workspaceRoot);\n\n\tfor (const glob of globs) {\n\t\tconst pattern = `${glob}/package.json`;\n\t\tconst matches = fg.sync(pattern, {\n\t\t\tcwd: workspaceRoot,\n\t\t\tabsolute: true,\n\t\t\tignore: ['**/node_modules/**'],\n\t\t});\n\t\tresults.push(...matches);\n\t}\n\n\treturn [...new Set(results)];\n}\n","import { execSync } from 'node:child_process';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport prompts from 'prompts';\nimport { createStageSecrets } from '../secrets/generator.js';\nimport { getKeyPath } from '../secrets/keystore.js';\nimport { writeStageSecrets } from '../secrets/storage.js';\nimport {\n\tgenerateDbPassword,\n\tgenerateDbUrl,\n} from '../setup/fullstack-secrets.js';\nimport type { ComposeServiceName, EventsBackend } from '../types.js';\nimport { generateAuthAppFiles } from './generators/auth.js';\nimport { generateConfigFiles } from './generators/config.js';\nimport {\n\ttype DatabaseAppConfig,\n\tgenerateDockerFiles,\n} from './generators/docker.js';\nimport { generateEnvFiles } from './generators/env.js';\nimport { generateExpoAppFiles } from './generators/mobile-expo.js';\nimport { generateModelsPackage } from './generators/models.js';\nimport { generateMonorepoFiles } from './generators/monorepo.js';\nimport { generatePackageJson } from './generators/package.js';\nimport { generateSourceFiles } from './generators/source.js';\nimport { generateTestFiles } from './generators/test.js';\nimport { generateUiPackageFiles } from './generators/ui.js';\nimport { generateWebAppFiles } from './generators/web.js';\nimport { generateTanStackWebFiles } from './generators/web-tanstack.js';\nimport {\n\ttype DeployTarget,\n\tdeployTargetChoices,\n\teventsBackendChoices,\n\ttype FullstackFrontendFramework,\n\tfrontendFrameworkChoices,\n\tgetTemplate,\n\tisFullstackTemplate,\n\tloggerTypeChoices,\n\ttype PackageManager,\n\tpackageManagerChoices,\n\troutesStructureChoices,\n\ttype ServicesSelection,\n\tservicesChoices,\n\ttype TemplateName,\n\ttype TemplateOptions,\n\ttemplateChoices,\n} from './templates/index.js';\nimport {\n\tcheckDirectoryExists,\n\tdetectPackageManager,\n\tgetInstallCommand,\n\tgetRunCommand,\n\tvalidateProjectName,\n} from './utils.js';\n\nexport interface InitOptions {\n\t/** Project name */\n\tname?: string;\n\t/** Template to use */\n\ttemplate?: TemplateName;\n\t/** Skip dependency installation */\n\tskipInstall?: boolean;\n\t/** Use defaults for all prompts */\n\tyes?: boolean;\n\t/** Force monorepo setup (deprecated, use fullstack template) */\n\tmonorepo?: boolean;\n\t/** API app path in monorepo */\n\tapiPath?: string;\n\t/** Package manager to use */\n\tpm?: PackageManager;\n}\n\n/**\n * Main init command - scaffolds a new project\n */\nexport async function initCommand(\n\tprojectName?: string,\n\toptions: InitOptions = {},\n): Promise<void> {\n\tconst cwd = process.cwd();\n\tconst detectedPkgManager = detectPackageManager(cwd);\n\n\t// Handle Ctrl+C gracefully\n\tprompts.override({});\n\tconst onCancel = () => {\n\t\tprocess.exit(0);\n\t};\n\n\t// Gather answers via prompts\n\tconst answers = await prompts(\n\t\t[\n\t\t\t{\n\t\t\t\ttype: projectName || options.name ? null : 'text',\n\t\t\t\tname: 'name',\n\t\t\t\tmessage: 'Project name:',\n\t\t\t\tinitial: 'my-app',\n\t\t\t\tvalidate: (value: string) => {\n\t\t\t\t\tconst nameValid = validateProjectName(value);\n\t\t\t\t\tif (nameValid !== true) return nameValid;\n\t\t\t\t\tconst dirValid = checkDirectoryExists(value, cwd);\n\t\t\t\t\tif (dirValid !== true) return dirValid;\n\t\t\t\t\treturn true;\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.template || options.yes ? null : 'select',\n\t\t\t\tname: 'template',\n\t\t\t\tmessage: 'Template:',\n\t\t\t\tchoices: templateChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'multiselect',\n\t\t\t\tname: 'services',\n\t\t\t\tmessage: 'Services (space to select, enter to confirm):',\n\t\t\t\tchoices: servicesChoices.map((c) => ({ ...c, selected: true })),\n\t\t\t\thint: '- Space to select. Return to submit',\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'select',\n\t\t\t\tname: 'eventsBackend',\n\t\t\t\tmessage: 'Event backend:',\n\t\t\t\tchoices: eventsBackendChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'select',\n\t\t\t\tname: 'packageManager',\n\t\t\t\tmessage: 'Package manager:',\n\t\t\t\tchoices: packageManagerChoices,\n\t\t\t\tinitial: packageManagerChoices.findIndex(\n\t\t\t\t\t(c) => c.value === detectedPkgManager,\n\t\t\t\t),\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'select',\n\t\t\t\tname: 'deployTarget',\n\t\t\t\tmessage: 'Deployment target:',\n\t\t\t\tchoices: deployTargetChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'confirm',\n\t\t\t\tname: 'telescope',\n\t\t\t\tmessage: 'Include Telescope (debugging dashboard)?',\n\t\t\t\tinitial: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'select',\n\t\t\t\tname: 'loggerType',\n\t\t\t\tmessage: 'Logger:',\n\t\t\t\tchoices: loggerTypeChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'select',\n\t\t\t\tname: 'routesStructure',\n\t\t\t\tmessage: 'Routes structure:',\n\t\t\t\tchoices: routesStructureChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\t// Only prompt for frontend framework on the fullstack template.\n\t\t\t\ttype: (_prev, values) =>\n\t\t\t\t\t!options.yes &&\n\t\t\t\t\t(options.template === 'fullstack' || values.template === 'fullstack')\n\t\t\t\t\t\t? 'select'\n\t\t\t\t\t\t: null,\n\t\t\t\tname: 'frontendFramework',\n\t\t\t\tmessage: 'Frontend framework:',\n\t\t\t\tchoices: frontendFrameworkChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t],\n\t\t{ onCancel },\n\t);\n\n\t// Build final options\n\tconst name = projectName || options.name || answers.name;\n\tif (!name) {\n\t\tconsole.error('Project name is required');\n\t\tprocess.exit(1);\n\t}\n\n\t// Validate name if provided via argument\n\tif (projectName || options.name) {\n\t\tconst nameToValidate = projectName || options.name!;\n\t\tconst nameValid = validateProjectName(nameToValidate);\n\t\tif (nameValid !== true) {\n\t\t\tconsole.error(nameValid);\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tconst dirValid = checkDirectoryExists(nameToValidate, cwd);\n\t\tif (dirValid !== true) {\n\t\t\tconsole.error(dirValid);\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\tconst template: TemplateName = options.template || answers.template || 'api';\n\tconst isFullstack = isFullstackTemplate(template);\n\n\t// For fullstack, force monorepo mode\n\t// For api template, monorepo is optional (via --monorepo flag)\n\tconst monorepo = isFullstack || options.monorepo || false;\n\n\t// Parse services selection\n\tconst servicesArray: string[] = options.yes\n\t\t? ['db', 'cache', 'mail', 'storage']\n\t\t: answers.services || [];\n\n\t// Determine events backend (default to pgboss for all templates with --yes)\n\tconst eventsBackend: EventsBackend | undefined = options.yes\n\t\t? 'pgboss'\n\t\t: answers.eventsBackend;\n\n\tconst services: ServicesSelection = {\n\t\tdb: servicesArray.includes('db'),\n\t\tcache: servicesArray.includes('cache'),\n\t\tmail: servicesArray.includes('mail'),\n\t\tstorage: servicesArray.includes('storage'),\n\t\tevents: eventsBackend,\n\t};\n\n\t// pgboss requires postgres\n\tif (services.events === 'pgboss') {\n\t\tservices.db = true;\n\t}\n\n\tconst pkgManager: PackageManager = options.pm\n\t\t? options.pm\n\t\t: options.yes\n\t\t\t? 'pnpm'\n\t\t\t: (answers.packageManager ?? detectedPkgManager);\n\n\tconst deployTarget: DeployTarget = options.yes\n\t\t? 'dokploy'\n\t\t: (answers.deployTarget ?? 'dokploy');\n\n\tconst database = services.db;\n\tconst frontendFramework: FullstackFrontendFramework | undefined = isFullstack\n\t\t? options.yes\n\t\t\t? 'nextjs'\n\t\t\t: (answers.frontendFramework ?? 'nextjs')\n\t\t: undefined;\n\tconst templateOptions: TemplateOptions = {\n\t\tname,\n\t\ttemplate,\n\t\ttelescope: options.yes ? true : (answers.telescope ?? true),\n\t\tdatabase,\n\t\tstudio: database,\n\t\tloggerType: options.yes ? 'pino' : (answers.loggerType ?? 'pino'),\n\t\troutesStructure: options.yes\n\t\t\t? 'centralized-endpoints'\n\t\t\t: (answers.routesStructure ?? 'centralized-endpoints'),\n\t\tmonorepo,\n\t\tapiPath: monorepo ? (options.apiPath ?? 'apps/api') : '',\n\t\tpackageManager: pkgManager,\n\t\tdeployTarget,\n\t\tservices,\n\t\tfrontendFramework,\n\t};\n\n\tconst targetDir = join(cwd, name);\n\tconst baseTemplate = getTemplate(templateOptions.template);\n\n\tconst isMonorepo = templateOptions.monorepo;\n\tconst apiPath = templateOptions.apiPath;\n\n\tconsole.log('\\n🚀 Creating your project...\\n');\n\n\t// Create project directory\n\tawait mkdir(targetDir, { recursive: true });\n\n\t// For monorepo, app files go in the specified apiPath (e.g., apps/api)\n\tconst appDir = isMonorepo ? join(targetDir, apiPath) : targetDir;\n\tif (isMonorepo) {\n\t\tawait mkdir(appDir, { recursive: true });\n\t}\n\n\t// Generate per-app database configs for fullstack template\n\tconst dbApps: DatabaseAppConfig[] = [];\n\tif (isFullstack && services.db) {\n\t\tdbApps.push(\n\t\t\t{ name: 'api', password: generateDbPassword() },\n\t\t\t{ name: 'auth', password: generateDbPassword() },\n\t\t);\n\t}\n\n\t// Collect app files (backend/api)\n\t// Note: Docker files go to root for monorepo, so exclude them here\n\tconst appFiles = baseTemplate\n\t\t? [\n\t\t\t\t...generatePackageJson(templateOptions, baseTemplate),\n\t\t\t\t...generateConfigFiles(templateOptions, baseTemplate),\n\t\t\t\t...generateEnvFiles(templateOptions, baseTemplate),\n\t\t\t\t...generateSourceFiles(templateOptions, baseTemplate),\n\t\t\t\t...generateTestFiles(templateOptions, baseTemplate),\n\t\t\t\t...(isMonorepo\n\t\t\t\t\t? []\n\t\t\t\t\t: generateDockerFiles(templateOptions, baseTemplate, dbApps)),\n\t\t\t]\n\t\t: [];\n\n\t// For monorepo, docker files go at root level\n\tconst dockerFiles =\n\t\tisMonorepo && baseTemplate\n\t\t\t? generateDockerFiles(templateOptions, baseTemplate, dbApps)\n\t\t\t: [];\n\n\t// Collect root monorepo files (includes packages/models)\n\tconst rootFiles = baseTemplate\n\t\t? [\n\t\t\t\t...generateMonorepoFiles(templateOptions, baseTemplate),\n\t\t\t\t...generateModelsPackage(templateOptions),\n\t\t\t]\n\t\t: [];\n\n\t// Collect frontend app files for fullstack template, dispatched by framework\n\tconst webAppFiles = isFullstack\n\t\t? frontendFramework === 'tanstack-start'\n\t\t\t? generateTanStackWebFiles(templateOptions)\n\t\t\t: frontendFramework === 'expo'\n\t\t\t\t? generateExpoAppFiles(templateOptions)\n\t\t\t\t: generateWebAppFiles(templateOptions)\n\t\t: [];\n\n\t// Collect auth app files for fullstack template\n\tconst authAppFiles = isFullstack ? generateAuthAppFiles(templateOptions) : [];\n\n\t// Collect UI package files for fullstack template\n\tconst uiPackageFiles = isFullstack\n\t\t? generateUiPackageFiles(templateOptions)\n\t\t: [];\n\n\t// Write root files (for monorepo)\n\tfor (const { path, content } of rootFiles) {\n\t\tconst fullPath = join(targetDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Write docker files at root for monorepo\n\tfor (const { path, content } of dockerFiles) {\n\t\tconst fullPath = join(targetDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Write app files (backend)\n\tfor (const { path, content } of appFiles) {\n\t\tconst fullPath = join(appDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Write web app files (frontend)\n\tfor (const { path, content } of webAppFiles) {\n\t\tconst fullPath = join(targetDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Write auth app files (authentication service)\n\tfor (const { path, content } of authAppFiles) {\n\t\tconst fullPath = join(targetDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Write UI package files (shared components)\n\tfor (const { path, content } of uiPackageFiles) {\n\t\tconst fullPath = join(targetDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Initialize encrypted secrets for development stage\n\tconsole.log('🔐 Initializing encrypted secrets...\\n');\n\tconst secretServices: ComposeServiceName[] = [];\n\tif (services.db) secretServices.push('postgres');\n\tif (services.cache) secretServices.push('redis');\n\tif (services.storage) secretServices.push('minio');\n\tif (services.mail) secretServices.push('mailpit');\n\tif (services.events === 'sns') secretServices.push('localstack');\n\tif (services.events === 'rabbitmq') secretServices.push('rabbitmq');\n\n\tconst devSecrets = createStageSecrets('development', secretServices, {\n\t\tprojectName: name,\n\t\teventsBackend: services.events,\n\t});\n\n\t// Add common custom secrets\n\tconst customSecrets: Record<string, string> = {\n\t\tNODE_ENV: 'development',\n\t\tPORT: '3000',\n\t\tLOG_LEVEL: 'debug',\n\t\tJWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n\t};\n\n\t// Add per-app database URLs and passwords for fullstack template\n\tif (isFullstack && dbApps.length > 0) {\n\t\tfor (const app of dbApps) {\n\t\t\t// Database URL for the app to use (all apps use same database, different users/schemas)\n\t\t\tconst urlKey = `${app.name.toUpperCase()}_DATABASE_URL`;\n\t\t\tcustomSecrets[urlKey] = generateDbUrl(app.name, app.password, name);\n\n\t\t\t// Database password for docker-compose init script\n\t\t\tconst passwordKey = `${app.name.toUpperCase()}_DB_PASSWORD`;\n\t\t\tcustomSecrets[passwordKey] = app.password;\n\t\t}\n\n\t\t// Auth service secrets (better-auth)\n\t\tcustomSecrets.AUTH_PORT = '3002';\n\t\tcustomSecrets.AUTH_URL = 'http://localhost:3002'; // For API app to call auth service\n\t\tcustomSecrets.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n\t\tcustomSecrets.BETTER_AUTH_URL = 'http://localhost:3002';\n\t\tcustomSecrets.BETTER_AUTH_TRUSTED_ORIGINS =\n\t\t\t'http://localhost:3000,http://localhost:3001';\n\t}\n\n\tdevSecrets.custom = customSecrets;\n\n\tawait writeStageSecrets(devSecrets, targetDir);\n\tconst keyPath = getKeyPath('development', name);\n\tconsole.log(` Secrets: .gkm/secrets/development.json (encrypted)`);\n\tconsole.log(` Key: ${keyPath}\\n`);\n\n\t// Install dependencies\n\tif (!options.skipInstall) {\n\t\tconsole.log('\\n📦 Installing dependencies...\\n');\n\t\ttry {\n\t\t\texecSync(getInstallCommand(pkgManager), {\n\t\t\t\tcwd: targetDir,\n\t\t\t\tstdio: 'inherit',\n\t\t\t});\n\t\t} catch {\n\t\t\tconsole.error('Failed to install dependencies');\n\t\t}\n\n\t\t// Format generated files with biome\n\t\ttry {\n\t\t\texecSync('npx @biomejs/biome format --write --unsafe .', {\n\t\t\t\tcwd: targetDir,\n\t\t\t\tstdio: 'inherit',\n\t\t\t});\n\t\t} catch {\n\t\t\t// Silently ignore format errors\n\t\t}\n\t}\n\n\t// Initialize git repository\n\tconsole.log('\\n📦 Initializing git repository...\\n');\n\ttry {\n\t\texecSync('git init', { cwd: targetDir, stdio: 'pipe' });\n\t\texecSync('git branch -M main', { cwd: targetDir, stdio: 'pipe' });\n\t\texecSync('git add .', { cwd: targetDir, stdio: 'pipe' });\n\t\texecSync('git commit -m \"🎉 Project created with @geekmidas/toolbox\"', {\n\t\t\tcwd: targetDir,\n\t\t\tstdio: 'pipe',\n\t\t});\n\t\tconsole.log(' Initialized git repository on branch main');\n\t} catch {\n\t\tconsole.log(\n\t\t\t' Could not initialize git repository (git may not be installed)',\n\t\t);\n\t}\n\n\t// Print success message with next steps\n\tprintNextSteps(name, templateOptions, pkgManager);\n}\n\n/**\n * Print success message with next steps\n */\nfunction printNextSteps(\n\tprojectName: string,\n\toptions: TemplateOptions,\n\tpkgManager: PackageManager,\n): void {\n\tconst devCommand = getRunCommand(pkgManager, 'dev');\n\tconst cdCommand = `cd ${projectName}`;\n\n\tconsole.log(`\\n${'─'.repeat(50)}`);\n\tconsole.log('\\n✅ Project created successfully!\\n');\n\n\tconsole.log('Next steps:\\n');\n\tconsole.log(` ${cdCommand}`);\n\n\tif (options.services.db) {\n\t\tconsole.log(` # Start PostgreSQL (if not running)`);\n\t\tconsole.log(` docker compose up -d postgres`);\n\t}\n\n\tconsole.log(` ${devCommand}`);\n\tconsole.log('');\n\n\tif (options.monorepo) {\n\t\tconsole.log('📁 Project structure:');\n\t\tconsole.log(` ${projectName}/`);\n\t\tconsole.log(` ├── apps/`);\n\t\tconsole.log(` │ ├── api/ # Backend API`);\n\t\tif (isFullstackTemplate(options.template)) {\n\t\t\tconsole.log(` │ ├── auth/ # Auth service (better-auth)`);\n\t\t\tswitch (options.frontendFramework) {\n\t\t\t\tcase 'tanstack-start':\n\t\t\t\t\tconsole.log(` │ └── web/ # TanStack Start frontend`);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'expo':\n\t\t\t\t\tconsole.log(` │ └── app/ # Expo (React Native) app`);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tconsole.log(` │ └── web/ # Next.js frontend`);\n\t\t\t}\n\t\t}\n\t\tconsole.log(` ├── packages/`);\n\t\tconsole.log(` │ ├── models/ # Shared Zod schemas`);\n\t\tif (isFullstackTemplate(options.template)) {\n\t\t\tconsole.log(` │ └── ui/ # Shared UI components`);\n\t\t}\n\t\tconsole.log(` ├── .gkm/secrets/ # Encrypted secrets`);\n\t\tconsole.log(` ├── gkm.config.ts # Workspace config`);\n\t\tconsole.log(` └── turbo.json # Turbo config`);\n\t\tconsole.log('');\n\t}\n\n\tconsole.log('🔐 Secrets management:');\n\tconsole.log(` gkm secrets:show --stage development # View secrets`);\n\tconsole.log(` gkm secrets:set KEY VALUE --stage development # Add secret`);\n\tconsole.log(\n\t\t` gkm secrets:init --stage production # Create production secrets`,\n\t);\n\tconsole.log('');\n\n\tif (options.deployTarget === 'dokploy') {\n\t\tconsole.log('🚀 Deployment:');\n\t\tconsole.log(` ${getRunCommand(pkgManager, 'deploy')}`);\n\t\tconsole.log('');\n\t}\n\n\tconsole.log('📚 Documentation: https://geekmidas.github.io/toolbox/');\n\tconsole.log('');\n}\n","import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { loadConfig, loadWorkspaceConfig } from '../config';\nimport { generateFullstackCustomSecrets } from '../setup/fullstack-secrets';\nimport type { ComposeServiceName, ComposeServicesConfig } from '../types';\nimport { createStageSecrets, rotateServicePassword } from './generator';\nimport {\n\tmaskPassword,\n\treadStageSecrets,\n\tsecretsExist,\n\tsetCustomSecret,\n\twriteStageSecrets,\n} from './storage';\n\nconst logger = console;\n\nexport interface SecretsInitOptions {\n\tstage: string;\n\tforce?: boolean;\n}\n\nexport interface SecretsSetOptions {\n\tstage: string;\n}\n\nexport interface SecretsShowOptions {\n\tstage: string;\n\treveal?: boolean;\n}\n\nexport interface SecretsRotateOptions {\n\tstage: string;\n\tservice?: ComposeServiceName;\n}\n\nexport interface SecretsImportOptions {\n\tstage: string;\n\t/** Merge with existing secrets (default: true) */\n\tmerge?: boolean;\n}\n\n/**\n * Extract service names from compose config.\n */\nexport function getServicesFromConfig(\n\tservices: ComposeServicesConfig | ComposeServiceName[] | undefined,\n): ComposeServiceName[] {\n\tif (!services) {\n\t\treturn [];\n\t}\n\n\tif (Array.isArray(services)) {\n\t\treturn services;\n\t}\n\n\t// Object format - get keys where value is truthy\n\treturn (Object.entries(services) as [ComposeServiceName, unknown][])\n\t\t.filter(([, config]) => config)\n\t\t.map(([name]) => name);\n}\n\n/**\n * Initialize secrets for a stage.\n * Generates secure random passwords for configured services.\n */\nexport async function secretsInitCommand(\n\toptions: SecretsInitOptions,\n): Promise<void> {\n\tconst { stage, force } = options;\n\n\t// Check if secrets already exist\n\tif (!force && secretsExist(stage)) {\n\t\tlogger.error(\n\t\t\t`Secrets already exist for stage \"${stage}\". Use --force to overwrite.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Load config to get services\n\tconst config = await loadConfig();\n\tconst services = getServicesFromConfig(config.docker?.compose?.services);\n\n\tif (services.length === 0) {\n\t\tlogger.warn(\n\t\t\t'No services configured in docker.compose.services. Creating secrets with empty services.',\n\t\t);\n\t}\n\n\t// Detect workspace mode for project name and fullstack secrets\n\tlet projectName: string | undefined;\n\tlet workspaceSecrets: Record<string, string> | undefined;\n\ttry {\n\t\tconst loaded = await loadWorkspaceConfig();\n\t\tprojectName = loaded.workspace.name;\n\t\tconst isMultiApp = Object.keys(loaded.workspace.apps).length > 1;\n\n\t\tif (isMultiApp) {\n\t\t\tworkspaceSecrets = generateFullstackCustomSecrets(loaded.workspace);\n\t\t\tlogger.log(' Detected workspace mode — generating per-app secrets');\n\t\t}\n\t} catch {\n\t\t// Not a workspace — single-app mode, skip custom secrets\n\t}\n\n\t// Generate secrets (with project name so DATABASE_URL matches app-specific URLs)\n\tconst secrets = createStageSecrets(stage, services, { projectName });\n\n\tif (workspaceSecrets) {\n\t\tsecrets.custom = workspaceSecrets;\n\t}\n\n\t// Write to file\n\tawait writeStageSecrets(secrets);\n\n\tlogger.log(`\\n✓ Secrets initialized for stage \"${stage}\"`);\n\tlogger.log(` Location: .gkm/secrets/${stage}.json`);\n\tlogger.log('\\n Generated credentials for:');\n\n\tfor (const service of services) {\n\t\tlogger.log(` - ${service}`);\n\t}\n\n\tif (secrets.urls.DATABASE_URL) {\n\t\tlogger.log(`\\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);\n\t}\n\tif (secrets.urls.REDIS_URL) {\n\t\tlogger.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);\n\t}\n\tif (secrets.urls.RABBITMQ_URL) {\n\t\tlogger.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);\n\t}\n\tif (secrets.urls.STORAGE_ENDPOINT) {\n\t\tlogger.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);\n\t}\n\n\tif (Object.keys(secrets.custom).length > 0) {\n\t\tlogger.log(`\\n Custom secrets: ${Object.keys(secrets.custom).length}`);\n\t}\n\n\tlogger.log(`\\n Use \"gkm secrets:show --stage ${stage}\" to view secrets`);\n\tlogger.log(\n\t\t' Use \"gkm secrets:set <KEY> <VALUE> --stage ' +\n\t\t\tstage +\n\t\t\t'\" to add custom secrets',\n\t);\n}\n\n/**\n * Read all data from stdin.\n */\nasync function readStdin(): Promise<string> {\n\tconst chunks: Buffer[] = [];\n\n\tfor await (const chunk of process.stdin) {\n\t\tchunks.push(chunk);\n\t}\n\n\treturn Buffer.concat(chunks).toString('utf-8').trim();\n}\n\n/**\n * Set a custom secret.\n * If value is not provided, reads from stdin.\n */\nexport async function secretsSetCommand(\n\tkey: string,\n\tvalue: string | undefined,\n\toptions: SecretsSetOptions,\n): Promise<void> {\n\tconst { stage } = options;\n\n\t// Read from stdin if value not provided\n\tlet secretValue = value;\n\tif (!secretValue) {\n\t\tif (process.stdin.isTTY) {\n\t\t\tlogger.error(\n\t\t\t\t'No value provided. Use: gkm secrets:set KEY VALUE --stage <stage>',\n\t\t\t);\n\t\t\tlogger.error(\n\t\t\t\t'Or pipe from stdin: echo \"value\" | gkm secrets:set KEY --stage <stage>',\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tsecretValue = await readStdin();\n\t\tif (!secretValue) {\n\t\t\tlogger.error('No value received from stdin');\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\ttry {\n\t\tawait setCustomSecret(stage, key, secretValue);\n\t\tlogger.log(`\\n✓ Secret \"${key}\" set for stage \"${stage}\"`);\n\t} catch (error) {\n\t\tlogger.error(\n\t\t\terror instanceof Error ? error.message : 'Failed to set secret',\n\t\t);\n\t\tprocess.exit(1);\n\t}\n}\n\n/**\n * Show secrets for a stage.\n */\nexport async function secretsShowCommand(\n\toptions: SecretsShowOptions,\n): Promise<void> {\n\tconst { stage, reveal } = options;\n\n\tconst secrets = await readStageSecrets(stage);\n\n\tif (!secrets) {\n\t\tlogger.error(\n\t\t\t`No secrets found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\tlogger.log(`\\nSecrets for stage \"${stage}\":`);\n\tlogger.log(` Created: ${secrets.createdAt}`);\n\tlogger.log(` Updated: ${secrets.updatedAt}`);\n\n\t// Show service credentials\n\tlogger.log('\\nService Credentials:');\n\tfor (const [service, creds] of Object.entries(secrets.services)) {\n\t\tif (creds) {\n\t\t\tlogger.log(`\\n ${service}:`);\n\t\t\tlogger.log(` host: ${creds.host}`);\n\t\t\tlogger.log(` port: ${creds.port}`);\n\t\t\tlogger.log(` username: ${creds.username}`);\n\t\t\tlogger.log(\n\t\t\t\t` password: ${reveal ? creds.password : maskPassword(creds.password)}`,\n\t\t\t);\n\t\t\tif (creds.database) {\n\t\t\t\tlogger.log(` database: ${creds.database}`);\n\t\t\t}\n\t\t\tif (creds.vhost) {\n\t\t\t\tlogger.log(` vhost: ${creds.vhost}`);\n\t\t\t}\n\t\t\tif (creds.bucket) {\n\t\t\t\tlogger.log(` bucket: ${creds.bucket}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Show URLs\n\tlogger.log('\\nConnection URLs:');\n\tif (secrets.urls.DATABASE_URL) {\n\t\tlogger.log(\n\t\t\t` DATABASE_URL: ${reveal ? secrets.urls.DATABASE_URL : maskUrl(secrets.urls.DATABASE_URL)}`,\n\t\t);\n\t}\n\tif (secrets.urls.REDIS_URL) {\n\t\tlogger.log(\n\t\t\t` REDIS_URL: ${reveal ? secrets.urls.REDIS_URL : maskUrl(secrets.urls.REDIS_URL)}`,\n\t\t);\n\t}\n\tif (secrets.urls.RABBITMQ_URL) {\n\t\tlogger.log(\n\t\t\t` RABBITMQ_URL: ${reveal ? secrets.urls.RABBITMQ_URL : maskUrl(secrets.urls.RABBITMQ_URL)}`,\n\t\t);\n\t}\n\tif (secrets.urls.STORAGE_ENDPOINT) {\n\t\tlogger.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);\n\t}\n\n\t// Show custom secrets\n\tconst customKeys = Object.keys(secrets.custom);\n\tif (customKeys.length > 0) {\n\t\tlogger.log('\\nCustom Secrets:');\n\t\tfor (const [key, value] of Object.entries(secrets.custom)) {\n\t\t\tlogger.log(` ${key}: ${reveal ? value : maskPassword(value)}`);\n\t\t}\n\t}\n\n\tif (!reveal) {\n\t\tlogger.log('\\nUse --reveal to show actual values');\n\t}\n}\n\n/**\n * Rotate passwords for services.\n */\nexport async function secretsRotateCommand(\n\toptions: SecretsRotateOptions,\n): Promise<void> {\n\tconst { stage, service } = options;\n\n\tconst secrets = await readStageSecrets(stage);\n\n\tif (!secrets) {\n\t\tlogger.error(\n\t\t\t`No secrets found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\tif (service) {\n\t\t// Rotate specific service\n\t\tif (!secrets.services[service]) {\n\t\t\tlogger.error(`Service \"${service}\" not configured in stage \"${stage}\"`);\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tconst updated = rotateServicePassword(secrets, service);\n\t\tawait writeStageSecrets(updated);\n\t\tlogger.log(`\\n✓ Password rotated for ${service} in stage \"${stage}\"`);\n\t} else {\n\t\t// Rotate all services\n\t\tlet updated = secrets;\n\t\tconst services = Object.keys(secrets.services) as ComposeServiceName[];\n\n\t\tfor (const svc of services) {\n\t\t\tupdated = rotateServicePassword(updated, svc);\n\t\t}\n\n\t\tawait writeStageSecrets(updated);\n\t\tlogger.log(\n\t\t\t`\\n✓ Passwords rotated for all services in stage \"${stage}\": ${services.join(', ')}`,\n\t\t);\n\t}\n\n\tlogger.log(`\\nUse \"gkm secrets:show --stage ${stage}\" to view new values`);\n}\n\n/**\n * Import secrets from a JSON file.\n */\nexport async function secretsImportCommand(\n\tfile: string,\n\toptions: SecretsImportOptions,\n): Promise<void> {\n\tconst { stage, merge = true } = options;\n\n\t// Check if file exists\n\tif (!existsSync(file)) {\n\t\tlogger.error(`File not found: ${file}`);\n\t\tprocess.exit(1);\n\t}\n\n\t// Read and parse JSON file\n\tlet importedSecrets: Record<string, string>;\n\ttry {\n\t\tconst content = await readFile(file, 'utf-8');\n\t\timportedSecrets = JSON.parse(content);\n\n\t\t// Validate it's a flat object with string values\n\t\tif (typeof importedSecrets !== 'object' || importedSecrets === null) {\n\t\t\tthrow new Error('JSON must be an object');\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(importedSecrets)) {\n\t\t\tif (typeof value !== 'string') {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Value for \"${key}\" must be a string, got ${typeof value}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tlogger.error(\n\t\t\t`Failed to parse JSON file: ${error instanceof Error ? error.message : 'Invalid JSON'}`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Check if secrets exist for stage\n\tconst secrets = await readStageSecrets(stage);\n\n\tif (!secrets) {\n\t\tlogger.error(\n\t\t\t`No secrets found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Merge or replace custom secrets\n\tconst updatedCustom = merge\n\t\t? { ...secrets.custom, ...importedSecrets }\n\t\t: importedSecrets;\n\n\tconst updated = {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tcustom: updatedCustom,\n\t};\n\n\tawait writeStageSecrets(updated);\n\n\tconst importedCount = Object.keys(importedSecrets).length;\n\tconst totalCount = Object.keys(updatedCustom).length;\n\n\tlogger.log(`\\n✓ Imported ${importedCount} secrets for stage \"${stage}\"`);\n\n\tif (merge && totalCount > importedCount) {\n\t\tlogger.log(` Total custom secrets: ${totalCount}`);\n\t}\n\n\tlogger.log('\\n Imported keys:');\n\tfor (const key of Object.keys(importedSecrets)) {\n\t\tlogger.log(` - ${key}`);\n\t}\n}\n\n/**\n * Mask password in a URL for display.\n */\nexport function maskUrl(url: string): string {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\tif (parsed.password) {\n\t\t\tparsed.password = maskPassword(parsed.password);\n\t\t}\n\t\treturn parsed.toString();\n\t} catch {\n\t\treturn url;\n\t}\n}\n","import { spawn } from 'node:child_process';\nimport { writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcreateCredentialsPreload,\n\tloadEnvFiles,\n\tprepareEntryCredentials,\n} from '../credentials';\nimport { sniffAppEnvironment } from '../deploy/sniffer';\n\nexport interface TestOptions {\n\t/** Stage to load secrets from (default: development) */\n\tstage?: string;\n\t/** Run tests once without watch mode */\n\trun?: boolean;\n\t/** Enable watch mode */\n\twatch?: boolean;\n\t/** Generate coverage report */\n\tcoverage?: boolean;\n\t/** Open Vitest UI */\n\tui?: boolean;\n\t/** Pattern to filter tests */\n\tpattern?: string;\n\t/**\n\t * Generate a fresh stage (secrets + encryption key) from the workspace config\n\t * when none exists, instead of requiring committed secrets / a shared key.\n\t * Intended for CI. Also enabled by the GKM_AUTO_SETUP env var.\n\t */\n\tautoSetup?: boolean;\n}\n\n/**\n * Run tests with secrets, dependency URLs, and .env files loaded.\n * Environment variables are sniffed to inject only what the app needs.\n */\nexport async function testCommand(options: TestOptions = {}): Promise<void> {\n\tconst stage = options.stage ?? 'development';\n\tconst cwd = process.cwd();\n\n\tconsole.log(`\\n🧪 Running tests with ${stage} environment...\\n`);\n\n\t// 0. Auto-setup (CI): regenerate a fresh stage from the workspace config when\n\t// none exists, so tests run without committed secrets or a shared key.\n\tconst autoSetup = options.autoSetup || Boolean(process.env.GKM_AUTO_SETUP);\n\tif (autoSetup) {\n\t\tconst { ensureStageSecrets } = await import('../setup/index.js');\n\t\tconst generated = await ensureStageSecrets(stage, cwd);\n\t\tif (generated) {\n\t\t\tconsole.log(` 🔐 Generated fresh ${stage} secrets (auto-setup)`);\n\t\t}\n\t}\n\n\t// 1. Load .env files\n\tconst defaultEnv = loadEnvFiles('.env');\n\tif (defaultEnv.loaded.length > 0) {\n\t\tconsole.log(` 📦 Loaded env: ${defaultEnv.loaded.join(', ')}`);\n\t}\n\n\t// 2. Prepare credentials: loads secrets, resolves Docker ports,\n\t// starts services, rewrites URLs, injects dependency URLs\n\tconst result = await prepareEntryCredentials({\n\t\tstages: [stage],\n\t\tstartDocker: true,\n\t\tsecretsFileName: 'test-secrets.json',\n\t\tresolveDockerPorts: 'full',\n\t});\n\n\tlet finalCredentials = { ...result.credentials };\n\n\t// 3. Sniff env vars to filter only what the app needs (workspace only)\n\tif (result.appInfo) {\n\t\tconst sniffed = await sniffAppEnvironment(\n\t\t\tresult.appInfo.app,\n\t\t\tresult.appInfo.appName,\n\t\t\tresult.appInfo.workspaceRoot,\n\t\t\t{ logWarnings: false },\n\t\t);\n\n\t\tif (sniffed.requiredEnvVars.length > 0) {\n\t\t\tconst needed = new Set(sniffed.requiredEnvVars);\n\t\t\tconst filtered: Record<string, string> = {};\n\t\t\tfor (const [key, value] of Object.entries(finalCredentials)) {\n\t\t\t\tif (needed.has(key)) {\n\t\t\t\t\tfiltered[key] = value;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinalCredentials = filtered;\n\t\t\tconsole.log(\n\t\t\t\t` 🔍 Sniffed ${sniffed.requiredEnvVars.length} required env var(s)`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// 4. Rewrite DATABASE_URL for test isolation (append _test suffix)\n\tfinalCredentials = rewriteDatabaseUrlForTests(finalCredentials);\n\n\tconsole.log('');\n\n\t// 5. Write final credentials and create preload script\n\tawait writeFile(\n\t\tresult.secretsJsonPath,\n\t\tJSON.stringify(finalCredentials, null, 2),\n\t);\n\n\tconst gkmDir = join(cwd, '.gkm');\n\tconst preloadPath = join(gkmDir, 'test-credentials-preload.ts');\n\tawait createCredentialsPreload(preloadPath, result.secretsJsonPath);\n\n\t// Merge NODE_OPTIONS with existing value (if any)\n\tconst existingNodeOptions = process.env.NODE_OPTIONS ?? '';\n\tconst tsxImport = '--import=tsx';\n\tconst preloadImport = `--import=${preloadPath}`;\n\tconst nodeOptions = [existingNodeOptions, tsxImport, preloadImport]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\t// Build vitest args\n\tconst args: string[] = [];\n\n\tif (options.run) {\n\t\targs.push('run');\n\t} else if (options.watch) {\n\t\targs.push('--watch');\n\t}\n\n\tif (options.coverage) {\n\t\targs.push('--coverage');\n\t}\n\n\tif (options.ui) {\n\t\targs.push('--ui');\n\t}\n\n\tif (options.pattern) {\n\t\targs.push(options.pattern);\n\t}\n\n\t// Run vitest with combined environment and credentials preload\n\tconst vitestProcess = spawn('npx', ['vitest', ...args], {\n\t\tcwd,\n\t\tstdio: 'inherit',\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t...finalCredentials,\n\t\t\tNODE_ENV: 'test',\n\t\t\tNODE_OPTIONS: nodeOptions,\n\t\t},\n\t});\n\n\t// Wait for vitest to complete\n\treturn new Promise((resolve, reject) => {\n\t\tvitestProcess.on('close', (code) => {\n\t\t\tif (code === 0) {\n\t\t\t\tresolve();\n\t\t\t} else {\n\t\t\t\treject(new Error(`Tests failed with exit code ${code}`));\n\t\t\t}\n\t\t});\n\n\t\tvitestProcess.on('error', (error) => {\n\t\t\treject(error);\n\t\t});\n\t});\n}\n\nconst TEST_DB_SUFFIX = '_test';\n\n/**\n * Rewrite DATABASE_URL to point to a separate test database.\n * Appends `_test` to the database name (e.g., `app` -> `app_test`).\n * @internal Exported for testing\n */\nexport function rewriteDatabaseUrlForTests(\n\tenv: Record<string, string>,\n): Record<string, string> {\n\tconst result = { ...env };\n\n\tfor (const key of Object.keys(result)) {\n\t\tif (!key.includes('DATABASE_URL')) continue;\n\n\t\tconst value = result[key] as string;\n\t\ttry {\n\t\t\tconst url = new URL(value);\n\t\t\tconst dbName = url.pathname.slice(1);\n\t\t\tif (dbName && !dbName.endsWith(TEST_DB_SUFFIX)) {\n\t\t\t\turl.pathname = `/${dbName}${TEST_DB_SUFFIX}`;\n\t\t\t\tresult[key] = url.toString();\n\t\t\t\tconsole.log(\n\t\t\t\t\t` 🧪 ${key}: using test database \"${dbName}${TEST_DB_SUFFIX}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Not a valid URL, skip\n\t\t}\n\t}\n\n\treturn result;\n}\n","import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport {\n\tdetectPackageManager,\n\tfindWorkspacePackages,\n\ttype PackageManager,\n} from '../init/utils.js';\n\nconst logger = console;\n\nexport interface UpgradeOptions {\n\tdryRun?: boolean;\n}\n\ninterface FoundDependency {\n\tpackageName: string;\n\tcurrentVersion: string;\n\tdepType: 'dependencies' | 'devDependencies' | 'peerDependencies';\n\tpackageJsonPath: string;\n\tworkspaceName: string;\n}\n\ninterface UpgradeInfo extends FoundDependency {\n\tlatestVersion: string;\n\tisWorkspaceRef: boolean;\n\tneedsUpgrade: boolean;\n}\n\nexport async function upgradeCommand(\n\toptions: UpgradeOptions = {},\n): Promise<void> {\n\tconst cwd = process.cwd();\n\n\tlogger.log('\\n📦 Scanning workspace for @geekmidas packages...\\n');\n\n\tconst pm = detectPackageManager(cwd);\n\tlogger.log(` Package manager: ${pm}`);\n\n\tconst packageJsonPaths = findWorkspacePackages(cwd, pm);\n\tlogger.log(` Found ${packageJsonPaths.length} package(s) in workspace\\n`);\n\n\tconst dependencies = scanForGeekmidasDeps(packageJsonPaths);\n\n\tif (dependencies.length === 0) {\n\t\tlogger.log(' No @geekmidas packages found.\\n');\n\t\treturn;\n\t}\n\n\tconst uniquePackages = [...new Set(dependencies.map((d) => d.packageName))];\n\tlogger.log(\n\t\t` Checking ${uniquePackages.length} unique @geekmidas package(s) on npm...\\n`,\n\t);\n\n\tconst latestVersions = await fetchLatestVersions(uniquePackages);\n\n\tconst upgradeInfos = dependencies.map((dep) =>\n\t\tresolveUpgradeInfo(dep, latestVersions),\n\t);\n\n\tprintUpgradeTable(upgradeInfos);\n\n\tconst upgradable = upgradeInfos.filter(\n\t\t(info) => info.needsUpgrade && !info.isWorkspaceRef,\n\t);\n\n\tif (upgradable.length === 0) {\n\t\tlogger.log('\\n All @geekmidas packages are up to date!\\n');\n\t\treturn;\n\t}\n\n\tlogger.log(`\\n ${upgradable.length} package(s) can be upgraded.\\n`);\n\n\tif (options.dryRun) {\n\t\tlogger.log(' --dry-run: No changes made.\\n');\n\t\tprintUpgradeCommands(upgradable, pm);\n\t\treturn;\n\t}\n\n\texecuteUpgrade(upgradable, pm, cwd);\n\n\tlogger.log('\\n ✅ Upgrade complete! Run your tests to verify.\\n');\n}\n\nfunction scanForGeekmidasDeps(packageJsonPaths: string[]): FoundDependency[] {\n\tconst results: FoundDependency[] = [];\n\tconst depTypes = [\n\t\t'dependencies',\n\t\t'devDependencies',\n\t\t'peerDependencies',\n\t] as const;\n\n\tfor (const pkgJsonPath of packageJsonPaths) {\n\t\tconst pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));\n\t\tconst workspaceName = pkg.name ?? pkgJsonPath;\n\n\t\tfor (const depType of depTypes) {\n\t\t\tconst deps = pkg[depType];\n\t\t\tif (!deps) continue;\n\n\t\t\tfor (const [name, version] of Object.entries(deps)) {\n\t\t\t\tif (!name.startsWith('@geekmidas/')) continue;\n\n\t\t\t\tresults.push({\n\t\t\t\t\tpackageName: name,\n\t\t\t\t\tcurrentVersion: version as string,\n\t\t\t\t\tdepType,\n\t\t\t\t\tpackageJsonPath: pkgJsonPath,\n\t\t\t\t\tworkspaceName,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n\nasync function fetchLatestVersions(\n\tpackageNames: string[],\n): Promise<Map<string, string>> {\n\tconst versions = new Map<string, string>();\n\n\tconst results = await Promise.allSettled(\n\t\tpackageNames.map(async (name) => {\n\t\t\tconst res = await fetch(`https://registry.npmjs.org/${name}/latest`);\n\t\t\tif (!res.ok) throw new Error(`HTTP ${res.status}`);\n\t\t\tconst data = (await res.json()) as { version: string };\n\t\t\treturn { name, version: data.version };\n\t\t}),\n\t);\n\n\tfor (const result of results) {\n\t\tif (result.status === 'fulfilled') {\n\t\t\tversions.set(result.value.name, result.value.version);\n\t\t}\n\t}\n\n\treturn versions;\n}\n\nfunction resolveUpgradeInfo(\n\tdep: FoundDependency,\n\tlatestVersions: Map<string, string>,\n): UpgradeInfo {\n\tconst isWorkspaceRef = dep.currentVersion.startsWith('workspace:');\n\tconst latestVersion = latestVersions.get(dep.packageName) ?? 'unknown';\n\n\tconst currentBare = dep.currentVersion.replace(/^[\\^~>=<]*/g, '');\n\tconst needsUpgrade =\n\t\t!isWorkspaceRef &&\n\t\tlatestVersion !== 'unknown' &&\n\t\tcurrentBare !== latestVersion;\n\n\treturn {\n\t\t...dep,\n\t\tlatestVersion,\n\t\tisWorkspaceRef,\n\t\tneedsUpgrade,\n\t};\n}\n\nfunction printUpgradeTable(infos: UpgradeInfo[]): void {\n\tconst byPackage = new Map<string, UpgradeInfo>();\n\tfor (const info of infos) {\n\t\tif (!byPackage.has(info.packageName)) {\n\t\t\tbyPackage.set(info.packageName, info);\n\t\t}\n\t}\n\n\tconst nameWidth = 33;\n\tconst verWidth = 14;\n\tconst statusWidth = 14;\n\n\tconst hr = ` ${'─'.repeat(nameWidth + verWidth * 2 + statusWidth + 5)}`;\n\n\tlogger.log(hr);\n\tlogger.log(\n\t\t` ${'Package'.padEnd(nameWidth)} ${'Current'.padEnd(verWidth)} ${'Latest'.padEnd(verWidth)} ${'Status'.padEnd(statusWidth)}`,\n\t);\n\tlogger.log(hr);\n\n\tfor (const [, info] of byPackage) {\n\t\tconst name = info.packageName.padEnd(nameWidth);\n\t\tconst current = info.currentVersion.padEnd(verWidth);\n\t\tconst latest = info.latestVersion.padEnd(verWidth);\n\n\t\tlet status: string;\n\t\tif (info.isWorkspaceRef) {\n\t\t\tstatus = 'workspace';\n\t\t} else if (info.needsUpgrade) {\n\t\t\tstatus = '⬆ upgrade';\n\t\t} else {\n\t\t\tstatus = '✓ up-to-date';\n\t\t}\n\n\t\tlogger.log(` ${name} ${current} ${latest} ${status}`);\n\t}\n\n\tlogger.log(hr);\n}\n\nfunction printUpgradeCommands(\n\tupgradable: UpgradeInfo[],\n\tpm: PackageManager,\n): void {\n\tlogger.log(' Commands that would be run:\\n');\n\n\tconst uniquePackages = [...new Set(upgradable.map((i) => i.packageName))];\n\tconst cmd = getWorkspaceUpgradeCommand(pm, uniquePackages);\n\tlogger.log(` ${cmd}\\n`);\n}\n\nfunction getWorkspaceUpgradeCommand(\n\tpm: PackageManager,\n\tpackages: string[],\n): string {\n\tconst pkgList = packages.join(' ');\n\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn `pnpm update -r ${pkgList} --latest`;\n\t\tcase 'yarn':\n\t\t\treturn `yarn upgrade ${pkgList}`;\n\t\tcase 'bun':\n\t\t\treturn `bun update ${pkgList}`;\n\t\tcase 'npm':\n\t\t\treturn `npm update ${pkgList} --workspaces`;\n\t\tdefault:\n\t\t\treturn `npm update ${pkgList}`;\n\t}\n}\n\nfunction executeUpgrade(\n\tupgradable: UpgradeInfo[],\n\tpm: PackageManager,\n\tcwd: string,\n): void {\n\tconst uniquePackages = [...new Set(upgradable.map((i) => i.packageName))];\n\n\tconst cmd = getWorkspaceUpgradeCommand(pm, uniquePackages);\n\tlogger.log(` Running: ${cmd}\\n`);\n\n\ttry {\n\t\texecSync(cmd, {\n\t\t\tcwd,\n\t\t\tstdio: 'inherit',\n\t\t\ttimeout: 120_000,\n\t\t});\n\t} catch {\n\t\tthrow new Error(\n\t\t\t'Package upgrade failed. Check the output above for details.',\n\t\t);\n\t}\n}\n","#!/usr/bin/env -S npx tsx\n\nimport { Command } from 'commander';\nimport pkg from '../package.json';\nimport { loginCommand, logoutCommand, whoamiCommand } from './auth';\nimport { buildCommand } from './build/index';\nimport { enableDebug, formatError } from './debug';\nimport { type DeployProvider, deployCommand } from './deploy/index';\nimport { deployInitCommand, deployListCommand } from './deploy/init';\nimport {\n\tstateDiffCommand,\n\tstatePullCommand,\n\tstatePushCommand,\n\tstateShowCommand,\n} from './deploy/state-commands';\nimport { devCommand, execCommand } from './dev/index';\nimport { type DockerOptions, dockerCommand } from './docker/index';\nimport { type InitOptions, initCommand } from './init/index';\nimport { openapiCommand } from './openapi';\nimport { generateReactQueryCommand } from './openapi-react-query';\nimport {\n\tsecretsImportCommand,\n\tsecretsInitCommand,\n\tsecretsRotateCommand,\n\tsecretsSetCommand,\n\tsecretsShowCommand,\n} from './secrets';\nimport { type SetupOptions, setupCommand } from './setup/index';\nimport { type TestOptions, testCommand } from './test/index';\nimport type { ComposeServiceName, LegacyProvider, MainProvider } from './types';\nimport { type UpgradeOptions, upgradeCommand } from './upgrade/index';\n\nconst program = new Command();\n\nprogram\n\t.name('gkm')\n\t.description('GeekMidas backend framework CLI')\n\t.version(pkg.version)\n\t.option('--cwd <path>', 'Change working directory')\n\t.option('--debug', 'Enable debug mode (verbose errors with stack traces)')\n\t.hook('preAction', () => {\n\t\tconst opts = program.opts();\n\t\tif (opts.debug) enableDebug();\n\t});\n\nprogram\n\t.command('init')\n\t.description('Scaffold a new project')\n\t.argument('[name]', 'Project name')\n\t.option(\n\t\t'--template <template>',\n\t\t'Project template (minimal, api, serverless, worker)',\n\t)\n\t.option('--skip-install', 'Skip dependency installation', false)\n\t.option('-y, --yes', 'Skip prompts, use defaults', false)\n\t.option('--monorepo', 'Setup as monorepo with packages/models', false)\n\t.option('--api-path <path>', 'API app path in monorepo (default: apps/api)')\n\t.option('--pm <manager>', 'Package manager (pnpm, npm, yarn, bun)')\n\t.action(async (name: string | undefined, options: InitOptions) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait initCommand(name, options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('setup')\n\t.description('Setup development environment (secrets, Docker, database)')\n\t.option('--stage <stage>', 'Stage name', 'development')\n\t.option('--force', 'Regenerate secrets even if they exist')\n\t.option('--skip-docker', 'Skip starting Docker services')\n\t.option('-y, --yes', 'Skip prompts')\n\t.action(async (options: SetupOptions) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait setupCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('build')\n\t.description('Build handlers from endpoints, functions, and crons')\n\t.option(\n\t\t'--provider <provider>',\n\t\t'Target provider for generated handlers (aws, server)',\n\t)\n\t.option(\n\t\t'--providers <providers>',\n\t\t'[DEPRECATED] Use --provider instead. Target providers for generated handlers (comma-separated)',\n\t)\n\t.option(\n\t\t'--enable-openapi',\n\t\t'Enable OpenAPI documentation generation for server builds',\n\t)\n\t.option('--production', 'Build for production (no dev tools, bundled output)')\n\t.option('--skip-bundle', 'Skip bundling step in production build')\n\t.option('--stage <stage>', 'Inject encrypted secrets for deployment stage')\n\t.option(\n\t\t'--mark-optional',\n\t\t'Suffix optional env vars with ? in manifest envVars field (e.g. PORT?)',\n\t)\n\t.action(\n\t\tasync (options: {\n\t\t\tprovider?: string;\n\t\t\tproviders?: string;\n\t\t\tenableOpenapi?: boolean;\n\t\t\tproduction?: boolean;\n\t\t\tskipBundle?: boolean;\n\t\t\tstage?: string;\n\t\t\tmarkOptional?: boolean;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\t// Handle new single provider option\n\t\t\t\tif (options.provider) {\n\t\t\t\t\tif (!['aws', 'server'].includes(options.provider)) {\n\t\t\t\t\t\tprocess.exit(1);\n\t\t\t\t\t}\n\t\t\t\t\tawait buildCommand({\n\t\t\t\t\t\tprovider: options.provider as MainProvider,\n\t\t\t\t\t\tenableOpenApi: options.enableOpenapi || false,\n\t\t\t\t\t\tproduction: options.production || false,\n\t\t\t\t\t\tskipBundle: options.skipBundle || false,\n\t\t\t\t\t\tstage: options.stage,\n\t\t\t\t\t\tmarkOptional: options.markOptional || false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// Handle legacy providers option\n\t\t\t\telse if (options.providers) {\n\t\t\t\t\tconst providerList = [\n\t\t\t\t\t\t...new Set(options.providers.split(',').map((p) => p.trim())),\n\t\t\t\t\t] as LegacyProvider[];\n\t\t\t\t\tawait buildCommand({\n\t\t\t\t\t\tproviders: providerList,\n\t\t\t\t\t\tenableOpenApi: options.enableOpenapi || false,\n\t\t\t\t\t\tproduction: options.production || false,\n\t\t\t\t\t\tskipBundle: options.skipBundle || false,\n\t\t\t\t\t\tstage: options.stage,\n\t\t\t\t\t\tmarkOptional: options.markOptional || false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// Default to config-driven build\n\t\t\t\telse {\n\t\t\t\t\tawait buildCommand({\n\t\t\t\t\t\tenableOpenApi: options.enableOpenapi || false,\n\t\t\t\t\t\tproduction: options.production || false,\n\t\t\t\t\t\tskipBundle: options.skipBundle || false,\n\t\t\t\t\t\tstage: options.stage,\n\t\t\t\t\t\tmarkOptional: options.markOptional || false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command('dev')\n\t.description('Start development server with automatic reload')\n\t.option('-p, --port <port>', 'Port to run the development server on')\n\t.option('--entry <file>', 'Entry file to run (bypasses gkm config)')\n\t.option('--watch', 'Watch for file changes (default: true with --entry)')\n\t.option('--no-watch', 'Disable file watching')\n\t.option(\n\t\t'--enable-openapi',\n\t\t'Enable OpenAPI documentation for development server',\n\t\ttrue,\n\t)\n\t.action(\n\t\tasync (options: {\n\t\t\tport?: string;\n\t\t\tentry?: string;\n\t\t\twatch?: boolean;\n\t\t\tenableOpenapi?: boolean;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\tawait devCommand({\n\t\t\t\t\tport: options.port ? Number.parseInt(options.port, 10) : 3000,\n\t\t\t\t\tportExplicit: !!options.port,\n\t\t\t\t\tenableOpenApi: options.enableOpenapi ?? true,\n\t\t\t\t\tentry: options.entry,\n\t\t\t\t\twatch: options.watch,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command('exec')\n\t.description('Run a command with secrets injected into Credentials')\n\t.argument('<command...>', 'Command to run (use -- before command)')\n\t.action(async (commandArgs: string[]) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait execCommand(commandArgs);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('test')\n\t.description('Run tests with secrets loaded from environment')\n\t.option('--stage <stage>', 'Stage to load secrets from', 'development')\n\t.option('--run', 'Run tests once without watch mode')\n\t.option('--watch', 'Enable watch mode')\n\t.option('--coverage', 'Generate coverage report')\n\t.option('--ui', 'Open Vitest UI')\n\t.option(\n\t\t'--auto-setup',\n\t\t'Generate a fresh stage (secrets + key) from the workspace config when none exists (for CI; also via GKM_AUTO_SETUP)',\n\t)\n\t.argument('[pattern]', 'Pattern to filter tests')\n\t.action(async (pattern: string | undefined, options: TestOptions) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait testCommand({ ...options, pattern });\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('cron')\n\t.description('Manage cron jobs')\n\t.action(() => {\n\t\tconst globalOptions = program.opts();\n\t\tif (globalOptions.cwd) {\n\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t}\n\t\tprocess.stdout.write('Cron management - coming soon\\n');\n\t});\n\nprogram\n\t.command('function')\n\t.description('Manage serverless functions')\n\t.action(() => {\n\t\tconst globalOptions = program.opts();\n\t\tif (globalOptions.cwd) {\n\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t}\n\t\tprocess.stdout.write('Serverless function management - coming soon\\n');\n\t});\n\nprogram\n\t.command('api')\n\t.description('Manage REST API endpoints')\n\t.action(() => {\n\t\tconst globalOptions = program.opts();\n\t\tif (globalOptions.cwd) {\n\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t}\n\t\tprocess.stdout.write('REST API management - coming soon\\n');\n\t});\n\nprogram\n\t.command('openapi')\n\t.description('Generate OpenAPI specification from endpoints')\n\t.option(\n\t\t'--app <name>',\n\t\t'Workspace mode: generate for a single named backend app',\n\t)\n\t.action(async (options: { app?: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait openapiCommand({ app: options.app });\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('generate:react-query')\n\t.description('Generate React Query hooks from OpenAPI specification')\n\t.option('--input <path>', 'Input OpenAPI spec file path', 'openapi.json')\n\t.option(\n\t\t'--output <path>',\n\t\t'Output file path for generated hooks',\n\t\t'src/api/hooks.ts',\n\t)\n\t.option('--name <name>', 'API name prefix for generated code', 'API')\n\t.action(\n\t\tasync (options: { input?: string; output?: string; name?: string }) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\t\t\t\tawait generateReactQueryCommand(options);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command('docker')\n\t.description('Generate Docker deployment files')\n\t.option('--build', 'Build Docker image after generating files')\n\t.option('--push', 'Push image to registry after building')\n\t.option('--tag <tag>', 'Image tag', 'latest')\n\t.option('--registry <registry>', 'Container registry URL')\n\t.option('--slim', 'Use slim Dockerfile (assumes pre-built bundle exists)')\n\t.option('--turbo', 'Use turbo prune for monorepo optimization')\n\t.option('--turbo-package <name>', 'Package name for turbo prune')\n\t.action(async (options: DockerOptions) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait dockerCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('prepack')\n\t.description('Generate Docker files for production deployment')\n\t.option('--build', 'Build Docker image after generating files')\n\t.option('--push', 'Push image to registry after building')\n\t.option('--tag <tag>', 'Image tag', 'latest')\n\t.option('--registry <registry>', 'Container registry URL')\n\t.option('--slim', 'Build locally first, then use slim Dockerfile')\n\t.option('--skip-bundle', 'Skip bundling step (only with --slim)')\n\t.option('--turbo', 'Use turbo prune for monorepo optimization')\n\t.option('--turbo-package <name>', 'Package name for turbo prune')\n\t.action(\n\t\tasync (options: {\n\t\t\tbuild?: boolean;\n\t\t\tpush?: boolean;\n\t\t\ttag?: string;\n\t\t\tregistry?: string;\n\t\t\tslim?: boolean;\n\t\t\tskipBundle?: boolean;\n\t\t\tturbo?: boolean;\n\t\t\tturboPackage?: string;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\tif (options.slim) {\n\t\t\t\t\tawait buildCommand({\n\t\t\t\t\t\tprovider: 'server',\n\t\t\t\t\t\tproduction: true,\n\t\t\t\t\t\tskipBundle: options.skipBundle,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tawait dockerCommand({\n\t\t\t\t\tbuild: options.build,\n\t\t\t\t\tpush: options.push,\n\t\t\t\t\ttag: options.tag,\n\t\t\t\t\tregistry: options.registry,\n\t\t\t\t\tslim: options.slim,\n\t\t\t\t\tturbo: options.turbo,\n\t\t\t\t\tturboPackage: options.turboPackage,\n\t\t\t\t});\n\t\t\t\tif (options.slim) {\n\t\t\t\t} else {\n\t\t\t\t}\n\n\t\t\t\tif (options.build) {\n\t\t\t\t\tconst tag = options.tag ?? 'latest';\n\t\t\t\t\tconst registry = options.registry;\n\t\t\t\t\tconst _imageRef = registry ? `${registry}/api:${tag}` : `api:${tag}`;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n// Secrets management commands\nprogram\n\t.command('secrets:init')\n\t.description('Initialize secrets for a deployment stage')\n\t.requiredOption('--stage <stage>', 'Stage name (e.g., production, staging)')\n\t.option('--force', 'Overwrite existing secrets')\n\t.action(async (options: { stage: string; force?: boolean }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait secretsInitCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:set')\n\t.description('Set a custom secret for a stage')\n\t.argument('<key>', 'Secret key (e.g., API_KEY)')\n\t.argument('[value]', 'Secret value (reads from stdin if omitted)')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.action(\n\t\tasync (\n\t\t\tkey: string,\n\t\t\tvalue: string | undefined,\n\t\t\toptions: { stage: string },\n\t\t) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\t\t\t\tawait secretsSetCommand(key, value, options);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command('secrets:show')\n\t.description('Show secrets for a stage')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.option('--reveal', 'Show actual secret values (not masked)')\n\t.action(async (options: { stage: string; reveal?: boolean }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait secretsShowCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:rotate')\n\t.description('Rotate service passwords')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.option(\n\t\t'--service <service>',\n\t\t'Specific service to rotate (postgres, redis, rabbitmq)',\n\t)\n\t.action(async (options: { stage: string; service?: ComposeServiceName }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait secretsRotateCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:import')\n\t.description('Import secrets from a JSON file')\n\t.argument('<file>', 'JSON file path (e.g., secrets.json)')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.option('--no-merge', 'Replace all custom secrets instead of merging')\n\t.action(async (file: string, options: { stage: string; merge?: boolean }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait secretsImportCommand(file, options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:push')\n\t.description('Push secrets to remote provider (SSM)')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\n\t\t\tconst { loadWorkspaceConfig } = await import('./config');\n\t\t\tconst { pushSecrets } = await import('./secrets/sync');\n\t\t\tconst { reconcileMissingSecrets } = await import('./secrets/reconcile');\n\t\t\tconst { readStageSecrets, writeStageSecrets } = await import(\n\t\t\t\t'./secrets/storage'\n\t\t\t);\n\n\t\t\tconst { workspace } = await loadWorkspaceConfig();\n\n\t\t\tconst secrets = await readStageSecrets(options.stage, workspace.root);\n\t\t\tif (secrets) {\n\t\t\t\tconst result = reconcileMissingSecrets(secrets, workspace);\n\t\t\t\tif (result) {\n\t\t\t\t\tawait writeStageSecrets(result.secrets, workspace.root);\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t` Reconciled ${result.addedKeys.length} missing secret(s):`,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const key of result.addedKeys) {\n\t\t\t\t\t\tconsole.log(` + ${key}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait pushSecrets(options.stage, workspace);\n\t\t\tconsole.log(`\\n✓ Secrets pushed for stage \"${options.stage}\"`);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:pull')\n\t.description('Pull secrets from remote provider (SSM)')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\n\t\t\tconst { loadWorkspaceConfig } = await import('./config');\n\t\t\tconst { pullSecrets } = await import('./secrets/sync');\n\t\t\tconst { writeStageSecrets } = await import('./secrets/storage');\n\t\t\tconst { reconcileMissingSecrets } = await import('./secrets/reconcile');\n\n\t\t\tconst { workspace } = await loadWorkspaceConfig();\n\t\t\tlet secrets = await pullSecrets(options.stage, workspace);\n\n\t\t\tif (!secrets) {\n\t\t\t\tconsole.error(`No remote secrets found for stage \"${options.stage}\".`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\tconst result = reconcileMissingSecrets(secrets, workspace);\n\t\t\tif (result) {\n\t\t\t\tsecrets = result.secrets;\n\t\t\t\tconsole.log(\n\t\t\t\t\t` Reconciled ${result.addedKeys.length} missing secret(s):`,\n\t\t\t\t);\n\t\t\t\tfor (const key of result.addedKeys) {\n\t\t\t\t\tconsole.log(` + ${key}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait writeStageSecrets(secrets, workspace.root);\n\t\t\tconsole.log(`\\n✓ Secrets pulled for stage \"${options.stage}\"`);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:reconcile')\n\t.description('Backfill missing custom secrets from workspace config')\n\t.option('--stage <stage>', 'Stage name', 'development')\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\n\t\t\tconst { loadWorkspaceConfig } = await import('./config');\n\t\t\tconst { reconcileMissingSecrets } = await import('./secrets/reconcile');\n\t\t\tconst { readStageSecrets, writeStageSecrets } = await import(\n\t\t\t\t'./secrets/storage'\n\t\t\t);\n\n\t\t\tconst { workspace } = await loadWorkspaceConfig();\n\t\t\tconst secrets = await readStageSecrets(options.stage, workspace.root);\n\n\t\t\tif (!secrets) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`No secrets found for stage \"${options.stage}\". Run \"gkm secrets:init --stage ${options.stage}\" first.`,\n\t\t\t\t);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\tconst result = reconcileMissingSecrets(secrets, workspace);\n\n\t\t\tif (!result) {\n\t\t\t\tconsole.log(`\\n✓ Secrets for stage \"${options.stage}\" are up-to-date`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait writeStageSecrets(result.secrets, workspace.root);\n\t\t\tconsole.log(\n\t\t\t\t`\\n✓ Reconciled ${result.addedKeys.length} missing secret(s) for stage \"${options.stage}\":`,\n\t\t\t);\n\t\t\tfor (const key of result.addedKeys) {\n\t\t\t\tconsole.log(` + ${key}`);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\n// Deploy command\nprogram\n\t.command('deploy')\n\t.description('Deploy application to a provider')\n\t.requiredOption(\n\t\t'--provider <provider>',\n\t\t'Deploy provider (docker, dokploy, aws-lambda)',\n\t)\n\t.requiredOption(\n\t\t'--stage <stage>',\n\t\t'Deployment stage (e.g., production, staging)',\n\t)\n\t.option('--tag <tag>', 'Image tag (default: stage-timestamp)')\n\t.option('--skip-push', 'Skip pushing image to registry')\n\t.option('--skip-build', 'Skip build step (use existing build)')\n\t.action(\n\t\tasync (options: {\n\t\t\tprovider: string;\n\t\t\tstage: string;\n\t\t\ttag?: string;\n\t\t\tskipPush?: boolean;\n\t\t\tskipBuild?: boolean;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\tconst validProviders = ['docker', 'dokploy', 'aws-lambda'];\n\t\t\t\tif (!validProviders.includes(options.provider)) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`Invalid provider: ${options.provider}\\n` +\n\t\t\t\t\t\t\t`Valid providers: ${validProviders.join(', ')}`,\n\t\t\t\t\t);\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t}\n\n\t\t\t\tawait deployCommand({\n\t\t\t\t\tprovider: options.provider as DeployProvider,\n\t\t\t\t\tstage: options.stage,\n\t\t\t\t\ttag: options.tag,\n\t\t\t\t\tskipPush: options.skipPush,\n\t\t\t\t\tskipBuild: options.skipBuild,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n// Deploy init command - Initialize Dokploy project and application\nprogram\n\t.command('deploy:init')\n\t.description('Initialize Dokploy deployment (create project and application)')\n\t.option(\n\t\t'--endpoint <url>',\n\t\t'Dokploy server URL (uses stored credentials if logged in)',\n\t)\n\t.requiredOption('--project <name>', 'Project name (creates if not exists)')\n\t.requiredOption('--app <name>', 'Application name')\n\t.option('--project-id <id>', 'Use existing project ID instead of creating')\n\t.option('--registry-id <id>', 'Configure registry for the application')\n\t.action(\n\t\tasync (options: {\n\t\t\tendpoint?: string;\n\t\t\tproject: string;\n\t\t\tapp: string;\n\t\t\tprojectId?: string;\n\t\t\tregistryId?: string;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\tawait deployInitCommand({\n\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t\tprojectName: options.project,\n\t\t\t\t\tappName: options.app,\n\t\t\t\t\tprojectId: options.projectId,\n\t\t\t\t\tregistryId: options.registryId,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n// Deploy list command - List Dokploy resources\nprogram\n\t.command('deploy:list')\n\t.description('List Dokploy resources (projects, registries)')\n\t.option(\n\t\t'--endpoint <url>',\n\t\t'Dokploy server URL (uses stored credentials if logged in)',\n\t)\n\t.option('--projects', 'List projects')\n\t.option('--registries', 'List registries')\n\t.action(\n\t\tasync (options: {\n\t\t\tendpoint?: string;\n\t\t\tprojects?: boolean;\n\t\t\tregistries?: boolean;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\tif (options.projects) {\n\t\t\t\t\tawait deployListCommand({\n\t\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t\t\tresource: 'projects',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (options.registries) {\n\t\t\t\t\tawait deployListCommand({\n\t\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t\t\tresource: 'registries',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (!options.projects && !options.registries) {\n\t\t\t\t\t// Default to listing both\n\t\t\t\t\tawait deployListCommand({\n\t\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t\t\tresource: 'projects',\n\t\t\t\t\t});\n\t\t\t\t\tawait deployListCommand({\n\t\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t\t\tresource: 'registries',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n// Login command\nprogram\n\t.command('login')\n\t.description('Authenticate with a deployment service')\n\t.option('--service <service>', 'Service to login to (dokploy)', 'dokploy')\n\t.option('--token <token>', 'API token (will prompt if not provided)')\n\t.option('--endpoint <url>', 'Service endpoint URL')\n\t.action(\n\t\tasync (options: { service: string; token?: string; endpoint?: string }) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\tif (options.service !== 'dokploy') {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`Unknown service: ${options.service}. Supported: dokploy`,\n\t\t\t\t\t);\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t}\n\n\t\t\t\tawait loginCommand({\n\t\t\t\t\tservice: options.service as 'dokploy',\n\t\t\t\t\ttoken: options.token,\n\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n// Logout command\nprogram\n\t.command('logout')\n\t.description('Remove stored credentials')\n\t.option(\n\t\t'--service <service>',\n\t\t'Service to logout from (dokploy, all)',\n\t\t'dokploy',\n\t)\n\t.action(async (options: { service: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\n\t\t\tawait logoutCommand({\n\t\t\t\tservice: options.service as 'dokploy' | 'all',\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\n// Whoami command\nprogram\n\t.command('whoami')\n\t.description('Show current authentication status')\n\t.action(async () => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\n\t\t\tawait whoamiCommand();\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\n// State management commands\nprogram\n\t.command('state:pull')\n\t.description('Pull deployment state from remote to local')\n\t.requiredOption(\n\t\t'--stage <stage>',\n\t\t'Deployment stage (e.g., production, staging)',\n\t)\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait statePullCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('state:push')\n\t.description('Push deployment state from local to remote')\n\t.requiredOption(\n\t\t'--stage <stage>',\n\t\t'Deployment stage (e.g., production, staging)',\n\t)\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait statePushCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('state:show')\n\t.description('Show deployment state for a stage')\n\t.requiredOption(\n\t\t'--stage <stage>',\n\t\t'Deployment stage (e.g., production, staging)',\n\t)\n\t.option('--json', 'Output as JSON')\n\t.action(async (options: { stage: string; json?: boolean }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait stateShowCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('state:diff')\n\t.description('Compare local and remote deployment state')\n\t.requiredOption(\n\t\t'--stage <stage>',\n\t\t'Deployment stage (e.g., production, staging)',\n\t)\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait stateDiffCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('upgrade')\n\t.description('Upgrade all @geekmidas packages to their latest versions')\n\t.option('--dry-run', 'Show what would be upgraded without making changes')\n\t.action(async (options: UpgradeOptions) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait upgradeCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram.parse();\n"],"mappings":"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Aa;CACb;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;AACD;;;;AAKD,MAAa,oBAAoB,CAChC;CACC,OAAO;CACP,OAAO;CACP,aAAa;AACb,GACD;CACC,OAAO;CACP,OAAO;CACP,aAAa;AACb,CACD;;;;AAKD,MAAa,yBAAyB;CACrC;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;AACD;;;;AAKD,MAAa,wBAAwB;CACpC;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;AACD;;;;AAKD,MAAa,sBAAsB,CAClC;CACC,OAAO;CACP,OAAO;CACP,aAAa;AACb,GACD;CACC,OAAO;CACP,OAAO;CACP,aAAa;AACb,CACD;;;;AAKD,MAAa,kBAAkB;CAC9B;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;AACD;;;;AAKD,MAAa,uBAAuB;CACnC;EACC,OAAO;EACP,OAAO;EACP,aACC;CACD;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;CACD;EACC,OAAO;EACP,OAAO;EACP,aAAa;CACb;CACD;EACC,OAAO;EACP;EACA,aAAa;CACb;AACD;;;;AAKD,SAAgB,YAAYC,QAA2C;AACtE,KAAIC,WAAS,YAEZ,QAAO,UAAU;CAElB,MAAM,WAAW,UAAUA;AAC3B,MAAK,SACJ,OAAM,IAAI,OAAO,oBAAoBA,OAAK;AAE3C,QAAO;AACP;;;;AAKD,SAAgB,oBAAoBD,QAA6B;AAChE,QAAOC,WAAS;AAChB;;;;;;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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["logger","endpoint: string","token: string","DokployApi","prompt","message: string","resolve","err: Error","char: Buffer","options: LoginOptions","options: LogoutOptions","config: GkmConfig","options: BuildOptions","providers: LegacyProvider[]","mainProvider: MainProvider","providersConfig?: ProvidersConfig","providersConfig: ProvidersConfig","config:\n\t\t| boolean\n\t\t| AWSApiGatewayConfig\n\t\t| AWSLambdaConfig\n\t\t| ServerConfig\n\t\t| undefined","context: BuildContext","constructs: GeneratedConstruct<\n\t\t\tCron<any, any, any, any, any, any, any, any>\n\t\t>[]","outputDir: string","options?: GeneratorOptions","logger","cronInfos: CronInfo[]","value: any","sourceFile: string","exportName: string","NONE: RuntimeDrivers","CACHE_DRIVERS: Record<CacheBackend, RuntimeDrivers>","options: {\n\tappRoot: string;\n\t/**\n\t * The cache backends actually in play — normally one, and more than one only\n\t * where an app declares two caches that live in different places.\n\t *\n\t * Absent or empty means nothing declared a cache, so no cache driver is\n\t * registered at all.\n\t */\n\tcache?: CacheBackend | readonly CacheBackend[] | false;\n}","manifest: Record<string, { kind: string; of?: string }>","configured: CacheBackend","appRoot: string","pkg","value: any","context: BuildContext","constructs: GeneratedConstruct<\n\t\t\tFunction<any, any, any, any, any, any, any, any, any, any, any, any>\n\t\t>[]","outputDir: string","options?: GeneratorOptions","logger","functionInfos: FunctionInfo[]","sourceFile: string","exportName: string","value: any","context: BuildContext","constructs: GeneratedConstruct<Queue<any, any, any, any>>[]","outputDir: string","options?: GeneratorOptions","logger","queueInfos: QueueInfo[]","sourceFile: string","exportName: string","queues: GeneratedConstruct<Queue<any, any, any, any>>[]","exports","value: any","context: BuildContext","constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","outputDir: string","options?: GeneratorOptions","logger","subscriberInfos: SubscriberInfo[]","sourceFile: string","exportName: string","_subscriber: Subscriber<any, any, any, any, any, any>","subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","exports","value: any","_context: BuildContext","constructs: GeneratedConstruct<Topic<any, any>>[]","_outputDir: string","_options?: GeneratorOptions","logger","logger","commandArgs: string[]","options: ExecOptions","resolve","code: number | null","error: Error","logger","config: GkmConfig['telescope']","isEnabled","telescopeConfig: TelescopeConfig","config: GkmConfig['studio']","studioConfig: StudioConfig","config: GkmConfig['hooks']","cwd: string","cliProduction: boolean","configProduction?: ProductionConfig","config: GkmConfig","options: DevOptions","appRoot: string","secretsRoot: string","workspaceAppName: string | undefined","workspaceAppPort: number | undefined","workspace: NormalizedWorkspace | undefined","buildContext: BuildContext","runtime: Runtime","secretsJsonPath: string | undefined","rebuildTimeout: NodeJS.Timeout | null","workspace: NormalizedWorkspace","env: Record<string, string>","conflicts: { app1: string; app2: string; port: number }[]","FRONTEND_FRAMEWORKS: Record<ClientFramework, FrontendFrameworkSpec>","framework: string | undefined","fullPath: string","deps: Record<string, string>","order: ClientFramework[]","name","appName: string","appPath: string","workspaceRoot: string","framework?: ClientFramework","errors: string[]","warnings: string[]","pkg: {\n\t\tdependencies?: Record<string, string>;\n\t\tdevDependencies?: Record<string, string>;\n\t\tscripts?: Record<string, string>;\n\t}","pkg","results: FrontendValidationResult[]","secretsEnv: Record<string, string>","turboFilter: string[]","turboEnv: Record<string, string>","resolve","config: any","context: BuildContext","provider: LegacyProvider","enableOpenApi: boolean","wrapperPath: string","entryPath: string","watch: boolean","port: number","restartTimeout: NodeJS.Timeout | null","options: {\n\tsecretsJsonPath?: string;\n\truntime?: Runtime;\n\tenableOpenApi?: boolean;\n\tappImportPath?: string;\n}","requestedPort: number","portExplicit: boolean","telescope: NormalizedTelescopeConfig | undefined","studio: NormalizedStudioConfig | undefined","secretsJsonPath?: string","lines: string[]","s: string","logger","field: ManifestField<T>","fieldName: string","typeName: string","partitioned: boolean","outputDir: string","routes: ManifestField<RouteInfo>","functions: ManifestField<FunctionInfo>","crons: ManifestField<CronInfo>","subscribers: ManifestField<SubscriberInfo>","queues: ManifestField<QueueInfo>","topics: ManifestField<TopicInfo>","constructs: ConstructManifest","backends: { cache?: string; email?: string }","appInfo: ServerAppInfo","result: Record<string, RouteInfo[]>","r: RouteInfo","result: Record<\n\t\tstring,\n\t\t{ path: string; method: string; authorizer: string }[]\n\t>","s: SubscriberInfo","result: Record<\n\t\tstring,\n\t\t{ name: string; subscribedEvents: readonly string[] }[]\n\t>","q: QueueInfo","result: Record<string, { name: string }[]>","t: TopicInfo","result: Record<string, { name: string; events: readonly string[] }[]>","constructs: GeneratedConstruct<T>[]","infos: T[]","constructs: GeneratedConstruct<any>[]","groups: Record<string, T[]>","outputDir: string","source: ConstructSource","logger","options: BuildOptions","constructGlobs","constructSources: Record<string, ConstructSource>","buildContext: BuildContext","result: BuildResult","provider: LegacyProvider","context: BuildContext","rootOutputDir: string","endpointGenerator: EndpointGenerator","functionGenerator: FunctionGenerator","cronGenerator: CronGenerator","subscriberGenerator: SubscriberGenerator","queueGenerator: QueueGenerator","topicGenerator: TopicGenerator","endpoints: GeneratedConstruct<Endpoint<any, any, any, any, any, any>>[]","functions: GeneratedConstruct<Function<any, any, any, any>>[]","crons: GeneratedConstruct<Cron<any, any, any, any>>[]","subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","queues: GeneratedConstruct<Queue<any, any, any, any>>[]","topics: GeneratedConstruct<Topic<any, any>>[]","enableOpenApi: boolean","skipBundle: boolean","stage?: string","routeMetadata: RouteInfo[]","appInfo: ServerAppInfo","masterKey: string | undefined","detectPackageManager","pm: 'pnpm' | 'npm' | 'yarn'","filters: string | readonly string[]","workspace: NormalizedWorkspace","filters: string[]","unpackaged: string[]","name","from: string","results: AppBuildResult[]","resolve","_appName: string","app: NormalizedAppConfig","routes: Routes","infos: T[]","constructs: GeneratedConstruct<any>[]","error: unknown","postgres: { appName: string }","scope: { stage: string; app: string }","id: string","PROVISIONERS: Partial<Record<DeclarationKind, Provisioner>>","name","kind: DeclarationKind","options: {\n\tid: string;\n\tdatabase: string;\n\truntime: string;\n\towner: string;\n\treader?: string;\n\tschema: string;\n\tcontext: DokployProvisionContext;\n}","context: DokployProvisionContext","role: string","password: string","host: string","database: string","service: string","bucket: string","options: {\n\tservice: string;\n\tbucket: string;\n\tuser: string;\n\tpassword: string;\n}","manifest: ConstructManifest","name: string","kind: string","parent: string","backend: string | undefined","databases: readonly string[]","backend: string","options: DeclaredOptions","empty: DeclaredResult","context: DokployProvisionContext","env: Record<string, string>","client: SqlClient","statements: readonly Statement[]","ordered: readonly string[]","manifest: ConstructManifest","id: string","workspace: NormalizedWorkspace","appUrls: Readonly<Record<string, string>>","addresses: Record<string, string>","stage: string","projectId: string","environmentId: string","state: DokployStageState | null","appName: string","state: DokployStageState","applicationId: string","secretName: string","value: string","hostname: string","serverIp: string","name: string","type: string","name","type","record: Omit<CreatedDnsRecord, 'createdAt'>","backupState: BackupState","backupId: string","value: unknown","options: CreateDnsProviderOptions","logger","config: DnsConfig","hostname: string","dnsConfig: Record<string, DnsProviderConfig>","appHostnames: Map<string, string>","rootDomain: string","serverIp: string","records: RequiredDnsRecord[]","type","status: string","providerConfig: DnsProviderConfig","provider: DnsProvider | null","results: RequiredDnsRecord[]","upsertRecords: UpsertDnsRecord[]","dnsConfig: DnsConfig | undefined","dokployEndpoint: string","state?: DokployStageState","allRecords: RequiredDnsRecord[]","state: DokployStageState","results: DnsVerificationResult[]","appName: string","app: NormalizedAppConfig","stage: string","dokployConfig: DokployWorkspaceConfig | undefined","isMainFrontend: boolean","allApps: Record<string, NormalizedAppConfig>","name","sites: readonly string[]","DEFAULT_SERVICE_IMAGES: Record<ComposeServiceName, string>","DEFAULT_SERVICE_VERSIONS: Record<ComposeServiceName, string>","serviceName: ComposeServiceName","serviceName","services: ComposeServicesConfig | ComposeServiceName[]","name","version","options: ComposeOptions","options: Omit<ComposeOptions, 'services'>","workspace: NormalizedWorkspace","options: WorkspaceComposeOptions","serviceName: 'postgres' | 'redis' | 'minio'","pin: string | undefined","defaults: Record<'postgres' | 'redis' | 'minio', string>","appName: string","app: NormalizedAppConfig","allApps: [string, NormalizedAppConfig][]","options: {\n\t\tregistry?: string;\n\t\tprojectName: string;\n\t\thasPostgres: boolean;\n\t\thasRedis: boolean;\n\t\thasMinio: boolean;\n\t\thasMail: boolean;\n\t\teventsBackend?: import('../types').EventsBackend;\n\t}","dependencies: string[]","dependencies","LOCKFILES: [string, PackageManager][]","detectPackageManager","cwd: string","pm: PackageManager","commands: Record<PackageManager, string>","options: MultiStageDockerfileOptions","options: DockerTemplateOptions","config: GkmConfig","pkg","options: FrontendDockerfileOptions","options: FrontendDockerfileOptions & { healthCheckPath?: string }","options: EntryDockerfileOptions","logger","options: DockerOptions","pkg","result: DockerGeneratedFiles","cwd: string","imageName: string","appPath: string","workspace: NormalizedWorkspace","results: AppDockerResult[]","dockerfile: string","logger","registry: string | undefined","imageName: string","tag: string","imageRef: string","_appName?: string","buildArgs?: string[]","options: DockerDeployOptions","stage: string","project: string","app: string","state: DokployStageState","appName: string","secretName: string","varName: string","context: EnvResolverContext","requiredVars: string[]","resolved: Record<string, string>","missing: string[]","stage: string","value: unknown","options: CreateStateProviderOptions","CachedStateProvider","stageSecrets: StageSecrets","sniffedEnv: SniffedEnvironment","filtered: EmbeddableSecrets","found: string[]","missing: string[]","filteredSecrets: FilteredAppSecrets","sniffedApps: Map<string, SniffedEnvironment>","encryptedApps: Map<string, EncryptedAppSecrets>","appsWithSecrets: string[]","appsWithoutSecrets: string[]","appsWithMissingSecrets: Array<{ appName: string; missing: string[] }>","require","baseName: string","app: NormalizedAppConfig","appName: string","workspacePath: string","options: SniffAppOptions","sniffedVars: string[]","sniffedOptional: string[]","configPaths: string[]","envVars: string[]","optionalEnvVars: string[]","markOptional: boolean","entryPath: string","appPath: string","stdout","routes: string | string[]","envParserPath: string","SnifferEnvironmentParser: any","sniffWithFireAndForget: any","apps: Record<string, NormalizedAppConfig>","logger","message: string","resolve","char: Buffer","host: string","port: number","user: string","password: string","database: string","PgClient","appName: string","api: DokployApi","postgres: DokployCluster","serverHostname: string","statements: readonly Statement[]","client: SqlClient","lastError: unknown","endpoint: string","stage: string","manifest: ConstructManifest","workspace: NormalizedWorkspace","requirements:\n\t\t| { requiredEnvVars: string[]; optionalEnvVars: string[] }\n\t\t| undefined","name","configured: NormalizedWorkspace","options: DeployOptions","environmentId: string","declaredEnv: Record<string, string>","declaredClusters: Record<string, DokployCluster>","appUrls: Record<string, string>","publicUrls: Record<string, string>","results: AppDeployResult[]","frontendUrls: string[]","application: DokployApplication | null","buildArgs: string[]","dependencyUrls: Record<string, string>","envContext: EnvResolverContext","envVars: string[]","publicUrlArgNames: string[]","logger","providedEndpoint?: string","endpoint: string","content: string","config: DokployDeployConfig","cwd: string","newContent: string","options: DeployInitOptions","projectId: string","environmentId: string","options: {\n\tendpoint?: string;\n\tresource: 'projects' | 'registries';\n}","options: StateCommandOptions","options: StateCommandOptions & { json?: boolean }","state: DokployStageState","name","require","options: TemplateOptions","name: string","name","options: TemplateOptions","template: TemplateConfig","files: GeneratedFile[]","files","_template: TemplateConfig","_helpers: ConfigHelperOptions","options: TemplateOptions","template: TemplateConfig","dbApps?: DatabaseAppConfig[]","services: string[]","volumes: string[]","files: GeneratedFile[]","files","apps: DatabaseAppConfig[]","_eventsBackend?: EventsBackend","options: TemplateOptions","_template: TemplateConfig","files: GeneratedFile[]","files","options: TemplateOptions","options: TemplateOptions","options: TemplateOptions","_template: TemplateConfig","files: GeneratedFile[]","files","apiTemplate: TemplateConfig","options: TemplateOptions","name","path: string","file: string","files: GeneratedFile[]","files","minimalTemplate: TemplateConfig","options: TemplateOptions","name","file: string","files: GeneratedFile[]","files","serverlessTemplate: TemplateConfig","options: TemplateOptions","file: string","files: GeneratedFile[]","name","files","workerTemplate: TemplateConfig","options: TemplateOptions","file: string","files: GeneratedFile[]","name","files","OPENAPI_OUTPUT_PATH","templates: Record<\n\tExclude<TemplateName, 'fullstack'>,\n\tTemplateConfig\n>","name: TemplateName","name","options: TemplateOptions","template: TemplateConfig","dependencies","devDependencies","scripts","name","obj: Record<string, string>","OPENAPI_OUTPUT_PATH","options: TemplateOptions","template: TemplateConfig","options: TemplateOptions","_template: TemplateConfig","options: TemplateOptions","options: TemplateOptions","options: TemplateOptions","cwd: string","name: string","name","pkgManager: PackageManager","script: string","lockfileByPm: Record<PackageManager, string>","pm: PackageManager","pkg","root: string","results: string[]","projectName?: string","options: InitOptions","value: string","name","template: TemplateName","servicesArray: string[]","eventsBackend: EventsBackend | undefined","services: ServicesSelection","pkgManager: PackageManager","deployTarget: DeployTarget","frontendFramework: FullstackFrontendFramework | undefined","templateOptions: TemplateOptions","isMonorepo","dbApps: DatabaseAppConfig[]","secretServices: ComposeServiceName[]","customSecrets: Record<string, string>","projectName: string","options: TemplateOptions","devCommand","logger","services: ComposeServicesConfig | ComposeServiceName[] | undefined","name","options: SecretsInitOptions","projectName: string | undefined","workspaceSecrets: Record<string, string> | undefined","chunks: Buffer[]","key: string","value: string | undefined","options: SecretsSetOptions","options: SecretsShowOptions","options: SecretsRotateOptions","file: string","options: SecretsImportOptions","importedSecrets: Record<string, string>","url: string","options: TestOptions","filtered: Record<string, string>","args: string[]","resolve","env: Record<string, string>","options: UpgradeOptions","dependencies","packageJsonPaths: string[]","results: FoundDependency[]","pkg","name","version","packageNames: string[]","dep: FoundDependency","latestVersions: Map<string, string>","infos: UpgradeInfo[]","status: string","upgradable: UpgradeInfo[]","pm: PackageManager","packages: string[]","cwd: string","pkg","name: string | undefined","options: InitOptions","name","options: { dryRun?: boolean }","options: SetupOptions","options: {\n\t\t\tprovider?: string;\n\t\t\tproviders?: string;\n\t\t\tenableOpenapi?: boolean;\n\t\t\tproduction?: boolean;\n\t\t\tskipBundle?: boolean;\n\t\t\tstage?: string;\n\t\t\tmarkOptional?: boolean;\n\t\t}","options: {\n\t\t\tport?: string;\n\t\t\tentry?: string;\n\t\t\twatch?: boolean;\n\t\t\tenableOpenapi?: boolean;\n\t\t}","commandArgs: string[]","pattern: string | undefined","options: TestOptions","options: { app?: string }","options: { input?: string; output?: string; name?: string }","options: DockerOptions","options: {\n\t\t\tbuild?: boolean;\n\t\t\tpush?: boolean;\n\t\t\ttag?: string;\n\t\t\tregistry?: string;\n\t\t\tslim?: boolean;\n\t\t\tskipBundle?: boolean;\n\t\t\tturbo?: boolean;\n\t\t\tturboPackage?: string;\n\t\t}","options: { stage: string; force?: boolean }","key: string","value: string | undefined","options: { stage: string }","options: { stage: string; reveal?: boolean }","options: { stage: string; service?: ComposeServiceName }","file: string","options: { stage: string; merge?: boolean }","options: {\n\t\t\tprovider: string;\n\t\t\tstage: string;\n\t\t\ttag?: string;\n\t\t\tskipPush?: boolean;\n\t\t\tskipBuild?: boolean;\n\t\t}","options: {\n\t\t\tendpoint?: string;\n\t\t\tproject: string;\n\t\t\tapp: string;\n\t\t\tprojectId?: string;\n\t\t\tregistryId?: string;\n\t\t}","options: {\n\t\t\tendpoint?: string;\n\t\t\tprojects?: boolean;\n\t\t\tregistries?: boolean;\n\t\t}","options: {\n\t\t\tprovider: string;\n\t\t\tservice?: string;\n\t\t\ttoken?: string;\n\t\t\tendpoint?: string;\n\t\t}","options: { provider: string; service?: string }","options: { stage: string; json?: boolean }","options: UpgradeOptions"],"sources":["../package.json","../src/auth/index.ts","../src/build/providerResolver.ts","../src/generators/CronGenerator.ts","../src/generators/drivers.ts","../src/generators/FunctionGenerator.ts","../src/generators/QueueGenerator.ts","../src/generators/SubscriberGenerator.ts","../src/generators/TopicGenerator.ts","../src/exec/index.ts","../src/dev/index.ts","../src/build/manifests.ts","../src/build/partitions.ts","../src/build/index.ts","../src/debug.ts","../src/deploy/fromManifest.ts","../src/deploy/declared.ts","../src/deploy/state.ts","../src/deploy/dns/DnsProvider.ts","../src/deploy/dns/index.ts","../src/deploy/domain.ts","../src/docker/compose.ts","../src/docker/templates.ts","../src/docker/index.ts","../src/deploy/docker.ts","../src/deploy/env-resolver.ts","../src/deploy/StateProvider.ts","../src/deploy/secrets.ts","../src/deploy/sniffer.ts","../src/deploy/index.ts","../src/deploy/init.ts","../src/deploy/state-commands.ts","../src/init/versions.ts","../src/init/generators/auth.ts","../src/init/constructs.ts","../src/init/generators/config.ts","../src/init/generators/docker.ts","../src/init/generators/env.ts","../src/init/generators/mobile-expo.ts","../src/init/generators/models.ts","../src/init/generators/monorepo.ts","../src/init/templates/api.ts","../src/init/templates/minimal.ts","../src/init/templates/serverless.ts","../src/init/templates/worker.ts","../src/init/templates/index.ts","../src/init/generators/package.ts","../src/init/generators/source.ts","../src/init/generators/test.ts","../src/init/generators/ui.ts","../src/init/generators/web.ts","../src/init/generators/web-tanstack.ts","../src/init/utils.ts","../src/init/index.ts","../src/secrets/index.ts","../src/test/index.ts","../src/upgrade/index.ts","../src/index.ts"],"sourcesContent":["{\n\t\"name\": \"@geekmidas/cli\",\n\t\"version\": \"9.0.2\",\n\t\"description\": \"CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs\",\n\t\"private\": false,\n\t\"type\": \"module\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"import\": {\n\t\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\t\"default\": \"./dist/index.mjs\"\n\t\t\t},\n\t\t\t\"require\": {\n\t\t\t\t\"types\": \"./dist/index.d.cts\",\n\t\t\t\t\"default\": \"./dist/index.cjs\"\n\t\t\t}\n\t\t},\n\t\t\"./config\": {\n\t\t\t\"import\": {\n\t\t\t\t\"types\": \"./dist/config.d.mts\",\n\t\t\t\t\"default\": \"./dist/config.mjs\"\n\t\t\t},\n\t\t\t\"require\": {\n\t\t\t\t\"types\": \"./dist/config.d.cts\",\n\t\t\t\t\"default\": \"./dist/config.cjs\"\n\t\t\t}\n\t\t},\n\t\t\"./reconcile\": {\n\t\t\t\"import\": {\n\t\t\t\t\"types\": \"./dist/reconcile/public.d.mts\",\n\t\t\t\t\"default\": \"./dist/reconcile/public.mjs\"\n\t\t\t},\n\t\t\t\"require\": {\n\t\t\t\t\"types\": \"./dist/reconcile/public.d.cts\",\n\t\t\t\t\"default\": \"./dist/reconcile/public.cjs\"\n\t\t\t}\n\t\t},\n\t\t\"./workspace\": {\n\t\t\t\"import\": {\n\t\t\t\t\"types\": \"./dist/workspace/index.d.mts\",\n\t\t\t\t\"default\": \"./dist/workspace/index.mjs\"\n\t\t\t},\n\t\t\t\"require\": {\n\t\t\t\t\"types\": \"./dist/workspace/index.d.cts\",\n\t\t\t\t\"default\": \"./dist/workspace/index.cjs\"\n\t\t\t}\n\t\t},\n\t\t\"./openapi\": {\n\t\t\t\"import\": {\n\t\t\t\t\"types\": \"./dist/openapi.d.mts\",\n\t\t\t\t\"default\": \"./dist/openapi.mjs\"\n\t\t\t},\n\t\t\t\"require\": {\n\t\t\t\t\"types\": \"./dist/openapi.d.cts\",\n\t\t\t\t\"default\": \"./dist/openapi.cjs\"\n\t\t\t}\n\t\t},\n\t\t\"./openapi-react-query\": {\n\t\t\t\"import\": {\n\t\t\t\t\"types\": \"./dist/openapi-react-query.d.mts\",\n\t\t\t\t\"default\": \"./dist/openapi-react-query.mjs\"\n\t\t\t},\n\t\t\t\"require\": {\n\t\t\t\t\"types\": \"./dist/openapi-react-query.d.cts\",\n\t\t\t\t\"default\": \"./dist/openapi-react-query.cjs\"\n\t\t\t}\n\t\t}\n\t},\n\t\"files\": [\n\t\t\"dist\",\n\t\t\"bin\"\n\t],\n\t\"bin\": {\n\t\t\"gkm\": \"./bin/gkm.mjs\"\n\t},\n\t\"scripts\": {\n\t\t\"ts\": \"tsc --noEmit --skipLibCheck src/**/*.ts\",\n\t\t\"sync-versions\": \"tsx scripts/sync-versions.ts\",\n\t\t\"prebuild\": \"pnpm sync-versions\",\n\t\t\"test\": \"vitest\",\n\t\t\"test:once\": \"vitest run\",\n\t\t\"test:coverage\": \"vitest run --coverage\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"https://github.com/geekmidas/toolbox\"\n\t},\n\t\"dependencies\": {\n\t\t\"@apidevtools/swagger-parser\": \"^10.1.0\",\n\t\t\"@aws-sdk/client-iam\": \"~3.992.0\",\n\t\t\"@aws-sdk/client-route-53\": \"~3.992.0\",\n\t\t\"@aws-sdk/client-s3\": \"~3.992.0\",\n\t\t\"@aws-sdk/client-ssm\": \"~3.992.0\",\n\t\t\"@aws-sdk/credential-providers\": \"~3.992.0\",\n\t\t\"@geekmidas/cache\": \"workspace:*\",\n\t\t\"@geekmidas/constructs\": \"workspace:~\",\n\t\t\"@geekmidas/db\": \"workspace:*\",\n\t\t\"@geekmidas/envkit\": \"workspace:~\",\n\t\t\"@geekmidas/errors\": \"workspace:~\",\n\t\t\"@geekmidas/logger\": \"workspace:~\",\n\t\t\"@geekmidas/manifest\": \"workspace:*\",\n\t\t\"@geekmidas/schema\": \"workspace:~\",\n\t\t\"chokidar\": \"~4.0.3\",\n\t\t\"commander\": \"^12.1.0\",\n\t\t\"dotenv\": \"~17.2.3\",\n\t\t\"esbuild\": \"^0.28.2\",\n\t\t\"fast-glob\": \"^3.3.2\",\n\t\t\"hono\": \"~4.8.0\",\n\t\t\"lodash.kebabcase\": \"^4.1.1\",\n\t\t\"openapi-typescript\": \"^7.4.2\",\n\t\t\"pg\": \"~8.17.1\",\n\t\t\"prompts\": \"~2.4.2\",\n\t\t\"tsx\": \"~4.20.3\",\n\t\t\"yaml\": \"~2.8.2\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@geekmidas/testkit\": \"workspace:*\",\n\t\t\"@types/lodash.kebabcase\": \"^4.1.9\",\n\t\t\"@types/node\": \"~24.9.1\",\n\t\t\"@types/pg\": \"~8.16.0\",\n\t\t\"@types/prompts\": \"~2.4.9\",\n\t\t\"typescript\": \"^5.8.2\",\n\t\t\"vitest\": \"^3.2.4\",\n\t\t\"zod\": \"~4.1.13\"\n\t},\n\t\"peerDependencies\": {\n\t\t\"@geekmidas/telescope\": \"workspace:~\"\n\t},\n\t\"peerDependenciesMeta\": {\n\t\t\"@geekmidas/telescope\": {\n\t\t\t\"optional\": true\n\t\t}\n\t}\n}\n","import { stdin as input, stdout as output } from 'node:process';\nimport * as readline from 'node:readline/promises';\nimport {\n\tgetCredentialsPath,\n\tgetDokployCredentials,\n\tremoveDokployCredentials,\n\tstoreDokployCredentials,\n\tstoreHostingerToken,\n} from './credentials';\n\nconst logger = console;\n\nexport interface LoginOptions {\n\t/** Which provider's credentials to store — the same word `gkm deploy` uses. */\n\tprovider: 'dokploy' | 'hostinger';\n\t/** API token (if not provided, will prompt) */\n\ttoken?: string;\n\t/** Endpoint URL */\n\tendpoint?: string;\n}\n\nexport interface LogoutOptions {\n\t/** Whose credentials to remove — `all` clears every stored provider. */\n\tprovider?: 'dokploy' | 'all';\n}\n\n/**\n * Validate Dokploy token by making a test API call\n */\nexport async function validateDokployToken(\n\tendpoint: string,\n\ttoken: string,\n): Promise<boolean> {\n\tconst { DokployApi } = await import('../deploy/dokploy-api');\n\tconst api = new DokployApi({ baseUrl: endpoint, token });\n\treturn api.validateToken();\n}\n\n/**\n * Prompt for input (handles both TTY and non-TTY)\n */\nasync function prompt(message: string, hidden = false): Promise<string> {\n\tif (!process.stdin.isTTY) {\n\t\tthrow new Error(\n\t\t\t'Interactive input required. Please provide --token option.',\n\t\t);\n\t}\n\n\tif (hidden) {\n\t\t// For hidden input, use raw mode directly without readline\n\t\tprocess.stdout.write(message);\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tlet value = '';\n\n\t\t\tconst cleanup = () => {\n\t\t\t\tprocess.stdin.setRawMode(false);\n\t\t\t\tprocess.stdin.pause();\n\t\t\t\tprocess.stdin.removeListener('data', onData);\n\t\t\t\tprocess.stdin.removeListener('error', onError);\n\t\t\t};\n\n\t\t\tconst onError = (err: Error) => {\n\t\t\t\tcleanup();\n\t\t\t\treject(err);\n\t\t\t};\n\n\t\t\tconst onData = (char: Buffer) => {\n\t\t\t\tconst c = char.toString();\n\n\t\t\t\tif (c === '\\n' || c === '\\r') {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\tresolve(value);\n\t\t\t\t} else if (c === '\\u0003') {\n\t\t\t\t\t// Ctrl+C\n\t\t\t\t\tcleanup();\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t} else if (c === '\\u007F' || c === '\\b') {\n\t\t\t\t\t// Backspace\n\t\t\t\t\tif (value.length > 0) {\n\t\t\t\t\t\tvalue = value.slice(0, -1);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tvalue += c;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tprocess.stdin.setRawMode(true);\n\t\t\tprocess.stdin.resume();\n\t\t\tprocess.stdin.on('data', onData);\n\t\t\tprocess.stdin.on('error', onError);\n\t\t});\n\t} else {\n\t\t// For visible input, use readline\n\t\tconst rl = readline.createInterface({ input, output });\n\t\ttry {\n\t\t\treturn await rl.question(message);\n\t\t} finally {\n\t\t\trl.close();\n\t\t}\n\t}\n}\n\n/**\n * Login to a service\n */\nexport async function loginCommand(options: LoginOptions): Promise<void> {\n\tconst {\n\t\tprovider,\n\t\ttoken: providedToken,\n\t\tendpoint: providedEndpoint,\n\t} = options;\n\n\tif (provider === 'dokploy') {\n\t\tlogger.log('\\n🔐 Logging in to Dokploy...\\n');\n\n\t\t// Get endpoint\n\t\tlet endpoint = providedEndpoint;\n\t\tif (!endpoint) {\n\t\t\tendpoint = await prompt(\n\t\t\t\t'Dokploy URL (e.g., https://dokploy.example.com): ',\n\t\t\t);\n\t\t}\n\n\t\t// Normalize endpoint (remove trailing slash)\n\t\tendpoint = endpoint.replace(/\\/$/, '');\n\n\t\t// Validate endpoint format\n\t\ttry {\n\t\t\tnew URL(endpoint);\n\t\t} catch {\n\t\t\tlogger.error('Invalid URL format');\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Get token\n\t\tlet token = providedToken;\n\t\tif (!token) {\n\t\t\tlogger.log(`\\nGenerate a token at: ${endpoint}/settings/profile\\n`);\n\t\t\ttoken = await prompt('API Token: ', true);\n\t\t}\n\n\t\tif (!token) {\n\t\t\tlogger.error('Token is required');\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Validate token\n\t\tlogger.log('\\nValidating credentials...');\n\t\tconst isValid = await validateDokployToken(endpoint, token);\n\n\t\tif (!isValid) {\n\t\t\tlogger.error(\n\t\t\t\t'\\n✗ Invalid credentials. Please check your token and try again.',\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Store credentials\n\t\tawait storeDokployCredentials(token, endpoint);\n\n\t\tlogger.log('\\n✓ Successfully logged in to Dokploy!');\n\t\tlogger.log(` Endpoint: ${endpoint}`);\n\t\tlogger.log(` Credentials stored in: ${getCredentialsPath()}`);\n\t\tlogger.log(\n\t\t\t'\\nYou can now use deploy commands without setting DOKPLOY_API_TOKEN.',\n\t\t);\n\t}\n\n\tif (provider === 'hostinger') {\n\t\t// The DNS provider has told people to run this since it was written, and\n\t\t// this branch did not exist — so the only way to supply the token was the\n\t\t// environment variable, which the message does not mention.\n\t\tlogger.log('\\n🔐 Logging in to Hostinger...\\n');\n\n\t\tlet token = providedToken;\n\t\tif (!token) {\n\t\t\tlogger.log(\n\t\t\t\t'\\nGenerate a token at: https://hpanel.hostinger.com/profile/api\\n',\n\t\t\t);\n\t\t\ttoken = await prompt('API Token: ', true);\n\t\t}\n\n\t\tif (!token) {\n\t\t\tlogger.error('Token is required');\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Stored without validating, and that is worth saying rather than hiding.\n\t\t// Hostinger's DNS API is zone-scoped — every endpoint takes a domain — so\n\t\t// there is nothing to call that means \"is this token good\" without\n\t\t// already knowing a domain this account owns. A check against a guessed\n\t\t// one would fail for two different reasons and report one.\n\t\tawait storeHostingerToken(token);\n\n\t\tlogger.log('\\n✓ Hostinger token stored.');\n\t\tlogger.log(` Credentials stored in: ${getCredentialsPath()}`);\n\t\tlogger.log(\n\t\t\t' Not validated here: every DNS endpoint is scoped to a domain, so the\\n' +\n\t\t\t\t' first deploy that touches DNS is where a bad token reports itself.',\n\t\t);\n\t\tlogger.log(\n\t\t\t'\\nYou can now use it as a DNS provider without setting HOSTINGER_API_TOKEN.',\n\t\t);\n\t}\n}\n\n/**\n * Logout from a service\n */\nexport async function logoutCommand(options: LogoutOptions): Promise<void> {\n\tconst { provider = 'dokploy' } = options;\n\n\tif (provider === 'all') {\n\t\tconst dokployRemoved = await removeDokployCredentials();\n\n\t\tif (dokployRemoved) {\n\t\t\tlogger.log('\\n✓ Logged out from all services');\n\t\t} else {\n\t\t\tlogger.log('\\nNo stored credentials found');\n\t\t}\n\t\treturn;\n\t}\n\n\tif (provider === 'dokploy') {\n\t\tconst removed = await removeDokployCredentials();\n\n\t\tif (removed) {\n\t\t\tlogger.log('\\n✓ Logged out from Dokploy');\n\t\t} else {\n\t\t\tlogger.log('\\nNo Dokploy credentials found');\n\t\t}\n\t}\n}\n\n/**\n * Show current login status\n */\nexport async function whoamiCommand(): Promise<void> {\n\tlogger.log('\\n📋 Current credentials:\\n');\n\n\tconst dokploy = await getDokployCredentials();\n\n\tif (dokploy) {\n\t\tlogger.log(' Dokploy:');\n\t\tlogger.log(` Endpoint: ${dokploy.endpoint}`);\n\t\tlogger.log(` Token: ${maskToken(dokploy.token)}`);\n\t} else {\n\t\tlogger.log(' Dokploy: Not logged in');\n\t}\n\n\tlogger.log(`\\n Credentials file: ${getCredentialsPath()}`);\n}\n\n/**\n * Mask a token for display\n */\nexport function maskToken(token: string): string {\n\tif (token.length <= 8) {\n\t\treturn '****';\n\t}\n\treturn `${token.slice(0, 4)}...${token.slice(-4)}`;\n}\n\n// Re-export credentials utilities for use in other modules\nexport {\n\tgetDokployCredentials,\n\tgetDokployEndpoint,\n\tgetDokployRegistryId,\n\tgetDokployToken,\n\tstoreDokployCredentials,\n\tstoreDokployRegistryId,\n} from './credentials';\n","import type {\n\tAWSApiGatewayConfig,\n\tAWSLambdaConfig,\n\tBuildOptions,\n\tGkmConfig,\n\tLegacyProvider,\n\tMainProvider,\n\tProvidersConfig,\n\tServerConfig,\n} from '../types';\n\nexport interface ResolvedProviders {\n\tproviders: LegacyProvider[];\n\tenableOpenApi: boolean;\n}\n\n/**\n * Resolves provider configuration from the new simplified system\n * to the internal legacy format for backward compatibility\n */\nexport function resolveProviders(\n\tconfig: GkmConfig,\n\toptions: BuildOptions,\n): ResolvedProviders {\n\tconst providers: LegacyProvider[] = [];\n\tlet enableOpenApi = options.enableOpenApi || false;\n\n\t// Handle legacy providers option (deprecated)\n\tif (options.providers) {\n\t\treturn {\n\t\t\tproviders: options.providers,\n\t\t\tenableOpenApi,\n\t\t};\n\t}\n\n\t// Handle new provider option\n\tif (options.provider) {\n\t\tconst resolvedProviders = resolveMainProvider(\n\t\t\toptions.provider,\n\t\t\tconfig.providers,\n\t\t);\n\t\tproviders.push(...resolvedProviders.providers);\n\t\tenableOpenApi = resolvedProviders.enableOpenApi || enableOpenApi;\n\t}\n\t// Default: build all configured providers\n\telse if (config.providers) {\n\t\tconst resolvedProviders = resolveAllConfiguredProviders(config.providers);\n\t\tproviders.push(...resolvedProviders.providers);\n\t\tenableOpenApi = resolvedProviders.enableOpenApi || enableOpenApi;\n\t}\n\t// Fallback: use default AWS configuration\n\telse {\n\t\tproviders.push('aws-apigatewayv2', 'aws-lambda');\n\t}\n\n\treturn {\n\t\tproviders: [...new Set(providers)], // Remove duplicates\n\t\tenableOpenApi,\n\t};\n}\n\nfunction resolveMainProvider(\n\tmainProvider: MainProvider,\n\tprovidersConfig?: ProvidersConfig,\n): ResolvedProviders {\n\tconst providers: LegacyProvider[] = [];\n\tlet enableOpenApi = false;\n\n\tif (mainProvider === 'aws') {\n\t\tconst awsConfig = providersConfig?.aws;\n\n\t\t// Resolve API Gateway providers\n\t\tif (awsConfig?.apiGateway) {\n\t\t\tif (isEnabled(awsConfig.apiGateway.v1)) {\n\t\t\t\tproviders.push('aws-apigatewayv1');\n\t\t\t}\n\t\t\tif (isEnabled(awsConfig.apiGateway.v2)) {\n\t\t\t\tproviders.push('aws-apigatewayv2');\n\t\t\t}\n\t\t} else {\n\t\t\t// Default: enable v2 if no specific config\n\t\t\tproviders.push('aws-apigatewayv2');\n\t\t}\n\n\t\t// Resolve Lambda providers\n\t\tif (awsConfig?.lambda) {\n\t\t\tif (\n\t\t\t\tisEnabled(awsConfig.lambda.functions) ||\n\t\t\t\tisEnabled(awsConfig.lambda.crons)\n\t\t\t) {\n\t\t\t\tproviders.push('aws-lambda');\n\t\t\t}\n\t\t} else {\n\t\t\t// Default: enable lambda if no specific config\n\t\t\tproviders.push('aws-lambda');\n\t\t}\n\t} else if (mainProvider === 'server') {\n\t\tproviders.push('server');\n\t\tconst serverConfig = providersConfig?.server;\n\n\t\tif (typeof serverConfig === 'object' && serverConfig?.enableOpenApi) {\n\t\t\tenableOpenApi = true;\n\t\t}\n\t}\n\n\treturn { providers, enableOpenApi };\n}\n\nfunction resolveAllConfiguredProviders(\n\tprovidersConfig: ProvidersConfig,\n): ResolvedProviders {\n\tconst providers: LegacyProvider[] = [];\n\tlet enableOpenApi = false;\n\n\t// AWS providers\n\tif (providersConfig.aws) {\n\t\tconst awsProviders = resolveMainProvider('aws', providersConfig);\n\t\tproviders.push(...awsProviders.providers);\n\t}\n\n\t// Server provider\n\tif (providersConfig.server && isEnabled(providersConfig.server)) {\n\t\tproviders.push('server');\n\t\tif (\n\t\t\ttypeof providersConfig.server === 'object' &&\n\t\t\tprovidersConfig.server.enableOpenApi\n\t\t) {\n\t\t\tenableOpenApi = true;\n\t\t}\n\t}\n\n\treturn { providers, enableOpenApi };\n}\n\nfunction isEnabled(\n\tconfig:\n\t\t| boolean\n\t\t| AWSApiGatewayConfig\n\t\t| AWSLambdaConfig\n\t\t| ServerConfig\n\t\t| undefined,\n): boolean {\n\tif (config === undefined) return false;\n\tif (typeof config === 'boolean') return config;\n\treturn config.enabled !== false; // Default to true if enabled is not explicitly false\n}\n\n/**\n * Gets configuration for a specific AWS service\n */\nexport function getAWSServiceConfig<\n\tT extends AWSApiGatewayConfig | AWSLambdaConfig,\n>(\n\tconfig: GkmConfig,\n\tservice: 'apiGateway' | 'lambda',\n\tsubService?: 'v1' | 'v2' | 'functions' | 'crons',\n): T | undefined {\n\tconst awsConfig = config.providers?.aws;\n\tif (!awsConfig) return undefined;\n\n\tif (service === 'apiGateway' && awsConfig.apiGateway) {\n\t\tconst apiConfig = subService\n\t\t\t? awsConfig.apiGateway[subService as 'v1' | 'v2']\n\t\t\t: undefined;\n\t\treturn typeof apiConfig === 'object' ? (apiConfig as T) : undefined;\n\t}\n\n\tif (service === 'lambda' && awsConfig.lambda) {\n\t\tconst lambdaConfig = subService\n\t\t\t? awsConfig.lambda[subService as 'functions' | 'crons']\n\t\t\t: undefined;\n\t\treturn typeof lambdaConfig === 'object' ? (lambdaConfig as T) : undefined;\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Gets server configuration\n */\nexport function getServerConfig(config: GkmConfig): ServerConfig | undefined {\n\tconst serverConfig = config.providers?.server;\n\treturn typeof serverConfig === 'object' ? serverConfig : undefined;\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Cron } from '@geekmidas/constructs/crons';\nimport type { BuildContext } from '../build/types';\nimport type { CronInfo } from '../types';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\n\nexport class CronGenerator extends ConstructGenerator<\n\tCron<any, any, any, any, any, any, any, any>,\n\tCronInfo[]\n> {\n\tasync build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<\n\t\t\tCron<any, any, any, any, any, any, any, any>\n\t\t>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<CronInfo[]> {\n\t\tconst provider = options?.provider || 'aws-lambda';\n\t\tconst logger = console;\n\t\tconst cronInfos: CronInfo[] = [];\n\n\t\tif (constructs.length === 0 || provider !== 'aws-lambda') {\n\t\t\treturn cronInfos;\n\t\t}\n\n\t\t// Create crons subdirectory\n\t\tconst cronsDir = join(outputDir, 'crons');\n\t\tawait mkdir(cronsDir, { recursive: true });\n\n\t\t// Generate cron handlers\n\t\tfor (const { key, construct, path } of constructs) {\n\t\t\tconst handlerFile = await this.generateCronHandler(\n\t\t\t\tcronsDir,\n\t\t\t\tpath.relative,\n\t\t\t\tkey,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tcronInfos.push({\n\t\t\t\tname: key,\n\t\t\t\thandler: relative(process.cwd(), handlerFile).replace(\n\t\t\t\t\t/\\.ts$/,\n\t\t\t\t\t'.handler',\n\t\t\t\t),\n\t\t\t\tschedule: construct.schedule || 'rate(1 hour)',\n\t\t\t\ttimeout: construct.timeout,\n\t\t\t\tmemorySize: construct.memorySize,\n\t\t\t\tenvironment: await construct.getEnvironment({\n\t\t\t\t\tmarkOptional: context.markOptional,\n\t\t\t\t}),\n\t\t\t\tdependencies: construct.constructs,\n\t\t\t});\n\n\t\t\tlogger.log(`Generated cron handler: ${key}`);\n\t\t}\n\n\t\treturn cronInfos;\n\t}\n\n\tisConstruct(\n\t\tvalue: any,\n\t): value is Cron<any, any, any, any, any, any, any, any> {\n\t\treturn Cron.isCron(value);\n\t}\n\n\tprivate async generateCronHandler(\n\t\toutputDir: string,\n\t\tsourceFile: string,\n\t\texportName: string,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst handlerFileName = `${exportName}.ts`;\n\t\tconst handlerPath = join(outputDir, handlerFileName);\n\n\t\tconst relativePath = relative(dirname(handlerPath), sourceFile);\n\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\tconst relativeEnvParserPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.envParserPath,\n\t\t);\n\t\tconst relativeLoggerPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.loggerPath,\n\t\t);\n\n\t\tconst content = `import { AWSScheduledFunction } from '@geekmidas/constructs/crons';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\nimport ${context.loggerImportPattern} from '${relativeLoggerPath}';\n\nconst adapter = new AWSScheduledFunction(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n\t\tawait writeFile(handlerPath, content);\n\t\treturn handlerPath;\n\t}\n}\n","/**\n * Driver registration for generated entry points.\n *\n * A construct declares that it needs object storage and is handed one URL; the\n * scheme in that URL picks the driver that builds the client. Which drivers\n * exist is therefore the *entry point's* decision, not the construct's and not\n * the application's — an app that never talks to S3 should never resolve the AWS\n * SDK, and application code that registers a driver has named a provider in the\n * one layer this design keeps provider-free.\n *\n * So the generated entries register: `gkm dev`'s server for local development,\n * and each Lambda handler for its own target. Registration is an explicit call\n * rather than an import side effect, because a side-effecting module is exactly\n * what a bundler is entitled to drop.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type CacheBackend, DEFAULT_CACHE } from '../types.js';\n\n/** The `s3://` driver, which serves MinIO locally and S3 deployed. */\nconst S3 = {\n\timports: `import { registerStorageDriver } from '@geekmidas/storage';\\nimport { s3Driver } from '@geekmidas/storage/aws';`,\n\tsetup: 'registerStorageDriver(s3Driver);',\n};\n\n/** What a generated entry needs in order to register the drivers it uses. */\nexport interface RuntimeDrivers {\n\t/** Import lines, or `''` when there is nothing to register. */\n\timports: string;\n\t/** The registration calls, or `''`. */\n\tsetup: string;\n}\n\n/** @deprecated The same shape, from when only storage had drivers. */\nexport type StorageDrivers = RuntimeDrivers;\n\nconst NONE: RuntimeDrivers = { imports: '', setup: '' };\n\n/**\n * The cache driver for a backend — exactly one, never all three.\n *\n * Keyed off the *backend* rather than off a dependency, because unlike storage\n * the backend already says which driver is right: a project caching in Postgres\n * resolves `pg` and never `ioredis`, and one caching in Upstash resolves\n * neither. That is the whole reason the drivers live behind separate subpaths.\n *\n * Both HTTP schemes are registered for Upstash, because the local proxy speaks\n * `http://` and Upstash speaks `https://`, and an entry that registered one\n * would work in exactly one of the two places.\n */\nconst CACHE_DRIVERS: Record<CacheBackend, RuntimeDrivers> = {\n\tupstash: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { upstashCacheDriver, upstashInsecureCacheDriver } from '@geekmidas/cache/upstash';`,\n\t\tsetup:\n\t\t\t'registerCacheDriver(upstashCacheDriver);\\nregisterCacheDriver(upstashInsecureCacheDriver);',\n\t},\n\telasticache: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { redisCacheDriver, redissCacheDriver } from '@geekmidas/cache/redis';`,\n\t\tsetup:\n\t\t\t'registerCacheDriver(redisCacheDriver);\\nregisterCacheDriver(redissCacheDriver);',\n\t},\n\tdb: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { postgresCacheDriver } from '@geekmidas/cache/postgres';`,\n\t\tsetup: 'registerCacheDriver(postgresCacheDriver);',\n\t},\n};\n\n/**\n * Everything a generated entry should register: object storage, and the cache.\n *\n * Merged here rather than threaded separately, because a generated file has one\n * import block and one setup block whatever fills them.\n */\nexport function driversFor(options: {\n\tappRoot: string;\n\t/**\n\t * The cache backends actually in play — normally one, and more than one only\n\t * where an app declares two caches that live in different places.\n\t *\n\t * Absent or empty means nothing declared a cache, so no cache driver is\n\t * registered at all.\n\t */\n\tcache?: CacheBackend | readonly CacheBackend[] | false;\n}): RuntimeDrivers {\n\tconst backends =\n\t\toptions.cache === false || options.cache === undefined\n\t\t\t? []\n\t\t\t: typeof options.cache === 'string'\n\t\t\t\t? [options.cache]\n\t\t\t\t: options.cache;\n\n\tconst parts = [\n\t\tstorageDriversFor(options.appRoot),\n\t\t...[...new Set(backends)].map(\n\t\t\t(backend) => CACHE_DRIVERS[backend] ?? CACHE_DRIVERS[DEFAULT_CACHE.aws],\n\t\t),\n\t].filter((part) => part.imports || part.setup);\n\n\tif (parts.length === 0) return NONE;\n\n\treturn {\n\t\timports: parts.map((part) => part.imports).join('\\n'),\n\t\tsetup: parts.map((part) => part.setup).join('\\n'),\n\t};\n}\n\n/**\n * Which cache backends an app's entry actually has to speak.\n *\n * The config answers this for a cache that named nowhere, and the *declaration*\n * answers it for one that named a database — `orders.cache('Sessions')` is a\n * statement about the application, so a deployment cannot move that cache and\n * the entry cannot register a driver for somewhere else.\n *\n * Reading only the config is how an entry ends up registering the Upstash\n * driver for a URL the target composed as `postgres://`, which fails at the\n * first request with `UnregisteredCacheScheme` and a stack that points at the\n * cache rather than at the config that disagreed.\n */\nexport function cacheBackendsIn(\n\tmanifest: Record<string, { kind: string; of?: string }>,\n\tconfigured: CacheBackend,\n): CacheBackend[] {\n\tconst caches = Object.values(manifest).filter((d) => d.kind === 'cache');\n\n\treturn [...new Set(caches.map((c) => (c.of ? 'db' : configured)))];\n}\n\n/**\n * The drivers an app's entry points should register.\n *\n * Keyed off the dependency rather than off a declared bucket, because the entry\n * is generated before discovery has run on the watcher's rebuild path — and an\n * app that installed `@geekmidas/storage` has already paid for the SDK it would\n * otherwise resolve lazily.\n */\nexport function storageDriversFor(appRoot: string): RuntimeDrivers {\n\treturn dependsOnStorage(appRoot) ? S3 : NONE;\n}\n\nfunction dependsOnStorage(appRoot: string): boolean {\n\tconst manifest = join(appRoot, 'package.json');\n\tif (!existsSync(manifest)) return false;\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(manifest, 'utf8')) as {\n\t\t\tdependencies?: Record<string, string>;\n\t\t\tdevDependencies?: Record<string, string>;\n\t\t};\n\n\t\treturn Boolean(\n\t\t\tpkg.dependencies?.['@geekmidas/storage'] ??\n\t\t\t\tpkg.devDependencies?.['@geekmidas/storage'],\n\t\t);\n\t} catch {\n\t\t// An unreadable package.json is the build's problem, not this function's.\n\t\treturn false;\n\t}\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Function } from '@geekmidas/constructs/functions';\nimport type { BuildContext } from '../build/types';\nimport type { FunctionInfo } from '../types';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\n\nexport class FunctionGenerator extends ConstructGenerator<\n\tFunction<any, any, any, any, any, any, any, any, any, any, any, any>,\n\tFunctionInfo[]\n> {\n\tisConstruct(\n\t\tvalue: any,\n\t): value is Function<\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany\n\t> {\n\t\treturn Function.isFunction(value);\n\t}\n\n\tasync build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<\n\t\t\tFunction<any, any, any, any, any, any, any, any, any, any, any, any>\n\t\t>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<FunctionInfo[]> {\n\t\tconst provider = options?.provider || 'aws-lambda';\n\t\tconst logger = console;\n\t\tconst functionInfos: FunctionInfo[] = [];\n\n\t\tif (constructs.length === 0 || provider !== 'aws-lambda') {\n\t\t\treturn functionInfos;\n\t\t}\n\n\t\t// Create functions subdirectory\n\t\tconst functionsDir = join(outputDir, 'functions');\n\t\tawait mkdir(functionsDir, { recursive: true });\n\n\t\t// Generate function handlers\n\t\tfor (const { key, construct, path } of constructs) {\n\t\t\tconst handlerFile = await this.generateFunctionHandler(\n\t\t\t\tfunctionsDir,\n\t\t\t\tpath.relative,\n\t\t\t\tkey,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tfunctionInfos.push({\n\t\t\t\tname: key,\n\t\t\t\thandler: relative(process.cwd(), handlerFile).replace(\n\t\t\t\t\t/\\.ts$/,\n\t\t\t\t\t'.handler',\n\t\t\t\t),\n\t\t\t\ttimeout: construct.timeout,\n\t\t\t\tmemorySize: construct.memorySize,\n\t\t\t\tenvironment: await construct.getEnvironment({\n\t\t\t\t\tmarkOptional: context.markOptional,\n\t\t\t\t}),\n\t\t\t\tdependencies: construct.constructs,\n\t\t\t});\n\n\t\t\tlogger.log(`Generated function handler: ${key}`);\n\t\t}\n\n\t\treturn functionInfos;\n\t}\n\n\tprivate async generateFunctionHandler(\n\t\toutputDir: string,\n\t\tsourceFile: string,\n\t\texportName: string,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst handlerFileName = `${exportName}.ts`;\n\t\tconst handlerPath = join(outputDir, handlerFileName);\n\n\t\tconst relativePath = relative(dirname(handlerPath), sourceFile);\n\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\tconst relativeEnvParserPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.envParserPath,\n\t\t);\n\t\tconst relativeLoggerPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.loggerPath,\n\t\t);\n\n\t\tconst content = `import { AWSLambdaFunction } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\nimport ${context.loggerImportPattern} from '${relativeLoggerPath}';\n\nconst adapter = new AWSLambdaFunction(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n\t\tawait writeFile(handlerPath, content);\n\t\treturn handlerPath;\n\t}\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Queue } from '@geekmidas/constructs/queue';\nimport type { BuildContext } from '../build/types';\nimport type { QueueInfo } from '../types';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\n\n/**\n * Generates the runtime for `q` queue workers.\n *\n * - **server** (`gkm dev`): a single `queues.ts` exposing `setupQueues()` that\n * runs an in-process pg-boss poller alongside the Hono server — no SQS/Lambda.\n * Each queue subscribes by its **name** on the shared event connection.\n * - **aws-lambda**: one handler file per queue wrapping `AWSLambdaQueue`, backed\n * by an SQS event-source mapping.\n */\nexport class QueueGenerator extends ConstructGenerator<\n\tQueue<any, any, any, any>,\n\tQueueInfo[]\n> {\n\tisConstruct(value: any): value is Queue<any, any, any, any> {\n\t\treturn Queue.isQueue(value);\n\t}\n\n\tasync build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<Queue<any, any, any, any>>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<QueueInfo[]> {\n\t\tconst provider = options?.provider || 'aws-lambda';\n\t\tconst logger = console;\n\t\tconst queueInfos: QueueInfo[] = [];\n\n\t\tif (provider === 'server') {\n\t\t\t// Generate queues.ts for in-process polling (even if empty, so the\n\t\t\t// server entry can always import setupQueues).\n\t\t\tawait this.generateServerQueuesFile(outputDir, constructs);\n\t\t\tlogger.log(\n\t\t\t\t`Generated server queues file with ${constructs.length} queues (polling mode)`,\n\t\t\t);\n\t\t\treturn queueInfos;\n\t\t}\n\n\t\tif (constructs.length === 0 || provider !== 'aws-lambda') {\n\t\t\treturn queueInfos;\n\t\t}\n\n\t\tconst queuesDir = join(outputDir, 'queues');\n\t\tawait mkdir(queuesDir, { recursive: true });\n\n\t\tfor (const { key, construct, path } of constructs) {\n\t\t\tconst handlerFile = await this.generateQueueHandler(\n\t\t\t\tqueuesDir,\n\t\t\t\tpath.relative,\n\t\t\t\tkey,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tqueueInfos.push({\n\t\t\t\tname: construct.name,\n\t\t\t\thandler: relative(process.cwd(), handlerFile).replace(\n\t\t\t\t\t/\\.ts$/,\n\t\t\t\t\t'.handler',\n\t\t\t\t),\n\t\t\t\tbatchSize: construct.batchSize,\n\t\t\t\tfifo: construct.fifo,\n\t\t\t\ttimeout: construct.timeout,\n\t\t\t\tenvironment: await construct.getEnvironment({\n\t\t\t\t\tmarkOptional: context.markOptional,\n\t\t\t\t}),\n\t\t\t\tdependencies: construct.constructs,\n\t\t\t});\n\n\t\t\tlogger.log(`Generated queue handler: ${key}`);\n\t\t}\n\n\t\treturn queueInfos;\n\t}\n\n\tprivate async generateQueueHandler(\n\t\toutputDir: string,\n\t\tsourceFile: string,\n\t\texportName: string,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst handlerPath = join(outputDir, `${exportName}.ts`);\n\t\tconst importPath = relative(dirname(handlerPath), sourceFile).replace(\n\t\t\t/\\.ts$/,\n\t\t\t'.js',\n\t\t);\n\t\tconst relativeEnvParserPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.envParserPath,\n\t\t);\n\n\t\tconst content = `import { AWSLambdaQueue } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\n\nconst adapter = new AWSLambdaQueue(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n\t\tawait writeFile(handlerPath, content);\n\t\treturn handlerPath;\n\t}\n\n\tprivate async generateServerQueuesFile(\n\t\toutputDir: string,\n\t\tqueues: GeneratedConstruct<Queue<any, any, any, any>>[],\n\t): Promise<string> {\n\t\tawait mkdir(outputDir, { recursive: true });\n\n\t\tconst queuesPath = join(outputDir, 'queues.ts');\n\n\t\t// Group imports by file\n\t\tconst importsByFile = new Map<string, string[]>();\n\t\tfor (const { path, key } of queues) {\n\t\t\tconst importPath = relative(dirname(queuesPath), path.relative).replace(\n\t\t\t\t/\\.ts$/,\n\t\t\t\t'.js',\n\t\t\t);\n\t\t\tif (!importsByFile.has(importPath)) {\n\t\t\t\timportsByFile.set(importPath, []);\n\t\t\t}\n\t\t\timportsByFile.get(importPath)?.push(key);\n\t\t}\n\n\t\tconst imports = Array.from(importsByFile.entries())\n\t\t\t.map(\n\t\t\t\t([importPath, exports]) =>\n\t\t\t\t\t`import { ${exports.join(', ')} } from '${importPath}';`,\n\t\t\t)\n\t\t\t.join('\\n');\n\n\t\tconst allExportNames = queues.map(({ key }) => key);\n\n\t\tconst content = `/**\n * Generated queue workers setup\n *\n * ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY\n * This runs an in-process pg-boss poller alongside the HTTP server. Each queue\n * subscribes by its name on the shared event connection\n * (EVENT_SUBSCRIBER_CONNECTION_STRING), so producers using\n * <NAME>_PUBLISHER_CONNECTION_STRING (pgboss:// locally) reach it.\n *\n * For production, queues run as AWS Lambda with SQS event-source mappings.\n */\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { EventConnectionFactory, Subscriber } from '@geekmidas/events';\nimport type { EventConnection, EventSubscriber } from '@geekmidas/events';\nimport { ServiceDiscovery } from '@geekmidas/services';\n${imports}\n\nconst queues = [\n ${allExportNames.join(',\\n ')}\n];\n\nconst activeSubscribers: EventSubscriber<any>[] = [];\n\nexport async function setupQueues(\n envParser: EnvironmentParser<any>,\n logger: Logger,\n): Promise<void> {\n if (queues.length === 0) {\n return;\n }\n\n logger.info('Setting up queue workers in polling mode (local development)');\n\n const config = envParser.create((get) => ({\n connectionString: get('EVENT_SUBSCRIBER_CONNECTION_STRING').string(),\n })).parse();\n\n const serviceDiscovery = ServiceDiscovery.getInstance(envParser);\n\n let connection: EventConnection;\n try {\n connection = await EventConnectionFactory.fromConnectionString(config.connectionString);\n const connectionType = new URL(config.connectionString).protocol.replace(':', '');\n logger.info({ connectionType }, 'Created shared event connection for queues');\n } catch (error) {\n logger.error({ error }, 'Failed to create event connection for queues');\n return;\n }\n\n for (const queue of queues) {\n try {\n const eventSubscriber = await Subscriber.fromConnection(connection);\n\n const services = queue.services.length > 0\n ? await serviceDiscovery.register(queue.services)\n : {};\n\n // A queue subscribes to a single \"type\" — its own name.\n await eventSubscriber.subscribe([queue.name], async (message) => {\n try {\n const validation = await queue.messageSchema['~standard'].validate(message.payload);\n if (validation.issues) {\n logger.error({ issues: validation.issues, queue: queue.name }, 'Queue message failed validation');\n return;\n }\n\n await queue.handler({\n messages: [validation.value],\n services: services,\n logger: queue.logger,\n });\n\n logger.debug({ queue: queue.name }, 'Successfully processed queue message');\n } catch (error) {\n logger.error({ error, queue: queue.name }, 'Failed to process queue message');\n // Message will become visible again for retry.\n }\n });\n\n activeSubscribers.push(eventSubscriber);\n logger.info({ queue: queue.name }, 'Queue worker started polling');\n } catch (error) {\n logger.error({ error, queue: queue.name }, 'Failed to setup queue worker');\n }\n }\n\n const shutdown = () => {\n logger.info('Stopping all queue workers');\n for (const _ of activeSubscribers) {\n connection.stop();\n }\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n}\n`;\n\n\t\tawait writeFile(queuesPath, content);\n\t\treturn queuesPath;\n\t}\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Subscriber } from '@geekmidas/constructs/subscribers';\nimport type { BuildContext } from '../build/types';\nimport type { SubscriberInfo } from '../types';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\n\nexport class SubscriberGenerator extends ConstructGenerator<\n\tSubscriber<any, any, any, any, any, any>,\n\tSubscriberInfo[]\n> {\n\tisConstruct(value: any): value is Subscriber<any, any, any, any, any, any> {\n\t\treturn Subscriber.isSubscriber(value);\n\t}\n\n\tasync build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<SubscriberInfo[]> {\n\t\tconst provider = options?.provider || 'aws-lambda';\n\t\tconst logger = console;\n\t\tconst subscriberInfos: SubscriberInfo[] = [];\n\n\t\tif (provider === 'server') {\n\t\t\t// Generate subscribers.ts for server-based polling (even if empty)\n\t\t\tawait this.generateServerSubscribersFile(outputDir, constructs);\n\n\t\t\tlogger.log(\n\t\t\t\t`Generated server subscribers file with ${constructs.length} subscribers (polling mode)`,\n\t\t\t);\n\n\t\t\t// Return empty array as server subscribers don't have individual handlers\n\t\t\treturn subscriberInfos;\n\t\t}\n\n\t\tif (constructs.length === 0) {\n\t\t\treturn subscriberInfos;\n\t\t}\n\n\t\tif (provider !== 'aws-lambda') {\n\t\t\treturn subscriberInfos;\n\t\t}\n\n\t\t// Create subscribers subdirectory\n\t\tconst subscribersDir = join(outputDir, 'subscribers');\n\t\tawait mkdir(subscribersDir, { recursive: true });\n\n\t\t// Generate subscriber handlers\n\t\tfor (const { key, construct, path } of constructs) {\n\t\t\tconst handlerFile = await this.generateSubscriberHandler(\n\t\t\t\tsubscribersDir,\n\t\t\t\tpath.relative,\n\t\t\t\tkey,\n\t\t\t\tconstruct,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tsubscriberInfos.push({\n\t\t\t\tname: key,\n\t\t\t\thandler: relative(process.cwd(), handlerFile).replace(\n\t\t\t\t\t/\\.ts$/,\n\t\t\t\t\t'.handler',\n\t\t\t\t),\n\t\t\t\tsubscribedEvents: construct.subscribedEvents || [],\n\t\t\t\t// A subscriber bound to a topic (via `s.topic(topic)`) records the\n\t\t\t\t// binding so infra wires the SNS subscription.\n\t\t\t\t...(construct.topicName\n\t\t\t\t\t? { transport: 'topic' as const, topic: construct.topicName }\n\t\t\t\t\t: {}),\n\t\t\t\ttimeout: construct.timeout,\n\t\t\t\tmemorySize: construct.memorySize,\n\t\t\t\tenvironment: await construct.getEnvironment({\n\t\t\t\t\tmarkOptional: context.markOptional,\n\t\t\t\t}),\n\t\t\t\tdependencies: construct.constructs,\n\t\t\t});\n\n\t\t\tlogger.log(`Generated subscriber handler: ${key}`);\n\t\t}\n\n\t\treturn subscriberInfos;\n\t}\n\n\tprivate async generateSubscriberHandler(\n\t\toutputDir: string,\n\t\tsourceFile: string,\n\t\texportName: string,\n\t\t_subscriber: Subscriber<any, any, any, any, any, any>,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst handlerFileName = `${exportName}.ts`;\n\t\tconst handlerPath = join(outputDir, handlerFileName);\n\n\t\tconst relativePath = relative(dirname(handlerPath), sourceFile);\n\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\tconst relativeEnvParserPath = relative(\n\t\t\tdirname(handlerPath),\n\t\t\tcontext.envParserPath,\n\t\t);\n\n\t\tconst content = `import { AWSLambdaSubscriber } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\n\nconst adapter = new AWSLambdaSubscriber(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n\t\tawait writeFile(handlerPath, content);\n\t\treturn handlerPath;\n\t}\n\n\tprivate async generateServerSubscribersFile(\n\t\toutputDir: string,\n\t\tsubscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n\t): Promise<string> {\n\t\t// Ensure output directory exists\n\t\tawait mkdir(outputDir, { recursive: true });\n\n\t\tconst subscribersFileName = 'subscribers.ts';\n\t\tconst subscribersPath = join(outputDir, subscribersFileName);\n\n\t\t// Group imports by file\n\t\tconst importsByFile = new Map<string, string[]>();\n\n\t\tfor (const { path, key } of subscribers) {\n\t\t\tconst relativePath = relative(dirname(subscribersPath), path.relative);\n\t\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\t\tif (!importsByFile.has(importPath)) {\n\t\t\t\timportsByFile.set(importPath, []);\n\t\t\t}\n\t\t\timportsByFile.get(importPath)?.push(key);\n\t\t}\n\n\t\t// Generate import statements\n\t\tconst imports = Array.from(importsByFile.entries())\n\t\t\t.map(\n\t\t\t\t([importPath, exports]) =>\n\t\t\t\t\t`import { ${exports.join(', ')} } from '${importPath}';`,\n\t\t\t)\n\t\t\t.join('\\n');\n\n\t\tconst allExportNames = subscribers.map(({ key }) => key);\n\n\t\tconst content = `/**\n * Generated subscribers setup\n *\n * ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY\n * This uses event polling which is not suitable for production.\n *\n * For production, use AWS Lambda with SQS/SNS event source mappings.\n * Lambda automatically:\n * - Scales based on queue depth\n * - Handles batch processing and retries\n * - Manages dead letter queues\n * - Provides better cost optimization\n *\n * This polling implementation is useful for:\n * - Local development and testing\n * - Understanding event flow without Lambda deployment\n *\n * Supported connection strings:\n * - sqs://region/account-id/queue-name (SQS queue)\n * - sns://region/account-id/topic-name (SNS topic)\n * - rabbitmq://host:port/queue-name (RabbitMQ)\n * - pgboss://user:pass@host:port/database (pg-boss / PostgreSQL)\n * - basic://in-memory (In-memory for testing)\n */\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { EventConnectionFactory, Subscriber } from '@geekmidas/events';\nimport type { EventConnection, EventSubscriber } from '@geekmidas/events';\nimport { ServiceDiscovery } from '@geekmidas/services';\n${imports}\n\nconst subscribers = [\n ${allExportNames.join(',\\n ')}\n];\n\nconst activeSubscribers: EventSubscriber<any>[] = [];\n\nexport async function setupSubscribers(\n envParser: EnvironmentParser<any>,\n logger: Logger,\n): Promise<void> {\n if (subscribers.length === 0) {\n return;\n }\n\n logger.info('Setting up subscribers in polling mode (local development)');\n\n const config = envParser.create((get) => ({\n connectionString: get('EVENT_SUBSCRIBER_CONNECTION_STRING').string(),\n })).parse();\n\n const serviceDiscovery = ServiceDiscovery.getInstance(envParser);\n\n // Create connection once, outside the loop (more efficient)\n // EventConnectionFactory automatically determines the right connection type\n let connection: EventConnection;\n try {\n connection = await EventConnectionFactory.fromConnectionString(config.connectionString);\n\n const connectionType = new URL(config.connectionString).protocol.replace(':', '');\n logger.info({ connectionType }, 'Created shared event connection');\n } catch (error) {\n logger.error({ error }, 'Failed to create event connection');\n return;\n }\n\n for (const subscriber of subscribers) {\n try {\n // Create subscriber from shared connection\n const eventSubscriber = await Subscriber.fromConnection(connection);\n\n // Register services\n const services = subscriber.services.length > 0\n ? await serviceDiscovery.register(subscriber.services)\n : {};\n\n // Subscribe to events\n const subscribedEvents = subscriber.subscribedEvents || [];\n\n if (subscribedEvents.length === 0) {\n logger.warn({ subscriber: subscriber.constructor.name }, 'Subscriber has no subscribed events, skipping');\n continue;\n }\n\n await eventSubscriber.subscribe(subscribedEvents, async (event) => {\n try {\n // Process single event (batch of 1)\n await subscriber.handler({\n events: [event],\n services: services as any,\n logger: subscriber.logger,\n });\n\n logger.debug({ eventType: event.type }, 'Successfully processed event');\n } catch (error) {\n logger.error({ error, event }, 'Failed to process event');\n // Event will become visible again for retry\n }\n });\n\n activeSubscribers.push(eventSubscriber);\n\n logger.info(\n {\n events: subscribedEvents,\n },\n 'Subscriber started polling'\n );\n } catch (error) {\n logger.error({ error, subscriber: subscriber.constructor.name }, 'Failed to setup subscriber');\n }\n }\n\n // Setup graceful shutdown\n const shutdown = () => {\n logger.info('Stopping all subscribers');\n for (const eventSubscriber of activeSubscribers) {\n connection.stop();\n }\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n}\n`;\n\n\t\tawait writeFile(subscribersPath, content);\n\t\treturn subscribersPath;\n\t}\n}\n","import { Topic } from '@geekmidas/constructs/topic';\nimport type { BuildContext } from '../build/types';\nimport type { TopicInfo } from '../types';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\n\n/**\n * Discovers `t` topics into `manifest.topics`. A topic is a *resource*, not a\n * function — it has no handler to generate. Producers reach it via its derived\n * publisher (`topic.publisher`, whose connection string is sniffed onto the\n * producing construct) and subscribers bind via `s.topic(topic)`; this generator\n * only records the topic + its event contract so infra can provision the SNS\n * topic. Identical output for every provider (locally pg-boss routes by event\n * name, so there's nothing to run).\n */\nexport class TopicGenerator extends ConstructGenerator<\n\tTopic<any, any>,\n\tTopicInfo[]\n> {\n\tisConstruct(value: any): value is Topic<any, any> {\n\t\treturn Topic.isTopic(value);\n\t}\n\n\tasync build(\n\t\t_context: BuildContext,\n\t\tconstructs: GeneratedConstruct<Topic<any, any>>[],\n\t\t_outputDir: string,\n\t\t_options?: GeneratorOptions,\n\t): Promise<TopicInfo[]> {\n\t\tconst logger = console;\n\t\tconst topicInfos = constructs.map(({ construct }) => ({\n\t\t\tname: construct.name,\n\t\t\tevents: construct.eventTypes,\n\t\t}));\n\n\t\tfor (const topic of topicInfos) {\n\t\t\tlogger.log(`Discovered topic: ${topic.name}`);\n\t\t}\n\n\t\treturn topicInfos;\n\t}\n}\n","import { spawn } from 'node:child_process';\nimport { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcreateCredentialsPreload,\n\tloadEnvFiles,\n\tprepareEntryCredentials,\n} from '../credentials';\n\nconst logger = console;\n\n/**\n * Options for the exec command.\n */\nexport interface ExecOptions {\n\t/** Working directory */\n\tcwd?: string;\n}\n\n/**\n * Run a command with secrets injected into Credentials.\n * Uses Node's --import flag to preload a script that populates Credentials\n * before the command loads any modules that depend on them.\n *\n * @example\n * ```bash\n * gkm exec -- npx @better-auth/cli migrate\n * gkm exec -- npx prisma migrate dev\n * ```\n */\nexport async function execCommand(\n\tcommandArgs: string[],\n\toptions: ExecOptions = {},\n): Promise<void> {\n\tconst cwd = options.cwd ?? process.cwd();\n\n\tif (commandArgs.length === 0) {\n\t\tthrow new Error('No command specified. Usage: gkm exec -- <command>');\n\t}\n\n\t// Load .env files\n\tconst defaultEnv = loadEnvFiles('.env');\n\tif (defaultEnv.loaded.length > 0) {\n\t\tlogger.log(`📦 Loaded env: ${defaultEnv.loaded.join(', ')}`);\n\t}\n\n\t// Prepare credentials: loads secrets, resolves Docker ports, rewrites URLs,\n\t// injects dependency URLs. Uses readonly port mode (no probing for new ports).\n\tconst { credentials, secretsJsonPath, appName } =\n\t\tawait prepareEntryCredentials({ cwd, resolveDockerPorts: 'readonly' });\n\n\tif (appName) {\n\t\tlogger.log(`📦 App: ${appName}`);\n\t}\n\n\tconst secretCount = Object.keys(credentials).filter(\n\t\t(k) => k !== 'PORT',\n\t).length;\n\tif (secretCount > 0) {\n\t\tlogger.log(`🔐 Loaded ${secretCount} secret(s)`);\n\t}\n\n\t// Create preload script that injects Credentials\n\t// Written as .mjs (plain ESM) so it doesn't need tsx — this avoids\n\t// breaking frameworks like Next.js whose workers inherit NODE_OPTIONS.\n\tconst preloadDir = join(cwd, '.gkm');\n\tawait mkdir(preloadDir, { recursive: true });\n\tconst preloadPath = join(preloadDir, 'credentials-preload.mjs');\n\tawait createCredentialsPreload(preloadPath, secretsJsonPath);\n\n\t// Build command\n\tconst [cmd, ...args] = commandArgs;\n\n\tif (!cmd) {\n\t\tthrow new Error('No command specified');\n\t}\n\n\tlogger.log(`🚀 Running: ${[cmd, ...args].join(' ')}`);\n\n\t// Build NODE_OPTIONS for the child process.\n\t// Strip any --import flags from the inherited NODE_OPTIONS — the gkm binary\n\t// adds --import=tsx for itself, but the spawned command (e.g. next, prisma)\n\t// doesn't need it and it breaks frameworks that spawn their own workers.\n\tconst existingNodeOptions = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst preloadImport = `--import=${preloadPath}`;\n\n\tconst nodeOptions = [existingNodeOptions, preloadImport]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\t// Spawn the command with secrets in both:\n\t// 1. Environment variables (for tools that read process.env directly)\n\t// 2. Preload script (for tools that use Credentials object)\n\tconst child = spawn(cmd, args, {\n\t\tcwd,\n\t\tstdio: 'inherit',\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t...credentials, // Inject secrets as env vars\n\t\t\tNODE_OPTIONS: nodeOptions,\n\t\t},\n\t});\n\n\t// Wait for the command to complete\n\tconst exitCode = await new Promise<number>((resolve) => {\n\t\tchild.on('close', (code: number | null) => resolve(code ?? 0));\n\t\tchild.on('error', (error: Error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t});\n\n\tif (exitCode !== 0) {\n\t\tprocess.exit(exitCode);\n\t}\n}\n","import { type ChildProcess, execSync, spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport chokidar from 'chokidar';\nimport fg from 'fast-glob';\nimport { resolveProviders } from '../build/providerResolver';\nimport type {\n\tBuildContext,\n\tNormalizedHooksConfig,\n\tNormalizedProductionConfig,\n\tNormalizedStudioConfig,\n\tNormalizedTelescopeConfig,\n} from '../build/types';\nimport {\n\tgetAppNameFromCwd,\n\tloadAppConfig,\n\tloadWorkspaceConfig,\n\tparseModuleConfig,\n} from '../config';\nimport {\n\tcreateEntryWrapper,\n\tfindAvailablePort,\n\tisPortAvailable,\n\tloadEnvFiles,\n\tloadSecretsForApp,\n\tprepareEntryCredentials,\n\tresolveServicePorts,\n\trewriteUrlsWithPorts,\n\tstartWorkspaceServices,\n} from '../credentials';\nimport {\n\tCronGenerator,\n\tcacheBackendsIn,\n\tdriversFor,\n\tEndpointGenerator,\n\tFunctionGenerator,\n\tQueueGenerator,\n\tSubscriberGenerator,\n\tTopicGenerator,\n} from '../generators';\nimport {\n\tgenerateOpenApi,\n\tOPENAPI_OUTPUT_PATH,\n\tresolveOpenApiConfig,\n} from '../openapi';\nimport { discover } from '../reconcile/discover.js';\nimport { reconcileWorkspace, usesConstructs } from '../reconcile/workspace.js';\nimport {\n\treadStageSecrets,\n\tsecretsExist,\n\ttoEmbeddableSecrets,\n} from '../secrets/storage.js';\nimport type {\n\tGkmConfig,\n\tLegacyProvider,\n\tProductionConfig,\n\tRuntime,\n\tServerConfig,\n\tStudioConfig,\n\tTelescopeConfig,\n} from '../types';\nimport { cacheBackendOf } from '../workspace/backends.js';\nimport {\n\ttype FrontendFramework,\n\tgetAppBuildOrder,\n\tgetDependencyEnvVars,\n\ttype MobileFramework,\n\ttype NormalizedWorkspace,\n} from '../workspace/index.js';\n\n// Re-export shared utilities from credentials module so existing imports\n// from '../dev' or '../dev/index' continue to work.\nexport {\n\tbuildDockerComposeEnv,\n\ttype ComposePortMapping,\n\tcreateCredentialsPreload,\n\tcreateEntryWrapper,\n\ttype EntryCredentialsResult,\n\tfindAvailablePort,\n\tfindSecretsRoot,\n\tgetContainerHostPort,\n\tisPortAvailable,\n\tloadEnvFiles,\n\tloadPortState,\n\tloadSecretsForApp,\n\ttype PortState,\n\tparseComposePortMappings,\n\tparseComposeServiceNames,\n\tprepareEntryCredentials,\n\ttype ResolvedServicePorts,\n\treplacePortInUrl,\n\tresolveServicePorts,\n\trewriteUrlsWithPorts,\n\tsavePortState,\n\tstartComposeServices,\n\tstartWorkspaceServices,\n} from '../credentials';\n\n// Re-export execCommand from its own module\nexport { type ExecOptions, execCommand } from '../exec';\n\nconst logger = console;\n\n/**\n * Normalize telescope configuration\n * @internal Exported for testing\n */\nexport function normalizeTelescopeConfig(\n\tconfig: GkmConfig['telescope'],\n): NormalizedTelescopeConfig | undefined {\n\tif (config === false) {\n\t\treturn undefined;\n\t}\n\n\t// Handle string path (e.g., './src/config/telescope')\n\tif (typeof config === 'string') {\n\t\tconst { path: telescopePath, importPattern: telescopeImportPattern } =\n\t\t\tparseModuleConfig(config, 'telescope');\n\n\t\treturn {\n\t\t\tenabled: true,\n\t\t\ttelescopePath,\n\t\t\ttelescopeImportPattern,\n\t\t\tpath: '/__telescope',\n\t\t\tignore: [],\n\t\t\trecordBody: true,\n\t\t\tmaxEntries: 1000,\n\t\t\twebsocket: true,\n\t\t};\n\t}\n\n\t// Default to enabled in development mode\n\tconst isEnabled =\n\t\tconfig === true || config === undefined || config.enabled !== false;\n\n\tif (!isEnabled) {\n\t\treturn undefined;\n\t}\n\n\tconst telescopeConfig: TelescopeConfig =\n\t\ttypeof config === 'object' ? config : {};\n\n\treturn {\n\t\tenabled: true,\n\t\tpath: telescopeConfig.path ?? '/__telescope',\n\t\tignore: telescopeConfig.ignore ?? [],\n\t\trecordBody: telescopeConfig.recordBody ?? true,\n\t\tmaxEntries: telescopeConfig.maxEntries ?? 1000,\n\t\twebsocket: telescopeConfig.websocket ?? true,\n\t};\n}\n\n/**\n * Normalize studio configuration\n * @internal Exported for testing\n */\nexport function normalizeStudioConfig(\n\tconfig: GkmConfig['studio'],\n): NormalizedStudioConfig | undefined {\n\tif (config === false) {\n\t\treturn undefined;\n\t}\n\n\t// Handle string path (e.g., './src/config/studio')\n\tif (typeof config === 'string') {\n\t\tconst { path: studioPath, importPattern: studioImportPattern } =\n\t\t\tparseModuleConfig(config, 'studio');\n\n\t\treturn {\n\t\t\tenabled: true,\n\t\t\tstudioPath,\n\t\t\tstudioImportPattern,\n\t\t\tpath: '/__studio',\n\t\t\tschema: 'public',\n\t\t};\n\t}\n\n\t// Default to enabled in development mode\n\tconst isEnabled =\n\t\tconfig === true || config === undefined || config.enabled !== false;\n\n\tif (!isEnabled) {\n\t\treturn undefined;\n\t}\n\n\tconst studioConfig: StudioConfig = typeof config === 'object' ? config : {};\n\n\treturn {\n\t\tenabled: true,\n\t\tpath: studioConfig.path ?? '/__studio',\n\t\tschema: studioConfig.schema ?? 'public',\n\t};\n}\n\n/**\n * Normalize hooks configuration\n * @internal Exported for testing\n */\nexport function normalizeHooksConfig(\n\tconfig: GkmConfig['hooks'],\n\tcwd: string = process.cwd(),\n): NormalizedHooksConfig | undefined {\n\tif (!config?.server) {\n\t\treturn undefined;\n\t}\n\n\t// Resolve the path (handle .ts extension)\n\tconst serverPath = config.server.endsWith('.ts')\n\t\t? config.server\n\t\t: `${config.server}.ts`;\n\n\tconst resolvedPath = resolve(cwd, serverPath);\n\n\treturn {\n\t\tserverHooksPath: resolvedPath,\n\t};\n}\n\n/**\n * Normalize production configuration\n * @internal Exported for testing\n */\nexport function normalizeProductionConfig(\n\tcliProduction: boolean,\n\tconfigProduction?: ProductionConfig,\n): NormalizedProductionConfig | undefined {\n\t// Production mode is only enabled if --production CLI flag is passed\n\tif (!cliProduction) {\n\t\treturn undefined;\n\t}\n\n\t// Merge CLI flag with config options\n\tconst config = configProduction ?? {};\n\n\treturn {\n\t\tenabled: true,\n\t\tbundle: config.bundle ?? true,\n\t\tminify: config.minify ?? true,\n\t\thealthCheck: config.healthCheck ?? '/health',\n\t\tgracefulShutdown: config.gracefulShutdown ?? true,\n\t\texternal: config.external ?? [],\n\t\tsubscribers: config.subscribers ?? 'exclude',\n\t\topenapi: config.openapi ?? false,\n\t\toptimizedHandlers: config.optimizedHandlers ?? true, // Default to optimized handlers in production\n\t};\n}\n\n/**\n * Get production config from GkmConfig\n * @internal\n */\nexport function getProductionConfigFromGkm(\n\tconfig: GkmConfig,\n): ProductionConfig | undefined {\n\tconst serverConfig = config.providers?.server;\n\tif (typeof serverConfig === 'object') {\n\t\treturn (serverConfig as ServerConfig).production;\n\t}\n\treturn undefined;\n}\n\nexport interface DevOptions {\n\tport?: number;\n\tportExplicit?: boolean;\n\tenableOpenApi?: boolean;\n\t/** Specific app to run in workspace mode (default: all apps) */\n\tapp?: string;\n\t/** Filter apps by pattern (passed to turbo --filter) */\n\tfilter?: string;\n\t/** Entry file to run (bypasses gkm config) */\n\tentry?: string;\n\t/** Watch for file changes (default: true with --entry) */\n\twatch?: boolean;\n}\n\nexport async function devCommand(options: DevOptions): Promise<void> {\n\t// Handle --entry mode: run any file with secret injection\n\tif (options.entry) {\n\t\treturn entryDevCommand(options);\n\t}\n\n\t// Load default .env file BEFORE loading config\n\t// This ensures env vars are available when config and its dependencies are loaded\n\tconst defaultEnv = loadEnvFiles('.env');\n\tif (defaultEnv.loaded.length > 0) {\n\t\tlogger.log(`📦 Loaded env: ${defaultEnv.loaded.join(', ')}`);\n\t}\n\n\t// Check if we're in an app subdirectory\n\tconst appName = getAppNameFromCwd();\n\tlet config: GkmConfig;\n\tlet appRoot: string = process.cwd();\n\tlet secretsRoot: string = process.cwd(); // Where .gkm/secrets/ lives\n\tlet workspaceAppName: string | undefined; // Set if in workspace mode\n\tlet workspaceAppPort: number | undefined; // Port from workspace config\n\tlet workspace: NormalizedWorkspace | undefined; // Set if in workspace mode\n\n\tif (appName) {\n\t\t// Try to load app-specific config from workspace\n\t\ttry {\n\t\t\tconst appConfig = await loadAppConfig();\n\t\t\tconfig = appConfig.gkmConfig;\n\t\t\tappRoot = appConfig.appRoot;\n\t\t\tsecretsRoot = appConfig.workspaceRoot;\n\t\t\tworkspaceAppName = appConfig.appName;\n\t\t\tworkspaceAppPort = appConfig.app.port;\n\t\t\tworkspace = appConfig.workspace;\n\t\t\tlogger.log(\n\t\t\t\t`📦 Running app: ${appConfig.appName} on port ${workspaceAppPort}`,\n\t\t\t);\n\n\t\t\t// Check if app has an entry point (non-gkm app like better-auth)\n\t\t\tif (appConfig.app.entry) {\n\t\t\t\tlogger.log(`📄 Using entry point: ${appConfig.app.entry}`);\n\t\t\t\treturn entryDevCommand({\n\t\t\t\t\t...options,\n\t\t\t\t\tentry: appConfig.app.entry,\n\t\t\t\t\tport: workspaceAppPort,\n\t\t\t\t\tportExplicit: true,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// Not in a workspace or app not found in workspace - fall back to regular loading\n\t\t\tconst loadedConfig = await loadWorkspaceConfig();\n\n\t\t\t// Route to workspace dev mode for multi-app workspaces\n\t\t\tif (loadedConfig.type === 'workspace') {\n\t\t\t\tlogger.log('📦 Detected workspace configuration');\n\t\t\t\treturn workspaceDevCommand(loadedConfig.workspace, options);\n\t\t\t}\n\n\t\t\tconfig = loadedConfig.raw as GkmConfig;\n\t\t}\n\t} else {\n\t\t// Try to load workspace config\n\t\tconst loadedConfig = await loadWorkspaceConfig();\n\n\t\t// Route to workspace dev mode for multi-app workspaces\n\t\tif (loadedConfig.type === 'workspace') {\n\t\t\tlogger.log('📦 Detected workspace configuration');\n\t\t\treturn workspaceDevCommand(loadedConfig.workspace, options);\n\t\t}\n\n\t\t// Single-app mode - use existing logic\n\t\tconfig = loadedConfig.raw as GkmConfig;\n\t\t// Wrapped as a one-app workspace, which is what reconcile reads. Nothing\n\t\t// below depends on an app name, so this only turns on the parts that need\n\t\t// a workspace at all.\n\t\tworkspace = loadedConfig.workspace;\n\t}\n\n\t// Load any additional env files specified in config\n\tif (config.env) {\n\t\tconst { loaded, missing } = loadEnvFiles(config.env, appRoot);\n\t\tif (loaded.length > 0) {\n\t\t\tlogger.log(`📦 Loaded env: ${loaded.join(', ')}`);\n\t\t}\n\t\tif (missing.length > 0) {\n\t\t\tlogger.warn(`⚠️ Missing env files: ${missing.join(', ')}`);\n\t\t}\n\t}\n\n\t// Force server provider for dev mode\n\tconst resolved = resolveProviders(config, { provider: 'server' });\n\n\tlogger.log('🚀 Starting development server...');\n\tlogger.log(`Loading routes from: ${config.routes}`);\n\tif (config.functions) {\n\t\tlogger.log(`Loading functions from: ${config.functions}`);\n\t}\n\tif (config.crons) {\n\t\tlogger.log(`Loading crons from: ${config.crons}`);\n\t}\n\tif (config.subscribers) {\n\t\tlogger.log(`Loading subscribers from: ${config.subscribers}`);\n\t}\n\tlogger.log(`Using envParser: ${config.envParser}`);\n\n\t// Parse envParser and logger configuration\n\tconst { path: envParserPath, importPattern: envParserImportPattern } =\n\t\tparseModuleConfig(config.envParser, 'envParser');\n\tconst { path: loggerPath, importPattern: loggerImportPattern } =\n\t\tparseModuleConfig(config.logger, 'logger');\n\n\t// Normalize telescope configuration\n\tconst telescope = normalizeTelescopeConfig(config.telescope);\n\tif (telescope) {\n\t\tlogger.log(`🔭 Telescope enabled at ${telescope.path}`);\n\t}\n\n\t// Normalize studio configuration\n\tconst studio = normalizeStudioConfig(config.studio);\n\tif (studio) {\n\t\tlogger.log(`🗄️ Studio enabled at ${studio.path}`);\n\t}\n\n\t// Normalize hooks configuration\n\tconst hooks = normalizeHooksConfig(config.hooks, appRoot);\n\tif (hooks) {\n\t\tlogger.log(`🪝 Server hooks enabled from ${config.hooks?.server}`);\n\t}\n\n\t// Resolve OpenAPI configuration\n\tconst openApiConfig = resolveOpenApiConfig(config);\n\t// Enable OpenAPI docs endpoint if either root config or provider config enables it\n\tconst enableOpenApi = openApiConfig.enabled || resolved.enableOpenApi;\n\tif (enableOpenApi) {\n\t\tlogger.log(`📄 OpenAPI output: ${OPENAPI_OUTPUT_PATH}`);\n\t}\n\n\tconst buildContext: BuildContext = {\n\t\tenvParserPath,\n\t\tenvParserImportPattern,\n\t\tloggerPath,\n\t\tloggerImportPattern,\n\t\ttelescope,\n\t\tstudio,\n\t\thooks,\n\t};\n\n\t// Build initial version\n\tawait buildServer(\n\t\tconfig,\n\t\tbuildContext,\n\t\tresolved.providers[0] as LegacyProvider,\n\t\tenableOpenApi,\n\t\tappRoot,\n\t);\n\n\t// Generate OpenAPI spec on startup\n\tif (enableOpenApi) {\n\t\tawait generateOpenApi(config);\n\t}\n\n\t// Determine runtime (default to node)\n\tconst runtime: Runtime = config.runtime ?? 'node';\n\n\t// Load secrets for dev mode, resolve Docker ports, and write to JSON file\n\tlet secretsJsonPath: string | undefined;\n\tconst appSecrets = await loadSecretsForApp(secretsRoot, workspaceAppName);\n\n\tif (workspace && usesConstructs(workspace)) {\n\t\t// The same reconcile `gkm setup` and the workspace dev path run: derive\n\t\t// the containers from what the app declares, start them, create what the\n\t\t// URLs name, and inject those URLs. It replaces the branch below, which\n\t\t// has no hand-written compose file left to read ports out of.\n\t\tconst reconciled = await reconcileWorkspace(workspace, {\n\t\t\tstage: 'development',\n\t\t});\n\n\t\tif (reconciled.changed && reconciled.plan.containers.length > 0) {\n\t\t\tlogger.log(`🐳 Services: ${reconciled.plan.containers.join(', ')}`);\n\t\t\tfor (const [container, address] of Object.entries(reconciled.addresses)) {\n\t\t\t\tlogger.log(` ${container}: ${address}`);\n\t\t\t}\n\t\t}\n\n\t\t// Declared URLs win over anything sniffed or stored — the manifest is the\n\t\t// statement of what exists.\n\t\tObject.assign(appSecrets, reconciled.env);\n\t} else {\n\t\t// Resolve Docker service ports and rewrite connection URLs\n\t\tconst resolvedPorts = await resolveServicePorts(secretsRoot);\n\t\tif (Object.keys(resolvedPorts.ports).length > 0) {\n\t\t\tconst rewritten = rewriteUrlsWithPorts(appSecrets, resolvedPorts);\n\t\t\tObject.assign(appSecrets, rewritten);\n\t\t\tlogger.log(\n\t\t\t\t`🔌 Applied ${Object.keys(resolvedPorts.ports).length} port mapping(s)`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// Inject dependency URLs if in workspace mode\n\tif (workspace && workspaceAppName) {\n\t\tconst depEnv = getDependencyEnvVars(workspace, workspaceAppName);\n\t\tObject.assign(appSecrets, depEnv);\n\t}\n\n\tif (Object.keys(appSecrets).length > 0) {\n\t\tconst secretsDir = join(secretsRoot, '.gkm');\n\t\tawait mkdir(secretsDir, { recursive: true });\n\t\tconst secretsFileName = workspaceAppName\n\t\t\t? `dev-secrets-${workspaceAppName}.json`\n\t\t\t: 'dev-secrets.json';\n\t\tsecretsJsonPath = join(secretsDir, secretsFileName);\n\t\tawait writeFile(secretsJsonPath, JSON.stringify(appSecrets, null, 2));\n\t\tlogger.log(`🔐 Loaded ${Object.keys(appSecrets).length} secret(s)`);\n\t}\n\n\t// Start the dev server\n\t// Priority: explicit --port option > workspace app port > default 3000\n\tconst devServer = new DevServer(\n\t\tresolved.providers[0] as LegacyProvider,\n\t\toptions.port ?? workspaceAppPort ?? 3000,\n\t\toptions.portExplicit ?? false,\n\t\tenableOpenApi,\n\t\ttelescope,\n\t\tstudio,\n\t\truntime,\n\t\tappRoot,\n\t\tsecretsJsonPath,\n\t);\n\n\tawait devServer.start();\n\n\t// Watch for file changes\n\tconst envParserFile = config.envParser.split('#')[0] ?? config.envParser;\n\tconst loggerFile = config.logger.split('#')[0] ?? config.logger;\n\n\t// Get hooks file path for watching\n\tconst hooksFileParts = config.hooks?.server?.split('#');\n\tconst hooksFile = hooksFileParts?.[0];\n\n\tconst watchPatterns = [\n\t\tconfig.routes,\n\t\t...(config.functions ? [config.functions] : []),\n\t\t...(config.crons ? [config.crons] : []),\n\t\t...(config.subscribers ? [config.subscribers] : []),\n\t\t// Add .ts extension if not present for config files\n\t\tenvParserFile.endsWith('.ts') ? envParserFile : `${envParserFile}.ts`,\n\t\tloggerFile.endsWith('.ts') ? loggerFile : `${loggerFile}.ts`,\n\t\t// Add hooks file to watch list\n\t\t...(hooksFile\n\t\t\t? [hooksFile.endsWith('.ts') ? hooksFile : `${hooksFile}.ts`]\n\t\t\t: []),\n\t]\n\t\t.flat()\n\t\t.filter((p): p is string => typeof p === 'string');\n\n\t// Normalize patterns - remove leading ./ when using cwd option\n\tconst normalizedPatterns = watchPatterns.map((p) =>\n\t\tp.startsWith('./') ? p.slice(2) : p,\n\t);\n\n\tlogger.log(`👀 Watching for changes in: ${normalizedPatterns.join(', ')}`);\n\n\t// Resolve glob patterns to actual files (chokidar 4.x doesn't support globs)\n\tconst resolvedFiles = await fg(normalizedPatterns, {\n\t\tcwd: appRoot,\n\t\tabsolute: false,\n\t\tonlyFiles: true,\n\t});\n\n\t// Also watch the directories for new files\n\tconst dirsToWatch = [\n\t\t...new Set(\n\t\t\tresolvedFiles.map((f) => {\n\t\t\t\tconst parts = f.split('/');\n\t\t\t\treturn parts.slice(0, -1).join('/');\n\t\t\t}),\n\t\t),\n\t];\n\n\tlogger.log(\n\t\t`📁 Found ${resolvedFiles.length} files in ${dirsToWatch.length} directories`,\n\t);\n\n\tconst watcher = chokidar.watch([...resolvedFiles, ...dirsToWatch], {\n\t\tignored: /(^|[/\\\\])\\../, // ignore dotfiles\n\t\tpersistent: true,\n\t\tignoreInitial: true,\n\t\tcwd: appRoot,\n\t});\n\n\twatcher.on('ready', () => {\n\t\tlogger.log('🔍 File watcher ready');\n\t});\n\n\twatcher.on('error', (error) => {\n\t\tlogger.error('❌ Watcher error:', error);\n\t});\n\n\tlet rebuildTimeout: NodeJS.Timeout | null = null;\n\n\twatcher.on('change', async (path) => {\n\t\tlogger.log(`📝 File changed: ${path}`);\n\n\t\t// Debounce rebuilds\n\t\tif (rebuildTimeout) {\n\t\t\tclearTimeout(rebuildTimeout);\n\t\t}\n\n\t\trebuildTimeout = setTimeout(async () => {\n\t\t\ttry {\n\t\t\t\tlogger.log('🔄 Rebuilding...');\n\t\t\t\tawait buildServer(\n\t\t\t\t\tconfig,\n\t\t\t\t\tbuildContext,\n\t\t\t\t\tresolved.providers[0] as LegacyProvider,\n\t\t\t\t\tenableOpenApi,\n\t\t\t\t\tappRoot,\n\t\t\t\t\ttrue, // bust module cache on rebuild\n\t\t\t\t);\n\n\t\t\t\t// Regenerate OpenAPI if enabled\n\t\t\t\tif (enableOpenApi) {\n\t\t\t\t\tawait generateOpenApi(config, { silent: true, bustCache: true });\n\t\t\t\t}\n\n\t\t\t\tlogger.log('✅ Rebuild complete, restarting server...');\n\t\t\t\tawait devServer.restart();\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error as Error;\n\t\t\t\tlogger.error(`❌ Rebuild failed: ${err.message}`);\n\t\t\t\tif (err.stack) {\n\t\t\t\t\tlogger.error(err.stack);\n\t\t\t\t}\n\t\t\t}\n\t\t}, 300);\n\t});\n\n\t// Handle graceful shutdown\n\tlet isShuttingDown = false;\n\tconst shutdown = () => {\n\t\tif (isShuttingDown) return;\n\t\tisShuttingDown = true;\n\n\t\tlogger.log('\\n🛑 Shutting down...');\n\n\t\t// Use sync-style shutdown to ensure it completes before exit\n\t\tPromise.all([watcher.close(), devServer.stop()])\n\t\t\t.catch((err) => {\n\t\t\t\tlogger.error('Error during shutdown:', err);\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tprocess.exit(0);\n\t\t\t});\n\t};\n\n\tprocess.on('SIGINT', shutdown);\n\tprocess.on('SIGTERM', shutdown);\n}\n\n/**\n * Generate all dependency environment variables for all apps.\n * Returns a flat object with all {APP_NAME}_URL variables.\n * @internal Exported for testing\n */\nexport function generateAllDependencyEnvVars(\n\tworkspace: NormalizedWorkspace,\n\turlPrefix = 'http://localhost',\n): Record<string, string> {\n\tconst env: Record<string, string> = {};\n\n\tfor (const appName of Object.keys(workspace.apps)) {\n\t\tconst appEnv = getDependencyEnvVars(workspace, appName, urlPrefix);\n\t\tObject.assign(env, appEnv);\n\t}\n\n\treturn env;\n}\n\n/**\n * Check for port conflicts across all apps.\n * Returns list of conflicts if any ports are duplicated.\n * @internal Exported for testing\n */\nexport function checkPortConflicts(\n\tworkspace: NormalizedWorkspace,\n): { app1: string; app2: string; port: number }[] {\n\tconst conflicts: { app1: string; app2: string; port: number }[] = [];\n\tconst portToApp = new Map<number, string>();\n\n\tfor (const [appName, app] of Object.entries(workspace.apps)) {\n\t\tconst existingApp = portToApp.get(app.port);\n\t\tif (existingApp) {\n\t\t\tconflicts.push({ app1: existingApp, app2: appName, port: app.port });\n\t\t} else {\n\t\t\tportToApp.set(app.port, appName);\n\t\t}\n\t}\n\n\treturn conflicts;\n}\n\n/**\n * A framework the dev server starts an app with.\n *\n * Expo is a mobile framework, not a frontend one, but it is detected and\n * validated here the same way — so the union is what this table is keyed by.\n * It used to be keyed by `FrontendFramework` alone, with the Expo entry typed\n * as an excess property and the one call site casting around it.\n */\ntype ClientFramework = FrontendFramework | MobileFramework;\n\n/**\n * Per-framework validation spec for the frameworks the dev server supports.\n */\ninterface FrontendFrameworkSpec {\n\tdisplayName: string;\n\tconfigFiles: string[];\n\tdependency: string;\n\tinstallCommand: string;\n}\n\nconst FRONTEND_FRAMEWORKS: Record<ClientFramework, FrontendFrameworkSpec> = {\n\tnextjs: {\n\t\tdisplayName: 'Next.js',\n\t\tconfigFiles: [\n\t\t\t'next.config.js',\n\t\t\t'next.config.ts',\n\t\t\t'next.config.mjs',\n\t\t\t'next.config.cjs',\n\t\t],\n\t\tdependency: 'next',\n\t\tinstallCommand: 'pnpm add next react react-dom',\n\t},\n\tremix: {\n\t\tdisplayName: 'Remix',\n\t\tconfigFiles: [\n\t\t\t'remix.config.js',\n\t\t\t'remix.config.mjs',\n\t\t\t'remix.config.ts',\n\t\t\t'vite.config.js',\n\t\t\t'vite.config.ts',\n\t\t\t'vite.config.mjs',\n\t\t\t'vite.config.cjs',\n\t\t],\n\t\tdependency: '@remix-run/dev',\n\t\tinstallCommand: 'pnpm add -D @remix-run/dev',\n\t},\n\tvite: {\n\t\tdisplayName: 'Vite',\n\t\tconfigFiles: [\n\t\t\t'vite.config.js',\n\t\t\t'vite.config.ts',\n\t\t\t'vite.config.mjs',\n\t\t\t'vite.config.cjs',\n\t\t],\n\t\tdependency: 'vite',\n\t\tinstallCommand: 'pnpm add -D vite',\n\t},\n\t'tanstack-start': {\n\t\tdisplayName: 'TanStack Start',\n\t\tconfigFiles: [\n\t\t\t'vite.config.js',\n\t\t\t'vite.config.ts',\n\t\t\t'vite.config.mjs',\n\t\t\t'vite.config.cjs',\n\t\t],\n\t\tdependency: '@tanstack/react-start',\n\t\tinstallCommand: 'pnpm add @tanstack/react-start @tanstack/react-router',\n\t},\n\texpo: {\n\t\tdisplayName: 'Expo',\n\t\tconfigFiles: ['app.config.ts', 'app.config.js', 'app.json'],\n\t\tdependency: 'expo',\n\t\tinstallCommand: 'pnpm add expo',\n\t},\n};\n\n/** Whether a configured framework is one this table knows how to start. */\nfunction isClientFramework(\n\tframework: string | undefined,\n): framework is ClientFramework {\n\treturn framework !== undefined && framework in FRONTEND_FRAMEWORKS;\n}\n\n/**\n * Auto-detect the frontend framework by scanning package.json deps and config\n * files. Dependency match wins over config file match because deps are more\n * distinctive (a Remix app has both vite.config.ts AND @remix-run/dev).\n */\nfunction detectFrontendFramework(\n\tfullPath: string,\n\tdeps: Record<string, string>,\n): ClientFramework | undefined {\n\t// tanstack-start before vite/remix so its more specific dep wins; vite last\n\t// because plain vite is the fallback when neither nextjs/remix/tanstack match.\n\t// Order matters: more specific deps win over general ones.\n\t// expo first (own dep), then tanstack-start (uses vite), remix (uses vite),\n\t// nextjs (own dep), and plain vite last as the fallback.\n\tconst order: ClientFramework[] = [\n\t\t'expo',\n\t\t'tanstack-start',\n\t\t'nextjs',\n\t\t'remix',\n\t\t'vite',\n\t];\n\tfor (const name of order) {\n\t\tif (deps[FRONTEND_FRAMEWORKS[name].dependency]) {\n\t\t\treturn name;\n\t\t}\n\t}\n\tfor (const name of order) {\n\t\tconst hasConfig = FRONTEND_FRAMEWORKS[name].configFiles.some((file) =>\n\t\t\texistsSync(join(fullPath, file)),\n\t\t);\n\t\tif (hasConfig) {\n\t\t\treturn name;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Validation result for a frontend app.\n */\nexport interface FrontendValidationResult {\n\tappName: string;\n\tvalid: boolean;\n\terrors: string[];\n\twarnings: string[];\n}\n\n/**\n * Validate a frontend app configuration.\n *\n * If `framework` is provided, validates strictly against that framework's\n * expected config file and dependency. Otherwise auto-detects the framework\n * from package.json and config files. If no recognized framework is found, the\n * app is allowed through with a warning provided it has a `dev` script.\n *\n * @internal Exported for testing\n */\nexport async function validateFrontendApp(\n\tappName: string,\n\tappPath: string,\n\tworkspaceRoot: string,\n\tframework?: ClientFramework,\n): Promise<FrontendValidationResult> {\n\tconst errors: string[] = [];\n\tconst warnings: string[] = [];\n\tconst fullPath = join(workspaceRoot, appPath);\n\n\tconst packageJsonPath = join(fullPath, 'package.json');\n\tif (!existsSync(packageJsonPath)) {\n\t\terrors.push(\n\t\t\t`package.json not found at ${appPath}. Run: pnpm init in the app directory.`,\n\t\t);\n\t\treturn { appName, valid: false, errors, warnings };\n\t}\n\n\tlet pkg: {\n\t\tdependencies?: Record<string, string>;\n\t\tdevDependencies?: Record<string, string>;\n\t\tscripts?: Record<string, string>;\n\t};\n\ttry {\n\t\t// eslint-disable-next-line @typescript-eslint/no-require-imports\n\t\tpkg = require(packageJsonPath);\n\t} catch {\n\t\terrors.push(`Failed to read package.json at ${packageJsonPath}`);\n\t\treturn { appName, valid: false, errors, warnings };\n\t}\n\n\tconst deps: Record<string, string> = {\n\t\t...pkg.dependencies,\n\t\t...pkg.devDependencies,\n\t};\n\n\tconst resolvedFramework =\n\t\tframework ?? detectFrontendFramework(fullPath, deps);\n\n\tif (resolvedFramework) {\n\t\tconst spec = FRONTEND_FRAMEWORKS[resolvedFramework];\n\t\tconst hasConfig = spec.configFiles.some((file) =>\n\t\t\texistsSync(join(fullPath, file)),\n\t\t);\n\t\tif (!hasConfig) {\n\t\t\terrors.push(\n\t\t\t\t`${spec.displayName} config file not found. Expected one of: ${spec.configFiles.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (!deps[spec.dependency]) {\n\t\t\terrors.push(\n\t\t\t\t`${spec.displayName} not found in dependencies. Run: ${spec.installCommand}`,\n\t\t\t);\n\t\t}\n\t} else {\n\t\twarnings.push(\n\t\t\t'No recognized frontend framework detected (Next.js, Vite, Remix). ' +\n\t\t\t\t'The app will run via its \"dev\" script. Set `framework` in your app config to enable strict validation.',\n\t\t);\n\t}\n\n\tif (!pkg.scripts?.dev) {\n\t\tif (resolvedFramework) {\n\t\t\twarnings.push(\n\t\t\t\t'No \"dev\" script found in package.json. Turbo expects a \"dev\" script to run.',\n\t\t\t);\n\t\t} else {\n\t\t\terrors.push(\n\t\t\t\t'No \"dev\" script found in package.json. Without a recognized framework or a dev script, there is nothing to run.',\n\t\t\t);\n\t\t}\n\t}\n\n\treturn {\n\t\tappName,\n\t\tvalid: errors.length === 0,\n\t\terrors,\n\t\twarnings,\n\t};\n}\n\n/**\n * Validate all frontend apps in the workspace.\n * Returns validation results for each frontend app.\n * @internal Exported for testing\n */\nexport async function validateFrontendApps(\n\tworkspace: NormalizedWorkspace,\n): Promise<FrontendValidationResult[]> {\n\tconst results: FrontendValidationResult[] = [];\n\n\tfor (const [appName, app] of Object.entries(workspace.apps)) {\n\t\tif (app.type === 'web') {\n\t\t\tconst result = await validateFrontendApp(\n\t\t\t\tappName,\n\t\t\t\tapp.path,\n\t\t\t\tworkspace.root,\n\t\t\t\tisClientFramework(app.framework) ? app.framework : undefined,\n\t\t\t);\n\t\t\tresults.push(result);\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Load secrets for development stage.\n * Returns env vars to inject, or empty object if secrets not configured/found.\n * @internal Exported for testing\n */\nexport async function loadDevSecrets(\n\tworkspace: NormalizedWorkspace,\n): Promise<Record<string, string>> {\n\t// Check if secrets are enabled in workspace config\n\tif (!workspace.secrets.enabled) {\n\t\treturn {};\n\t}\n\n\t// Try 'dev' stage first, then 'development'\n\tconst stages = ['dev', 'development'];\n\n\tfor (const stage of stages) {\n\t\tif (secretsExist(stage, workspace.root)) {\n\t\t\tconst secrets = await readStageSecrets(stage, workspace.root);\n\t\t\tif (secrets) {\n\t\t\t\tlogger.log(`🔐 Loading secrets from stage: ${stage}`);\n\t\t\t\treturn toEmbeddableSecrets(secrets);\n\t\t\t}\n\t\t}\n\t}\n\n\tlogger.warn(\n\t\t'⚠️ Secrets enabled but no dev/development secrets found. Run \"gkm setup\" to initialize your development environment',\n\t);\n\treturn {};\n}\n\n/**\n * Workspace dev command - orchestrates multi-app development using Turbo.\n *\n * Flow:\n * 1. Check for port conflicts\n * 2. Start docker-compose services (db, cache, mail)\n * 3. Generate dependency URLs ({APP_NAME}_URL)\n * 4. Spawn turbo run dev with injected env vars\n */\nasync function workspaceDevCommand(\n\tworkspace: NormalizedWorkspace,\n\toptions: DevOptions,\n): Promise<void> {\n\tconst appCount = Object.keys(workspace.apps).length;\n\tconst backendApps = Object.entries(workspace.apps).filter(\n\t\t([_, app]) => app.type === 'backend',\n\t);\n\tconst frontendApps = Object.entries(workspace.apps).filter(\n\t\t([_, app]) => app.type === 'web',\n\t);\n\tconst mobileApps = Object.entries(workspace.apps).filter(\n\t\t([_, app]) => app.type === 'mobile',\n\t);\n\n\tlogger.log(`\\n🚀 Starting workspace: ${workspace.name}`);\n\tconst counts = [\n\t\t`${backendApps.length} backend`,\n\t\t`${frontendApps.length} web`,\n\t];\n\tif (mobileApps.length > 0) counts.push(`${mobileApps.length} mobile`);\n\tlogger.log(` ${counts.join(', ')} app(s)`);\n\n\t// Check for port conflicts\n\tconst conflicts = checkPortConflicts(workspace);\n\tif (conflicts.length > 0) {\n\t\tfor (const conflict of conflicts) {\n\t\t\tlogger.error(\n\t\t\t\t`❌ Port conflict: Apps \"${conflict.app1}\" and \"${conflict.app2}\" both use port ${conflict.port}`,\n\t\t\t);\n\t\t}\n\t\tthrow new Error(\n\t\t\t'Port conflicts detected. Please assign unique ports to each app.',\n\t\t);\n\t}\n\n\t// Validate frontend apps (Next.js setup)\n\tif (frontendApps.length > 0) {\n\t\tlogger.log('\\n🔍 Validating frontend apps...');\n\t\tconst validationResults = await validateFrontendApps(workspace);\n\n\t\tlet hasErrors = false;\n\t\tfor (const result of validationResults) {\n\t\t\tif (!result.valid) {\n\t\t\t\thasErrors = true;\n\t\t\t\tlogger.error(\n\t\t\t\t\t`\\n❌ Frontend app \"${result.appName}\" validation failed:`,\n\t\t\t\t);\n\t\t\t\tfor (const error of result.errors) {\n\t\t\t\t\tlogger.error(` • ${error}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const warning of result.warnings) {\n\t\t\t\tlogger.warn(` ⚠️ ${result.appName}: ${warning}`);\n\t\t\t}\n\t\t}\n\n\t\tif (hasErrors) {\n\t\t\tthrow new Error(\n\t\t\t\t'Frontend app validation failed. Fix the issues above and try again.',\n\t\t\t);\n\t\t}\n\t\tlogger.log('✅ Frontend apps validated');\n\t}\n\n\t// Frontend apps import API clients directly from backend packages\n\t// (e.g. import { createApi } from '@myapp/api/client')\n\t// No file copying needed — pnpm workspace resolution handles it.\n\n\t// Load secrets BEFORE starting Docker so POSTGRES_USER, POSTGRES_PASSWORD,\n\t// etc. are available for docker-compose variable interpolation\n\tconst rawSecrets = await loadDevSecrets(workspace);\n\n\tlet secretsEnv: Record<string, string>;\n\n\tif (usesConstructs(workspace)) {\n\t\t// Derive the containers from what the app declares, start them, create\n\t\t// what the URLs name, and inject those URLs. Safe to do on every start\n\t\t// because the blast radius is this project's containers and `.gkm/`, and\n\t\t// because the converged case costs one hash and one health check.\n\t\tconst reconciled = await reconcileWorkspace(workspace, {\n\t\t\tstage: 'development',\n\t\t});\n\n\t\tif (reconciled.changed && reconciled.plan.containers.length > 0) {\n\t\t\tlogger.log(`🐳 Services: ${reconciled.plan.containers.join(', ')}`);\n\t\t\tfor (const [container, address] of Object.entries(reconciled.addresses)) {\n\t\t\t\tlogger.log(` ${container}: ${address}`);\n\t\t\t}\n\t\t}\n\n\t\tsecretsEnv = { ...rawSecrets, ...reconciled.env };\n\t} else {\n\t\t// Resolve dynamic service ports from the hand-written docker-compose.yml\n\t\tconst resolvedPorts = await resolveServicePorts(workspace.root);\n\n\t\t// Start docker-compose services with resolved ports AND secrets\n\t\tawait startWorkspaceServices(\n\t\t\tworkspace,\n\t\t\tresolvedPorts.dockerEnv,\n\t\t\trawSecrets,\n\t\t);\n\n\t\t// Rewrite URLs with resolved ports (hostnames and port numbers)\n\t\tsecretsEnv = rewriteUrlsWithPorts(rawSecrets, resolvedPorts);\n\t}\n\n\tif (Object.keys(secretsEnv).length > 0) {\n\t\tlogger.log(` Loaded ${Object.keys(secretsEnv).length} secret(s)`);\n\t}\n\n\t// Generate dependency URLs\n\tconst dependencyEnv = generateAllDependencyEnvVars(workspace);\n\tif (Object.keys(dependencyEnv).length > 0) {\n\t\tlogger.log('📡 Dependency URLs:');\n\t\tfor (const [key, value] of Object.entries(dependencyEnv)) {\n\t\t\tlogger.log(` ${key}=${value}`);\n\t\t}\n\t}\n\n\t// Build turbo filter\n\tlet turboFilter: string[] = [];\n\tif (options.app) {\n\t\t// Run specific app\n\t\tif (!workspace.apps[options.app]) {\n\t\t\tconst appNames = Object.keys(workspace.apps).join(', ');\n\t\t\tthrow new Error(\n\t\t\t\t`App \"${options.app}\" not found. Available apps: ${appNames}`,\n\t\t\t);\n\t\t}\n\t\tturboFilter = ['--filter', options.app];\n\t\tlogger.log(`\\n🎯 Running single app: ${options.app}`);\n\t} else if (options.filter) {\n\t\t// Use custom filter\n\t\tturboFilter = ['--filter', options.filter];\n\t\tlogger.log(`\\n🔍 Using filter: ${options.filter}`);\n\t} else {\n\t\t// Run all apps\n\t\tlogger.log(`\\n🎯 Running all ${appCount} apps`);\n\t}\n\n\t// List apps and their ports\n\tconst buildOrder = getAppBuildOrder(workspace);\n\tlogger.log('\\n📋 Apps (in dependency order):');\n\tfor (const appName of buildOrder) {\n\t\tconst app = workspace.apps[appName];\n\t\tif (!app) continue;\n\t\tconst deps =\n\t\t\tapp.dependencies.length > 0\n\t\t\t\t? ` (depends on: ${app.dependencies.join(', ')})`\n\t\t\t\t: '';\n\t\tconst icon =\n\t\t\tapp.type === 'backend' ? '🔧' : app.type === 'mobile' ? '📱' : '🌐';\n\t\tlogger.log(` ${icon} ${appName} → http://localhost:${app.port}${deps}`);\n\t}\n\n\t// Find the config file path for GKM_CONFIG_PATH\n\tconst configFiles = ['gkm.config.ts', 'gkm.config.js', 'gkm.config.json'];\n\tlet configPath = '';\n\tfor (const file of configFiles) {\n\t\tconst fullPath = join(workspace.root, file);\n\t\tif (existsSync(fullPath)) {\n\t\t\tconfigPath = fullPath;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Prepare environment variables\n\t// Order matters: secrets first, then dependencies (dependencies can override)\n\tconst turboEnv: Record<string, string> = {\n\t\t...process.env,\n\t\t...secretsEnv,\n\t\t...dependencyEnv,\n\t\tNODE_ENV: 'development',\n\t\t// Inject config path so child processes can find the workspace config\n\t\t...(configPath ? { GKM_CONFIG_PATH: configPath } : {}),\n\t};\n\n\t// Spawn turbo run dev\n\tlogger.log('\\n🏃 Starting turbo run dev...\\n');\n\n\tconst turboProcess = spawn('pnpm', ['turbo', 'run', 'dev', ...turboFilter], {\n\t\tcwd: workspace.root,\n\t\tstdio: 'inherit',\n\t\tenv: turboEnv,\n\t\tdetached: true,\n\t});\n\n\t// No file watcher needed — frontend apps import API clients directly\n\t// from backend packages via workspace dependencies.\n\n\t// Handle graceful shutdown\n\tlet isShuttingDown = false;\n\tconst shutdown = () => {\n\t\tif (isShuttingDown) return;\n\t\tisShuttingDown = true;\n\n\t\tlogger.log('\\n🛑 Shutting down workspace...');\n\n\t\t// Kill turbo process group\n\t\tconst pid = turboProcess.pid;\n\t\tif (pid) {\n\t\t\ttry {\n\t\t\t\tprocess.kill(-pid, 'SIGTERM');\n\t\t\t} catch {\n\t\t\t\ttry {\n\t\t\t\t\tprocess.kill(pid, 'SIGTERM');\n\t\t\t\t} catch {\n\t\t\t\t\t// Process already dead\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Force kill after timeout if processes are still alive\n\t\tsetTimeout(() => {\n\t\t\tif (pid) {\n\t\t\t\ttry {\n\t\t\t\t\tprocess.kill(-pid, 'SIGKILL');\n\t\t\t\t} catch {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tprocess.kill(pid, 'SIGKILL');\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Process already dead\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tprocess.exit(0);\n\t\t}, 3000);\n\t};\n\n\tprocess.on('SIGINT', shutdown);\n\tprocess.on('SIGTERM', shutdown);\n\n\t// Wait for turbo to exit\n\treturn new Promise((resolve, reject) => {\n\t\tturboProcess.on('error', (error) => {\n\t\t\tlogger.error('❌ Turbo error:', error);\n\t\t\treject(error);\n\t\t});\n\n\t\tturboProcess.on('exit', (code) => {\n\t\t\tif (code !== null && code !== 0) {\n\t\t\t\treject(new Error(`Turbo exited with code ${code}`));\n\t\t\t} else {\n\t\t\t\tresolve();\n\t\t\t}\n\t\t});\n\t});\n}\n\n/**\n * What the app declares, or nothing when it declares in the old way.\n *\n * Discovery imports the application's construct modules, which is why this is\n * guarded rather than unconditional: a project with no `constructs` glob has\n * not adopted the model, and there is nothing to import.\n */\nasync function declaredConstructs(\n\tconfig: any,\n\tappRoot: string,\n): Promise<Record<string, { kind: string; of?: string }>> {\n\tconst patterns =\n\t\ttypeof config?.constructs === 'string' || Array.isArray(config?.constructs)\n\t\t\t? config.constructs\n\t\t\t: undefined;\n\n\tif (!patterns) return {};\n\n\ttry {\n\t\treturn (await discover({\n\t\t\tpatterns: (Array.isArray(patterns) ? patterns : [patterns]).map((p) =>\n\t\t\t\tisAbsolute(p) ? p : join(appRoot, p),\n\t\t\t),\n\t\t\tcwd: appRoot,\n\t\t})) as Record<string, { kind: string; of?: string }>;\n\t} catch {\n\t\t// A discovery failure here is not fatal: the generators below import the\n\t\t// same modules and will report it with a better message than \"no drivers\".\n\t\treturn {};\n\t}\n}\n\nasync function buildServer(\n\tconfig: any,\n\tcontext: BuildContext,\n\tprovider: LegacyProvider,\n\tenableOpenApi: boolean,\n\tappRoot: string = process.cwd(),\n\tbustCache = false,\n): Promise<void> {\n\t// The entry point registers the drivers its target needs — see\n\t// `generators/drivers.ts`. Decided here because this is where the app root is\n\t// known, and read by every generator that writes an entry.\n\t//\n\t// Both halves of the answer, because both halves decide where a cache lives:\n\t// the declaration for a cache that named its database, and `services.cache`\n\t// for one that named nowhere. Reading only the config registered a driver\n\t// for a protocol the target had not composed.\n\tcontext = {\n\t\t...context,\n\t\tstorageDrivers: driversFor({\n\t\t\tappRoot,\n\t\t\tcache: cacheBackendsIn(\n\t\t\t\tawait declaredConstructs(config, appRoot),\n\t\t\t\tcacheBackendOf(config?.services?.cache),\n\t\t\t),\n\t\t}),\n\t};\n\n\t// Initialize generators\n\tconst endpointGenerator = new EndpointGenerator();\n\tconst functionGenerator = new FunctionGenerator();\n\tconst cronGenerator = new CronGenerator();\n\tconst subscriberGenerator = new SubscriberGenerator();\n\tconst queueGenerator = new QueueGenerator();\n\tconst topicGenerator = new TopicGenerator();\n\n\t// Load all constructs (resolve paths relative to appRoot)\n\tconst [\n\t\tallEndpoints,\n\t\tallFunctions,\n\t\tallCrons,\n\t\tallSubscribers,\n\t\tallQueues,\n\t\tallTopics,\n\t] = await Promise.all([\n\t\tendpointGenerator.load(config.routes, appRoot, bustCache),\n\t\tconfig.functions\n\t\t\t? functionGenerator.load(config.functions, appRoot, bustCache)\n\t\t\t: [],\n\t\tconfig.crons ? cronGenerator.load(config.crons, appRoot, bustCache) : [],\n\t\tconfig.subscribers\n\t\t\t? subscriberGenerator.load(config.subscribers, appRoot, bustCache)\n\t\t\t: [],\n\t\tconfig.queues ? queueGenerator.load(config.queues, appRoot, bustCache) : [],\n\t\tconfig.topics ? topicGenerator.load(config.topics, appRoot, bustCache) : [],\n\t]);\n\n\t// Ensure .gkm directory exists in app root\n\tconst outputDir = join(appRoot, '.gkm', provider);\n\tawait mkdir(outputDir, { recursive: true });\n\n\t// Build for server provider\n\tawait Promise.all([\n\t\tendpointGenerator.build(context, allEndpoints, outputDir, {\n\t\t\tprovider,\n\t\t\tenableOpenApi,\n\t\t}),\n\t\tfunctionGenerator.build(context, allFunctions, outputDir, { provider }),\n\t\tcronGenerator.build(context, allCrons, outputDir, { provider }),\n\t\tsubscriberGenerator.build(context, allSubscribers, outputDir, { provider }),\n\t\tqueueGenerator.build(context, allQueues, outputDir, { provider }),\n\t\ttopicGenerator.build(context, allTopics, outputDir, { provider }),\n\t]);\n}\n\n/**\n * Run any TypeScript file with secret injection.\n * Does not require gkm.config.ts.\n */\nasync function entryDevCommand(options: DevOptions): Promise<void> {\n\tconst { entry, watch = true } = options;\n\n\tif (!entry) {\n\t\tthrow new Error('--entry requires a file path');\n\t}\n\n\tconst entryPath = resolve(process.cwd(), entry);\n\n\tif (!existsSync(entryPath)) {\n\t\tthrow new Error(`Entry file not found: ${entryPath}`);\n\t}\n\n\t// Load .env files\n\tconst defaultEnv = loadEnvFiles('.env');\n\tif (defaultEnv.loaded.length > 0) {\n\t\tlogger.log(`📦 Loaded env: ${defaultEnv.loaded.join(', ')}`);\n\t}\n\n\t// Prepare credentials (loads workspace config, secrets, injects PORT)\n\t// Only pass explicitPort if --port was actually specified by the user\n\tconst { credentials, resolvedPort, secretsJsonPath, appName } =\n\t\tawait prepareEntryCredentials({\n\t\t\texplicitPort: options.portExplicit ? options.port : undefined,\n\t\t});\n\n\tif (appName) {\n\t\tlogger.log(`📦 App: ${appName} (port ${resolvedPort})`);\n\t}\n\n\tlogger.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);\n\n\tif (Object.keys(credentials).length > 1) {\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`,\n\t\t);\n\t}\n\n\t// Create wrapper entry that injects secrets before importing user's file\n\tconst wrapperDir = join(process.cwd(), '.gkm');\n\tawait mkdir(wrapperDir, { recursive: true });\n\tconst wrapperPath = join(wrapperDir, 'entry-wrapper.ts');\n\tawait createEntryWrapper(wrapperPath, entryPath, secretsJsonPath);\n\n\t// Start with tsx\n\tconst runner = new EntryRunner(wrapperPath, entryPath, watch, resolvedPort);\n\tawait runner.start();\n\n\t// Handle graceful shutdown\n\tlet isShuttingDown = false;\n\tconst shutdown = () => {\n\t\tif (isShuttingDown) return;\n\t\tisShuttingDown = true;\n\n\t\tlogger.log('\\n🛑 Shutting down...');\n\t\trunner.stop();\n\t\tprocess.exit(0);\n\t};\n\n\tprocess.on('SIGINT', shutdown);\n\tprocess.on('SIGTERM', shutdown);\n\n\t// Keep the process alive\n\tawait new Promise(() => {});\n}\n\n/**\n * Runs and watches a TypeScript entry file using tsx.\n */\nclass EntryRunner {\n\tprivate childProcess: ChildProcess | null = null;\n\tprivate watcher: ReturnType<typeof chokidar.watch> | null = null;\n\tprivate isRunning = false;\n\n\tconstructor(\n\t\tprivate wrapperPath: string,\n\t\tprivate entryPath: string,\n\t\tprivate watch: boolean,\n\t\tprivate port: number,\n\t) {}\n\n\tasync start(): Promise<void> {\n\t\tawait this.runProcess();\n\n\t\tif (this.watch) {\n\t\t\t// Watch the entry file's directory for changes\n\t\t\tconst watchDir = dirname(this.entryPath);\n\n\t\t\tthis.watcher = chokidar.watch(watchDir, {\n\t\t\t\tignored: /(^|[/\\\\])\\../,\n\t\t\t\tpersistent: true,\n\t\t\t\tignoreInitial: true,\n\t\t\t});\n\n\t\t\tlet restartTimeout: NodeJS.Timeout | null = null;\n\n\t\t\tthis.watcher.on('change', (path) => {\n\t\t\t\tlogger.log(`📝 File changed: ${path}`);\n\n\t\t\t\t// Debounce restarts\n\t\t\t\tif (restartTimeout) {\n\t\t\t\t\tclearTimeout(restartTimeout);\n\t\t\t\t}\n\n\t\t\t\trestartTimeout = setTimeout(async () => {\n\t\t\t\t\tlogger.log('🔄 Restarting...');\n\t\t\t\t\tawait this.restart();\n\t\t\t\t}, 300);\n\t\t\t});\n\n\t\t\tlogger.log(`👀 Watching for changes in: ${watchDir}`);\n\t\t}\n\t}\n\n\tprivate async runProcess(): Promise<void> {\n\t\t// Pass PORT as environment variable\n\t\tconst env = { ...process.env, PORT: String(this.port) };\n\n\t\tthis.childProcess = spawn('npx', ['tsx', this.wrapperPath], {\n\t\t\tstdio: 'inherit',\n\t\t\tenv,\n\t\t\tdetached: true,\n\t\t});\n\n\t\tthis.isRunning = true;\n\n\t\tthis.childProcess.on('error', (error) => {\n\t\t\tlogger.error('❌ Process error:', error);\n\t\t});\n\n\t\tthis.childProcess.on('exit', (code) => {\n\t\t\tif (code !== null && code !== 0 && code !== 143) {\n\t\t\t\t// 143 = SIGTERM\n\t\t\t\tlogger.error(`❌ Process exited with code ${code}`);\n\t\t\t}\n\t\t\tthis.isRunning = false;\n\t\t});\n\n\t\t// Give the process a moment to start\n\t\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\n\t\tif (this.isRunning) {\n\t\t\tlogger.log('');\n\t\t\tlogger.log(\n\t\t\t\t` \\x1b[32m✓ Ready\\x1b[0m at \\x1b[36mhttp://localhost:${this.port}\\x1b[0m`,\n\t\t\t);\n\t\t\tlogger.log('');\n\t\t}\n\t}\n\n\tasync restart(): Promise<void> {\n\t\tthis.stopProcess();\n\t\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\t\tawait this.runProcess();\n\t}\n\n\tstop(): void {\n\t\tthis.watcher?.close();\n\t\tthis.stopProcess();\n\t}\n\n\tprivate stopProcess(): void {\n\t\tif (this.childProcess && this.isRunning) {\n\t\t\tconst pid = this.childProcess.pid;\n\t\t\tif (pid) {\n\t\t\t\ttry {\n\t\t\t\t\tprocess.kill(-pid, 'SIGTERM');\n\t\t\t\t} catch {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tprocess.kill(pid, 'SIGTERM');\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Process already dead\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.childProcess = null;\n\t\t\tthis.isRunning = false;\n\t\t}\n\t}\n}\n\n/**\n * Generate the content of the dev server entry file (server.ts).\n * Uses dynamic import for createApp so Credentials are populated\n * before any app modules evaluate.\n * @internal Exported for testing\n */\nexport function generateServerEntryContent(options: {\n\tsecretsJsonPath?: string;\n\truntime?: Runtime;\n\tenableOpenApi?: boolean;\n\tappImportPath?: string;\n}): string {\n\tconst {\n\t\tsecretsJsonPath,\n\t\truntime = 'node',\n\t\tenableOpenApi = false,\n\t\tappImportPath = './app.js',\n\t} = options;\n\n\tconst credentialsInjection = secretsJsonPath\n\t\t? `import { existsSync, readFileSync } from 'node:fs';\n\n// Inject dev secrets via globalThis (must happen before app import)\nconst secretsPath = '${secretsJsonPath}';\nif (existsSync(secretsPath)) {\n const __secrets = JSON.parse(readFileSync(secretsPath, 'utf-8'));\n globalThis.__gkm_credentials__ = __secrets;\n Object.assign(process.env, __secrets);\n}\n\n`\n\t\t: '';\n\n\tconst serveCode =\n\t\truntime === 'bun'\n\t\t\t? `Bun.serve({\n port,\n fetch: app.fetch,\n });`\n\t\t\t: `const { serve } = await import('@hono/node-server');\n const server = serve({\n fetch: app.fetch,\n port,\n });\n // Inject WebSocket support if available\n const injectWs = (app as any).__injectWebSocket;\n if (injectWs) {\n injectWs(server);\n console.log('🔌 Telescope real-time updates enabled');\n }`;\n\n\treturn `#!/usr/bin/env node\n/**\n * Development server entry point\n * This file is auto-generated by 'gkm dev'\n */\n${credentialsInjection}\nconst port = process.argv.includes('--port')\n ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])\n : 3000;\n\n// Dynamic import so Credentials are populated before env.ts evaluates\nconst { createApp } = await import('${appImportPath}');\n\n// createApp is async to support optional WebSocket setup\nconst { app, start } = await createApp(undefined, ${enableOpenApi});\n\n// Start the server\nstart({\n port,\n serve: async (app, port) => {\n ${serveCode}\n },\n}).catch((error) => {\n console.error('Failed to start server:', error);\n process.exit(1);\n});\n`;\n}\n\nclass DevServer {\n\tprivate serverProcess: ChildProcess | null = null;\n\tprivate isRunning = false;\n\tprivate actualPort: number;\n\tprivate startTime = Date.now();\n\n\tconstructor(\n\t\tprivate provider: LegacyProvider,\n\t\tprivate requestedPort: number,\n\t\tprivate portExplicit: boolean,\n\t\tprivate enableOpenApi: boolean,\n\t\tprivate telescope: NormalizedTelescopeConfig | undefined,\n\t\tprivate studio: NormalizedStudioConfig | undefined,\n\t\tprivate runtime: Runtime = 'node',\n\t\tprivate appRoot: string = process.cwd(),\n\t\tprivate secretsJsonPath?: string,\n\t) {\n\t\tthis.actualPort = requestedPort;\n\t}\n\n\tasync start(): Promise<void> {\n\t\tthis.startTime = Date.now();\n\t\tif (this.isRunning) {\n\t\t\tawait this.stop();\n\t\t}\n\n\t\t// Check port availability\n\t\tif (this.portExplicit) {\n\t\t\t// Port was explicitly specified - throw if unavailable\n\t\t\tconst available = await isPortAvailable(this.requestedPort);\n\t\t\tif (!available) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Port ${this.requestedPort} is already in use. ` +\n\t\t\t\t\t\t`Either stop the process using that port or omit -p/--port to auto-select an available port.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.actualPort = this.requestedPort;\n\t\t} else {\n\t\t\t// Find an available port starting from the default\n\t\t\tthis.actualPort = await findAvailablePort(this.requestedPort);\n\n\t\t\tif (this.actualPort !== this.requestedPort) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst serverEntryPath = join(\n\t\t\tthis.appRoot,\n\t\t\t'.gkm',\n\t\t\tthis.provider,\n\t\t\t'server.ts',\n\t\t);\n\n\t\t// Create server entry file\n\t\tawait this.createServerEntry();\n\n\t\tlogger.log(`\\n⏳ Starting server...`);\n\n\t\t// Start the server using tsx (TypeScript execution)\n\t\t// Use detached: true so we can kill the entire process tree\n\t\tthis.serverProcess = spawn(\n\t\t\t'npx',\n\t\t\t['tsx', serverEntryPath, '--port', this.actualPort.toString()],\n\t\t\t{\n\t\t\t\tstdio: 'inherit',\n\t\t\t\tenv: { ...process.env, NODE_ENV: 'development' },\n\t\t\t\tdetached: true,\n\t\t\t},\n\t\t);\n\n\t\tthis.isRunning = true;\n\n\t\tthis.serverProcess.on('error', (error) => {\n\t\t\tlogger.error('❌ Server error:', error);\n\t\t});\n\n\t\tthis.serverProcess.on('exit', (code, signal) => {\n\t\t\tif (code !== null && code !== 0 && signal !== 'SIGTERM') {\n\t\t\t\tlogger.error(`❌ Server exited with code ${code}`);\n\t\t\t}\n\t\t\tthis.isRunning = false;\n\t\t});\n\n\t\t// Give the server a moment to start\n\t\tawait new Promise((resolve) => setTimeout(resolve, 1000));\n\n\t\tif (this.isRunning) {\n\t\t\tconst base = `http://localhost:${this.actualPort}`;\n\t\t\tconst lines: string[] = [` Local: ${base}`];\n\t\t\tif (this.enableOpenApi) {\n\t\t\t\tlines.push(` API Docs: ${base}/__docs`);\n\t\t\t}\n\t\t\tif (this.telescope) {\n\t\t\t\tlines.push(` Telescope: ${base}${this.telescope.path}`);\n\t\t\t}\n\t\t\tif (this.studio) {\n\t\t\t\tlines.push(` Studio: ${base}${this.studio.path}`);\n\t\t\t}\n\n\t\t\tconst maxLen = Math.max(...lines.map((l) => l.length));\n\t\t\tconst pad = (s: string) => s.padEnd(maxLen);\n\t\t\tconst border = '─'.repeat(maxLen + 2);\n\n\t\t\tlogger.log('');\n\t\t\tlogger.log(\n\t\t\t\t` \\x1b[32m✓ Ready\\x1b[0m in ${((Date.now() - this.startTime) / 1000).toFixed(1)}s`,\n\t\t\t);\n\t\t\tlogger.log('');\n\t\t\tlogger.log(` ┌${border}┐`);\n\t\t\tfor (const line of lines) {\n\t\t\t\tlogger.log(` │ ${pad(line)} │`);\n\t\t\t}\n\t\t\tlogger.log(` └${border}┘`);\n\t\t\tlogger.log('');\n\t\t}\n\t}\n\n\tasync stop(): Promise<void> {\n\t\tconst port = this.actualPort;\n\n\t\tif (this.serverProcess && this.isRunning) {\n\t\t\tconst pid = this.serverProcess.pid;\n\n\t\t\t// Use SIGKILL directly since the server ignores SIGTERM\n\t\t\tif (pid) {\n\t\t\t\ttry {\n\t\t\t\t\tprocess.kill(-pid, 'SIGKILL');\n\t\t\t\t} catch {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tprocess.kill(pid, 'SIGKILL');\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Process might already be dead\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.serverProcess = null;\n\t\t\tthis.isRunning = false;\n\t\t}\n\n\t\t// Also kill any processes still holding the port\n\t\tthis.killProcessesOnPort(port);\n\t}\n\n\tprivate killProcessesOnPort(port: number): void {\n\t\ttry {\n\t\t\t// Use lsof to find PIDs on the port and kill them with -9\n\t\t\texecSync(`lsof -ti tcp:${port} | xargs kill -9 2>/dev/null || true`, {\n\t\t\t\tstdio: 'ignore',\n\t\t\t});\n\t\t} catch {\n\t\t\t// Ignore errors - port may already be free\n\t\t}\n\t}\n\n\tasync restart(): Promise<void> {\n\t\tconst portToReuse = this.actualPort;\n\t\tawait this.stop();\n\n\t\t// Wait for port to be released (up to 3 seconds)\n\t\tlet attempts = 0;\n\t\twhile (attempts < 30) {\n\t\t\tif (await isPortAvailable(portToReuse)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 100));\n\t\t\tattempts++;\n\t\t}\n\n\t\t// Force reuse the same port\n\t\tthis.requestedPort = portToReuse;\n\t\tawait this.start();\n\t}\n\n\tprivate async createServerEntry(): Promise<void> {\n\t\tconst { writeFile: fsWriteFile } = await import('node:fs/promises');\n\n\t\tconst serverPath = join(this.appRoot, '.gkm', this.provider, 'server.ts');\n\n\t\tconst content = generateServerEntryContent({\n\t\t\tsecretsJsonPath: this.secretsJsonPath,\n\t\t\truntime: this.runtime,\n\t\t\tenableOpenApi: this.enableOpenApi,\n\t\t});\n\n\t\tawait fsWriteFile(serverPath, content);\n\t}\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { withCompute, withRoutes } from '../reconcile/emit.js';\nimport type {\n\tCronInfo,\n\tFunctionInfo,\n\tQueueInfo,\n\tRouteInfo,\n\tSubscriberInfo,\n\tTopicInfo,\n} from '../types';\n\nconst logger = console;\n\nexport type ManifestProvider = 'aws' | 'server';\n\nexport interface ServerAppInfo {\n\thandler: string;\n\tendpoints: string;\n}\n\n/**\n * A manifest field is either a flat array (no partition) or\n * an object keyed by partition name (partitioned).\n */\nexport type ManifestField<T> = T[] | Record<string, T[]>;\n\nfunction isPartitioned<T>(\n\tfield: ManifestField<T>,\n): field is Record<string, T[]> {\n\treturn !Array.isArray(field);\n}\n\n/**\n * Serialize a manifest field to a TypeScript string.\n * Flat arrays serialize as JSON arrays, partitioned fields as objects of arrays.\n */\nfunction serializeField<T>(field: ManifestField<T>, indent = 2): string {\n\tif (Array.isArray(field)) {\n\t\treturn JSON.stringify(field, null, indent);\n\t}\n\t// Partitioned: { admin: [...], default: [...] }\n\tconst entries = Object.entries(field)\n\t\t.map(\n\t\t\t([key, value]) =>\n\t\t\t\t` ${JSON.stringify(key)}: ${JSON.stringify(value, null, indent)}`,\n\t\t)\n\t\t.join(',\\n');\n\treturn `{\\n${entries},\\n }`;\n}\n\n/**\n * Count total items across a manifest field (flat or partitioned).\n */\nfunction countItems<T>(field: ManifestField<T>): number {\n\tif (Array.isArray(field)) return field.length;\n\treturn Object.values(field).reduce((sum, arr) => sum + arr.length, 0);\n}\n\n/**\n * Generate derived types for a construct field.\n * @param fieldName - The field name in the manifest (e.g., 'routes')\n * @param typeName - The exported type name (e.g., 'Route')\n * @param partitioned - Whether this field is partitioned\n */\nfunction generateDerivedType(\n\tfieldName: string,\n\ttypeName: string,\n\tpartitioned: boolean,\n): string {\n\tif (partitioned) {\n\t\tconst partitionTypeName = `${typeName}Partition`;\n\t\treturn [\n\t\t\t`export type ${partitionTypeName} = keyof typeof manifest.${fieldName};`,\n\t\t\t`export type ${typeName}<P extends ${partitionTypeName} = ${partitionTypeName}> = (typeof manifest.${fieldName})[P][number];`,\n\t\t].join('\\n');\n\t}\n\treturn `export type ${typeName} = (typeof manifest.${fieldName})[number];`;\n}\n\n/** A manifest field as a flat list, whether or not it is partitioned. */\nfunction flatten<T>(field: ManifestField<T>): T[] {\n\treturn Array.isArray(field) ? field : Object.values(field).flat();\n}\n\nexport async function generateAwsManifest(\n\toutputDir: string,\n\troutes: ManifestField<RouteInfo>,\n\tfunctions: ManifestField<FunctionInfo>,\n\tcrons: ManifestField<CronInfo>,\n\tsubscribers: ManifestField<SubscriberInfo>,\n\tqueues: ManifestField<QueueInfo> = [],\n\ttopics: ManifestField<TopicInfo> = [],\n\t/**\n\t * The construct manifest, and the backends this build resolved.\n\t *\n\t * Here rather than in a file of its own: one manifest per target, holding\n\t * everything that target needs, is the structure this directory already has.\n\t * A parallel `.gkm/manifest.ts` would be a second document describing the\n\t * same build, and the handler paths in it are target-specific anyway.\n\t */\n\tconstructs: ConstructManifest = {},\n\tbackends: { cache?: string; email?: string } = {},\n): Promise<void> {\n\tconst manifestDir = join(outputDir, 'manifest');\n\tawait mkdir(manifestDir, { recursive: true });\n\n\t// Filter out 'ALL' method routes (server-specific)\n\tconst awsRoutes = filterAllRoutes(routes);\n\n\tconst routesPartitioned = isPartitioned(awsRoutes);\n\tconst functionsPartitioned = isPartitioned(functions);\n\tconst cronsPartitioned = isPartitioned(crons);\n\tconst subscribersPartitioned = isPartitioned(subscribers);\n\tconst queuesPartitioned = isPartitioned(queues);\n\tconst topicsPartitioned = isPartitioned(topics);\n\n\t// The routes the constructs manifest carries are this target's, so they are\n\t// filtered the same way — an `ALL` catch-all describes the server build, not\n\t// the application.\n\n\t// Routes first, then the compute they sit beside: a queue's worker and a\n\t// topic's subscriber nest inside the resource that triggers them, so the\n\t// resource has to be there before they can be folded in.\n\tconst awsConstructs = withCompute(\n\t\twithRoutes(constructs, flatten(awsRoutes), { perRoute: true }),\n\t\t{\n\t\t\tfunctions: flatten(functions),\n\t\t\tcrons: flatten(crons),\n\t\t\tqueues: flatten(queues),\n\t\t\tsubscribers: flatten(subscribers),\n\t\t},\n\t);\n\n\tconst content = `export const manifest = {\n routes: ${serializeField(awsRoutes)},\n functions: ${serializeField(functions)},\n crons: ${serializeField(crons)},\n subscribers: ${serializeField(subscribers)},\n queues: ${serializeField(queues)},\n topics: ${serializeField(topics)},\n} as const;\n\n/**\n * What the application declares, as opposed to what this build generated.\n *\n * Discovery imports application code, so it runs once here rather than in every\n * consumer — a deploy config calling it would evaluate the whole runtime graph\n * inside its own toolchain.\n */\nexport const constructs = ${JSON.stringify(awsConstructs, null, 2)} as const;\n\n/**\n * Where the backends that are config rather than declaration resolved to.\n *\n * Recorded because they were answered twice: once by the build, which registers\n * exactly one cache driver, and once by a deploy, which told the provisioner\n * something else — leaving the running code with a URL it had no driver for.\n */\nexport const backends = ${JSON.stringify(backends, null, 2)} as const;\n\n// Derived types\n${generateDerivedType('routes', 'Route', routesPartitioned)}\n${generateDerivedType('functions', 'Function', functionsPartitioned)}\n${generateDerivedType('crons', 'Cron', cronsPartitioned)}\n${generateDerivedType('subscribers', 'Subscriber', subscribersPartitioned)}\n${generateDerivedType('queues', 'Queue', queuesPartitioned)}\n${generateDerivedType('topics', 'Topic', topicsPartitioned)}\n\nexport type ConstructId = keyof typeof constructs;\nexport type Construct<Id extends ConstructId> = (typeof constructs)[Id];\n\n// Useful union types\nexport type Authorizer = Route['authorizer'];\nexport type HttpMethod = Route['method'];\nexport type RoutePath = Route['path'];\n`;\n\n\tconst manifestPath = join(manifestDir, 'aws.ts');\n\tawait writeFile(manifestPath, content);\n\n\tlogger.log(\n\t\t`Generated AWS manifest with ${countItems(awsRoutes)} routes, ${countItems(functions)} functions, ${countItems(crons)} crons, ${countItems(subscribers)} subscribers, ${countItems(queues)} queues, ${countItems(topics)} topics`,\n\t);\n\tlogger.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);\n}\n\nexport async function generateServerManifest(\n\toutputDir: string,\n\tappInfo: ServerAppInfo,\n\troutes: ManifestField<RouteInfo>,\n\tsubscribers: ManifestField<SubscriberInfo>,\n\tqueues: ManifestField<QueueInfo> = [],\n\ttopics: ManifestField<TopicInfo> = [],\n\t/**\n\t * The construct manifest, and the backends this build resolved.\n\t *\n\t * Here rather than in a file of its own: one manifest per target, holding\n\t * everything that target needs, is the structure this directory already has.\n\t * A parallel `.gkm/manifest.ts` would be a second document describing the\n\t * same build, and the handler paths in it are target-specific anyway.\n\t */\n\tconstructs: ConstructManifest = {},\n\tbackends: { cache?: string; email?: string } = {},\n): Promise<void> {\n\tconst manifestDir = join(outputDir, 'manifest');\n\tawait mkdir(manifestDir, { recursive: true });\n\n\t// For server, extract route metadata (path, method, authorizer)\n\tconst serverRoutes = mapRouteMetadata(filterAllRoutes(routes));\n\n\t// Server subscribers only need name and events\n\tconst serverSubscribers = mapSubscriberMetadata(subscribers);\n\n\t// Server queues only need their name (the worker is wired by setupQueues)\n\tconst serverQueues = mapQueueMetadata(queues);\n\n\t// Server topics only need name + events (pg-boss routes by event name locally)\n\tconst serverTopics = mapTopicMetadata(topics);\n\n\tconst routesPartitioned = isPartitioned(serverRoutes);\n\tconst subscribersPartitioned = isPartitioned(serverSubscribers);\n\tconst queuesPartitioned = isPartitioned(serverQueues);\n\tconst topicsPartitioned = isPartitioned(serverTopics);\n\n\t// Unfolded, unlike the AWS manifest's. The server provider generates one\n\t// catch-all handler pointing at the Hono app that routes in-process, so\n\t// attributing its routes to a surface would write `ALL *` into a manifest for\n\t// an app with seven of them — see `withRoutes`. The routes are above; the\n\t// declarations are what this export is for.\n\tconst content = `export const manifest = {\n app: ${JSON.stringify(appInfo, null, 2)},\n routes: ${serializeField(serverRoutes)},\n subscribers: ${serializeField(serverSubscribers)},\n queues: ${serializeField(serverQueues)},\n topics: ${serializeField(serverTopics)},\n} as const;\n\n/**\n * What the application declares, as opposed to what this build generated.\n *\n * Discovery imports application code, so it runs once in the build rather than\n * in every consumer — a deploy config calling it would evaluate the whole\n * runtime graph inside its own toolchain.\n */\nexport const constructs = ${JSON.stringify(constructs, null, 2)} as const;\n\n/**\n * Where the backends that are config rather than declaration resolved to.\n *\n * Recorded because they were answered twice: once by the build, which registers\n * exactly one cache driver, and once by a deploy, which told the provisioner\n * something else — leaving the running code with a URL it had no driver for.\n */\nexport const backends = ${JSON.stringify(backends, null, 2)} as const;\n\n// Derived types\n${generateDerivedType('routes', 'Route', routesPartitioned)}\n${generateDerivedType('subscribers', 'Subscriber', subscribersPartitioned)}\n${generateDerivedType('queues', 'Queue', queuesPartitioned)}\n${generateDerivedType('topics', 'Topic', topicsPartitioned)}\n\nexport type ConstructId = keyof typeof constructs;\nexport type Construct<Id extends ConstructId> = (typeof constructs)[Id];\n\n// Useful union types\nexport type Authorizer = Route['authorizer'];\nexport type HttpMethod = Route['method'];\nexport type RoutePath = Route['path'];\n`;\n\n\tconst manifestPath = join(manifestDir, 'server.ts');\n\tawait writeFile(manifestPath, content);\n\n\tlogger.log(\n\t\t`Generated server manifest with ${countItems(serverRoutes)} routes, ${countItems(serverSubscribers)} subscribers, ${countItems(serverQueues)} queues, ${countItems(serverTopics)} topics`,\n\t);\n\tlogger.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);\n}\n\n/**\n * Filter out 'ALL' method routes from a manifest field (flat or partitioned).\n */\nfunction filterAllRoutes(\n\troutes: ManifestField<RouteInfo>,\n): ManifestField<RouteInfo> {\n\tif (Array.isArray(routes)) {\n\t\treturn routes.filter((r) => r.method !== 'ALL');\n\t}\n\tconst result: Record<string, RouteInfo[]> = {};\n\tfor (const [partition, partitionRoutes] of Object.entries(routes)) {\n\t\tresult[partition] = partitionRoutes.filter((r) => r.method !== 'ALL');\n\t}\n\treturn result;\n}\n\n/**\n * Map routes to server metadata (path, method, authorizer only).\n */\nfunction mapRouteMetadata(\n\troutes: ManifestField<RouteInfo>,\n): ManifestField<{ path: string; method: string; authorizer: string }> {\n\tconst mapFn = (r: RouteInfo) => ({\n\t\tpath: r.path,\n\t\tmethod: r.method,\n\t\tauthorizer: r.authorizer,\n\t});\n\n\tif (Array.isArray(routes)) {\n\t\treturn routes.map(mapFn);\n\t}\n\tconst result: Record<\n\t\tstring,\n\t\t{ path: string; method: string; authorizer: string }[]\n\t> = {};\n\tfor (const [partition, partitionRoutes] of Object.entries(routes)) {\n\t\tresult[partition] = partitionRoutes.map(mapFn);\n\t}\n\treturn result;\n}\n\n/**\n * Map subscribers to server metadata (name, subscribedEvents only).\n */\nfunction mapSubscriberMetadata(\n\tsubscribers: ManifestField<SubscriberInfo>,\n): ManifestField<{ name: string; subscribedEvents: readonly string[] }> {\n\tconst mapFn = (s: SubscriberInfo) => ({\n\t\tname: s.name,\n\t\tsubscribedEvents: s.subscribedEvents,\n\t});\n\n\tif (Array.isArray(subscribers)) {\n\t\treturn subscribers.map(mapFn);\n\t}\n\tconst result: Record<\n\t\tstring,\n\t\t{ name: string; subscribedEvents: readonly string[] }[]\n\t> = {};\n\tfor (const [partition, partitionSubs] of Object.entries(subscribers)) {\n\t\tresult[partition] = partitionSubs.map(mapFn);\n\t}\n\treturn result;\n}\n\nfunction mapQueueMetadata(\n\tqueues: ManifestField<QueueInfo>,\n): ManifestField<{ name: string }> {\n\tconst mapFn = (q: QueueInfo) => ({ name: q.name });\n\n\tif (Array.isArray(queues)) {\n\t\treturn queues.map(mapFn);\n\t}\n\tconst result: Record<string, { name: string }[]> = {};\n\tfor (const [partition, partitionQueues] of Object.entries(queues)) {\n\t\tresult[partition] = partitionQueues.map(mapFn);\n\t}\n\treturn result;\n}\n\nfunction mapTopicMetadata(\n\ttopics: ManifestField<TopicInfo>,\n): ManifestField<{ name: string; events: readonly string[] }> {\n\tconst mapFn = (t: TopicInfo) => ({ name: t.name, events: t.events });\n\n\tif (Array.isArray(topics)) {\n\t\treturn topics.map(mapFn);\n\t}\n\tconst result: Record<string, { name: string; events: readonly string[] }[]> =\n\t\t{};\n\tfor (const [partition, partitionTopics] of Object.entries(topics)) {\n\t\tresult[partition] = partitionTopics.map(mapFn);\n\t}\n\treturn result;\n}\n","import type { Construct } from '@geekmidas/constructs';\nimport type { GeneratedConstruct } from '../generators/Generator';\n\nexport const DEFAULT_PARTITION = 'default';\n\n/**\n * Group constructs by their partition field.\n * Constructs without a partition are placed in the 'default' group.\n */\nexport function groupByPartition<T extends Construct>(\n\tconstructs: GeneratedConstruct<T>[],\n): Map<string, GeneratedConstruct<T>[]> {\n\tconst groups = new Map<string, GeneratedConstruct<T>[]>();\n\n\tfor (const construct of constructs) {\n\t\tconst partition = construct.partition ?? DEFAULT_PARTITION;\n\t\tconst group = groups.get(partition);\n\t\tif (group) {\n\t\t\tgroup.push(construct);\n\t\t} else {\n\t\t\tgroups.set(partition, [construct]);\n\t\t}\n\t}\n\n\treturn groups;\n}\n\n/**\n * Check if any construct across the given arrays has a non-undefined partition.\n * When true, the manifest should use the partitioned shape for that construct type.\n */\nexport function hasPartitions<T extends Construct>(\n\tconstructs: GeneratedConstruct<T>[],\n): boolean {\n\treturn constructs.some((c) => c.partition !== undefined);\n}\n\n/**\n * Group an info array by partition, using the partition values from\n * the corresponding construct array. Both arrays must be the same length\n * and in the same order.\n */\nexport function groupInfosByPartition<T>(\n\tinfos: T[],\n\tconstructs: GeneratedConstruct<any>[],\n): Record<string, T[]> {\n\tconst groups: Record<string, T[]> = {};\n\n\tfor (let i = 0; i < infos.length; i++) {\n\t\tconst partition = constructs[i]?.partition ?? DEFAULT_PARTITION;\n\t\tif (!groups[partition]) {\n\t\t\tgroups[partition] = [];\n\t\t}\n\t\tgroups[partition].push(infos[i]!);\n\t}\n\n\treturn groups;\n}\n","/**\n * The entry for a surface that serves itself.\n *\n * Two lines, because the construct owns the serving. A generator writing the\n * mounts would be a second description of routes the declaration already\n * carries — which is how Better Auth came to be mounted by a hand-written hook,\n * and then served by nothing at all when that hook was deleted for duplicating\n * CORS the graph had started deriving.\n *\n * The import specifier comes from discovery, which globbed the file and\n * imported it. Nothing about the surface is named here.\n */\nasync function writeSurfaceEntry(\n\toutputDir: string,\n\tsource: ConstructSource,\n): Promise<void> {\n\tawait mkdir(outputDir, { recursive: true });\n\n\tconst rel = relative(outputDir, source.file).replace(/\\.ts$/, '.js');\n\tconst specifier = rel.startsWith('.') ? rel : `./${rel}`;\n\n\tawait writeFile(\n\t\tjoin(outputDir, 'app.ts'),\n\t\t`/**\n * Generated entry for a surface that serves itself.\n *\n * Its routes, its CORS origins and its clients all come from the construct's\n * own declaration. This file exists only to start it.\n */\nimport { snifferContext } from '@geekmidas/constructs';\nimport { defaultEnvParser } from '@geekmidas/constructs/endpoints';\nimport { ${source.exportName} as surface } from '${specifier}';\n\nconst envParser = defaultEnvParser();\n\nexport const { app } = await surface.server({ envParser, context: snifferContext });\n\nexport default app;\n`,\n\t);\n}\n\nimport { spawn } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport type { Cron } from '@geekmidas/constructs/crons';\nimport type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport type { Function } from '@geekmidas/constructs/functions';\nimport type { Queue } from '@geekmidas/constructs/queue';\nimport type { Subscriber } from '@geekmidas/constructs/subscribers';\nimport type { Topic } from '@geekmidas/constructs/topic';\nimport { provideKey } from '@geekmidas/manifest';\nimport {\n\tloadAppConfig,\n\tloadConfig,\n\tloadWorkspaceConfig,\n\tparseModuleConfig,\n} from '../config';\nimport {\n\tgetProductionConfigFromGkm,\n\tnormalizeHooksConfig,\n\tnormalizeProductionConfig,\n\tnormalizeStudioConfig,\n\tnormalizeTelescopeConfig,\n} from '../dev';\nimport {\n\tCronGenerator,\n\tcacheBackendsIn,\n\tdriversFor,\n\tEndpointGenerator,\n\tFunctionGenerator,\n\ttype GeneratedConstruct,\n\tQueueGenerator,\n\tSubscriberGenerator,\n\tTopicGenerator,\n} from '../generators';\nimport { generateOpenApi, openapiCommand } from '../openapi.js';\nimport { type ConstructSource, discover } from '../reconcile/discover.js';\nimport {\n\ttype BuildOptions,\n\ttype BuildResult,\n\tisPartitionedRoutes,\n\ttype LegacyProvider,\n\ttype RouteInfo,\n\ttype Routes,\n} from '../types';\nimport { cacheBackendOf, emailBackendOf } from '../workspace/backends.js';\nimport { resolveAppSpec } from '../workspace/derive.js';\nimport {\n\tallConstructGlobs,\n\tgetAppBuildOrder,\n\ttype NormalizedAppConfig,\n\ttype NormalizedWorkspace,\n} from '../workspace/index.js';\nimport {\n\tgenerateAwsManifest,\n\tgenerateServerManifest,\n\ttype ManifestField,\n\ttype ServerAppInfo,\n} from './manifests';\nimport { groupInfosByPartition, hasPartitions } from './partitions';\nimport { resolveProviders } from './providerResolver';\nimport type { BuildContext } from './types';\n\nconst logger = console;\n\nexport async function buildCommand(\n\toptions: BuildOptions,\n): Promise<BuildResult> {\n\t// Load config with workspace detection\n\tconst loadedConfig = await loadWorkspaceConfig();\n\n\t// Route to workspace build mode for multi-app workspaces\n\t// BUT only if we're at the workspace root (prevents recursive builds when\n\t// Turbo runs gkm build in each app subdirectory)\n\tif (loadedConfig.type === 'workspace') {\n\t\tconst cwd = resolve(process.cwd());\n\t\tconst workspaceRoot = resolve(loadedConfig.workspace.root);\n\t\tconst isAtWorkspaceRoot = cwd === workspaceRoot;\n\n\t\tif (isAtWorkspaceRoot) {\n\t\t\tlogger.log('📦 Detected workspace configuration');\n\t\t\treturn workspaceBuildCommand(loadedConfig.workspace, options);\n\t\t}\n\t\t// When running from inside an app directory, use app-specific config\n\t}\n\n\t// Single-app build - use app config if in workspace, otherwise legacy config\n\tconst config =\n\t\tloadedConfig.type === 'workspace'\n\t\t\t? (await loadAppConfig()).gkmConfig\n\t\t\t: await loadConfig();\n\n\t// Resolve providers from new config format\n\tconst resolved = resolveProviders(config, options);\n\n\t// One answer for which backends this app uses, read once. The build\n\t// registers drivers for it and records it in the manifest, so a deploy\n\t// cannot pick differently and hand the running code a URL it has no driver\n\t// for.\n\tconst backendConfig =\n\t\t(config as { services?: { cache?: unknown; mail?: unknown } }).services ??\n\t\t(\n\t\t\tloadedConfig as {\n\t\t\t\tworkspace?: { services?: { cache?: unknown; mail?: unknown } };\n\t\t\t}\n\t\t).workspace?.services;\n\tconst cacheBackend = cacheBackendOf(backendConfig?.cache);\n\n\t// Normalize production configuration\n\tconst productionConfigFromGkm = getProductionConfigFromGkm(config);\n\tconst production = normalizeProductionConfig(\n\t\toptions.production ?? false,\n\t\tproductionConfigFromGkm,\n\t);\n\n\tif (production) {\n\t\tlogger.log(`🏭 Building for PRODUCTION`);\n\t}\n\n\tlogger.log(`Building with providers: ${resolved.providers.join(', ')}`);\n\tlogger.log(`Loading routes from: ${formatRoutes(config.routes)}`);\n\tif (config.functions) {\n\t\tlogger.log(`Loading functions from: ${formatRoutes(config.functions)}`);\n\t}\n\tif (config.crons) {\n\t\tlogger.log(`Loading crons from: ${formatRoutes(config.crons)}`);\n\t}\n\tif (config.subscribers) {\n\t\tlogger.log(`Loading subscribers from: ${formatRoutes(config.subscribers)}`);\n\t}\n\tlogger.log(`Using envParser: ${config.envParser}`);\n\n\t// Parse envParser and logger configuration\n\tconst { path: envParserPath, importPattern: envParserImportPattern } =\n\t\tparseModuleConfig(config.envParser, 'envParser');\n\tconst { path: loggerPath, importPattern: loggerImportPattern } =\n\t\tparseModuleConfig(config.logger, 'logger');\n\n\t// Normalize telescope configuration (disabled in production)\n\tconst telescope = production\n\t\t? undefined\n\t\t: normalizeTelescopeConfig(config.telescope);\n\tif (telescope) {\n\t\tlogger.log(`🔭 Telescope enabled at ${telescope.path}`);\n\t}\n\n\t// Normalize studio configuration (disabled in production)\n\tconst studio = production ? undefined : normalizeStudioConfig(config.studio);\n\tif (studio) {\n\t\tlogger.log(`🗄️ Studio enabled at ${studio.path}`);\n\t}\n\n\t// Normalize hooks configuration\n\tconst hooks = normalizeHooksConfig(config.hooks);\n\tif (hooks) {\n\t\tlogger.log(`🪝 Server hooks enabled`);\n\t}\n\n\t// Extract docker compose services for env var auto-population\n\tconst services = config.docker?.compose?.services;\n\tconst dockerServices = services\n\t\t? Array.isArray(services)\n\t\t\t? {\n\t\t\t\t\tpostgres: services.includes('postgres'),\n\t\t\t\t\tredis: services.includes('redis'),\n\t\t\t\t\trabbitmq: services.includes('rabbitmq'),\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tpostgres: Boolean(services.postgres),\n\t\t\t\t\tredis: Boolean(services.redis),\n\t\t\t\t\trabbitmq: Boolean(services.rabbitmq),\n\t\t\t\t}\n\t\t: undefined;\n\n\t// `constructs` accepts the partitioned shape every other glob does; only the\n\t// flat forms name a construct file.\n\tconst constructGlobs =\n\t\ttypeof config.constructs === 'string' || Array.isArray(config.constructs)\n\t\t\t? config.constructs\n\t\t\t: undefined;\n\n\t// Discovery imports application code, so it runs once here and everything\n\t// downstream reads what it wrote — a deploy config calling it would evaluate\n\t// the whole runtime graph inside its own toolchain. It runs before the build\n\t// context rather than after the generators because the entry point's drivers\n\t// are decided from it.\n\tconst constructSources: Record<string, ConstructSource> = {};\n\tconst declared = constructGlobs\n\t\t? await discover({\n\t\t\t\tpatterns: constructGlobs,\n\t\t\t\tcwd: process.cwd(),\n\t\t\t\tsources: constructSources,\n\t\t\t})\n\t\t: {};\n\n\t// Which surface this server answers on, so the entry can derive its CORS.\n\t//\n\t// The app's own API, not the auth server it may also mount: an auth surface\n\t// declares its own endpoints, and the origins that may call *it* are a\n\t// different list from the ones that may call the API.\n\tconst surfaces = Object.values(declared).filter(\n\t\t(d): d is Extract<typeof d, { kind: 'rest-api' }> => d.kind === 'rest-api',\n\t);\n\tconst primary = surfaces.find((d) => d.endpoints.length === 0) ?? surfaces[0];\n\n\t// Which database Studio browses: the one the app declared. A reader is not a\n\t// candidate — it is the same data through a role that cannot write, so\n\t// browsing it would be the same rows under a second name.\n\tconst browsable = Object.entries(declared).find(\n\t\t([, d]) => d.kind === 'database',\n\t);\n\tconst browsableSource = browsable\n\t\t? constructSources[browsable[0]]\n\t\t: undefined;\n\n\tconst buildContext: BuildContext = {\n\t\t...(primary\n\t\t\t? {\n\t\t\t\t\tsurface: {\n\t\t\t\t\t\tid: primary.id,\n\t\t\t\t\t\ttrustedOriginsKey: provideKey(primary.id, 'trustedOrigins'),\n\t\t\t\t\t\t...(primary.cors ? { cors: primary.cors } : {}),\n\t\t\t\t\t\t...(constructSources[primary.id]\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tmodule: {\n\t\t\t\t\t\t\t\t\t\tspecifier: constructSources[primary.id]!.file,\n\t\t\t\t\t\t\t\t\t\texportName: constructSources[primary.id]!.exportName,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t\tenvParserPath,\n\t\tenvParserImportPattern,\n\t\tloggerPath,\n\t\tloggerImportPattern,\n\t\ttelescope,\n\t\tstudio:\n\t\t\tstudio && browsableSource\n\t\t\t\t? {\n\t\t\t\t\t\t...studio,\n\t\t\t\t\t\tdatabase: {\n\t\t\t\t\t\t\tspecifier: browsableSource.file,\n\t\t\t\t\t\t\texportName: browsableSource.exportName,\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: studio,\n\t\thooks,\n\t\tproduction,\n\t\tdockerServices,\n\t\tconstructGlobs,\n\t\tcacheBackend,\n\t\temailBackend: emailBackendOf(backendConfig?.mail),\n\t\t// Both halves of \"where does the cache live\": the declaration for one\n\t\t// that named its database, and `services.cache` for one that named\n\t\t// nowhere. Reading only the config registers a driver for a protocol the\n\t\t// target never composes.\n\t\tstorageDrivers: driversFor({\n\t\t\tappRoot: process.cwd(),\n\t\t\tcache: cacheBackendsIn(declared, cacheBackend),\n\t\t}),\n\t\tmarkOptional: options.markOptional ?? false,\n\t};\n\n\t// Initialize generators\n\tconst endpointGenerator = new EndpointGenerator();\n\tconst functionGenerator = new FunctionGenerator();\n\tconst cronGenerator = new CronGenerator();\n\tconst subscriberGenerator = new SubscriberGenerator();\n\tconst queueGenerator = new QueueGenerator();\n\tconst topicGenerator = new TopicGenerator();\n\n\t// Load all constructs in parallel\n\tconst [\n\t\tallEndpoints,\n\t\tallFunctions,\n\t\tallCrons,\n\t\tallSubscribers,\n\t\tallQueues,\n\t\tallTopics,\n\t] = await Promise.all([\n\t\tendpointGenerator.load(config.routes),\n\t\tconfig.functions ? functionGenerator.load(config.functions) : [],\n\t\tconfig.crons ? cronGenerator.load(config.crons) : [],\n\t\tconfig.subscribers ? subscriberGenerator.load(config.subscribers) : [],\n\t\tconfig.queues ? queueGenerator.load(config.queues) : [],\n\t\tconfig.topics ? topicGenerator.load(config.topics) : [],\n\t]);\n\n\tlogger.log(`Found ${allEndpoints.length} endpoints`);\n\tlogger.log(`Found ${allFunctions.length} functions`);\n\tlogger.log(`Found ${allCrons.length} crons`);\n\tlogger.log(`Found ${allSubscribers.length} subscribers`);\n\tlogger.log(`Found ${allQueues.length} queues`);\n\tlogger.log(`Found ${allTopics.length} topics`);\n\n\tif (\n\t\tallEndpoints.length === 0 &&\n\t\tallFunctions.length === 0 &&\n\t\tallCrons.length === 0 &&\n\t\tallSubscribers.length === 0 &&\n\t\tallQueues.length === 0 &&\n\t\tallTopics.length === 0\n\t) {\n\t\t// A surface whose routes are *declared* rather than discovered — an auth\n\t\t// server's wildcard. There is nothing for a glob to find, and that is not\n\t\t// an empty app: the construct serves itself and the entry only starts it.\n\t\tconst selfServing = Object.entries(declared).find(\n\t\t\t([id, d]) =>\n\t\t\t\td.kind === 'rest-api' &&\n\t\t\t\td.endpoints.length > 0 &&\n\t\t\t\tconstructSources[id] !== undefined &&\n\t\t\t\t// Resolved against the workspace root, not matched on the end of\n\t\t\t\t// the path — `apps/api` is a suffix of `other-apps/api` too.\n\t\t\t\t// Through `resolveAppSpec`, because a surface normally names no\n\t\t\t\t// path and the default is the one this would otherwise repeat.\n\t\t\t\tresolve(\n\t\t\t\t\tloadedConfig.workspace.root,\n\t\t\t\t\tresolveAppSpec(\n\t\t\t\t\t\tid,\n\t\t\t\t\t\td.app ?? {},\n\t\t\t\t\t\tloadedConfig.workspace.root,\n\t\t\t\t\t\t'rest-api',\n\t\t\t\t\t).path!,\n\t\t\t\t) === resolve(process.cwd()),\n\t\t);\n\n\t\tif (selfServing) {\n\t\t\tconst [id] = selfServing;\n\n\t\t\tawait writeSurfaceEntry(\n\t\t\t\tjoin(process.cwd(), '.gkm', 'server'),\n\t\t\t\tconstructSources[id]!,\n\t\t\t);\n\t\t\tlogger.log(`Generated a server for ${id} from its own declaration`);\n\n\t\t\treturn {};\n\t\t}\n\n\t\tlogger.log(\n\t\t\t'No endpoints, functions, crons, subscribers, queues, or topics found to process',\n\t\t);\n\t\treturn {};\n\t}\n\n\t// Ensure .gkm directory exists\n\tconst rootOutputDir = join(process.cwd(), '.gkm');\n\tawait mkdir(rootOutputDir, { recursive: true });\n\n\t// Build for each provider and generate per-provider manifests\n\tlet result: BuildResult = {};\n\tfor (const provider of resolved.providers) {\n\t\tconst providerResult = await buildForProvider(\n\t\t\tprovider,\n\t\t\tbuildContext,\n\t\t\trootOutputDir,\n\t\t\tendpointGenerator,\n\t\t\tfunctionGenerator,\n\t\t\tcronGenerator,\n\t\t\tsubscriberGenerator,\n\t\t\tqueueGenerator,\n\t\t\ttopicGenerator,\n\t\t\tallEndpoints,\n\t\t\tallFunctions,\n\t\t\tallCrons,\n\t\t\tallSubscribers,\n\t\t\tallQueues,\n\t\t\tallTopics,\n\t\t\tresolved.enableOpenApi,\n\t\t\toptions.skipBundle ?? false,\n\t\t\toptions.stage,\n\t\t);\n\t\t// Keep the master key from the server provider\n\t\tif (providerResult.masterKey) {\n\t\t\tresult = providerResult;\n\t\t}\n\t}\n\n\t// Generate OpenAPI spec as part of the build\n\tawait generateOpenApi(config, { bustCache: true });\n\n\treturn result;\n}\n\nasync function buildForProvider(\n\tprovider: LegacyProvider,\n\tcontext: BuildContext,\n\trootOutputDir: string,\n\tendpointGenerator: EndpointGenerator,\n\tfunctionGenerator: FunctionGenerator,\n\tcronGenerator: CronGenerator,\n\tsubscriberGenerator: SubscriberGenerator,\n\tqueueGenerator: QueueGenerator,\n\ttopicGenerator: TopicGenerator,\n\tendpoints: GeneratedConstruct<Endpoint<any, any, any, any, any, any>>[],\n\tfunctions: GeneratedConstruct<Function<any, any, any, any>>[],\n\tcrons: GeneratedConstruct<Cron<any, any, any, any>>[],\n\tsubscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n\tqueues: GeneratedConstruct<Queue<any, any, any, any>>[],\n\ttopics: GeneratedConstruct<Topic<any, any>>[],\n\tenableOpenApi: boolean,\n\tskipBundle: boolean,\n\tstage?: string,\n): Promise<BuildResult> {\n\tconst outputDir = join(process.cwd(), '.gkm', provider);\n\n\t// Ensure output directory exists\n\tawait mkdir(outputDir, { recursive: true });\n\n\tlogger.log(`\\nGenerating handlers for provider: ${provider}`);\n\n\t// Build all constructs in parallel.\n\t// context.markOptional is forwarded to each generator so that\n\t// getEnvironment({ markOptional }) produces `VARNAME?` for optional vars.\n\tconst [\n\t\troutes,\n\t\tfunctionInfos,\n\t\tcronInfos,\n\t\tsubscriberInfos,\n\t\tqueueInfos,\n\t\ttopicInfos,\n\t] = await Promise.all([\n\t\tendpointGenerator.build(context, endpoints, outputDir, {\n\t\t\tprovider,\n\t\t\tenableOpenApi,\n\t\t}),\n\t\tfunctionGenerator.build(context, functions, outputDir, { provider }),\n\t\tcronGenerator.build(context, crons, outputDir, { provider }),\n\t\tsubscriberGenerator.build(context, subscribers, outputDir, { provider }),\n\t\tqueueGenerator.build(context, queues, outputDir, { provider }),\n\t\ttopicGenerator.build(context, topics, outputDir, { provider }),\n\t]);\n\n\tlogger.log(\n\t\t`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers, ${queueInfos.length} queues, ${topicInfos.length} topics for ${provider}`,\n\t);\n\n\t// Assemble manifest fields (flat or partitioned per construct type)\n\tconst manifestRoutes = assembleManifestField(routes, endpoints);\n\n\t// Discovery imports application code, so it runs once here and everything\n\t// downstream reads what it wrote — a deploy config calling it would evaluate\n\t// the whole runtime graph inside its own toolchain.\n\tconst discovered = context.constructGlobs\n\t\t? await discover({ patterns: context.constructGlobs, cwd: process.cwd() })\n\t\t: {};\n\n\tconst manifestFunctions = assembleManifestField(functionInfos, functions);\n\tconst manifestCrons = assembleManifestField(cronInfos, crons);\n\tconst manifestQueues = assembleManifestField(queueInfos, queues);\n\tconst manifestTopics = assembleManifestField(topicInfos, topics);\n\tconst manifestSubscribers = assembleManifestField(\n\t\tsubscriberInfos,\n\t\tsubscribers,\n\t);\n\n\t// Generate provider-specific manifest\n\tif (provider === 'server') {\n\t\t// For server, collect actual route metadata from endpoint constructs\n\t\tconst routeMetadata: RouteInfo[] = await Promise.all(\n\t\t\tendpoints.map(async ({ construct }) => ({\n\t\t\t\tpath: construct._path,\n\t\t\t\tmethod: construct.method,\n\t\t\t\thandler: '', // Not needed for server manifest\n\t\t\t\tauthorizer: construct.authorizer?.name ?? 'none',\n\t\t\t})),\n\t\t);\n\n\t\tconst serverRouteField = assembleManifestField(routeMetadata, endpoints);\n\n\t\tconst appInfo: ServerAppInfo = {\n\t\t\thandler: relative(process.cwd(), join(outputDir, 'app.ts')),\n\t\t\tendpoints: relative(process.cwd(), join(outputDir, 'endpoints.ts')),\n\t\t};\n\n\t\tawait generateServerManifest(\n\t\t\trootOutputDir,\n\t\t\tappInfo,\n\t\t\tserverRouteField,\n\t\t\tmanifestSubscribers,\n\t\t\tmanifestQueues,\n\t\t\tmanifestTopics,\n\t\t);\n\n\t\t// Bundle for production if enabled\n\t\tlet masterKey: string | undefined;\n\t\tif (context.production?.bundle && !skipBundle) {\n\t\t\tlogger.log(`\\n📦 Bundling production server...`);\n\t\t\tconst { bundleServer } = await import('./bundler');\n\n\t\t\t// Collect all constructs for environment variable validation\n\t\t\tconst allConstructs = [\n\t\t\t\t...endpoints.map((e) => e.construct),\n\t\t\t\t...functions.map((f) => f.construct),\n\t\t\t\t...crons.map((c) => c.construct),\n\t\t\t\t...subscribers.map((s) => s.construct),\n\t\t\t\t...queues.map((q) => q.construct),\n\t\t\t];\n\n\t\t\t// Get docker compose services for auto-populating env vars\n\t\t\tconst dockerServices = context.dockerServices;\n\n\t\t\tconst bundleResult = await bundleServer({\n\t\t\t\tentryPoint: join(outputDir, 'server.ts'),\n\t\t\t\toutputDir: join(outputDir, 'dist'),\n\t\t\t\tminify: context.production.minify,\n\t\t\t\tsourcemap: false,\n\t\t\t\texternal: context.production.external,\n\t\t\t\tstage,\n\t\t\t\tconstructs: allConstructs,\n\t\t\t\tdockerServices,\n\t\t\t});\n\t\t\tmasterKey = bundleResult.masterKey;\n\t\t\tlogger.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);\n\n\t\t\t// Display master key if secrets were injected\n\t\t\tif (masterKey) {\n\t\t\t\tlogger.log(`\\n🔐 Secrets encrypted for deployment`);\n\t\t\t\tlogger.log(` Deploy with: GKM_MASTER_KEY=${masterKey}`);\n\t\t\t}\n\t\t}\n\n\t\treturn { masterKey };\n\t} else {\n\t\t// For AWS providers, generate AWS manifest\n\t\tawait generateAwsManifest(\n\t\t\trootOutputDir,\n\t\t\tmanifestRoutes,\n\t\t\tmanifestFunctions,\n\t\t\tmanifestCrons,\n\t\t\tmanifestSubscribers,\n\t\t\tmanifestQueues,\n\t\t\tmanifestTopics,\n\t\t\tdiscovered,\n\t\t\t{ cache: context.cacheBackend, email: context.emailBackend },\n\t\t);\n\t}\n\n\treturn {};\n}\n\n/**\n * Result of building a single app in a workspace.\n */\nexport interface AppBuildResult {\n\tappName: string;\n\ttype: 'backend' | 'web' | 'mobile';\n\tsuccess: boolean;\n\toutputPath?: string;\n\terror?: string;\n}\n\n/**\n * Result of workspace build command.\n */\nexport interface WorkspaceBuildResult extends BuildResult {\n\tapps: AppBuildResult[];\n}\n\n/**\n * Detect available package manager.\n * @internal Exported for testing\n */\nexport function detectPackageManager(): 'pnpm' | 'npm' | 'yarn' {\n\tif (existsSync('pnpm-lock.yaml')) return 'pnpm';\n\tif (existsSync('yarn.lock')) return 'yarn';\n\treturn 'npm';\n}\n\n/**\n * Get the turbo command for running builds.\n *\n * `filters` names the packages to build. Passing none lets turbo infer its own\n * scope from the working directory, which for a workspace root means the\n * workspace package itself — and since that package's `build` script is\n * `gkm build`, inferring is how you get a build that runs itself forever.\n * `workspaceBuildCommand` always names the apps.\n *\n * @internal Exported for testing\n */\nexport function getTurboCommand(\n\tpm: 'pnpm' | 'npm' | 'yarn',\n\tfilters: string | readonly string[] = [],\n): string {\n\tconst list = typeof filters === 'string' ? [filters] : filters;\n\tconst filterArgs = list.map((f) => ` --filter=${f}`).join('');\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn `pnpm exec turbo run build${filterArgs}`;\n\t\tcase 'yarn':\n\t\t\treturn `yarn turbo run build${filterArgs}`;\n\t\tcase 'npm':\n\t\t\treturn `npx turbo run build${filterArgs}`;\n\t}\n}\n\n/**\n * The package names turbo should build: one per app in the workspace.\n *\n * Read from each app's own `package.json`, because that is the name turbo\n * knows it by — and the name `loadAppConfig` reads back when turbo runs\n * `gkm build` inside the app, which is what routes that invocation to the\n * single-app path instead of back here.\n *\n * An app without a `package.json` is not a package turbo can run, so it is\n * reported rather than silently skipped.\n *\n * @internal Exported for testing\n */\nexport function turboFilters(workspace: NormalizedWorkspace): {\n\tfilters: string[];\n\tunpackaged: string[];\n} {\n\tconst filters: string[] = [];\n\tconst unpackaged: string[] = [];\n\n\tfor (const [appName, app] of Object.entries(workspace.apps)) {\n\t\tconst pkgPath = join(workspace.root, app.path, 'package.json');\n\t\tif (!existsSync(pkgPath)) {\n\t\t\tunpackaged.push(appName);\n\t\t\tcontinue;\n\t\t}\n\t\tconst name = JSON.parse(readFileSync(pkgPath, 'utf8')).name;\n\t\tif (typeof name === 'string' && name.length > 0) {\n\t\t\tfilters.push(name);\n\t\t} else {\n\t\t\tunpackaged.push(appName);\n\t\t}\n\t}\n\n\treturn { filters, unpackaged };\n}\n\n/**\n * The directory turbo treats as the repo root: the nearest ancestor with a\n * `turbo.json`. Its own path is what `$TURBO_ROOT$` resolves to.\n */\nfunction turboRoot(from: string): string | undefined {\n\tlet dir = resolve(from);\n\n\tfor (;;) {\n\t\tif (existsSync(join(dir, 'turbo.json'))) return dir;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return undefined;\n\t\tdir = parent;\n\t}\n}\n\n/**\n * Write each app's turbo task config, so a construct edit invalidates the build\n * that read it.\n *\n * Turbo hashes a package's own files. An app's constructs live *above* it, in\n * the workspace that declares them for everybody — so without this, editing a\n * database or a surface leaves every cached app build in place and the apps\n * keep serving a graph that no longer exists. It is a silent wrong answer,\n * which is the worst kind: the build succeeds and the output is stale.\n *\n * Generated rather than documented because the globs are already known here and\n * a hand-written copy is one more list to keep in step — the same reason the\n * apps themselves are no longer written down. A file that someone has since\n * edited by hand is left alone.\n */\nexport async function writeTurboConfigs(\n\tworkspace: NormalizedWorkspace,\n): Promise<void> {\n\tconst root = turboRoot(workspace.root);\n\tif (!root) return;\n\n\tconst globs = allConstructGlobs(workspace).map(\n\t\t(glob) => `$TURBO_ROOT$/${relative(root, glob)}`,\n\t);\n\tif (globs.length === 0) return;\n\n\tconst configPath = join(workspace.root, 'gkm.config.ts');\n\tconst inputs = [\n\t\t'$TURBO_DEFAULT$',\n\t\t...globs,\n\t\t...(existsSync(configPath)\n\t\t\t? [`$TURBO_ROOT$/${relative(root, configPath)}`]\n\t\t\t: []),\n\t];\n\n\tfor (const app of Object.values(workspace.apps)) {\n\t\tconst dir = join(workspace.root, app.path);\n\t\tif (!existsSync(join(dir, 'package.json'))) continue;\n\n\t\tconst file = join(dir, 'turbo.json');\n\t\tconst existing = existsSync(file) ? readFileSync(file, 'utf8') : undefined;\n\t\tif (existing && !existing.includes(GENERATED_BY_GKM)) continue;\n\n\t\tconst content = `{\n\t\"$schema\": \"https://turborepo.com/schema.json\",\n\t${GENERATED_BY_GKM}\n\t\"extends\": [\"//\"],\n\t\"tasks\": {\n\t\t\"build\": {\n\t\t\t\"inputs\": [\n${inputs.map((glob) => `\\t\\t\\t\\t${JSON.stringify(glob)}`).join(',\\n')}\n\t\t\t],\n\t\t\t\"outputs\": [${app.type === 'backend' ? '\".gkm/**\"' : '\"dist/**\", \".next/**\", \"!.next/cache/**\"'}]\n\t\t}\n\t}\n}\n`;\n\n\t\tif (existing === content) continue;\n\t\tawait writeFile(file, content);\n\t}\n}\n\n/** Marks a turbo config as gkm's to rewrite. Edit the file and it is left alone. */\nconst GENERATED_BY_GKM =\n\t'// Generated by `gkm build`. Delete this line to take ownership of the file.';\n\n/**\n * Build all apps in a workspace using Turbo for dependency-ordered parallel builds.\n * @internal Exported for testing\n */\nexport async function workspaceBuildCommand(\n\tworkspace: NormalizedWorkspace,\n\toptions: BuildOptions,\n): Promise<WorkspaceBuildResult> {\n\tconst results: AppBuildResult[] = [];\n\tconst apps = Object.entries(workspace.apps);\n\tconst backendApps = apps.filter(([, app]) => app.type === 'backend');\n\tconst frontendApps = apps.filter(([, app]) => app.type === 'web');\n\n\tlogger.log(`\\n🏗️ Building workspace: ${workspace.name}`);\n\tlogger.log(\n\t\t` Backend apps: ${backendApps.map(([name]) => name).join(', ') || 'none'}`,\n\t);\n\tlogger.log(\n\t\t` Frontend apps: ${frontendApps.map(([name]) => name).join(', ') || 'none'}`,\n\t);\n\n\tif (options.production) {\n\t\tlogger.log(` 🏭 Production mode enabled`);\n\t}\n\n\t// Get build order (topologically sorted by dependencies)\n\tconst buildOrder = getAppBuildOrder(workspace);\n\tlogger.log(` Build order: ${buildOrder.join(' → ')}`);\n\n\t// Use Turbo for parallel builds with dependency awareness\n\tconst pm = detectPackageManager();\n\tlogger.log(`\\n📦 Using ${pm} with Turbo for parallel builds...\\n`);\n\n\ttry {\n\t\t// Before turbo, so the hash it computes includes the constructs this\n\t\t// build reads.\n\t\tawait writeTurboConfigs(workspace);\n\n\t\t// Run turbo build which handles dependency ordering and parallelization\n\t\tconst { filters, unpackaged } = turboFilters(workspace);\n\t\tif (unpackaged.length > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`No package.json for workspace app(s): ${unpackaged.join(', ')}. ` +\n\t\t\t\t\t`Each app needs one — turbo builds packages, and gkm reads the ` +\n\t\t\t\t\t`name back to know which app it is building.`,\n\t\t\t);\n\t\t}\n\t\tconst turboCommand = getTurboCommand(pm, filters);\n\t\tlogger.log(`Running: ${turboCommand}`);\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst child = spawn(turboCommand, {\n\t\t\t\tshell: true,\n\t\t\t\tcwd: workspace.root,\n\t\t\t\tstdio: 'inherit',\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t\t// Pass production flag to builds\n\t\t\t\t\tNODE_ENV: options.production ? 'production' : 'development',\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tchild.on('close', (code) => {\n\t\t\t\tif (code === 0) {\n\t\t\t\t\tresolve();\n\t\t\t\t} else {\n\t\t\t\t\treject(new Error(`Turbo build failed with exit code ${code}`));\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tchild.on('error', (err) => {\n\t\t\t\treject(err);\n\t\t\t});\n\t\t});\n\n\t\t// Mark all apps as successful\n\t\tfor (const [appName, app] of apps) {\n\t\t\tconst outputPath = getAppOutputPath(workspace, appName, app);\n\t\t\tresults.push({\n\t\t\t\tappName,\n\t\t\t\ttype: app.type,\n\t\t\t\tsuccess: true,\n\t\t\t\toutputPath,\n\t\t\t});\n\t\t}\n\n\t\tlogger.log(`\\n✅ Workspace build complete!`);\n\n\t\t// Generate OpenAPI specs and copy to frontend apps\n\t\tawait openapiCommand({ cwd: workspace.root });\n\n\t\t// Summary\n\t\tlogger.log(`\\n📋 Build Summary:`);\n\t\tfor (const result of results) {\n\t\t\tconst icon =\n\t\t\t\tresult.type === 'backend'\n\t\t\t\t\t? '⚙️'\n\t\t\t\t\t: result.type === 'mobile'\n\t\t\t\t\t\t? '📱'\n\t\t\t\t\t\t: '🌐';\n\t\t\tlogger.log(\n\t\t\t\t` ${icon} ${result.appName}: ${result.outputPath || 'built'}`,\n\t\t\t);\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage =\n\t\t\terror instanceof Error ? error.message : 'Build failed';\n\t\tlogger.log(`\\n❌ Build failed: ${errorMessage}`);\n\n\t\t// Mark all apps as failed\n\t\tfor (const [appName, app] of apps) {\n\t\t\tresults.push({\n\t\t\t\tappName,\n\t\t\t\ttype: app.type,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: errorMessage,\n\t\t\t});\n\t\t}\n\n\t\tthrow error;\n\t}\n\n\treturn { apps: results };\n}\n\n/**\n * Get the output path for a built app.\n */\nfunction getAppOutputPath(\n\tworkspace: NormalizedWorkspace,\n\t_appName: string,\n\tapp: NormalizedAppConfig,\n): string {\n\tconst appPath = join(workspace.root, app.path);\n\n\tif (app.type === 'mobile') {\n\t\t// Mobile builds are produced by the framework's own toolchain\n\t\t// (e.g. EAS Build) — no local output path.\n\t\treturn '';\n\t}\n\tif (app.type === 'web') {\n\t\tswitch (app.framework) {\n\t\t\tcase 'vite':\n\t\t\tcase 'tanstack-start':\n\t\t\t\treturn join(appPath, 'dist');\n\t\t\tcase 'remix':\n\t\t\t\treturn join(appPath, 'build');\n\t\t\tdefault:\n\t\t\t\treturn join(appPath, '.next');\n\t\t}\n\t}\n\t// Backend .gkm output\n\treturn join(appPath, '.gkm');\n}\n\n/**\n * Format routes for logging, handling PartitionedRoutes.\n */\nfunction formatRoutes(routes: Routes): string {\n\tif (isPartitionedRoutes(routes)) {\n\t\tconst paths = Array.isArray(routes.paths)\n\t\t\t? routes.paths.join(', ')\n\t\t\t: routes.paths;\n\t\treturn `${paths} (partitioned)`;\n\t}\n\treturn Array.isArray(routes) ? routes.join(', ') : routes;\n}\n\n/**\n * Assemble a ManifestField from build infos and constructs.\n * If any construct has a partition, returns a Record<string, T[]>.\n * Otherwise, returns a flat T[].\n */\nfunction assembleManifestField<T>(\n\tinfos: T[],\n\tconstructs: GeneratedConstruct<any>[],\n): ManifestField<T> {\n\tif (!hasPartitions(constructs)) {\n\t\treturn infos;\n\t}\n\treturn groupInfosByPartition(infos, constructs);\n}\n","let _debug = false;\n\n/**\n * Enable debug mode globally.\n * When enabled, verbose error details are shown everywhere.\n */\nexport function enableDebug(): void {\n\t_debug = true;\n}\n\n/**\n * Check if debug mode is active.\n * Activated by `--debug` flag or `GKM_DEBUG=1` env var.\n */\nexport function isDebug(): boolean {\n\treturn _debug || process.env.GKM_DEBUG === '1';\n}\n\n/**\n * Log a message only when debug mode is active.\n */\nexport function debug(...args: unknown[]): void {\n\tif (isDebug()) {\n\t\tconsole.debug('[debug]', ...args);\n\t}\n}\n\n/**\n * Format a fatal error for display.\n * Always includes the full stack trace since these are process-ending errors.\n */\nexport function formatError(error: unknown): string {\n\tif (!(error instanceof Error)) {\n\t\treturn String(error);\n\t}\n\n\tlet output = error.stack ?? error.message;\n\n\t// Include cause chain if present\n\tlet cause = error.cause;\n\twhile (cause) {\n\t\tif (cause instanceof Error) {\n\t\t\toutput += `\\n\\nCaused by: ${cause.stack ?? cause.message}`;\n\t\t\tcause = cause.cause;\n\t\t} else {\n\t\t\toutput += `\\n\\nCaused by: ${String(cause)}`;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn output;\n}\n\n/**\n * Format a non-fatal error for display.\n * Shows only the message by default, full stack in debug mode.\n */\nexport function formatWarning(error: unknown): string {\n\tif (!(error instanceof Error)) {\n\t\treturn String(error);\n\t}\n\n\tif (!isDebug()) {\n\t\treturn error.message;\n\t}\n\n\treturn error.stack ?? error.message;\n}\n","/**\n * The Dokploy target, read off the construct manifest.\n *\n * The AWS target has done this since `fromManifest` landed: a table keyed by\n * declaration kind, each entry turning one declaration into infrastructure and\n * the env keys it resolves. Dokploy had no such thing — it resolved environment\n * from the *sniffer*, which walks application code for `get('X')` calls, and a\n * construct reads its own key inside `@geekmidas/constructs` where there is\n * nothing to find. So a declared app deployed here got none of the model, and\n * nothing reported the absence: the deploy succeeded and the first request that\n * needed a URL failed at runtime.\n *\n * This is the same table, over the REST wrapper instead of over Pulumi.\n *\n * **Two phases, and the split is not incidental.** Creating a Postgres and\n * creating a role inside it are different kinds of act: the first is an API\n * call, the second needs a connection to a database that does not exist yet\n * when the call is made. So provisioners *accumulate* DDL rather than running\n * it, and it is applied once against a cluster that is up — which is exactly\n * what `DatabaseBootstrap` does on AWS, for exactly the same reason.\n *\n * **The DDL itself is shared.** `roleStatements` and `cacheTableStatements` are\n * the same generators the local target and the AWS target use. Dokploy used to\n * write its own `DO $$` block, which meant three targets applying three\n * different definitions of \"the role split\" and no way to notice they had\n * drifted.\n *\n * Entries are deliberately small and free of I/O ordering: each takes what it\n * needs and returns what it resolved. That is what would let a Pulumi dynamic\n * provider wrap one later without the table itself changing — see §6b.\n */\n\nimport { createHash } from 'node:crypto';\nimport { cacheTableStatements } from '@geekmidas/cache/postgres';\nimport {\n\townerRole,\n\ttype RoleStatement,\n\treaderRole,\n\troleStatements,\n} from '@geekmidas/db/pg/roles';\nimport {\n\ttype ConstructManifest,\n\tcacheTable,\n\tcloudName,\n\tcookieDomain,\n\ttype Declaration,\n\ttype DeclarationKind,\n\tdependentsOf,\n\tprovideKey,\n} from '@geekmidas/manifest';\nimport { resourceName } from '../reconcile/plan.js';\nimport type { DokployApi } from './dokploy-api';\n\n/** What one provisioned construct resolves for anything that depends on it. */\nexport interface Provisioned {\n\t/** The env keys this construct publishes, and their values. */\n\tprovides: Record<string, string>;\n}\n\n/** A statement to run once the cluster it operates on is reachable. */\nexport interface DeferredStatement extends RoleStatement {\n\t/** The construct that asked for it, so a failure can name one. */\n\tid: string;\n\t/** The database to connect to, where it is not the cluster's default. */\n\tdatabase?: string;\n}\n\nexport interface DokployProvisionContext {\n\tmanifest: ConstructManifest;\n\t/** Everything already provisioned. Parents are present; siblings may not be. */\n\tprovisioned: Record<string, Provisioned>;\n\tapi: DokployApi;\n\tprojectId: string;\n\tenvironmentId: string;\n\t/** The stage, which scopes every name exactly as it does locally. */\n\tstage: string;\n\t/**\n\t * Seeds every derived password.\n\t *\n\t * Derived rather than random so a second deploy does not lock the running\n\t * app out of its own database, and seeded per project so two projects do not\n\t * share a credential.\n\t */\n\tproject: string;\n\t/** Where a declared cache lives — the same config the other targets read. */\n\tcache?: 'upstash' | 'elasticache' | 'db';\n\t/** Where a declared bucket lives. Only `minio` has a Dokploy primitive. */\n\tstorage?: 'minio' | 's3' | 'r2';\n\t/**\n\t * What carries a declared queue or topic — the same config the local target\n\t * reads, and for the same reason it is config rather than a declaration: the\n\t * handlers are written once and the transport is a deployment choice.\n\t */\n\tevents?: 'pgboss' | 'sns' | 'rabbitmq';\n\t/**\n\t * Where each surface answers, keyed by construct id.\n\t *\n\t * Assigned by whatever created the domain, which is the existing engine —\n\t * so it arrives here rather than being composed, the same way the local\n\t * target receives addresses it did not choose.\n\t */\n\taddresses?: Readonly<Record<string, string>>;\n\t/**\n\t * Secrets already generated for this stage, so a redeploy does not rotate\n\t * one and invalidate every live session.\n\t */\n\tsecrets?: Readonly<Record<string, string>>;\n\t/** DDL accumulated by the provisioners, applied once at the end. */\n\tdeferred: DeferredStatement[];\n\t/**\n\t * The Postgres each declared database resolved to, keyed by database name.\n\t *\n\t * Recorded because the deferred DDL has to run against *the cluster the\n\t * manifest created*, and nothing else knows which that is: a project may\n\t * also have a legacy `services.postgres`, and applying a construct's roles\n\t * to that one would create them where nothing connects.\n\t */\n\tclusters: Record<string, DokployCluster>;\n}\n\n/** The half of a Dokploy Postgres a caller outside this module needs. */\nexport interface DokployCluster {\n\tpostgresId: string;\n\tappName: string;\n\tdatabaseName: string;\n\tdatabaseUser: string;\n\tdatabasePassword: string;\n}\n\ntype Provisioner = (\n\tdeclaration: Declaration,\n\tcontext: DokployProvisionContext,\n) => Promise<Provisioned>;\n\n/**\n * The internal hostname a Dokploy Postgres answers on.\n *\n * Its own service name on the Docker network — not the server's public address.\n * An app reaching its database over the public internet would be a credential\n * crossing a network that did not need to see it.\n */\nfunction clusterHost(postgres: { appName: string }): string {\n\treturn postgres.appName;\n}\n\n/**\n * What a resource is called in the provider.\n *\n * `cloudName` and nothing else — the same scoped kebab rule the AWS target\n * uses, so a name reads identically on both: `production-shop-orders`.\n *\n * It briefly carried a `-postgres` suffix on the theory that a Dokploy project\n * is one flat list where an application and its database would collide. It is\n * not: `project.one` returns typed collections — `applications`, `postgres`,\n * `redis`, `compose`, `mariadb`, `mongo`, `mysql` — so the kind is already in\n * the shape of the response and an application and a Postgres of the same name\n * are different objects with different ids. The suffix was answering a question\n * nobody had asked.\n */\nexport function serviceName(\n\tscope: { stage: string; app: string },\n\tid: string,\n): string {\n\treturn cloudName(scope, id);\n}\n\nconst PROVISIONERS: Partial<Record<DeclarationKind, Provisioner>> = {\n\t/**\n\t * A Postgres service, plus the roles that reach it.\n\t *\n\t * The service is `findOrCreate`, because a deploy that recreated the\n\t * database would be a deploy that lost the data. The roles are deferred:\n\t * they need a connection to a cluster this call has only just asked for.\n\t */\n\tdatabase: async (declaration, context) => {\n\t\tif (declaration.kind !== 'database') throw new WrongKind(declaration.kind);\n\n\t\t// Two names, deliberately: what Postgres calls the database, and what the\n\t\t// Dokploy service list calls the service.\n\t\tconst name = resourceName(declaration.id, declaration.kind, context.stage);\n\t\tconst { postgres } = await context.api.findOrCreatePostgres(\n\t\t\tserviceName(\n\t\t\t\t{ stage: context.stage, app: context.project },\n\t\t\t\tdeclaration.id,\n\t\t\t),\n\t\t\tcontext.projectId,\n\t\t\tcontext.environmentId,\n\t\t\t{\n\t\t\t\tdatabaseName: name,\n\t\t\t\t// The cluster master, which nothing in the application ever holds:\n\t\t\t\t// every handler connects as a role created *by* this one.\n\t\t\t\tdatabasePassword: derivedPassword(context, `${name}:master`),\n\t\t\t},\n\t\t);\n\n\t\tconst host = clusterHost(postgres);\n\t\tcontext.clusters[postgres.databaseName] = postgres;\n\t\tconst schema = declaration.schema ?? 'app';\n\t\tconst runtime = name;\n\t\tconst owner = ownerRole(runtime);\n\t\tconst reads = readsFrom(context.manifest, declaration.id);\n\n\t\tcontext.deferred.push(\n\t\t\t...statementsFor({\n\t\t\t\tid: declaration.id,\n\t\t\t\tdatabase: postgres.databaseName,\n\t\t\t\truntime,\n\t\t\t\towner,\n\t\t\t\t...(reads ? { reader: readerRole(runtime) } : {}),\n\t\t\t\tschema,\n\t\t\t\tcontext,\n\t\t\t}),\n\t\t);\n\n\t\treturn {\n\t\t\tprovides: {\n\t\t\t\t[provideKey(declaration.id, 'url')]: postgresUrl(\n\t\t\t\t\truntime,\n\t\t\t\t\tderivedPassword(context, runtime),\n\t\t\t\t\thost,\n\t\t\t\t\tpostgres.databaseName,\n\t\t\t\t),\n\t\t\t\t// Deliberately not in `provides` on any manifest edge — the migrator\n\t\t\t\t// reads it from the injected environment and nothing can depend on\n\t\t\t\t// it, so DDL rights are never granted by an edge.\n\t\t\t\t[provideKey(declaration.id, 'ownerUrl')]: postgresUrl(\n\t\t\t\t\towner,\n\t\t\t\t\tderivedPassword(context, owner),\n\t\t\t\t\thost,\n\t\t\t\t\tpostgres.databaseName,\n\t\t\t\t),\n\t\t\t},\n\t\t};\n\t},\n\n\t/**\n\t * A schema in the parent's database, with its own role and its own URL — so\n\t * the parent's role holds no grant on these tables at all.\n\t */\n\t'database-schema': async (declaration, context) => {\n\t\tif (declaration.kind !== 'database-schema')\n\t\t\tthrow new WrongKind(declaration.kind);\n\n\t\tconst parent = context.provisioned[declaration.of];\n\t\tif (!parent) throw new UnresolvedParent(declaration.id, declaration.of);\n\n\t\tconst runtime = resourceName(\n\t\t\tdeclaration.id,\n\t\t\tdeclaration.kind,\n\t\t\tcontext.stage,\n\t\t);\n\t\tconst owner = ownerRole(runtime);\n\t\tconst reads = readsFrom(context.manifest, declaration.id);\n\t\tconst parentUrl = new URL(\n\t\t\tparent.provides[provideKey(declaration.of, 'url')] ?? '',\n\t\t);\n\n\t\tcontext.deferred.push(\n\t\t\t...statementsFor({\n\t\t\t\tid: declaration.id,\n\t\t\t\tdatabase: parentUrl.pathname.slice(1),\n\t\t\t\truntime,\n\t\t\t\towner,\n\t\t\t\t...(reads ? { reader: readerRole(runtime) } : {}),\n\t\t\t\tschema: declaration.schema,\n\t\t\t\tcontext,\n\t\t\t}),\n\t\t);\n\n\t\treturn {\n\t\t\tprovides: {\n\t\t\t\t[provideKey(declaration.id, 'url')]: postgresUrl(\n\t\t\t\t\truntime,\n\t\t\t\t\tderivedPassword(context, runtime),\n\t\t\t\t\tparentUrl.hostname,\n\t\t\t\t\tparentUrl.pathname.slice(1),\n\t\t\t\t),\n\t\t\t\t[provideKey(declaration.id, 'ownerUrl')]: postgresUrl(\n\t\t\t\t\towner,\n\t\t\t\t\tderivedPassword(context, owner),\n\t\t\t\t\tparentUrl.hostname,\n\t\t\t\t\tparentUrl.pathname.slice(1),\n\t\t\t\t),\n\t\t\t},\n\t\t};\n\t},\n\n\t/**\n\t * A read-only endpoint on the same cluster.\n\t *\n\t * Nothing is provisioned: a Dokploy Postgres has one endpoint, so this\n\t * resolves to the writer's address — and that is safe rather than a\n\t * loophole, because read-only is enforced by the reader role's grants and\n\t * not by which host the URL names. The role itself was created with its\n\t * parent.\n\t */\n\t'database-reader': async (declaration, context) => {\n\t\tif (declaration.kind !== 'database-reader')\n\t\t\tthrow new WrongKind(declaration.kind);\n\n\t\tconst parent = context.provisioned[declaration.of];\n\t\tif (!parent) throw new UnresolvedParent(declaration.id, declaration.of);\n\n\t\tconst parentUrl = new URL(\n\t\t\tparent.provides[provideKey(declaration.of, 'url')] ?? '',\n\t\t);\n\t\tconst reader = readerRole(parentUrl.username);\n\n\t\treturn {\n\t\t\tprovides: {\n\t\t\t\t[provideKey(declaration.id, 'url')]: postgresUrl(\n\t\t\t\t\treader,\n\t\t\t\t\tderivedPassword(context, reader),\n\t\t\t\t\tparentUrl.hostname,\n\t\t\t\t\tparentUrl.pathname.slice(1),\n\t\t\t\t),\n\t\t\t},\n\t\t};\n\t},\n\n\t/**\n\t * A cache, wherever the project said it lives.\n\t *\n\t * `db` is a table in a database the manifest already declares, so it\n\t * provisions no service and costs nothing to run — the same relationship\n\t * pg-boss has. The table's DDL is deferred with the roles, and the table\n\t * name travels in the URL because two caches in one database resolve the\n\t * same connection string.\n\t */\n\tcache: async (declaration, context) => {\n\t\tif (declaration.kind !== 'cache') throw new WrongKind(declaration.kind);\n\n\t\tconst parentId =\n\t\t\tdeclaration.of ??\n\t\t\t(context.cache === 'db' ? soleDatabase(context) : undefined);\n\n\t\tif (!parentId) throw new CacheNeedsAHome(declaration.id, context.cache);\n\n\t\tconst parent = context.provisioned[parentId];\n\t\tif (!parent) throw new UnresolvedParent(declaration.id, parentId);\n\n\t\tconst parentUrl = parent.provides[provideKey(parentId, 'url')];\n\t\tif (!parentUrl) throw new UnresolvedParent(declaration.id, parentId);\n\n\t\tconst table = declaration.table ?? cacheTable(declaration.id);\n\t\tconst schema = schemaOf(context.manifest, parentId);\n\t\tconst qualified = schema ? `${schema}.${table}` : table;\n\n\t\tcontext.deferred.push(\n\t\t\t...cacheTableStatements({ table: qualified }).map((statement) => ({\n\t\t\t\tid: declaration.id,\n\t\t\t\tdatabase: new URL(parentUrl).pathname.slice(1),\n\t\t\t\tdescribe: statement.describe,\n\t\t\t\tsql: statement.sql,\n\t\t\t\t...(statement.exists ? { exists: statement.exists } : {}),\n\t\t\t})),\n\t\t\t// Created by the master, so it belongs to nobody the app connects as\n\t\t\t// until it is handed over — and default privileges cover only what the\n\t\t\t// owner creates.\n\t\t\t{\n\t\t\t\tid: declaration.id,\n\t\t\t\tdatabase: new URL(parentUrl).pathname.slice(1),\n\t\t\t\tdescribe: `${qualified} is owned by its schema's owner`,\n\t\t\t\tsql: `ALTER TABLE ${quoted(qualified)} OWNER TO ${quoted(ownerRole(new URL(parentUrl).username))}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: declaration.id,\n\t\t\t\tdatabase: new URL(parentUrl).pathname.slice(1),\n\t\t\t\tdescribe: `${new URL(parentUrl).username} may read and write ${qualified}`,\n\t\t\t\tsql: `GRANT SELECT, INSERT, UPDATE, DELETE ON ${quoted(qualified)} TO ${quoted(new URL(parentUrl).username)}`,\n\t\t\t},\n\t\t);\n\n\t\tconst url = new URL(parentUrl);\n\t\turl.searchParams.set('table', table);\n\n\t\treturn {\n\t\t\tprovides: { [provideKey(declaration.id, 'url')]: url.toString() },\n\t\t};\n\t},\n\n\t/**\n\t * A bucket, as a MinIO stack on the box.\n\t *\n\t * Dokploy has first-class primitives for Postgres and Redis and none for\n\t * object storage, so this is the one kind whose infrastructure this target\n\t * *writes* rather than configures — a compose file with one service in it.\n\t *\n\t * The service is named `cloudName(...)` like everything else, which is not\n\t * cosmetic here: containers on `dokploy-network` resolve each other by\n\t * service name, so the name is the address. Calling it `minio` would work\n\t * for exactly one project on the box and then collide.\n\t *\n\t * `s3://` carries no credentials, deliberately — deployed on AWS the SDK\n\t * reads them from an execution role, so a URL that embedded a key would be\n\t * one more thing to rotate and leak. There is no role here, so the same\n\t * chain reads `AWS_ACCESS_KEY_ID` beside the URL, exactly as it does\n\t * locally.\n\t */\n\tobjects: async (declaration, context) => {\n\t\tif (declaration.kind !== 'objects') throw new WrongKind(declaration.kind);\n\n\t\tconst backend = context.storage ?? 'minio';\n\t\tif (backend !== 'minio')\n\t\t\tthrow new UnprovisionableBucket(declaration.id, backend);\n\n\t\tconst service = serviceName(\n\t\t\t{ stage: context.stage, app: context.project },\n\t\t\tdeclaration.id,\n\t\t);\n\t\tconst bucket = resourceName(\n\t\t\tdeclaration.id,\n\t\t\tdeclaration.kind,\n\t\t\tcontext.stage,\n\t\t);\n\t\tconst user = `${bucket}-root`;\n\t\tconst password = derivedPassword(context, `objects:${declaration.id}`);\n\n\t\tconst { compose } = await context.api.findOrCreateCompose(\n\t\t\tservice,\n\t\t\tcontext.projectId,\n\t\t\tcontext.environmentId,\n\t\t\tminioCompose({ service, bucket, user, password }),\n\t\t);\n\n\t\tawait context.api.deployCompose(compose.composeId);\n\n\t\treturn {\n\t\t\tprovides: {\n\t\t\t\t[provideKey(declaration.id, 'url')]: s3Url(service, bucket),\n\t\t\t\t// Beside the URL rather than inside it, for the reason above.\n\t\t\t\tAWS_ACCESS_KEY_ID: user,\n\t\t\t\tAWS_SECRET_ACCESS_KEY: password,\n\t\t\t\tAWS_REGION: MINIO_REGION,\n\t\t\t},\n\t\t};\n\t},\n\n\t/**\n\t * The address a bucket's objects are served on.\n\t *\n\t * Nothing is provisioned: Dokploy runs Traefik, so a second reverse proxy\n\t * behind the first would terminate TLS twice — which is why the local\n\t * target's Caddy has no equivalent here. What this resolves is the address,\n\t * and the `open` prefixes are applied to the bucket rather than to an edge,\n\t * so a policy is what makes them public wherever the bucket lives.\n\t *\n\t * Until a Traefik rule exists it answers on MinIO directly, path-style,\n\t * which is honest about the shape it has today: the bucket is in the path\n\t * rather than fronted by a host of its own.\n\t */\n\t'file-server': async (declaration, context) => {\n\t\tif (declaration.kind !== 'file-server')\n\t\t\tthrow new WrongKind(declaration.kind);\n\n\t\tconst parent = context.provisioned[declaration.of];\n\t\tconst parentUrl = parent?.provides[provideKey(declaration.of, 'url')];\n\t\tif (!parentUrl) throw new UnresolvedParent(declaration.id, declaration.of);\n\n\t\tconst endpoint = new URL(parentUrl).searchParams.get('endpoint');\n\t\tconst bucket = new URL(parentUrl).hostname;\n\t\tif (!endpoint) throw new UnresolvedParent(declaration.id, declaration.of);\n\n\t\treturn {\n\t\t\tprovides: {\n\t\t\t\t[provideKey(declaration.id, 'url')]: `${endpoint}/${bucket}`,\n\t\t\t},\n\t\t};\n\t},\n\n\t/**\n\t * A topic, and below it a queue — the same broker either way.\n\t *\n\t * Under pg-boss there is nothing to create in Dokploy: the broker is a\n\t * schema tenant of the database the app already declared, exactly as it is\n\t * locally, so what this resolves is an address rather than a service. Which\n\t * is the point of putting the backend in config — the same handlers drain\n\t * pg-boss here and SQS on AWS because the string they were handed said so.\n\t *\n\t * The tenant gets its own role rather than the cluster master, for the\n\t * reason every other tenant does: pg-boss creates and owns its tables, and\n\t * a broker that could also read the application's is a broker with a grant\n\t * nothing asked for.\n\t */\n\ttopic: async (declaration, context) => {\n\t\tif (declaration.kind !== 'topic') throw new WrongKind(declaration.kind);\n\n\t\treturn broker(declaration.id, context);\n\t},\n\n\tqueue: async (declaration, context) => {\n\t\tif (declaration.kind !== 'queue') throw new WrongKind(declaration.kind);\n\n\t\treturn broker(declaration.id, context);\n\t},\n\n\t/**\n\t * A signing key, generated once and remembered.\n\t *\n\t * Regenerating it on every deploy would invalidate every live session, which\n\t * is why the value comes from state when there is one. Nothing is created in\n\t * Dokploy: a secret has no address.\n\t */\n\tsecret: async (declaration, context) => {\n\t\tif (declaration.kind !== 'secret') throw new WrongKind(declaration.kind);\n\n\t\t// The key the declaration publishes, not one derived here. A secret's key\n\t\t// is `environmentCase(id)` — `AuthSecret` becomes `AUTH_SECRET` — and\n\t\t// deriving `provideKey(id, 'value')` instead produced `AUTH_SECRET_VALUE`,\n\t\t// which nothing reads. Keys come from the declaration for exactly this\n\t\t// reason: what the build publishes and what the app reads cannot drift.\n\t\tconst key = declaration.provides?.[0];\n\t\tif (!key) return { provides: {} };\n\n\t\treturn {\n\t\t\tprovides: {\n\t\t\t\t[key]:\n\t\t\t\t\tcontext.secrets?.[key] ??\n\t\t\t\t\tderivedPassword(context, `secret:${declaration.id}`),\n\t\t\t},\n\t\t};\n\t},\n\n\t/**\n\t * A surface, at the address whatever created its domain assigned.\n\t *\n\t * Nothing is provisioned here: the application and its domain are the\n\t * existing engine's, and Dokploy's own Traefik terminates TLS with a Let's\n\t * Encrypt certificate. What this adds is the *derivation* — who may call it,\n\t * and where a cookie it sets is readable — from the graph rather than from a\n\t * hand-maintained list.\n\t */\n\t'rest-api': async (declaration, context) => {\n\t\tif (declaration.kind !== 'rest-api') throw new WrongKind(declaration.kind);\n\n\t\tconst url = context.addresses?.[declaration.id];\n\t\tif (!url) throw new SurfaceHasNoAddress(declaration.id);\n\n\t\t// A surface publishes three facts, not one: where it answers, who may\n\t\t// call it, and where a cookie it sets is readable. The last two are\n\t\t// derived from its *inbound* edges — the constructs that declared they\n\t\t// call it — which is the same derivation the local target makes and the\n\t\t// reason neither is a list anybody maintains.\n\t\t//\n\t\t// Returning only the URL left `AUTH_TRUSTED_ORIGINS` and\n\t\t// `AUTH_COOKIE_DOMAIN` unresolved, and Better Auth rejects an untrusted\n\t\t// origin whether or not it is a browser.\n\t\tconst origins = [\n\t\t\t...new Set(\n\t\t\t\tdependentsOf(context.manifest, declaration.id)\n\t\t\t\t\t.map((caller) => context.addresses?.[caller])\n\t\t\t\t\t.filter((address): address is string => Boolean(address)),\n\t\t\t),\n\t\t].sort();\n\n\t\tconst domain = cookieDomain([url, ...origins]);\n\n\t\treturn {\n\t\t\tprovides: {\n\t\t\t\t[provideKey(declaration.id, 'url')]: url,\n\t\t\t\t[provideKey(declaration.id, 'trustedOrigins')]: origins.join(','),\n\t\t\t\t...(domain\n\t\t\t\t\t? { [provideKey(declaration.id, 'cookieDomain')]: domain }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t};\n\t},\n};\n\n/** The provisioner for a kind, or nothing where this target has none yet. */\nexport function provisionerFor(kind: DeclarationKind): Provisioner | undefined {\n\treturn PROVISIONERS[kind];\n}\n\n/** Which kinds this target can provision today — the rest are §6b's remainder. */\nexport function provisionableKinds(): DeclarationKind[] {\n\treturn Object.keys(PROVISIONERS) as DeclarationKind[];\n}\n\n/**\n * The role DDL for one database or tenant, from the shared generator.\n *\n * The same statements the local target applies and the same ones the AWS\n * bootstrap runs. Dokploy used to write its own, which is how three targets\n * came to hold three definitions of the same split.\n */\nfunction statementsFor(options: {\n\tid: string;\n\tdatabase: string;\n\truntime: string;\n\towner: string;\n\treader?: string;\n\tschema: string;\n\tcontext: DokployProvisionContext;\n}): DeferredStatement[] {\n\tconst { id, database, runtime, owner, reader, schema, context } = options;\n\n\treturn roleStatements({\n\t\truntime,\n\t\towner,\n\t\t...(reader ? { reader } : {}),\n\t\tschema,\n\t\tpasswords: {\n\t\t\truntime: derivedPassword(context, runtime),\n\t\t\towner: derivedPassword(context, owner),\n\t\t\t...(reader ? { reader: derivedPassword(context, reader) } : {}),\n\t\t},\n\t}).map((statement) => ({ ...statement, id, database }));\n}\n\n/**\n * The connection string a declared queue or topic publishes on.\n *\n * One broker for the whole project, which is what the local target does and\n * what the runtime expects: the generated pollers open a single connection and\n * subscribe each worker by name on it. So every carrier resolves to the same\n * address, and the DDL that backs it is pushed once however many are declared.\n *\n * Only pg-boss is answered here. SNS needs a topic ARN that has to exist\n * first, and RabbitMQ needs a broker Dokploy has no primitive for — both are\n * the §4.3 question, and composing a plausible URL for either would fail at the\n * first publish instead of here.\n */\nfunction broker(id: string, context: DokployProvisionContext): Provisioned {\n\tconst backend = context.events ?? 'pgboss';\n\tif (backend !== 'pgboss') throw new UnprovisionableCarrier(id, backend);\n\n\tconst parentId = soleDatabase(context);\n\tif (!parentId) throw new BrokerNeedsADatabase(id);\n\n\tconst parent = context.provisioned[parentId];\n\tconst parentUrl = parent?.provides[provideKey(parentId, 'url')];\n\tif (!parentUrl) throw new UnresolvedParent(id, parentId);\n\n\tconst url = new URL(parentUrl);\n\tconst database = url.pathname.slice(1);\n\tconst runtime = `${database}_${PGBOSS_SCHEMA}`;\n\tconst owner = ownerRole(runtime);\n\n\t// Pushed under a fixed id rather than the carrier's, so a project declaring\n\t// a topic *and* a queue defers one set of statements rather than two\n\t// identical ones. The applier is convergent either way; this keeps the plan\n\t// honest about there being one broker.\n\tif (!context.provisioned[BROKER_ID]) {\n\t\tcontext.deferred.push(\n\t\t\t...statementsFor({\n\t\t\t\tid: BROKER_ID,\n\t\t\t\tdatabase,\n\t\t\t\truntime,\n\t\t\t\towner,\n\t\t\t\tschema: PGBOSS_SCHEMA,\n\t\t\t\tcontext,\n\t\t\t}),\n\t\t);\n\t\tcontext.provisioned[BROKER_ID] = { provides: {} };\n\t}\n\n\tconst connection = pgbossUrl(\n\t\truntime,\n\t\tderivedPassword(context, runtime),\n\t\turl.host,\n\t\tdatabase,\n\t);\n\n\treturn {\n\t\tprovides: {\n\t\t\t[provideKey(id, 'publisherConnectionString')]: connection,\n\t\t},\n\t};\n}\n\n/** The schema pg-boss creates its tables in, here as locally. */\nconst PGBOSS_SCHEMA = 'pgboss';\n\n/**\n * The id the broker's DDL is pushed under.\n *\n * Not a construct — no declaration names it — but the deferred statements need\n * an id, and every carrier in the project shares this one.\n */\nconst BROKER_ID = 'PgBoss';\n\n/**\n * A pg-boss address: a Postgres URL under a scheme that picks the transport.\n *\n * The protocol is what a producer branches on, so it never branches — the same\n * `.publish()` reaches pg-boss here and SQS on AWS.\n */\nfunction pgbossUrl(\n\trole: string,\n\tpassword: string,\n\thost: string,\n\tdatabase: string,\n): string {\n\treturn `pgboss://${role}:${encodeURIComponent(password)}@${host}/${database}?schema=${PGBOSS_SCHEMA}`;\n}\n\n/** The region MinIO is addressed with. It has no regions; the SDK wants one. */\nconst MINIO_REGION = 'us-east-1';\n\n/** An `s3://` URL pointing the same S3 client at MinIO on the internal network. */\nfunction s3Url(service: string, bucket: string): string {\n\treturn (\n\t\t`s3://${bucket}?region=${MINIO_REGION}` +\n\t\t`&endpoint=http://${service}:9000&forcePathStyle=true`\n\t);\n}\n\n/**\n * A one-service compose file for a bucket.\n *\n * `dokploy-network` is external and already exists — it is what every Dokploy\n * service is attached to, and joining it is what makes the app able to resolve\n * this one by name. The bucket itself is created by MinIO's entrypoint rather\n * than by a second call, so there is nothing to reconcile afterwards.\n */\nfunction minioCompose(options: {\n\tservice: string;\n\tbucket: string;\n\tuser: string;\n\tpassword: string;\n}): string {\n\tconst { service, bucket, user, password } = options;\n\n\treturn [\n\t\t'services:',\n\t\t` ${service}:`,\n\t\t' image: quay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z',\n\t\t` command: server /data --console-address \":9001\"`,\n\t\t' environment:',\n\t\t` MINIO_ROOT_USER: ${user}`,\n\t\t` MINIO_ROOT_PASSWORD: ${password}`,\n\t\t' volumes:',\n\t\t` - ${service}-data:/data`,\n\t\t' networks:',\n\t\t' - dokploy-network',\n\t\t' healthcheck:',\n\t\t' test: [\"CMD\", \"mc\", \"ready\", \"local\"]',\n\t\t' interval: 10s',\n\t\t' retries: 5',\n\t\t` ${service}-bucket:`,\n\t\t' image: minio/mc:latest',\n\t\t' depends_on:',\n\t\t` ${service}:`,\n\t\t' condition: service_healthy',\n\t\t' entrypoint: >',\n\t\t` /bin/sh -c \"mc alias set s3 http://${service}:9000 ${user} ${password} &&`,\n\t\t` mc mb --ignore-existing s3/${bucket}\"`,\n\t\t' networks:',\n\t\t' - dokploy-network',\n\t\t'volumes:',\n\t\t` ${service}-data:`,\n\t\t'networks:',\n\t\t' dokploy-network:',\n\t\t' external: true',\n\t\t'',\n\t].join('\\n');\n}\n\n/** Whether anything reads through a reader on this construct. */\nfunction readsFrom(manifest: ConstructManifest, id: string): boolean {\n\treturn Object.values(manifest).some(\n\t\t(declaration) =>\n\t\t\tdeclaration.kind === 'database-reader' && declaration.of === id,\n\t);\n}\n\n/** The schema a database or tenant pins on its roles. */\nfunction schemaOf(manifest: ConstructManifest, id: string): string | undefined {\n\tconst declaration = manifest[id];\n\tif (!declaration) return undefined;\n\n\treturn 'schema' in declaration\n\t\t? ((declaration.schema as string | undefined) ?? 'app')\n\t\t: undefined;\n}\n\n/**\n * The one declared database, where `cache: 'db'` has to pick.\n *\n * Unambiguous with one and arbitrary with two, so two is an error rather than a\n * coin toss — a cache landing in a database nobody chose surfaces as entries\n * that are never found, long after the deploy reported success.\n */\nfunction soleDatabase(context: DokployProvisionContext): string | undefined {\n\tconst databases = Object.entries(context.manifest)\n\t\t.filter(([, declaration]) => declaration.kind === 'database')\n\t\t.map(([id]) => id);\n\n\tif (databases.length > 1) {\n\t\tthrow new CacheIsAmbiguous(databases);\n\t}\n\n\treturn databases[0];\n}\n\n/**\n * A password derived from the project, the stage and the role.\n *\n * Derived rather than random for the reason the local target derives its own: a\n * redeploy must not lock the running application out of its own database, and\n * two projects must not share a credential.\n */\nfunction derivedPassword(\n\tcontext: DokployProvisionContext,\n\trole: string,\n): string {\n\treturn createHash('sha256')\n\t\t.update(`${context.project}:${context.stage}:role:${role}`)\n\t\t.digest('base64url')\n\t\t.slice(0, 32);\n}\n\n/** A connection string for a role on a cluster. */\nfunction postgresUrl(\n\trole: string,\n\tpassword: string,\n\thost: string,\n\tdatabase: string,\n): string {\n\treturn `postgres://${role}:${encodeURIComponent(password)}@${host}:5432/${database}`;\n}\n\n/** Quote an identifier, per segment, for DDL that cannot be parameterised. */\nfunction quoted(name: string): string {\n\treturn name\n\t\t.split('.')\n\t\t.map((part) => `\"${part.replace(/\"/g, '\"\"')}\"`)\n\t\t.join('.');\n}\n\n/** A provisioner was handed a declaration of the wrong kind. */\nexport class WrongKind extends Error {\n\tconstructor(readonly kind: string) {\n\t\tsuper(`No Dokploy provisioner handles '${kind}'`);\n\t\tthis.name = 'WrongKind';\n\t}\n}\n\n/** A derived construct whose parent has not been provisioned. */\nexport class UnresolvedParent extends Error {\n\tconstructor(\n\t\treadonly id: string,\n\t\treadonly parent: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${id}' derives from '${parent}', which has not been provisioned. ` +\n\t\t\t\t`Provision in \\`provisionOrder\\`, which puts parents first.`,\n\t\t);\n\t\tthis.name = 'UnresolvedParent';\n\t}\n}\n\n/** A cache with nowhere to live. */\nexport class CacheNeedsAHome extends Error {\n\tconstructor(\n\t\treadonly id: string,\n\t\treadonly backend: string | undefined,\n\t) {\n\t\tsuper(\n\t\t\t`'${id}' is a cache and nothing says where it lives. Declare it from ` +\n\t\t\t\t`its database — \\`orders.cache('${id}')\\` — or set services.cache.` +\n\t\t\t\t(backend ? ` The configured backend is '${backend}'.` : ''),\n\t\t);\n\t\tthis.name = 'CacheNeedsAHome';\n\t}\n}\n\n/** `services.cache: 'db'` in an app declaring more than one database. */\nexport class CacheIsAmbiguous extends Error {\n\tconstructor(readonly databases: readonly string[]) {\n\t\tsuper(\n\t\t\t`A cache backed by the database needs to know which one, and this app ` +\n\t\t\t\t`declares ${databases.length}: ${databases.join(', ')}. Declare the ` +\n\t\t\t\t`cache from its database rather than with services.cache.`,\n\t\t);\n\t\tthis.name = 'CacheIsAmbiguous';\n\t}\n}\n\n/** A surface reached provisioning before its domain existed. */\nexport class SurfaceHasNoAddress extends Error {\n\tconstructor(readonly id: string) {\n\t\tsuper(\n\t\t\t`'${id}' is a surface and no address was supplied for it. The domain is ` +\n\t\t\t\t`created before the manifest is provisioned, so this means the two ` +\n\t\t\t\t`ran out of order.`,\n\t\t);\n\t\tthis.name = 'SurfaceHasNoAddress';\n\t}\n}\n\n/** A queue or topic on a backend this target has no primitive for. */\nexport class UnprovisionableCarrier extends Error {\n\tconstructor(\n\t\treadonly id: string,\n\t\treadonly backend: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${id}' is carried by '${backend}', which this target cannot ` +\n\t\t\t\t`provision. SNS needs a topic ARN that has to exist first and ` +\n\t\t\t\t`RabbitMQ needs a broker Dokploy has no primitive for; pg-boss is a ` +\n\t\t\t\t`schema tenant of a database this app already declares, which is why ` +\n\t\t\t\t`it is the one that works here. Set services.events to 'pgboss', or ` +\n\t\t\t\t`deploy this app to a target that has the backend.`,\n\t\t);\n\t\tthis.name = 'UnprovisionableCarrier';\n\t}\n}\n\n/** A pg-boss carrier in an app that declares no database to host it. */\nexport class BrokerNeedsADatabase extends Error {\n\tconstructor(readonly id: string) {\n\t\tsuper(\n\t\t\t`'${id}' is carried by pg-boss, which lives in the database the app ` +\n\t\t\t\t`declares — and this app declares none. Declare a database, or set ` +\n\t\t\t\t`services.events to a backend that brings its own broker.`,\n\t\t);\n\t\tthis.name = 'BrokerNeedsADatabase';\n\t}\n}\n\n/** A bucket on a backend this target has no primitive for. */\nexport class UnprovisionableBucket extends Error {\n\tconstructor(\n\t\treadonly id: string,\n\t\treadonly backend: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${id}' is stored on '${backend}', which this target cannot ` +\n\t\t\t\t`provision: an S3 or R2 bucket belongs to an account this deploy ` +\n\t\t\t\t`does not hold, the way an API key does. Set services.storage to ` +\n\t\t\t\t`'minio' to run one on the box, or supply the bucket's URL.`,\n\t\t);\n\t\tthis.name = 'UnprovisionableBucket';\n\t}\n}\n","/**\n * The declared half of a Dokploy deploy.\n *\n * `fromManifest.ts` decides what each construct resolves to; this runs it —\n * discovers the manifest, walks it parents-first, and applies the DDL the\n * provisioners deferred.\n *\n * It is deliberately separate from the engine in `index.ts`. That file deploys\n * *applications*: images, registries, domains, the things a project has whether\n * or not it declares anything. This is the part that exists because the app\n * declared it, and keeping the two apart is what lets a project adopt the model\n * without rewriting its deploy — and lets this one be tested without one.\n */\n\nimport { type ConstructManifest, provisionOrder } from '@geekmidas/manifest';\nimport { discover } from '../reconcile/discover.js';\nimport {\n\tapplyPostgres,\n\ttype SqlClient,\n\ttype Statement,\n} from '../reconcile/provision.js';\nimport { constructGlobs, usesConstructs } from '../reconcile/workspace.js';\nimport {\n\tcacheBackendOf,\n\tproviderOf,\n\tstorageBackendOf,\n} from '../workspace/backends.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { DokployApi } from './dokploy-api';\nimport {\n\ttype DokployCluster,\n\ttype DokployProvisionContext,\n\ttype Provisioned,\n\tprovisionerFor,\n} from './fromManifest';\n\nexport interface DeclaredResult {\n\t/** Every env key the manifest resolved, and its value. */\n\tenv: Record<string, string>;\n\t/** The DDL still to run, once a connection to the cluster exists. */\n\tstatements: Statement[];\n\t/** What was provisioned, keyed by construct id. */\n\tprovisioned: Record<string, Provisioned>;\n\t/**\n\t * The cluster each statement's database lives in, keyed by database name.\n\t *\n\t * The DDL has to run against the Postgres *the manifest created*. A project\n\t * may also have a legacy `services.postgres`, and applying a construct's\n\t * roles there would create them where nothing connects.\n\t */\n\tclusters: Record<string, DokployCluster>;\n}\n\nexport interface DeclaredOptions {\n\tapi: DokployApi;\n\tworkspace: NormalizedWorkspace;\n\tprojectId: string;\n\tenvironmentId: string;\n\tstage: string;\n\t/**\n\t * Where each app answers, keyed by *app* name — `https://api.example.com`.\n\t *\n\t * Computed by the engine before it saves any environment, which is what\n\t * makes a surface's own URL available to the constructs that depend on it.\n\t */\n\tappUrls: Readonly<Record<string, string>>;\n\t/** Secrets already generated for this stage, so a redeploy rotates none. */\n\tsecrets?: Readonly<Record<string, string>>;\n\t/** A manifest already in hand, for a caller that has discovered one. */\n\tmanifest?: ConstructManifest;\n}\n\n/**\n * Provision everything the workspace declares, and return what it resolved.\n *\n * Nothing happens for a project that has not adopted the model: `usesConstructs`\n * is the same hard switch reconcile reads, so an existing deploy is untouched\n * until it declares something.\n */\nexport async function provisionDeclared(\n\toptions: DeclaredOptions,\n): Promise<DeclaredResult> {\n\tconst { workspace } = options;\n\tconst empty: DeclaredResult = {\n\t\tenv: {},\n\t\tstatements: [],\n\t\tprovisioned: {},\n\t\tclusters: {},\n\t};\n\n\tif (!usesConstructs(workspace)) return empty;\n\n\tconst manifest =\n\t\toptions.manifest ??\n\t\t(await discover({\n\t\t\tpatterns: constructGlobs(workspace),\n\t\t\tcwd: workspace.root,\n\t\t}));\n\n\tif (Object.keys(manifest).length === 0) return empty;\n\n\tconst context: DokployProvisionContext = {\n\t\tmanifest,\n\t\tprovisioned: {},\n\t\tapi: options.api,\n\t\tprojectId: options.projectId,\n\t\tenvironmentId: options.environmentId,\n\t\tstage: options.stage,\n\t\tproject: workspace.name,\n\t\tcache: cacheBackendOf(workspace.services.cache, providerOf(workspace)),\n\t\t...(workspace.services.events ? { events: workspace.services.events } : {}),\n\t\tstorage: storageBackendOf(\n\t\t\tworkspace.services.storage,\n\t\t\tproviderOf(workspace),\n\t\t),\n\t\taddresses: surfaceAddresses(workspace, manifest, options.appUrls),\n\t\t...(options.secrets ? { secrets: options.secrets } : {}),\n\t\tdeferred: [],\n\t\tclusters: {},\n\t};\n\n\tconst env: Record<string, string> = {};\n\n\t// Parents first. A derived construct reads its parent's resolved URL, and\n\t// `provisionOrder` is what guarantees there is one to read.\n\tfor (const id of carriersLast(provisionOrder(manifest), manifest)) {\n\t\tconst declaration = manifest[id];\n\t\tif (!declaration) continue;\n\n\t\tconst provisioner = provisionerFor(declaration.kind);\n\n\t\t// A kind this target cannot provision yet is skipped rather than fatal:\n\t\t// storage, mail and the brokers have no Dokploy primitive, and refusing\n\t\t// to deploy an app because it also declares a bucket would be worse than\n\t\t// deploying it without one. What it costs is honest — the key is absent,\n\t\t// and the construct that needs it says so on first use.\n\t\tif (!provisioner) continue;\n\n\t\tconst result = await provisioner(declaration, context);\n\t\tcontext.provisioned[id] = result;\n\t\tObject.assign(env, result.provides);\n\t}\n\n\treturn {\n\t\tenv,\n\t\tstatements: context.deferred.map((statement) => ({\n\t\t\tid: statement.id,\n\t\t\tdescribe: statement.describe,\n\t\t\t...(statement.database ? { database: statement.database } : {}),\n\t\t\t...(statement.exists ? { exists: statement.exists } : {}),\n\t\t\t// `create` is what the applier calls it — the same convergent applier\n\t\t\t// the local target runs, so a statement that already applied reports\n\t\t\t// unchanged here exactly as it does there.\n\t\t\tcreate: statement.sql,\n\t\t})),\n\t\tprovisioned: context.provisioned,\n\t\tclusters: context.clusters,\n\t};\n}\n\n/**\n * Run the deferred DDL against a cluster that is now reachable.\n *\n * The same applier the local target uses: every statement asks whether it is\n * needed first, so a redeploy is free and a half-applied run recovers by being\n * run again.\n */\nexport async function applyDeclared(\n\tclient: SqlClient,\n\tstatements: readonly Statement[],\n): Promise<number> {\n\tconst applied = await applyPostgres(client, statements);\n\n\treturn applied.filter((entry) => entry.created).length;\n}\n\n/**\n * The provisioning order, with queues and topics moved to the end.\n *\n * `provisionOrder` walks `of` edges, and a carrier has none: a topic is not\n * *derived from* a database, because under SNS it does not live in one. Where\n * it lives is a fact about the backend, which is config — so the dependency is\n * real under pg-boss and absent under SNS, and writing it into the declaration\n * would state it in the one place that cannot know.\n *\n * Here it can. This runs where the backend is known, and the rule needs no\n * branch on it: nothing depends on a carrier. A queue's worker and a topic's\n * subscribers are *bound* to it rather than dependent on it, which is why\n * neither holds a key of its own — so a carrier can always go last, and under\n * pg-boss that is exactly late enough for the database it reads to be there.\n */\nfunction carriersLast(\n\tordered: readonly string[],\n\tmanifest: ConstructManifest,\n): string[] {\n\tconst carries = (id: string) => {\n\t\tconst kind = manifest[id]?.kind;\n\t\treturn kind === 'queue' || kind === 'topic';\n\t};\n\n\treturn [...ordered.filter((id) => !carries(id)), ...ordered.filter(carries)];\n}\n\n/**\n * Where each declared surface answers.\n *\n * Every `rest-api` in a process answers on that process's address, so an app\n * serving both its own API and an auth server publishes one address twice —\n * which is exactly what it does at runtime, and the same rule the local target\n * applies.\n *\n * A workspace with two backends is the case this does not answer, and the\n * manifest cannot yet: which surface belongs to which app is what §2's endpoint\n * merge would record. Until then the first backend's address is used, and a\n * second backend's surfaces would be wrong rather than missing — worth knowing\n * before relying on it.\n */\nfunction surfaceAddresses(\n\tworkspace: NormalizedWorkspace,\n\tmanifest: ConstructManifest,\n\tappUrls: Readonly<Record<string, string>>,\n): Record<string, string> {\n\tconst addresses: Record<string, string> = {};\n\n\tconst backend = Object.entries(workspace.apps).find(\n\t\t([, app]) => app.type === 'backend',\n\t);\n\tconst url = backend ? appUrls[backend[0]] : undefined;\n\n\tif (!url) return addresses;\n\n\tfor (const [id, declaration] of Object.entries(manifest)) {\n\t\tif (declaration.kind === 'rest-api') addresses[id] = url;\n\t}\n\n\treturn addresses;\n}\n","/**\n * Deploy state management for Dokploy deployments\n *\n * Stores resource IDs (applications, services) per stage to avoid\n * re-creating resources on subsequent deploys.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/**\n * Per-app database credentials\n */\nexport interface AppDbCredentials {\n\tdbUser: string;\n\tdbPassword: string;\n}\n\n/**\n * DNS verification record for a hostname\n */\nexport interface DnsVerificationRecord {\n\tserverIp: string;\n\tverifiedAt: string;\n}\n\n/**\n * A DNS record that was created during deploy\n */\nexport interface CreatedDnsRecord {\n\t/** The domain this record belongs to (e.g., 'example.com') */\n\tdomain: string;\n\t/** Record name/subdomain (e.g., 'api' or '@' for root) */\n\tname: string;\n\t/** Record type (A, CNAME, etc.) */\n\ttype: string;\n\t/** Record value (IP address, hostname, etc.) */\n\tvalue: string;\n\t/** TTL in seconds */\n\tttl: number;\n\t/** When this record was created */\n\tcreatedAt: string;\n}\n\n/**\n * Backup destination state\n */\nexport interface BackupState {\n\t/** S3 bucket name for backups */\n\tbucketName: string;\n\t/** S3 bucket ARN */\n\tbucketArn: string;\n\t/** IAM user name created for backup access */\n\tiamUserName: string;\n\t/** IAM access key ID */\n\tiamAccessKeyId: string;\n\t/** IAM secret access key */\n\tiamSecretAccessKey: string;\n\t/** Dokploy destination ID */\n\tdestinationId: string;\n\t/** Dokploy backup schedule ID for postgres (if configured) */\n\tpostgresBackupId?: string;\n\t/** AWS region where bucket was created */\n\tregion: string;\n\t/** Timestamp when backup was configured */\n\tcreatedAt: string;\n}\n\n/**\n * State for a single stage deployment\n */\nexport interface DokployStageState {\n\tprovider: 'dokploy';\n\tstage: string;\n\t/** Dokploy project ID - created on first deploy */\n\tprojectId: string;\n\tenvironmentId: string;\n\tapplications: Record<string, string>; // appName -> applicationId\n\tservices: {\n\t\tpostgresId?: string;\n\t\tredisId?: string;\n\t};\n\t/** Per-app database credentials for reuse on subsequent deploys */\n\tappCredentials?: Record<string, AppDbCredentials>;\n\t/** Auto-generated secrets per app (e.g., BETTER_AUTH_SECRET) */\n\tgeneratedSecrets?: Record<string, Record<string, string>>;\n\t/** DNS verification state per hostname */\n\tdnsVerified?: Record<string, DnsVerificationRecord>;\n\t/** DNS records created during deploy (keyed by \"name:type\", e.g., \"api:A\") */\n\tdnsRecords?: Record<string, CreatedDnsRecord>;\n\t/** Backup destination state */\n\tbackups?: BackupState;\n\tlastDeployedAt: string;\n}\n\n/**\n * Get the state file path for a stage\n */\nfunction getStateFilePath(workspaceRoot: string, stage: string): string {\n\treturn join(workspaceRoot, '.gkm', `deploy-${stage}.json`);\n}\n\n/**\n * Read the deploy state for a stage\n * Returns null if state file doesn't exist\n */\nexport async function readStageState(\n\tworkspaceRoot: string,\n\tstage: string,\n): Promise<DokployStageState | null> {\n\tconst filePath = getStateFilePath(workspaceRoot, stage);\n\n\ttry {\n\t\tconst content = await readFile(filePath, 'utf-8');\n\t\treturn JSON.parse(content) as DokployStageState;\n\t} catch (error) {\n\t\t// File doesn't exist or is invalid - return null\n\t\tif ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n\t\t\treturn null;\n\t\t}\n\t\t// Log other errors but don't fail\n\t\tconsole.warn(`Warning: Could not read deploy state: ${error}`);\n\t\treturn null;\n\t}\n}\n\n/**\n * Write the deploy state for a stage\n */\nexport async function writeStageState(\n\tworkspaceRoot: string,\n\tstage: string,\n\tstate: DokployStageState,\n): Promise<void> {\n\tconst filePath = getStateFilePath(workspaceRoot, stage);\n\tconst dir = join(workspaceRoot, '.gkm');\n\n\t// Ensure .gkm directory exists\n\tawait mkdir(dir, { recursive: true });\n\n\t// Update last deployed timestamp\n\tstate.lastDeployedAt = new Date().toISOString();\n\n\tawait writeFile(filePath, JSON.stringify(state, null, 2));\n}\n\n/**\n * Create a new empty state for a stage\n */\nexport function createEmptyState(\n\tstage: string,\n\tprojectId: string,\n\tenvironmentId: string,\n): DokployStageState {\n\treturn {\n\t\tprovider: 'dokploy',\n\t\tstage,\n\t\tprojectId,\n\t\tenvironmentId,\n\t\tapplications: {},\n\t\tservices: {},\n\t\tlastDeployedAt: new Date().toISOString(),\n\t};\n}\n\n/**\n * Get application ID from state\n */\nexport function getApplicationId(\n\tstate: DokployStageState | null,\n\tappName: string,\n): string | undefined {\n\treturn state?.applications[appName];\n}\n\n/**\n * Set application ID in state (mutates state)\n */\nexport function setApplicationId(\n\tstate: DokployStageState,\n\tappName: string,\n\tapplicationId: string,\n): void {\n\tstate.applications[appName] = applicationId;\n}\n\n/**\n * Get postgres ID from state\n */\nexport function getPostgresId(\n\tstate: DokployStageState | null,\n): string | undefined {\n\treturn state?.services.postgresId;\n}\n\n/**\n * Set postgres ID in state (mutates state)\n */\nexport function setPostgresId(\n\tstate: DokployStageState,\n\tpostgresId: string,\n): void {\n\tstate.services.postgresId = postgresId;\n}\n\n/**\n * Get redis ID from state\n */\nexport function getRedisId(\n\tstate: DokployStageState | null,\n): string | undefined {\n\treturn state?.services.redisId;\n}\n\n/**\n * Set redis ID in state (mutates state)\n */\nexport function setRedisId(state: DokployStageState, redisId: string): void {\n\tstate.services.redisId = redisId;\n}\n\n/**\n * Get app credentials from state\n */\nexport function getAppCredentials(\n\tstate: DokployStageState | null,\n\tappName: string,\n): AppDbCredentials | undefined {\n\treturn state?.appCredentials?.[appName];\n}\n\n/**\n * Set app credentials in state (mutates state)\n */\nexport function setAppCredentials(\n\tstate: DokployStageState,\n\tappName: string,\n\tcredentials: AppDbCredentials,\n): void {\n\tif (!state.appCredentials) {\n\t\tstate.appCredentials = {};\n\t}\n\tstate.appCredentials[appName] = credentials;\n}\n\n/**\n * Get all app credentials from state\n */\nexport function getAllAppCredentials(\n\tstate: DokployStageState | null,\n): Record<string, AppDbCredentials> {\n\treturn state?.appCredentials ?? {};\n}\n\n// ============================================================================\n// Generated Secrets\n// ============================================================================\n\n/**\n * Get a generated secret for an app\n */\nexport function getGeneratedSecret(\n\tstate: DokployStageState | null,\n\tappName: string,\n\tsecretName: string,\n): string | undefined {\n\treturn state?.generatedSecrets?.[appName]?.[secretName];\n}\n\n/**\n * Set a generated secret for an app (mutates state)\n */\nexport function setGeneratedSecret(\n\tstate: DokployStageState,\n\tappName: string,\n\tsecretName: string,\n\tvalue: string,\n): void {\n\tif (!state.generatedSecrets) {\n\t\tstate.generatedSecrets = {};\n\t}\n\tif (!state.generatedSecrets[appName]) {\n\t\tstate.generatedSecrets[appName] = {};\n\t}\n\tstate.generatedSecrets[appName][secretName] = value;\n}\n\n/**\n * Get all generated secrets for an app\n */\nexport function getAppGeneratedSecrets(\n\tstate: DokployStageState | null,\n\tappName: string,\n): Record<string, string> {\n\treturn state?.generatedSecrets?.[appName] ?? {};\n}\n\n/**\n * Get all generated secrets from state\n */\nexport function getAllGeneratedSecrets(\n\tstate: DokployStageState | null,\n): Record<string, Record<string, string>> {\n\treturn state?.generatedSecrets ?? {};\n}\n\n// ============================================================================\n// DNS Verification\n// ============================================================================\n\n/**\n * Get DNS verification record for a hostname\n */\nexport function getDnsVerification(\n\tstate: DokployStageState | null,\n\thostname: string,\n): DnsVerificationRecord | undefined {\n\treturn state?.dnsVerified?.[hostname];\n}\n\n/**\n * Set DNS verification record for a hostname (mutates state)\n */\nexport function setDnsVerification(\n\tstate: DokployStageState,\n\thostname: string,\n\tserverIp: string,\n): void {\n\tif (!state.dnsVerified) {\n\t\tstate.dnsVerified = {};\n\t}\n\tstate.dnsVerified[hostname] = {\n\t\tserverIp,\n\t\tverifiedAt: new Date().toISOString(),\n\t};\n}\n\n/**\n * Check if a hostname is already verified with the given IP\n */\nexport function isDnsVerified(\n\tstate: DokployStageState | null,\n\thostname: string,\n\tserverIp: string,\n): boolean {\n\tconst record = state?.dnsVerified?.[hostname];\n\treturn record?.serverIp === serverIp;\n}\n\n/**\n * Get all DNS verification records from state\n */\nexport function getAllDnsVerifications(\n\tstate: DokployStageState | null,\n): Record<string, DnsVerificationRecord> {\n\treturn state?.dnsVerified ?? {};\n}\n\n// ============================================================================\n// DNS Records\n// ============================================================================\n\n/**\n * Get the key for a DNS record in state\n */\nfunction getDnsRecordKey(name: string, type: string): string {\n\treturn `${name}:${type}`;\n}\n\n/**\n * Get a created DNS record from state\n */\nexport function getDnsRecord(\n\tstate: DokployStageState | null,\n\tname: string,\n\ttype: string,\n): CreatedDnsRecord | undefined {\n\treturn state?.dnsRecords?.[getDnsRecordKey(name, type)];\n}\n\n/**\n * Set a created DNS record in state (mutates state)\n */\nexport function setDnsRecord(\n\tstate: DokployStageState,\n\trecord: Omit<CreatedDnsRecord, 'createdAt'>,\n): void {\n\tif (!state.dnsRecords) {\n\t\tstate.dnsRecords = {};\n\t}\n\tconst key = getDnsRecordKey(record.name, record.type);\n\tstate.dnsRecords[key] = {\n\t\t...record,\n\t\tcreatedAt: new Date().toISOString(),\n\t};\n}\n\n/**\n * Remove a DNS record from state (mutates state)\n */\nexport function removeDnsRecord(\n\tstate: DokployStageState,\n\tname: string,\n\ttype: string,\n): void {\n\tif (state.dnsRecords) {\n\t\tdelete state.dnsRecords[getDnsRecordKey(name, type)];\n\t}\n}\n\n/**\n * Get all created DNS records from state\n */\nexport function getAllDnsRecords(\n\tstate: DokployStageState | null,\n): CreatedDnsRecord[] {\n\tif (!state?.dnsRecords) {\n\t\treturn [];\n\t}\n\treturn Object.values(state.dnsRecords);\n}\n\n/**\n * Clear all DNS records from state (mutates state)\n */\nexport function clearDnsRecords(state: DokployStageState): void {\n\tstate.dnsRecords = {};\n\tstate.dnsVerified = {};\n}\n\n// ============================================================================\n// Backup State\n// ============================================================================\n\n/**\n * Get backup state from state\n */\nexport function getBackupState(\n\tstate: DokployStageState | null,\n): BackupState | undefined {\n\treturn state?.backups;\n}\n\n/**\n * Set backup state (mutates state)\n */\nexport function setBackupState(\n\tstate: DokployStageState,\n\tbackupState: BackupState,\n): void {\n\tstate.backups = backupState;\n}\n\n/**\n * Get backup destination ID from state\n */\nexport function getBackupDestinationId(\n\tstate: DokployStageState | null,\n): string | undefined {\n\treturn state?.backups?.destinationId;\n}\n\n/**\n * Get postgres backup ID from state\n */\nexport function getPostgresBackupId(\n\tstate: DokployStageState | null,\n): string | undefined {\n\treturn state?.backups?.postgresBackupId;\n}\n\n/**\n * Set postgres backup ID in state (mutates state)\n */\nexport function setPostgresBackupId(\n\tstate: DokployStageState,\n\tbackupId: string,\n): void {\n\tif (state.backups) {\n\t\tstate.backups.postgresBackupId = backupId;\n\t}\n}\n","/**\n * DNS Provider Interface\n *\n * Abstracts DNS operations for different providers.\n * Built-in providers: HostingerProvider, Route53Provider\n * Users can also supply custom implementations.\n */\n\nimport type { z } from 'zod/v4';\nimport type {\n\tCloudflareDnsProviderSchema,\n\tCustomDnsProviderSchema,\n\tDnsProviderSchema,\n\tDnsRecordSchema,\n\tDnsRecordTypeSchema,\n\tHostingerDnsProviderSchema,\n\tManualDnsProviderSchema,\n\tRoute53DnsProviderSchema,\n\tUpsertDnsRecordSchema,\n\tUpsertResultSchema,\n} from '../../workspace/schema';\n\n// =============================================================================\n// DNS Record Types (derived from Zod schemas)\n// =============================================================================\n\n/**\n * DNS record types supported across providers.\n */\nexport type DnsRecordType = z.infer<typeof DnsRecordTypeSchema>;\n\n/**\n * A DNS record as returned by the provider.\n */\nexport type DnsRecord = z.infer<typeof DnsRecordSchema>;\n\n/**\n * A DNS record to create or update.\n */\nexport type UpsertDnsRecord = z.infer<typeof UpsertDnsRecordSchema>;\n\n/**\n * Result of an upsert operation.\n */\nexport type UpsertResult = z.infer<typeof UpsertResultSchema>;\n\n// =============================================================================\n// DNS Provider Interface\n// =============================================================================\n\n/**\n * A record to delete from DNS.\n */\nexport interface DeleteDnsRecord {\n\t/** Record name/subdomain (e.g., 'api' or '@' for root) */\n\tname: string;\n\t/** Record type (A, CNAME, etc.) */\n\ttype: DnsRecordType;\n}\n\n/**\n * Result of a delete operation.\n */\nexport interface DeleteResult {\n\t/** The record that was requested for deletion */\n\trecord: DeleteDnsRecord;\n\t/** Whether the record was deleted */\n\tdeleted: boolean;\n\t/** Whether the record was not found (already deleted) */\n\tnotFound: boolean;\n\t/** Error message if deletion failed */\n\terror?: string;\n}\n\n/**\n * Interface for DNS providers.\n *\n * Implementations must handle:\n * - Getting all records for a domain\n * - Creating or updating records for a domain\n * - Deleting records from a domain\n */\nexport interface DnsProvider {\n\t/** Provider name for logging */\n\treadonly name: string;\n\n\t/**\n\t * Get all DNS records for a domain.\n\t *\n\t * @param domain - Root domain (e.g., 'example.com')\n\t * @returns Array of DNS records\n\t */\n\tgetRecords(domain: string): Promise<DnsRecord[]>;\n\n\t/**\n\t * Create or update DNS records.\n\t *\n\t * @param domain - Root domain (e.g., 'example.com')\n\t * @param records - Records to create or update\n\t * @returns Results of the upsert operations\n\t */\n\tupsertRecords(\n\t\tdomain: string,\n\t\trecords: UpsertDnsRecord[],\n\t): Promise<UpsertResult[]>;\n\n\t/**\n\t * Delete DNS records.\n\t *\n\t * @param domain - Root domain (e.g., 'example.com')\n\t * @param records - Records to delete\n\t * @returns Results of the delete operations\n\t */\n\tdeleteRecords(\n\t\tdomain: string,\n\t\trecords: DeleteDnsRecord[],\n\t): Promise<DeleteResult[]>;\n}\n\n// =============================================================================\n// DNS Provider Config Types (derived from Zod schemas)\n// =============================================================================\n\nexport type HostingerDnsConfig = z.infer<typeof HostingerDnsProviderSchema>;\nexport type Route53DnsConfig = z.infer<typeof Route53DnsProviderSchema>;\nexport type CloudflareDnsConfig = z.infer<typeof CloudflareDnsProviderSchema>;\nexport type ManualDnsConfig = z.infer<typeof ManualDnsProviderSchema>;\nexport type CustomDnsConfig = z.infer<typeof CustomDnsProviderSchema>;\n/** Single DNS provider config (for one domain) */\nexport type DnsConfig = z.infer<typeof DnsProviderSchema>;\n\n// =============================================================================\n// DNS Provider Factory\n// =============================================================================\n\n/**\n * Check if value is a DnsProvider implementation.\n */\nexport function isDnsProvider(value: unknown): value is DnsProvider {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\ttypeof (value as DnsProvider).name === 'string' &&\n\t\ttypeof (value as DnsProvider).getRecords === 'function' &&\n\t\ttypeof (value as DnsProvider).upsertRecords === 'function' &&\n\t\ttypeof (value as DnsProvider).deleteRecords === 'function'\n\t);\n}\n\nexport interface CreateDnsProviderOptions {\n\t/** DNS config from workspace */\n\tconfig: DnsConfig;\n}\n\n/**\n * Create a DNS provider based on configuration.\n *\n * - 'hostinger': HostingerProvider\n * - 'route53': Route53Provider\n * - 'manual': Returns null (user handles DNS)\n * - Custom: Use provided DnsProvider implementation\n */\nexport async function createDnsProvider(\n\toptions: CreateDnsProviderOptions,\n): Promise<DnsProvider | null> {\n\tconst { config } = options;\n\n\t// Manual mode - no provider needed\n\tif (config.provider === 'manual') {\n\t\treturn null;\n\t}\n\n\t// Custom provider implementation\n\tif (isDnsProvider(config.provider)) {\n\t\treturn config.provider;\n\t}\n\n\t// Built-in providers\n\tconst provider = config.provider;\n\n\tif (provider === 'hostinger') {\n\t\tconst { HostingerProvider } = await import('./HostingerProvider');\n\t\treturn new HostingerProvider();\n\t}\n\n\tif (provider === 'route53') {\n\t\tconst { Route53Provider } = await import('./Route53Provider');\n\t\tconst route53Config = config as Route53DnsConfig;\n\t\treturn new Route53Provider({\n\t\t\tregion: route53Config.region,\n\t\t\tprofile: route53Config.profile,\n\t\t\thostedZoneId: route53Config.hostedZoneId,\n\t\t});\n\t}\n\n\tif (provider === 'cloudflare') {\n\t\tthrow new Error('Cloudflare DNS provider not yet implemented');\n\t}\n\n\tthrow new Error(`Unknown DNS provider: ${JSON.stringify(config)}`);\n}\n","/**\n * DNS orchestration for deployments\n *\n * Handles automatic DNS record creation for deployed applications.\n */\n\nimport { lookup } from 'node:dns/promises';\nimport type {\n\tDnsConfig,\n\tDnsProvider as DnsProviderConfig,\n} from '../../workspace/types';\nimport {\n\ttype DokployStageState,\n\tisDnsVerified,\n\tsetDnsRecord,\n\tsetDnsVerification,\n} from '../state';\nimport {\n\tcreateDnsProvider,\n\ttype DnsProvider,\n\ttype DnsConfig as SchemaDnsConfig,\n\ttype UpsertDnsRecord,\n} from './DnsProvider';\n\nconst logger = console;\n\n/**\n * Check if DNS config is legacy format (single domain with `domain` property)\n */\nexport function isLegacyDnsConfig(\n\tconfig: DnsConfig,\n): config is SchemaDnsConfig & { domain: string } {\n\treturn (\n\t\ttypeof config === 'object' &&\n\t\tconfig !== null &&\n\t\t'provider' in config &&\n\t\t'domain' in config\n\t);\n}\n\n/**\n * Normalize DNS config to new multi-domain format\n */\nexport function normalizeDnsConfig(\n\tconfig: DnsConfig,\n): Record<string, DnsProviderConfig> {\n\tif (isLegacyDnsConfig(config)) {\n\t\t// Convert legacy format to new format\n\t\tconst { domain, ...providerConfig } = config;\n\t\treturn { [domain]: providerConfig as DnsProviderConfig };\n\t}\n\treturn config as Record<string, DnsProviderConfig>;\n}\n\n/**\n * Find the root domain for a hostname from available DNS configs\n *\n * @example\n * findRootDomain('api.geekmidas.com', { 'geekmidas.com': {...}, 'geekmidas.dev': {...} })\n * // Returns 'geekmidas.com'\n */\nexport function findRootDomain(\n\thostname: string,\n\tdnsConfig: Record<string, DnsProviderConfig>,\n): string | null {\n\t// Sort domains by length descending to match most specific first\n\tconst domains = Object.keys(dnsConfig).sort((a, b) => b.length - a.length);\n\n\tfor (const domain of domains) {\n\t\tif (hostname === domain || hostname.endsWith(`.${domain}`)) {\n\t\t\treturn domain;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Group hostnames by their root domain\n */\nexport function groupHostnamesByDomain(\n\tappHostnames: Map<string, string>,\n\tdnsConfig: Record<string, DnsProviderConfig>,\n): Map<string, Map<string, string>> {\n\tconst grouped = new Map<string, Map<string, string>>();\n\n\tfor (const [appName, hostname] of appHostnames) {\n\t\tconst rootDomain = findRootDomain(hostname, dnsConfig);\n\t\tif (!rootDomain) {\n\t\t\tlogger.log(` ⚠ No DNS config found for hostname: ${hostname}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!grouped.has(rootDomain)) {\n\t\t\tgrouped.set(rootDomain, new Map());\n\t\t}\n\t\tgrouped.get(rootDomain)!.set(appName, hostname);\n\t}\n\n\treturn grouped;\n}\n\n/**\n * Required DNS record for an app\n */\nexport interface RequiredDnsRecord {\n\t/** Full hostname (e.g., 'api.joemoer.traflabs.io') */\n\thostname: string;\n\t/** Subdomain part for the DNS provider (e.g., 'api.joemoer') */\n\tsubdomain: string;\n\t/** Record type */\n\ttype: 'A' | 'CNAME';\n\t/** Target value (IP or hostname) */\n\tvalue: string;\n\t/** App name */\n\tappName: string;\n\t/** Whether the record was created */\n\tcreated?: boolean;\n\t/** Whether the record already existed */\n\texisted?: boolean;\n\t/** Error if creation failed */\n\terror?: string;\n}\n\n/**\n * Result of DNS record creation\n */\nexport interface DnsCreationResult {\n\trecords: RequiredDnsRecord[];\n\tsuccess: boolean;\n\tserverIp: string;\n}\n\n/**\n * Resolve IP address from a hostname\n */\nexport async function resolveHostnameToIp(hostname: string): Promise<string> {\n\ttry {\n\t\tconst addresses = await lookup(hostname, { family: 4 });\n\t\treturn addresses.address;\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to resolve IP for ${hostname}: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n}\n\n/**\n * Extract subdomain from full hostname relative to root domain\n *\n * @example\n * extractSubdomain('api.joemoer.traflabs.io', 'traflabs.io') => 'api.joemoer'\n * extractSubdomain('joemoer.traflabs.io', 'traflabs.io') => 'joemoer'\n */\nexport function extractSubdomain(hostname: string, rootDomain: string): string {\n\tif (!hostname.endsWith(rootDomain)) {\n\t\tthrow new Error(\n\t\t\t`Hostname ${hostname} is not under root domain ${rootDomain}`,\n\t\t);\n\t}\n\n\tconst subdomain = hostname.slice(0, -(rootDomain.length + 1)); // +1 for the dot\n\treturn subdomain || '@'; // '@' represents the root domain itself\n}\n\n/**\n * Generate required DNS records for a deployment\n */\nexport function generateRequiredRecords(\n\tappHostnames: Map<string, string>, // appName -> hostname\n\trootDomain: string,\n\tserverIp: string,\n): RequiredDnsRecord[] {\n\tconst records: RequiredDnsRecord[] = [];\n\n\tfor (const [appName, hostname] of appHostnames) {\n\t\tconst subdomain = extractSubdomain(hostname, rootDomain);\n\t\trecords.push({\n\t\t\thostname,\n\t\t\tsubdomain,\n\t\t\ttype: 'A',\n\t\t\tvalue: serverIp,\n\t\t\tappName,\n\t\t});\n\t}\n\n\treturn records;\n}\n\n/**\n * Print DNS records table\n */\nexport function printDnsRecordsTable(\n\trecords: RequiredDnsRecord[],\n\trootDomain: string,\n): void {\n\tlogger.log(`\\n 📋 DNS Records for ${rootDomain}:`);\n\tlogger.log(\n\t\t' ┌─────────────────────────────────────┬──────┬─────────────────┬────────┐',\n\t);\n\tlogger.log(\n\t\t' │ Subdomain │ Type │ Value │ Status │',\n\t);\n\tlogger.log(\n\t\t' ├─────────────────────────────────────┼──────┼─────────────────┼────────┤',\n\t);\n\n\tfor (const record of records) {\n\t\tconst subdomain = record.subdomain.padEnd(35);\n\t\tconst type = record.type.padEnd(4);\n\t\tconst value = record.value.padEnd(15);\n\t\tlet status: string;\n\n\t\tif (record.error) {\n\t\t\tstatus = '✗';\n\t\t} else if (record.created) {\n\t\t\tstatus = '✓ new';\n\t\t} else if (record.existed) {\n\t\t\tstatus = '✓';\n\t\t} else {\n\t\t\tstatus = '?';\n\t\t}\n\n\t\tlogger.log(\n\t\t\t` │ ${subdomain} │ ${type} │ ${value} │ ${status.padEnd(6)} │`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t' └─────────────────────────────────────┴──────┴─────────────────┴────────┘',\n\t);\n}\n\n/**\n * Print DNS records in a simple format for manual setup\n */\nexport function printDnsRecordsSimple(\n\trecords: RequiredDnsRecord[],\n\trootDomain: string,\n): void {\n\tlogger.log('\\n 📋 Required DNS Records:');\n\tlogger.log(` Add these A records to your DNS provider (${rootDomain}):\\n`);\n\n\tfor (const record of records) {\n\t\tlogger.log(` ${record.subdomain} → ${record.value} (A record)`);\n\t}\n\n\tlogger.log('');\n}\n\n/**\n * Create DNS records for a single domain using its configured provider\n */\nexport async function createDnsRecordsForDomain(\n\trecords: RequiredDnsRecord[],\n\trootDomain: string,\n\tproviderConfig: DnsProviderConfig,\n): Promise<RequiredDnsRecord[]> {\n\t// Get TTL from config, default to 300. Manual mode doesn't have ttl property.\n\tconst ttl =\n\t\t'ttl' in providerConfig && providerConfig.ttl ? providerConfig.ttl : 300;\n\n\t// Get DNS provider from factory\n\tlet provider: DnsProvider | null;\n\ttry {\n\t\t// Cast to schema-derived DnsConfig for provider factory\n\t\tprovider = await createDnsProvider({\n\t\t\tconfig: providerConfig as SchemaDnsConfig,\n\t\t});\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : 'Unknown error';\n\t\tlogger.log(\n\t\t\t` ⚠ Failed to create DNS provider for ${rootDomain}: ${message}`,\n\t\t);\n\t\treturn records.map((r) => ({ ...r, error: message }));\n\t}\n\n\t// Manual mode - no provider, just mark records as needing manual creation\n\tif (!provider) {\n\t\treturn records.map((r) => ({ ...r, created: false, existed: false }));\n\t}\n\n\tconst results: RequiredDnsRecord[] = [];\n\n\t// Convert RequiredDnsRecord to UpsertDnsRecord format\n\tconst upsertRecords: UpsertDnsRecord[] = records.map((r) => ({\n\t\tname: r.subdomain,\n\t\ttype: r.type,\n\t\tttl,\n\t\tvalue: r.value,\n\t}));\n\n\ttry {\n\t\t// Use provider to upsert records\n\t\tconst upsertResults = await provider.upsertRecords(\n\t\t\trootDomain,\n\t\t\tupsertRecords,\n\t\t);\n\n\t\t// Map results back to RequiredDnsRecord format\n\t\tfor (const [i, record] of records.entries()) {\n\t\t\tconst result = upsertResults[i];\n\n\t\t\t// Handle case where upsertResults has fewer items (shouldn't happen but be safe)\n\t\t\tif (!result) {\n\t\t\t\tresults.push({\n\t\t\t\t\thostname: record.hostname,\n\t\t\t\t\tsubdomain: record.subdomain,\n\t\t\t\t\ttype: record.type,\n\t\t\t\t\tvalue: record.value,\n\t\t\t\t\tappName: record.appName,\n\t\t\t\t\terror: 'No result returned from provider',\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (result.unchanged) {\n\t\t\t\tresults.push({\n\t\t\t\t\thostname: record.hostname,\n\t\t\t\t\tsubdomain: record.subdomain,\n\t\t\t\t\ttype: record.type,\n\t\t\t\t\tvalue: record.value,\n\t\t\t\t\tappName: record.appName,\n\t\t\t\t\texisted: true,\n\t\t\t\t\tcreated: false,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tresults.push({\n\t\t\t\t\thostname: record.hostname,\n\t\t\t\t\tsubdomain: record.subdomain,\n\t\t\t\t\ttype: record.type,\n\t\t\t\t\tvalue: record.value,\n\t\t\t\t\tappName: record.appName,\n\t\t\t\t\tcreated: result.created,\n\t\t\t\t\texisted: !result.created,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : 'Unknown error';\n\t\tlogger.log(\n\t\t\t` ⚠ Failed to create DNS records for ${rootDomain}: ${message}`,\n\t\t);\n\t\treturn records.map((r) => ({\n\t\t\thostname: r.hostname,\n\t\t\tsubdomain: r.subdomain,\n\t\t\ttype: r.type,\n\t\t\tvalue: r.value,\n\t\t\tappName: r.appName,\n\t\t\terror: message,\n\t\t}));\n\t}\n\n\treturn results;\n}\n\n/**\n * Create DNS records using the configured provider\n * @deprecated Use createDnsRecordsForDomain for multi-domain support\n */\nexport async function createDnsRecords(\n\trecords: RequiredDnsRecord[],\n\tdnsConfig: DnsConfig,\n): Promise<RequiredDnsRecord[]> {\n\t// Handle legacy config format\n\tif (!isLegacyDnsConfig(dnsConfig)) {\n\t\tthrow new Error(\n\t\t\t'createDnsRecords requires legacy DnsConfig with domain property. Use createDnsRecordsForDomain instead.',\n\t\t);\n\t}\n\tconst { domain: rootDomain, ...providerConfig } = dnsConfig;\n\treturn createDnsRecordsForDomain(\n\t\trecords,\n\t\trootDomain,\n\t\tproviderConfig as DnsProviderConfig,\n\t);\n}\n\n/**\n * Main DNS orchestration function for deployments\n *\n * Supports both legacy single-domain format and new multi-domain format:\n * - Legacy: { provider: 'hostinger', domain: 'example.com' }\n * - Multi: { 'example.com': { provider: 'hostinger' }, 'example.dev': { provider: 'route53' } }\n *\n * @param appHostnames - Map of app names to hostnames\n * @param dnsConfig - DNS configuration (legacy or multi-domain)\n * @param dokployEndpoint - Dokploy server endpoint to resolve IP from\n * @param state - Optional state to save created records for later deletion\n */\nexport async function orchestrateDns(\n\tappHostnames: Map<string, string>, // appName -> hostname\n\tdnsConfig: DnsConfig | undefined,\n\tdokployEndpoint: string,\n\tstate?: DokployStageState,\n): Promise<DnsCreationResult | null> {\n\tif (!dnsConfig) {\n\t\treturn null;\n\t}\n\n\t// Normalize config to multi-domain format\n\tconst normalizedConfig = normalizeDnsConfig(dnsConfig);\n\n\t// Resolve Dokploy server IP from endpoint\n\tlogger.log('\\n🌐 Setting up DNS records...');\n\tlet serverIp: string;\n\n\ttry {\n\t\tconst endpointUrl = new URL(dokployEndpoint);\n\t\tserverIp = await resolveHostnameToIp(endpointUrl.hostname);\n\t\tlogger.log(` Server IP: ${serverIp} (from ${endpointUrl.hostname})`);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : 'Unknown error';\n\t\tlogger.log(` ⚠ Failed to resolve server IP: ${message}`);\n\t\treturn null;\n\t}\n\n\t// Group hostnames by their root domain\n\tconst groupedHostnames = groupHostnamesByDomain(\n\t\tappHostnames,\n\t\tnormalizedConfig,\n\t);\n\n\tif (groupedHostnames.size === 0) {\n\t\tlogger.log(\n\t\t\t' No DNS records needed (no hostnames match configured domains)',\n\t\t);\n\t\treturn { records: [], success: true, serverIp };\n\t}\n\n\tconst allRecords: RequiredDnsRecord[] = [];\n\tlet hasFailures = false;\n\n\t// Process each domain group with its specific provider\n\tfor (const [rootDomain, domainHostnames] of groupedHostnames) {\n\t\tconst providerConfig = normalizedConfig[rootDomain];\n\t\tif (!providerConfig) {\n\t\t\tlogger.log(` ⚠ No provider config for ${rootDomain}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst providerName =\n\t\t\ttypeof providerConfig.provider === 'string'\n\t\t\t\t? providerConfig.provider\n\t\t\t\t: 'custom';\n\n\t\t// Generate required records for this domain\n\t\tconst requiredRecords = generateRequiredRecords(\n\t\t\tdomainHostnames,\n\t\t\trootDomain,\n\t\t\tserverIp,\n\t\t);\n\n\t\tif (requiredRecords.length === 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Create records for this domain\n\t\tlogger.log(\n\t\t\t` Creating DNS records for ${rootDomain} (${providerName})...`,\n\t\t);\n\t\tconst domainRecords = await createDnsRecordsForDomain(\n\t\t\trequiredRecords,\n\t\t\trootDomain,\n\t\t\tproviderConfig,\n\t\t);\n\n\t\tallRecords.push(...domainRecords);\n\n\t\tconst created = domainRecords.filter((r) => r.created).length;\n\t\tconst existed = domainRecords.filter((r) => r.existed).length;\n\t\tconst failed = domainRecords.filter((r) => r.error).length;\n\n\t\tif (created > 0) {\n\t\t\tlogger.log(` ✓ Created ${created} DNS record(s) for ${rootDomain}`);\n\t\t}\n\t\tif (existed > 0) {\n\t\t\tlogger.log(` ✓ ${existed} record(s) already exist for ${rootDomain}`);\n\t\t}\n\t\tif (failed > 0) {\n\t\t\tlogger.log(` ⚠ ${failed} record(s) failed for ${rootDomain}`);\n\t\t\thasFailures = true;\n\t\t}\n\n\t\t// Save created/existing records to state for later deletion during undeploy\n\t\tif (state) {\n\t\t\tfor (const record of domainRecords) {\n\t\t\t\tif (record.created || record.existed) {\n\t\t\t\t\tsetDnsRecord(state, {\n\t\t\t\t\t\tdomain: rootDomain,\n\t\t\t\t\t\tname: record.subdomain,\n\t\t\t\t\t\ttype: record.type,\n\t\t\t\t\t\tvalue: record.value,\n\t\t\t\t\t\tttl:\n\t\t\t\t\t\t\t'ttl' in providerConfig && providerConfig.ttl\n\t\t\t\t\t\t\t\t? providerConfig.ttl\n\t\t\t\t\t\t\t\t: 300,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Print summary table for this domain\n\t\tprintDnsRecordsTable(domainRecords, rootDomain);\n\n\t\t// If manual mode or some failed, print simple instructions\n\t\tif (providerConfig.provider === 'manual' || failed > 0) {\n\t\t\tprintDnsRecordsSimple(\n\t\t\t\tdomainRecords.filter((r) => !r.created && !r.existed),\n\t\t\t\trootDomain,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn {\n\t\trecords: allRecords,\n\t\tsuccess: !hasFailures,\n\t\tserverIp,\n\t};\n}\n\n/**\n * Result of DNS verification for a single hostname\n */\nexport interface DnsVerificationResult {\n\thostname: string;\n\tappName: string;\n\tverified: boolean;\n\tresolvedIp?: string;\n\texpectedIp: string;\n\terror?: string;\n\tskipped?: boolean; // True if already verified in state\n}\n\n/**\n * Verify DNS records resolve correctly after deployment.\n *\n * This function:\n * 1. Checks state for previously verified hostnames (skips if already verified with same IP)\n * 2. Attempts to resolve each hostname to an IP\n * 3. Compares resolved IP with expected server IP\n * 4. Updates state with verification results\n *\n * @param appHostnames - Map of app names to hostnames\n * @param serverIp - Expected IP address the hostnames should resolve to\n * @param state - Deploy state for caching verification results\n * @returns Array of verification results\n */\nexport async function verifyDnsRecords(\n\tappHostnames: Map<string, string>,\n\tserverIp: string,\n\tstate: DokployStageState,\n): Promise<DnsVerificationResult[]> {\n\tconst results: DnsVerificationResult[] = [];\n\n\tlogger.log('\\n🔍 Verifying DNS records...');\n\n\tfor (const [appName, hostname] of appHostnames) {\n\t\t// Check if already verified with same IP\n\t\tif (isDnsVerified(state, hostname, serverIp)) {\n\t\t\tlogger.log(` ✓ ${hostname} (previously verified)`);\n\t\t\tresults.push({\n\t\t\t\thostname,\n\t\t\t\tappName,\n\t\t\t\tverified: true,\n\t\t\t\texpectedIp: serverIp,\n\t\t\t\tskipped: true,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Attempt to resolve hostname\n\t\ttry {\n\t\t\tconst resolvedIp = await resolveHostnameToIp(hostname);\n\n\t\t\tif (resolvedIp === serverIp) {\n\t\t\t\t// DNS verified successfully\n\t\t\t\tsetDnsVerification(state, hostname, serverIp);\n\t\t\t\tlogger.log(` ✓ ${hostname} → ${resolvedIp}`);\n\t\t\t\tresults.push({\n\t\t\t\t\thostname,\n\t\t\t\t\tappName,\n\t\t\t\t\tverified: true,\n\t\t\t\t\tresolvedIp,\n\t\t\t\t\texpectedIp: serverIp,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// DNS resolves but to wrong IP\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ⚠ ${hostname} resolves to ${resolvedIp}, expected ${serverIp}`,\n\t\t\t\t);\n\t\t\t\tresults.push({\n\t\t\t\t\thostname,\n\t\t\t\t\tappName,\n\t\t\t\t\tverified: false,\n\t\t\t\t\tresolvedIp,\n\t\t\t\t\texpectedIp: serverIp,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// DNS resolution failed\n\t\t\tconst message = error instanceof Error ? error.message : 'Unknown error';\n\t\t\tlogger.log(` ⚠ ${hostname} DNS not propagated (${message})`);\n\t\t\tresults.push({\n\t\t\t\thostname,\n\t\t\t\tappName,\n\t\t\t\tverified: false,\n\t\t\t\texpectedIp: serverIp,\n\t\t\t\terror: message,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Summary\n\tconst verified = results.filter((r) => r.verified).length;\n\tconst skipped = results.filter((r) => r.skipped).length;\n\tconst pending = results.filter((r) => !r.verified).length;\n\n\tif (pending > 0) {\n\t\tlogger.log(`\\n ${verified} verified, ${pending} pending propagation`);\n\t\tlogger.log(' DNS changes may take 5-30 minutes to propagate');\n\t} else if (skipped > 0) {\n\t\tlogger.log(` ${verified} verified (${skipped} from cache)`);\n\t}\n\n\treturn results;\n}\n","import { getPublicEnvPrefix } from '../workspace/index.js';\nimport type {\n\tDokployWorkspaceConfig,\n\tNormalizedAppConfig,\n} from '../workspace/types.js';\n\n/**\n * Resolve the hostname for an app based on stage configuration.\n *\n * Domain resolution priority:\n * 1. Explicit app.domain override (string or stage-specific)\n * 2. Default pattern based on app type:\n * - Main frontend app gets base domain (e.g., 'myapp.com')\n * - Other apps get prefixed domain (e.g., 'api.myapp.com')\n *\n * @param appName - The name of the app\n * @param app - The normalized app configuration\n * @param stage - The deployment stage (e.g., 'production', 'development')\n * @param dokployConfig - Dokploy workspace configuration with domain mappings\n * @param isMainFrontend - Whether this is the main frontend app\n * @returns The resolved hostname for the app\n * @throws Error if no domain configuration is found for the stage\n */\nexport function resolveHost(\n\tappName: string,\n\tapp: NormalizedAppConfig,\n\tstage: string,\n\tdokployConfig: DokployWorkspaceConfig | undefined,\n\tisMainFrontend: boolean,\n): string {\n\t// 1. Check for explicit app domain override\n\tif (app.domain) {\n\t\tif (typeof app.domain === 'string') {\n\t\t\treturn app.domain;\n\t\t}\n\t\tif (app.domain[stage]) {\n\t\t\treturn app.domain[stage]!;\n\t\t}\n\t}\n\n\t// 2. Get base domain for this stage\n\tconst baseDomain = dokployConfig?.domains?.[stage];\n\tif (!baseDomain) {\n\t\tthrow new Error(\n\t\t\t`No domain configured for stage \"${stage}\". ` +\n\t\t\t\t`Add deploy.dokploy.domains.${stage} to gkm.config.ts`,\n\t\t);\n\t}\n\n\t// 3. Main frontend app gets base domain, others get prefix\n\tif (isMainFrontend) {\n\t\treturn baseDomain;\n\t}\n\n\treturn `${appName}.${baseDomain}`;\n}\n\n/**\n * Which site the base domain points at.\n *\n * Three rules, in order, and the last one is the point:\n *\n * 1. **One site** — it is the root. Unambiguous, so nothing has to be said.\n * 2. **Named `web`** — the convention wins, because people already rely on it.\n * 3. **Declared `root: true`** — the site says so itself.\n *\n * Anything else throws. It used to take the *first* `type: 'web'` it iterated,\n * and that object is built from the manifest, which is built in glob traversal\n * order — so two sites with neither named `web` meant renaming a file could\n * move the production root domain, silently. The same shape as\n * `CacheIsAmbiguous`: unambiguous with one and arbitrary with two, so two is an\n * error rather than a coin toss.\n *\n * @throws {AmbiguousRootSite} when several sites could be the root and none says it is\n */\nexport function isMainFrontendApp(\n\tappName: string,\n\tapp: NormalizedAppConfig,\n\tallApps: Record<string, NormalizedAppConfig>,\n): boolean {\n\tif (app.type !== 'web') return false;\n\n\tconst sites = Object.entries(allApps).filter(([, a]) => a.type === 'web');\n\n\t// 1. The only site there is.\n\tif (sites.length === 1) return true;\n\n\t// 2. The convention.\n\tconst named = sites.filter(([name]) => name === 'web');\n\tif (named.length > 0) return appName === 'web';\n\n\t// 3. What a site declared about itself.\n\tconst declared = sites.filter(([, a]) => a.root === true);\n\tif (declared.length === 1) return declared[0]![0] === appName;\n\n\tthrow new AmbiguousRootSite(\n\t\tsites.map(([name]) => name),\n\t\tdeclared.length > 1,\n\t);\n}\n\n/** Several sites could hold the base domain, and none of them says it does. */\nexport class AmbiguousRootSite extends Error {\n\tconstructor(\n\t\treadonly sites: readonly string[],\n\t\treadonly tooMany = false,\n\t) {\n\t\tsuper(\n\t\t\ttooMany\n\t\t\t\t? `More than one site declares \\`root: true\\` — ${sites.join(', ')}. ` +\n\t\t\t\t\t\t`The base domain points at one of them.`\n\t\t\t\t: `${sites.length} sites and nothing says which holds the base ` +\n\t\t\t\t\t\t`domain: ${sites.join(', ')}. Name one of them \\`web\\`, or ` +\n\t\t\t\t\t\t`declare \\`root: true\\` on the one the base domain points at. ` +\n\t\t\t\t\t\t`It used to be whichever the glob reached first, which is not a ` +\n\t\t\t\t\t\t`thing to decide a production hostname.`,\n\t\t);\n\t\tthis.name = 'AmbiguousRootSite';\n\t}\n}\n\n/**\n * Generate public URL build args for a web/mobile app based on its dependencies.\n *\n * The prefix is chosen by the app's framework (NEXT_PUBLIC_, VITE_,\n * EXPO_PUBLIC_, or none for Remix). Apps without a public prefix get no\n * build args — they should fetch URLs at runtime instead.\n *\n * @param app - The web/mobile app configuration\n * @param deployedUrls - Map of app name to deployed public URL\n * @returns Array of build args like 'NEXT_PUBLIC_API_URL=https://api.example.com'\n */\nexport function generatePublicUrlBuildArgs(\n\tapp: NormalizedAppConfig,\n\tdeployedUrls: Record<string, string>,\n): string[] {\n\tconst prefix = getPublicEnvPrefix(app.framework);\n\tif (!prefix) return [];\n\n\tconst buildArgs: string[] = [];\n\tfor (const dep of app.dependencies) {\n\t\tconst publicUrl = deployedUrls[dep];\n\t\tif (publicUrl) {\n\t\t\tbuildArgs.push(`${prefix}${dep.toUpperCase()}_URL=${publicUrl}`);\n\t\t}\n\t}\n\n\treturn buildArgs;\n}\n\n/**\n * Get public URL arg names from app dependencies.\n *\n * @param app - The web/mobile app configuration\n * @returns Array of arg names like 'NEXT_PUBLIC_API_URL', or [] for frameworks\n * without a public prefix (e.g. Remix).\n */\nexport function getPublicUrlArgNames(app: NormalizedAppConfig): string[] {\n\tconst prefix = getPublicEnvPrefix(app.framework);\n\tif (!prefix) return [];\n\treturn app.dependencies.map((dep) => `${prefix}${dep.toUpperCase()}_URL`);\n}\n","import { DEFAULT_POSTGRES_VERSION } from '@geekmidas/manifest';\nimport type {\n\tComposeServiceName,\n\tComposeServicesConfig,\n\tServiceConfig,\n} from '../types';\nimport type {\n\tNormalizedAppConfig,\n\tNormalizedWorkspace,\n} from '../workspace/types.js';\n\n/** Default Docker images for services */\nexport const DEFAULT_SERVICE_IMAGES: Record<ComposeServiceName, string> = {\n\tpostgres: 'postgres',\n\tredis: 'redis',\n\trabbitmq: 'rabbitmq',\n\tminio: 'quay.io/minio/minio',\n\tmailpit: 'axllent/mailpit',\n\tlocalstack: 'floci/floci',\n};\n\n/** Default Docker image versions for services */\nexport const DEFAULT_SERVICE_VERSIONS: Record<ComposeServiceName, string> = {\n\tpostgres: `${DEFAULT_POSTGRES_VERSION}-alpine`,\n\tredis: '7-alpine',\n\trabbitmq: '3-management-alpine',\n\t// Pinned, unlike its neighbours, and deliberately: MinIO removed their\n\t// Docker Hub images, so `minio/minio:latest` stopped resolving — and\n\t// `latest` is what let that land here without anyone choosing it.\n\tminio: 'RELEASE.2025-09-07T16-13-09Z',\n\tmailpit: 'latest',\n\tlocalstack: 'latest',\n};\n\nexport interface ComposeOptions {\n\timageName: string;\n\tregistry: string;\n\tport: number;\n\thealthCheckPath: string;\n\t/** Services config - object format or legacy array format */\n\tservices: ComposeServicesConfig | ComposeServiceName[];\n}\n\n/** Get the default full image reference for a service */\nfunction getDefaultImage(serviceName: ComposeServiceName): string {\n\treturn `${DEFAULT_SERVICE_IMAGES[serviceName]}:${DEFAULT_SERVICE_VERSIONS[serviceName]}`;\n}\n\n/** Normalize services config to a consistent format - returns Map of service name to full image reference */\nfunction normalizeServices(\n\tservices: ComposeServicesConfig | ComposeServiceName[],\n): Map<ComposeServiceName, string> {\n\tconst result = new Map<ComposeServiceName, string>();\n\n\tif (Array.isArray(services)) {\n\t\t// Legacy array format - use default images\n\t\tfor (const name of services) {\n\t\t\tresult.set(name, getDefaultImage(name));\n\t\t}\n\t} else {\n\t\t// Object format\n\t\tfor (const [name, config] of Object.entries(services)) {\n\t\t\tconst serviceName = name as ComposeServiceName;\n\t\t\tif (config === true) {\n\t\t\t\t// boolean true - use default image\n\t\t\t\tresult.set(serviceName, getDefaultImage(serviceName));\n\t\t\t} else if (config && typeof config === 'object') {\n\t\t\t\tconst serviceConfig = config as ServiceConfig;\n\t\t\t\tif (serviceConfig.image) {\n\t\t\t\t\t// Full image reference provided\n\t\t\t\t\tresult.set(serviceName, serviceConfig.image);\n\t\t\t\t} else {\n\t\t\t\t\t// Version only - use default image name with custom version\n\t\t\t\t\tconst version =\n\t\t\t\t\t\tserviceConfig.version ?? DEFAULT_SERVICE_VERSIONS[serviceName];\n\t\t\t\t\tresult.set(\n\t\t\t\t\t\tserviceName,\n\t\t\t\t\t\t`${DEFAULT_SERVICE_IMAGES[serviceName]}:${version}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// false or undefined - skip\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Generate docker-compose.yml for production deployment\n */\nexport function generateDockerCompose(options: ComposeOptions): string {\n\tconst { imageName, registry, port, healthCheckPath, services } = options;\n\n\t// Normalize services to Map<name, version>\n\tconst serviceMap = normalizeServices(services);\n\n\tconst imageRef = registry ? `\\${REGISTRY:-${registry}}/` : '';\n\n\tlet yaml = `# Use \"gkm dev\" or \"gkm test\" to start services.\n# Running \"docker compose up\" directly will not inject secrets or resolve ports.\nversion: '3.8'\n\nservices:\n api:\n build:\n context: ../..\n dockerfile: .gkm/docker/Dockerfile\n image: ${imageRef}\\${IMAGE_NAME:-${imageName}}:\\${TAG:-latest}\n container_name: ${imageName}\n restart: unless-stopped\n ports:\n - \"\\${PORT:-${port}}:${port}\"\n environment:\n - NODE_ENV=production\n`;\n\n\t// Add environment variables based on services\n\tif (serviceMap.has('postgres')) {\n\t\tyaml += ` - DATABASE_URL=\\${DATABASE_URL:-postgresql://\\${POSTGRES_USER:-postgres}:\\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\\${POSTGRES_DB:-app}}\n`;\n\t}\n\n\tif (serviceMap.has('redis')) {\n\t\tyaml += ` - REDIS_URL=\\${REDIS_URL:-redis://redis:6379}\n`;\n\t}\n\n\tif (serviceMap.has('rabbitmq')) {\n\t\tyaml += ` - RABBITMQ_URL=\\${RABBITMQ_URL:-amqp://rabbitmq:5672}\n`;\n\t}\n\n\tif (serviceMap.has('minio')) {\n\t\tyaml += ` - STORAGE_ENDPOINT=\\${STORAGE_ENDPOINT:-http://minio:9000}\n - STORAGE_ACCESS_KEY_ID=\\${STORAGE_ACCESS_KEY_ID:-${imageName}}\n - STORAGE_SECRET_ACCESS_KEY=\\${STORAGE_SECRET_ACCESS_KEY:-${imageName}}\n - STORAGE_BUCKET=\\${STORAGE_BUCKET:-${imageName}}\n - STORAGE_REGION=\\${STORAGE_REGION:-eu-west-1}\n - STORAGE_FORCE_PATH_STYLE=true\n`;\n\t}\n\n\tif (serviceMap.has('mailpit')) {\n\t\tyaml += ` - SMTP_HOST=\\${SMTP_HOST:-mailpit}\n - SMTP_PORT=\\${SMTP_PORT:-1025}\n - SMTP_USER=\\${SMTP_USER:-${imageName}}\n - SMTP_PASS=\\${SMTP_PASS:-${imageName}}\n - SMTP_SECURE=\\${SMTP_SECURE:-false}\n - MAIL_FROM=\\${MAIL_FROM:-noreply@localhost}\n`;\n\t}\n\n\tif (serviceMap.has('localstack')) {\n\t\tyaml += ` - AWS_ACCESS_KEY_ID=\\${AWS_ACCESS_KEY_ID:-localstack}\n - AWS_SECRET_ACCESS_KEY=\\${AWS_SECRET_ACCESS_KEY:-localstack}\n - AWS_REGION=\\${AWS_REGION:-us-east-1}\n - AWS_ENDPOINT_URL=\\${AWS_ENDPOINT_URL:-http://localstack:4566}\n`;\n\t}\n\n\tyaml += ` healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:${port}${healthCheckPath}\"]\n interval: 30s\n timeout: 3s\n retries: 3\n`;\n\n\t// Add depends_on if there are services\n\tif (serviceMap.size > 0) {\n\t\tyaml += ` depends_on:\n`;\n\t\tfor (const serviceName of serviceMap.keys()) {\n\t\t\tyaml += ` ${serviceName}:\n condition: service_healthy\n`;\n\t\t}\n\t}\n\n\tyaml += ` networks:\n - app-network\n`;\n\n\t// Add service definitions with images\n\tconst postgresImage = serviceMap.get('postgres');\n\tif (postgresImage) {\n\t\tyaml += `\n postgres:\n image: ${postgresImage}\n container_name: postgres\n restart: unless-stopped\n environment:\n POSTGRES_USER: \\${POSTGRES_USER:-postgres}\n POSTGRES_PASSWORD: \\${POSTGRES_PASSWORD:-postgres}\n POSTGRES_DB: \\${POSTGRES_DB:-app}\n volumes:\n - dbdata:/var/lib/postgresql\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U $$POSTGRES_USER\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\tconst redisImage = serviceMap.get('redis');\n\tif (redisImage) {\n\t\tyaml += `\n redis:\n image: ${redisImage}\n container_name: redis\n restart: unless-stopped\n volumes:\n - redis_data:/data\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\tconst rabbitmqImage = serviceMap.get('rabbitmq');\n\tif (rabbitmqImage) {\n\t\tyaml += `\n rabbitmq:\n image: ${rabbitmqImage}\n container_name: rabbitmq\n restart: unless-stopped\n environment:\n RABBITMQ_DEFAULT_USER: \\${RABBITMQ_USER:-guest}\n RABBITMQ_DEFAULT_PASS: \\${RABBITMQ_PASSWORD:-guest}\n ports:\n - \"15672:15672\" # Management UI\n volumes:\n - rabbitmq_data:/var/lib/rabbitmq\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"-q\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\tconst minioImage = serviceMap.get('minio');\n\tif (minioImage) {\n\t\tyaml += `\n minio:\n image: ${minioImage}\n container_name: minio\n restart: unless-stopped\n entrypoint: sh\n command: -c 'mkdir -p /data/\\${STORAGE_BUCKET:-${imageName}} && /usr/bin/docker-entrypoint.sh server --console-address \":9001\" /data'\n environment:\n MINIO_ROOT_USER: \\${STORAGE_ACCESS_KEY_ID:-${imageName}}\n MINIO_ROOT_PASSWORD: \\${STORAGE_SECRET_ACCESS_KEY:-${imageName}}\n ports:\n - \"\\${MINIO_API_PORT:-9000}:9000\"\n - \"\\${MINIO_CONSOLE_PORT:-9001}:9001\"\n volumes:\n - minio_data:/data\n healthcheck:\n test: [\"CMD\", \"mc\", \"ready\", \"local\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\tconst mailpitImage = serviceMap.get('mailpit');\n\tif (mailpitImage) {\n\t\tyaml += `\n mailpit:\n image: ${mailpitImage}\n container_name: mailpit\n restart: unless-stopped\n environment:\n MP_SMTP_AUTH: \\${SMTP_USER:-${imageName}}:\\${SMTP_PASS:-${imageName}}\n MP_SMTP_AUTH_ALLOW_INSECURE: true\n ports:\n - \"\\${MAILPIT_PORT:-8025}:8025\" # Web UI / API\n - \"\\${SMTP_PORT:-1025}:1025\" # SMTP\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8025\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\tconst localstackImage = serviceMap.get('localstack');\n\tif (localstackImage) {\n\t\tyaml += `\n localstack:\n image: ${localstackImage}\n container_name: localstack\n restart: unless-stopped\n environment:\n AWS_DEFAULT_REGION: \\${AWS_REGION:-us-east-1}\n AWS_ACCESS_KEY_ID: \\${AWS_ACCESS_KEY_ID:-localstack}\n AWS_SECRET_ACCESS_KEY: \\${AWS_SECRET_ACCESS_KEY:-localstack}\n ports:\n - \"\\${LOCALSTACK_PORT:-4566}:4566\"\n volumes:\n - localstack_data:/var/lib/localstack\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:4566/_localstack/health\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - app-network\n`;\n\t}\n\n\t// Add volumes\n\tyaml += `\nvolumes:\n`;\n\n\tif (serviceMap.has('postgres')) {\n\t\tyaml += ` dbdata:\n`;\n\t}\n\n\tif (serviceMap.has('redis')) {\n\t\tyaml += ` redis_data:\n`;\n\t}\n\n\tif (serviceMap.has('rabbitmq')) {\n\t\tyaml += ` rabbitmq_data:\n`;\n\t}\n\n\tif (serviceMap.has('minio')) {\n\t\tyaml += ` minio_data:\n`;\n\t}\n\n\tif (serviceMap.has('localstack')) {\n\t\tyaml += ` localstack_data:\n`;\n\t}\n\n\t// Add networks\n\tyaml += `\nnetworks:\n app-network:\n driver: bridge\n`;\n\n\treturn yaml;\n}\n\n/**\n * Generate a minimal docker-compose.yml for API only\n */\nexport function generateMinimalDockerCompose(\n\toptions: Omit<ComposeOptions, 'services'>,\n): string {\n\tconst { imageName, registry, port, healthCheckPath } = options;\n\n\tconst imageRef = registry ? `\\${REGISTRY:-${registry}}/` : '';\n\n\treturn `# Use \"gkm dev\" or \"gkm test\" to start services.\n# Running \"docker compose up\" directly will not inject secrets or resolve ports.\nversion: '3.8'\n\nservices:\n api:\n build:\n context: ../..\n dockerfile: .gkm/docker/Dockerfile\n image: ${imageRef}\\${IMAGE_NAME:-${imageName}}:\\${TAG:-latest}\n container_name: ${imageName}\n restart: unless-stopped\n ports:\n - \"\\${PORT:-${port}}:${port}\"\n environment:\n - NODE_ENV=production\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:${port}${healthCheckPath}\"]\n interval: 30s\n timeout: 3s\n retries: 3\n networks:\n - app-network\n\nnetworks:\n app-network:\n driver: bridge\n`;\n}\n\n/**\n * Options for workspace compose generation.\n */\nexport interface WorkspaceComposeOptions {\n\t/** Container registry URL */\n\tregistry?: string;\n\t/**\n\t * The containers the manifest derived, by reconcile's names.\n\t *\n\t * Passed in rather than sniffed, because whether a Postgres exists is a fact\n\t * about what the app declared and this file cannot see declarations. Absent\n\t * means none, which is the right answer for a workspace that declares\n\t * nothing — and no longer the wrong one for a workspace that declared\n\t * everything and set no flags.\n\t */\n\tcontainers?: readonly string[];\n}\n\n/**\n * Generate docker-compose.yml for a workspace with all apps as services.\n * Apps can communicate with each other via service names.\n * @internal Exported for testing\n */\nexport function generateWorkspaceCompose(\n\tworkspace: NormalizedWorkspace,\n\toptions: WorkspaceComposeOptions = {},\n): string {\n\tconst { registry } = options;\n\tconst apps = Object.entries(workspace.apps);\n\tconst services = workspace.services;\n\n\t// Which containers exist is the manifest's answer, not this file's.\n\t//\n\t// It used to be one boolean per service — `db: true` starts a Postgres — and\n\t// that was the last place a container could exist because config asked for\n\t// one rather than because something declared it. A database implies Postgres,\n\t// a bucket implies MinIO, a declared cache implies whichever container its\n\t// backend needs. The caller derives the list once, from the same plan\n\t// reconcile builds, and hands it here.\n\tconst containers = new Set(options.containers ?? []);\n\n\tconst hasPostgres = containers.has('postgres');\n\t// `redis-http` is the Upstash-protocol proxy and `redis` is the wire\n\t// protocol; a project on the proxy runs both, because the proxy needs\n\t// something to proxy. Either one means a Redis in this file.\n\tconst hasRedis = containers.has('redis') || containers.has('redis-http');\n\tconst hasMail = containers.has('mailpit');\n\tconst hasMinio = containers.has('minio');\n\tconst hasLocalStack = containers.has('localstack');\n\tconst hasRabbitMQ = containers.has('rabbitmq');\n\tconst eventsBackend = services.events;\n\n\t// Image pins, which stay config: which container runs is derived, which\n\t// image it runs is not something a declaration could know.\n\tconst postgresImage = getInfraServiceImage(\n\t\t'postgres',\n\t\tservices.images?.postgres,\n\t);\n\tconst redisImage = getInfraServiceImage('redis', services.images?.redis);\n\tconst minioImage = getInfraServiceImage('minio', services.images?.minio);\n\n\tlet yaml = `# Docker Compose for ${workspace.name} workspace\n# Use \"gkm dev\" or \"gkm test\" to start services.\n# Running \"docker compose up\" directly will not inject secrets or resolve ports.\n\nservices:\n`;\n\n\t// Generate service for each app (mobile apps deploy via their own\n\t// toolchain — no compose service)\n\tfor (const [appName, app] of apps) {\n\t\tif (app.type === 'mobile') continue;\n\t\tyaml += generateAppService(appName, app, apps, {\n\t\t\tregistry,\n\t\t\tprojectName: workspace.name,\n\t\t\thasPostgres,\n\t\t\thasRedis,\n\t\t\thasMinio,\n\t\t\thasMail,\n\t\t\teventsBackend,\n\t\t});\n\t}\n\n\t// Add infrastructure services\n\tif (hasPostgres) {\n\t\tyaml += `\n postgres:\n image: ${postgresImage}\n container_name: ${workspace.name}-postgres\n restart: unless-stopped\n environment:\n POSTGRES_USER: \\${POSTGRES_USER:-postgres}\n POSTGRES_PASSWORD: \\${POSTGRES_PASSWORD:-postgres}\n POSTGRES_DB: \\${POSTGRES_DB:-app}\n volumes:\n - dbdata:/var/lib/postgresql\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U $$POSTGRES_USER\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\tif (hasRedis) {\n\t\tyaml += `\n redis:\n image: ${redisImage}\n container_name: ${workspace.name}-redis\n restart: unless-stopped\n volumes:\n - redis_data:/data\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\tif (hasMail) {\n\t\tyaml += `\n mailpit:\n image: axllent/mailpit:latest\n container_name: ${workspace.name}-mailpit\n restart: unless-stopped\n environment:\n MP_SMTP_AUTH: \\${SMTP_USER:-${workspace.name}}:\\${SMTP_PASS:-${workspace.name}}\n MP_SMTP_AUTH_ALLOW_INSECURE: true\n ports:\n - \"\\${MAILPIT_PORT:-8025}:8025\" # Web UI / API\n - \"\\${SMTP_PORT:-1025}:1025\" # SMTP\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8025\"]\n interval: 5s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\tif (hasMinio) {\n\t\tyaml += `\n minio:\n image: ${minioImage}\n container_name: ${workspace.name}-minio\n restart: unless-stopped\n entrypoint: sh\n command: -c 'mkdir -p /data/\\${STORAGE_BUCKET:-${workspace.name}} && /usr/bin/docker-entrypoint.sh server --console-address \":9001\" /data'\n environment:\n MINIO_ROOT_USER: \\${STORAGE_ACCESS_KEY_ID:-${workspace.name}}\n MINIO_ROOT_PASSWORD: \\${STORAGE_SECRET_ACCESS_KEY:-${workspace.name}}\n ports:\n - \"\\${MINIO_API_PORT:-9000}:9000\"\n - \"\\${MINIO_CONSOLE_PORT:-9001}:9001\"\n volumes:\n - minio_data:/data\n healthcheck:\n test: [\"CMD\", \"mc\", \"ready\", \"local\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\tif (hasLocalStack) {\n\t\tyaml += `\n localstack:\n image: floci/floci:latest\n container_name: ${workspace.name}-localstack\n restart: unless-stopped\n environment:\n AWS_DEFAULT_REGION: \\${AWS_REGION:-us-east-1}\n AWS_ACCESS_KEY_ID: \\${AWS_ACCESS_KEY_ID:-localstack}\n AWS_SECRET_ACCESS_KEY: \\${AWS_SECRET_ACCESS_KEY:-localstack}\n ports:\n - \"\\${LOCALSTACK_PORT:-4566}:4566\"\n volumes:\n - localstack_data:/var/lib/localstack\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:4566/_localstack/health\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\tif (hasRabbitMQ) {\n\t\tyaml += `\n rabbitmq:\n image: rabbitmq:3-management-alpine\n container_name: ${workspace.name}-rabbitmq\n restart: unless-stopped\n environment:\n RABBITMQ_DEFAULT_USER: \\${RABBITMQ_USER:-guest}\n RABBITMQ_DEFAULT_PASS: \\${RABBITMQ_PASSWORD:-guest}\n ports:\n - \"\\${RABBITMQ_HOST_PORT:-5672}:5672\"\n - \"\\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672\"\n volumes:\n - rabbitmq_data:/var/lib/rabbitmq\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"-q\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n networks:\n - workspace-network\n`;\n\t}\n\n\t// Add volumes section\n\tyaml += `\nvolumes:\n`;\n\n\tif (hasPostgres) {\n\t\tyaml += ` dbdata:\n`;\n\t}\n\n\tif (hasRedis) {\n\t\tyaml += ` redis_data:\n`;\n\t}\n\n\tif (hasMinio) {\n\t\tyaml += ` minio_data:\n`;\n\t}\n\n\tif (hasLocalStack) {\n\t\tyaml += ` localstack_data:\n`;\n\t}\n\n\tif (hasRabbitMQ) {\n\t\tyaml += ` rabbitmq_data:\n`;\n\t}\n\n\t// Add networks section\n\tyaml += `\nnetworks:\n workspace-network:\n driver: bridge\n`;\n\n\treturn yaml;\n}\n\n/**\n * Get infrastructure service image with version.\n */\nfunction getInfraServiceImage(\n\tserviceName: 'postgres' | 'redis' | 'minio',\n\tpin: string | undefined,\n): string {\n\tconst defaults: Record<'postgres' | 'redis' | 'minio', string> = {\n\t\tpostgres: 'postgres:18-alpine',\n\t\tredis: 'redis:7-alpine',\n\t\tminio: 'quay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z',\n\t};\n\n\t// A pin is a whole image reference now — `services.images` holds one string\n\t// per container, rather than a version this had to rebuild a reference from.\n\treturn pin || defaults[serviceName];\n}\n/**\n * Generate a service definition for an app.\n */\nfunction generateAppService(\n\tappName: string,\n\tapp: NormalizedAppConfig,\n\tallApps: [string, NormalizedAppConfig][],\n\toptions: {\n\t\tregistry?: string;\n\t\tprojectName: string;\n\t\thasPostgres: boolean;\n\t\thasRedis: boolean;\n\t\thasMinio: boolean;\n\t\thasMail: boolean;\n\t\teventsBackend?: import('../types').EventsBackend;\n\t},\n): string {\n\tconst {\n\t\tregistry,\n\t\tprojectName,\n\t\thasPostgres,\n\t\thasRedis,\n\t\thasMinio,\n\t\thasMail,\n\t\teventsBackend,\n\t} = options;\n\tconst imageRef = registry ? `\\${REGISTRY:-${registry}}/` : '';\n\n\t// Health check path - frontends use /, backends use /health\n\tconst healthCheckPath = app.type === 'web' ? '/' : '/health';\n\tconst healthCheckCmd =\n\t\tapp.type === 'web'\n\t\t\t? `[\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:${app.port}/\"]`\n\t\t\t: `[\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:${app.port}${healthCheckPath}\"]`;\n\n\tlet yaml = `\n ${appName}:\n build:\n context: .\n dockerfile: .gkm/docker/Dockerfile.${appName}\n image: ${imageRef}\\${${appName.toUpperCase()}_IMAGE:-${appName}}:\\${TAG:-latest}\n container_name: ${appName}\n restart: unless-stopped\n ports:\n - \"\\${${appName.toUpperCase()}_PORT:-${app.port}}:${app.port}\"\n environment:\n - NODE_ENV=production\n - PORT=${app.port}\n`;\n\n\t// Add dependency URLs - apps can reach other apps by service name\n\tfor (const dep of app.dependencies) {\n\t\tconst depApp = allApps.find(([name]) => name === dep)?.[1];\n\t\tif (depApp) {\n\t\t\tyaml += ` - ${dep.toUpperCase()}_URL=http://${dep}:${depApp.port}\n`;\n\t\t}\n\t}\n\n\t// Add infrastructure service URLs for backend apps\n\tif (app.type === 'backend') {\n\t\tif (hasPostgres) {\n\t\t\tyaml += ` - DATABASE_URL=\\${DATABASE_URL:-postgresql://\\${POSTGRES_USER:-postgres}:\\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\\${POSTGRES_DB:-app}}\n`;\n\t\t}\n\t\tif (hasRedis) {\n\t\t\tyaml += ` - REDIS_URL=\\${REDIS_URL:-redis://redis:6379}\n`;\n\t\t}\n\t\tif (hasMinio) {\n\t\t\tyaml += ` - STORAGE_ENDPOINT=\\${STORAGE_ENDPOINT:-http://minio:9000}\n - STORAGE_ACCESS_KEY_ID=\\${STORAGE_ACCESS_KEY_ID:-${projectName}}\n - STORAGE_SECRET_ACCESS_KEY=\\${STORAGE_SECRET_ACCESS_KEY:-${projectName}}\n - STORAGE_BUCKET=\\${STORAGE_BUCKET:-${projectName}}\n - STORAGE_REGION=\\${STORAGE_REGION:-eu-west-1}\n - STORAGE_FORCE_PATH_STYLE=true\n`;\n\t\t}\n\t\tif (hasMail) {\n\t\t\tyaml += ` - SMTP_HOST=\\${SMTP_HOST:-mailpit}\n - SMTP_PORT=\\${SMTP_PORT:-1025}\n - SMTP_USER=\\${SMTP_USER:-${projectName}}\n - SMTP_PASS=\\${SMTP_PASS:-${projectName}}\n - SMTP_SECURE=\\${SMTP_SECURE:-false}\n - MAIL_FROM=\\${MAIL_FROM:-noreply@localhost}\n`;\n\t\t}\n\t\tif (eventsBackend) {\n\t\t\tyaml += ` - EVENT_PUBLISHER_CONNECTION_STRING=\\${EVENT_PUBLISHER_CONNECTION_STRING}\n - EVENT_SUBSCRIBER_CONNECTION_STRING=\\${EVENT_SUBSCRIBER_CONNECTION_STRING}\n`;\n\t\t\tif (eventsBackend === 'sns') {\n\t\t\t\tyaml += ` - AWS_ACCESS_KEY_ID=\\${AWS_ACCESS_KEY_ID:-localstack}\n - AWS_SECRET_ACCESS_KEY=\\${AWS_SECRET_ACCESS_KEY:-localstack}\n - AWS_REGION=\\${AWS_REGION:-us-east-1}\n - AWS_ENDPOINT_URL=\\${AWS_ENDPOINT_URL:-http://localstack:4566}\n`;\n\t\t\t}\n\t\t}\n\t}\n\n\tyaml += ` healthcheck:\n test: ${healthCheckCmd}\n interval: 30s\n timeout: 3s\n retries: 3\n`;\n\n\t// Add depends_on for dependencies and infrastructure\n\tconst dependencies: string[] = [...app.dependencies];\n\tif (app.type === 'backend') {\n\t\tif (hasPostgres) dependencies.push('postgres');\n\t\tif (hasRedis) dependencies.push('redis');\n\t\tif (hasMinio) dependencies.push('minio');\n\t\tif (hasMail) dependencies.push('mailpit');\n\t\tif (eventsBackend === 'sns') dependencies.push('localstack');\n\t\tif (eventsBackend === 'rabbitmq') dependencies.push('rabbitmq');\n\t}\n\n\tif (dependencies.length > 0) {\n\t\tyaml += ` depends_on:\n`;\n\t\tfor (const dep of dependencies) {\n\t\t\tyaml += ` ${dep}:\n condition: service_healthy\n`;\n\t\t}\n\t}\n\n\tyaml += ` networks:\n - workspace-network\n`;\n\n\treturn yaml;\n}\n","import { existsSync } from 'node:fs';\nimport { dirname, join, parse } from 'node:path';\nimport type { DockerConfig, GkmConfig } from '../types';\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';\n\nexport interface DockerTemplateOptions {\n\timageName: string;\n\tbaseImage: string;\n\tport: number;\n\thealthCheckPath: string;\n\t/** Whether the build is pre-built (slim Dockerfile) or needs building */\n\tprebuilt: boolean;\n\t/** Detected package manager */\n\tpackageManager: PackageManager;\n}\n\nexport interface FrontendDockerfileOptions {\n\timageName: string;\n\tbaseImage: string;\n\tport: number;\n\t/** App path relative to workspace root */\n\tappPath: string;\n\t/** Package name for turbo prune */\n\tturboPackage: string;\n\t/** Detected package manager */\n\tpackageManager: PackageManager;\n\t/**\n\t * Public URL build args to include in the Dockerfile.\n\t * These will be declared as ARG and converted to ENV for Next.js build.\n\t * Example: ['NEXT_PUBLIC_API_URL', 'NEXT_PUBLIC_AUTH_URL']\n\t */\n\tpublicUrlArgs?: string[];\n}\n\nexport interface MultiStageDockerfileOptions extends DockerTemplateOptions {\n\t/** Enable turbo prune for monorepo optimization */\n\tturbo?: boolean;\n\t/** Package name for turbo prune (defaults to current directory name) */\n\tturboPackage?: string;\n}\n\nconst LOCKFILES: [string, PackageManager][] = [\n\t['pnpm-lock.yaml', 'pnpm'],\n\t['bun.lockb', 'bun'],\n\t['yarn.lock', 'yarn'],\n\t['package-lock.json', 'npm'],\n];\n\n/**\n * Detect package manager from lockfiles\n * Walks up the directory tree to find lockfile (for monorepos)\n */\nexport function detectPackageManager(\n\tcwd: string = process.cwd(),\n): PackageManager {\n\tlet dir = cwd;\n\tconst root = parse(dir).root;\n\n\t// Walk up the directory tree\n\twhile (dir !== root) {\n\t\tfor (const [lockfile, pm] of LOCKFILES) {\n\t\t\tif (existsSync(join(dir, lockfile))) {\n\t\t\t\treturn pm;\n\t\t\t}\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\n\t// Check root directory\n\tfor (const [lockfile, pm] of LOCKFILES) {\n\t\tif (existsSync(join(root, lockfile))) {\n\t\t\treturn pm;\n\t\t}\n\t}\n\n\treturn 'pnpm'; // default\n}\n\n/**\n * Find the lockfile path by walking up the directory tree\n * Returns the full path to the lockfile, or null if not found\n */\nexport function findLockfilePath(cwd: string = process.cwd()): string | null {\n\tlet dir = cwd;\n\tconst root = parse(dir).root;\n\n\t// Walk up the directory tree\n\twhile (dir !== root) {\n\t\tfor (const [lockfile] of LOCKFILES) {\n\t\t\tconst lockfilePath = join(dir, lockfile);\n\t\t\tif (existsSync(lockfilePath)) {\n\t\t\t\treturn lockfilePath;\n\t\t\t}\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\n\t// Check root directory\n\tfor (const [lockfile] of LOCKFILES) {\n\t\tconst lockfilePath = join(root, lockfile);\n\t\tif (existsSync(lockfilePath)) {\n\t\t\treturn lockfilePath;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Get the lockfile name for a package manager\n */\nexport function getLockfileName(pm: PackageManager): string {\n\tconst lockfileMap: Record<PackageManager, string> = {\n\t\tpnpm: 'pnpm-lock.yaml',\n\t\tnpm: 'package-lock.json',\n\t\tyarn: 'yarn.lock',\n\t\tbun: 'bun.lockb',\n\t};\n\treturn lockfileMap[pm];\n}\n\n/**\n * Check if we're in a monorepo (lockfile is in a parent directory)\n */\nexport function isMonorepo(cwd: string = process.cwd()): boolean {\n\tconst lockfilePath = findLockfilePath(cwd);\n\tif (!lockfilePath) {\n\t\treturn false;\n\t}\n\n\t// Check if lockfile is in a parent directory (not in cwd)\n\tconst lockfileDir = dirname(lockfilePath);\n\treturn lockfileDir !== cwd;\n}\n\n/**\n * Check if turbo.json exists (walks up directory tree)\n */\nexport function hasTurboConfig(cwd: string = process.cwd()): boolean {\n\tlet dir = cwd;\n\tconst root = parse(dir).root;\n\n\twhile (dir !== root) {\n\t\tif (existsSync(join(dir, 'turbo.json'))) {\n\t\t\treturn true;\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\n\treturn existsSync(join(root, 'turbo.json'));\n}\n\n/**\n * Get install command for turbo builds (without frozen lockfile)\n * Turbo prune creates a subset that may not perfectly match the lockfile\n */\nfunction getTurboInstallCmd(pm: PackageManager): string {\n\tconst commands: Record<PackageManager, string> = {\n\t\tpnpm: 'pnpm install',\n\t\tnpm: 'npm install',\n\t\tyarn: 'yarn install',\n\t\tbun: 'bun install',\n\t};\n\treturn commands[pm];\n}\n\n/**\n * Get package manager specific commands and paths\n */\nfunction getPmConfig(pm: PackageManager) {\n\tconst configs = {\n\t\tpnpm: {\n\t\t\tinstall: 'corepack enable && corepack prepare pnpm@latest --activate',\n\t\t\tlockfile: 'pnpm-lock.yaml',\n\t\t\tfetch: 'pnpm fetch',\n\t\t\tinstallCmd: 'pnpm install --frozen-lockfile --offline',\n\t\t\tcacheTarget: '/root/.local/share/pnpm/store',\n\t\t\tcacheId: 'pnpm',\n\t\t\trun: 'pnpm',\n\t\t\texec: 'pnpm exec',\n\t\t\tdlx: 'pnpm dlx',\n\t\t\taddGlobal: 'pnpm add -g',\n\t\t},\n\t\tnpm: {\n\t\t\tinstall: '', // npm comes with node\n\t\t\tlockfile: 'package-lock.json',\n\t\t\tfetch: '', // npm doesn't have fetch\n\t\t\tinstallCmd: 'npm ci',\n\t\t\tcacheTarget: '/root/.npm',\n\t\t\tcacheId: 'npm',\n\t\t\trun: 'npm run',\n\t\t\texec: 'npx',\n\t\t\tdlx: 'npx',\n\t\t\taddGlobal: 'npm install -g',\n\t\t},\n\t\tyarn: {\n\t\t\tinstall: 'corepack enable && corepack prepare yarn@stable --activate',\n\t\t\tlockfile: 'yarn.lock',\n\t\t\tfetch: '', // yarn doesn't have fetch\n\t\t\tinstallCmd: 'yarn install --frozen-lockfile',\n\t\t\tcacheTarget: '/root/.yarn/cache',\n\t\t\tcacheId: 'yarn',\n\t\t\trun: 'yarn',\n\t\t\texec: 'yarn exec',\n\t\t\tdlx: 'yarn dlx',\n\t\t\taddGlobal: 'yarn global add',\n\t\t},\n\t\tbun: {\n\t\t\tinstall: 'npm install -g bun',\n\t\t\tlockfile: 'bun.lockb',\n\t\t\tfetch: '', // bun doesn't have fetch\n\t\t\tinstallCmd: 'bun install --frozen-lockfile',\n\t\t\tcacheTarget: '/root/.bun/install/cache',\n\t\t\tcacheId: 'bun',\n\t\t\trun: 'bun run',\n\t\t\texec: 'bunx',\n\t\t\tdlx: 'bunx',\n\t\t\taddGlobal: 'bun add -g',\n\t\t},\n\t};\n\treturn configs[pm];\n}\n\n/**\n * Generate a multi-stage Dockerfile for building from source\n * Optimized for build speed with:\n * - BuildKit cache mounts for package manager store\n * - pnpm fetch for better layer caching (when using pnpm)\n * - Optional turbo prune for monorepos\n */\nexport function generateMultiStageDockerfile(\n\toptions: MultiStageDockerfileOptions,\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\thealthCheckPath,\n\t\tturbo,\n\t\tturboPackage,\n\t\tpackageManager,\n\t} = options;\n\n\tif (turbo) {\n\t\treturn generateTurboDockerfile({\n\t\t\t...options,\n\t\t\tturboPackage: turboPackage ?? 'api',\n\t\t});\n\t}\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install\n\t\t? `\\n# Install ${packageManager}\\nRUN ${pm.install}\\n`\n\t\t: '';\n\tconst hasFetch = packageManager === 'pnpm';\n\n\t// pnpm has fetch which allows better caching\n\tconst depsStage = hasFetch\n\t\t? `# Copy lockfile first for better caching\nCOPY ${pm.lockfile} ./\n\n# Fetch dependencies (downloads to virtual store, cached separately)\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${pm.fetch}\n\n# Copy package.json after fetch\nCOPY package.json ./\n\n# Install from cache (fast - no network needed)\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${pm.installCmd}`\n\t\t: `# Copy package files\nCOPY package.json ${pm.lockfile} ./\n\n# Install dependencies with cache\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${pm.installCmd}`;\n\n\treturn `# syntax=docker/dockerfile:1\n# Stage 1: Dependencies\nFROM ${baseImage} AS deps\n\nWORKDIR /app\n${installPm}\n${depsStage}\n\n# Stage 2: Build\nFROM deps AS builder\n\nWORKDIR /app\n\n# Copy source (deps already installed)\nCOPY . .\n\n# Build any workspace packages the app depends on.\n#\n# A no-op for a project whose dependencies come from a registry, and required\n# for one whose CLI is a sibling package: \\`turbo prune\\` honours .gitignore, so a\n# workspace package's \\`dist\\` never reaches the image and its bin has nothing to\n# import. \\`--if-present\\` is what makes the same Dockerfile correct for both.\nRUN ${pm.run} --if-present build\n\n# Build production server using gkm\nRUN ${pm.exec} gkm build --provider server --production\n\n# Stage 3: Production\nFROM ${baseImage} AS runner\n\nWORKDIR /app\n\n# Install tini for proper signal handling as PID 1\nRUN apk add --no-cache tini\n\n# Create non-root user\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 hono\n\n# Copy bundled server\nCOPY --from=builder --chown=hono:nodejs /app/.gkm/server/dist/server.mjs ./\n\n# Environment\nENV NODE_ENV=production\nENV PORT=${port}\n\n# Health check\nHEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \\\\\n CMD wget -qO- http://localhost:${port}${healthCheckPath} > /dev/null 2>&1 || exit 1\n\n# Switch to non-root user\nUSER hono\n\nEXPOSE ${port}\n\n# Use tini as entrypoint to handle PID 1 responsibilities\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"server.mjs\"]\n`;\n}\n\n/**\n * Generate a Dockerfile optimized for Turbo monorepos\n * Uses turbo prune to create minimal Docker context\n */\nfunction generateTurboDockerfile(options: MultiStageDockerfileOptions): string {\n\tconst { baseImage, port, healthCheckPath, turboPackage, packageManager } =\n\t\toptions;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\n\t// For turbo builds, we can't use --frozen-lockfile because turbo prune\n\t// creates a subset that may not perfectly match. Use relaxed install.\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\n\t// Use pnpm dlx for pnpm (avoids global bin dir issues in Docker)\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\n\treturn `# syntax=docker/dockerfile:1\n# Stage 1: Prune monorepo\nFROM ${baseImage} AS pruner\n\nWORKDIR /app\n\n${installPm}\n\nCOPY . .\n\n# Prune to only include necessary packages\nRUN ${turboCmd} prune ${turboPackage} --docker\n\n# Stage 2: Install dependencies\nFROM ${baseImage} AS deps\n\nWORKDIR /app\n\n${installPm}\n\n# Copy pruned lockfile and package.jsons\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\n\n# Install dependencies (no frozen-lockfile since turbo prune creates a subset)\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\n# Stage 3: Build\nFROM deps AS builder\n\nWORKDIR /app\n\n# Copy pruned source\nCOPY --from=pruner /app/out/full/ ./\n\n# Build any workspace packages the app depends on.\n#\n# A no-op for a project whose dependencies come from a registry, and required\n# for one whose CLI is a sibling package: \\`turbo prune\\` honours .gitignore, so a\n# workspace package's \\`dist\\` never reaches the image and its bin has nothing to\n# import. \\`--if-present\\` is what makes the same Dockerfile correct for both.\nRUN ${pm.run} --if-present build\n\n# Build production server using gkm\nRUN ${pm.exec} gkm build --provider server --production\n\n# Stage 4: Production\nFROM ${baseImage} AS runner\n\nWORKDIR /app\n\nRUN apk add --no-cache tini\n\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 hono\n\nCOPY --from=builder --chown=hono:nodejs /app/.gkm/server/dist/server.mjs ./\n\nENV NODE_ENV=production\nENV PORT=${port}\n\nHEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \\\\\n CMD wget -qO- http://localhost:${port}${healthCheckPath} > /dev/null 2>&1 || exit 1\n\nUSER hono\n\nEXPOSE ${port}\n\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"server.mjs\"]\n`;\n}\n\n/**\n * Generate a slim Dockerfile for pre-built bundles\n */\nexport function generateSlimDockerfile(options: DockerTemplateOptions): string {\n\tconst { baseImage, port, healthCheckPath } = options;\n\n\treturn `# Slim Dockerfile for pre-built production bundle\nFROM ${baseImage}\n\nWORKDIR /app\n\n# Install tini for proper signal handling as PID 1\n# Handles SIGTERM propagation and zombie process reaping\nRUN apk add --no-cache tini\n\n# Create non-root user\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 hono\n\n# Copy pre-built bundle\nCOPY .gkm/server/dist/server.mjs ./\n\n# Environment\nENV NODE_ENV=production\nENV PORT=${port}\n\n# Health check\nHEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \\\\\n CMD wget -qO- http://localhost:${port}${healthCheckPath} > /dev/null 2>&1 || exit 1\n\n# Switch to non-root user\nUSER hono\n\nEXPOSE ${port}\n\n# Use tini as entrypoint to handle PID 1 responsibilities\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"server.mjs\"]\n`;\n}\n\n/**\n * Generate .dockerignore file\n */\nexport function generateDockerignore(): string {\n\treturn `# Dependencies\nnode_modules\n.pnpm-store\n\n# Build output (except what we need)\n.gkm/aws*\n.gkm/server/*.ts\n!.gkm/server/dist\n\n# IDE and editor\n.idea\n.vscode\n*.swp\n*.swo\n\n# Git\n.git\n.gitignore\n\n# Logs\n*.log\nnpm-debug.log*\npnpm-debug.log*\n\n# Test files\n**/*.test.ts\n**/*.spec.ts\n**/__tests__\ncoverage\n\n# Documentation\ndocs\n*.md\n!README.md\n\n# Environment files (handle secrets separately)\n.env\n.env.*\n!.env.example\n\n# Docker files (don't copy recursively)\nDockerfile*\ndocker-compose*\n.dockerignore\n`;\n}\n\n/**\n * Generate docker-entrypoint.sh for custom startup logic\n */\nexport function generateDockerEntrypoint(): string {\n\treturn `#!/bin/sh\nset -e\n\n# Run any custom startup scripts here\n# Example: wait for database\n# until nc -z $DB_HOST $DB_PORT; do\n# echo \"Waiting for database...\"\n# sleep 1\n# done\n\n# Execute the main command\nexec \"$@\"\n`;\n}\n\n/**\n * Resolve Docker configuration from GkmConfig with defaults\n */\nexport function resolveDockerConfig(\n\tconfig: GkmConfig,\n): Required<Omit<DockerConfig, 'compose'>> & Pick<DockerConfig, 'compose'> {\n\tconst docker = config.docker ?? {};\n\n\t// Try to get image name from package.json name\n\tlet defaultImageName = 'api';\n\ttry {\n\t\t// eslint-disable-next-line @typescript-eslint/no-require-imports\n\t\tconst pkg = require(`${process.cwd()}/package.json`);\n\t\tif (pkg.name) {\n\t\t\t// Remove scope and use just the package name\n\t\t\tdefaultImageName = pkg.name.replace(/^@[^/]+\\//, '');\n\t\t}\n\t} catch {\n\t\t// Ignore if package.json doesn't exist\n\t}\n\n\treturn {\n\t\tregistry: docker.registry ?? '',\n\t\timageName: docker.imageName ?? defaultImageName,\n\t\tbaseImage: docker.baseImage ?? 'node:22-alpine',\n\t\tport: docker.port ?? 3000,\n\t\tcompose: docker.compose,\n\t};\n}\n\n/**\n * Generate a Dockerfile for Next.js frontend apps using standalone output.\n * Uses turbo prune for monorepo optimization.\n * @internal Exported for testing\n */\nexport function generateNextjsDockerfile(\n\toptions: FrontendDockerfileOptions,\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\tappPath,\n\t\tturboPackage,\n\t\tpackageManager,\n\t\tpublicUrlArgs = ['NEXT_PUBLIC_API_URL', 'NEXT_PUBLIC_AUTH_URL'],\n\t} = options;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\n\t// For turbo builds, we can't use --frozen-lockfile because turbo prune\n\t// creates a subset that may not perfectly match. Use relaxed install.\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\n\t// Use pnpm dlx for pnpm (avoids global bin dir issues in Docker)\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\n\t// Generate ARG and ENV declarations for public URLs\n\tconst publicUrlArgDeclarations = publicUrlArgs\n\t\t.map((arg) => `ARG ${arg}=\"\"`)\n\t\t.join('\\n');\n\tconst publicUrlEnvDeclarations = publicUrlArgs\n\t\t.map((arg) => `ENV ${arg}=$${arg}`)\n\t\t.join('\\n');\n\n\treturn `# syntax=docker/dockerfile:1\n# Next.js standalone Dockerfile with turbo prune optimization\n\n# Stage 1: Prune monorepo\nFROM ${baseImage} AS pruner\n\nWORKDIR /app\n\n${installPm}\n\nCOPY . .\n\n# Prune to only include necessary packages\nRUN ${turboCmd} prune ${turboPackage} --docker\n\n# Stage 2: Install dependencies\nFROM ${baseImage} AS deps\n\nWORKDIR /app\n\n${installPm}\n\n# Copy pruned lockfile and package.jsons\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\n\n# Install dependencies\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\n# Stage 3: Build\nFROM deps AS builder\n\nWORKDIR /app\n\n# Build-time args for public API URLs (populated by gkm deploy)\n# These get baked into the Next.js build as public environment variables\n${publicUrlArgDeclarations}\n\n# Convert ARGs to ENVs for Next.js build\n${publicUrlEnvDeclarations}\n\n# Copy pruned source\nCOPY --from=pruner /app/out/full/ ./\n\n# Copy workspace root configs for turbo builds (turbo prune doesn't include root configs)\n# Using wildcard to make it optional for single-app projects\nCOPY --from=pruner /app/tsconfig.* ./\n\n# Ensure public directory exists (may be empty for scaffolded projects)\nRUN mkdir -p ${appPath}/public\n\n# Set Next.js to produce standalone output\nENV NEXT_TELEMETRY_DISABLED=1\n\n# Build the application\nRUN ${turboCmd} run build --filter=${turboPackage}\n\n# Stage 4: Production\nFROM ${baseImage} AS runner\n\nWORKDIR /app\n\n# Install tini for proper signal handling\nRUN apk add --no-cache tini\n\n# Create non-root user\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 nextjs\n\n# Set environment\nENV NODE_ENV=production\nENV NEXT_TELEMETRY_DISABLED=1\nENV PORT=${port}\nENV HOSTNAME=\"0.0.0.0\"\n\n# Copy static files and standalone output\nCOPY --from=builder --chown=nextjs:nodejs /app/${appPath}/.next/standalone ./\nCOPY --from=builder --chown=nextjs:nodejs /app/${appPath}/.next/static ./${appPath}/.next/static\nCOPY --from=builder --chown=nextjs:nodejs /app/${appPath}/public ./${appPath}/public\n\nUSER nextjs\n\nEXPOSE ${port}\n\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"${appPath}/server.js\"]\n`;\n}\n\n/**\n * Generate a Dockerfile for backend apps in a workspace.\n * Uses turbo prune for monorepo optimization.\n * @internal Exported for testing\n */\nexport function generateBackendDockerfile(\n\toptions: FrontendDockerfileOptions & { healthCheckPath?: string },\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\tappPath,\n\t\tturboPackage,\n\t\tpackageManager,\n\t\thealthCheckPath = '/health',\n\t} = options;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\n\treturn `# syntax=docker/dockerfile:1\n# Backend Dockerfile with turbo prune optimization\n\n# Stage 1: Prune monorepo\nFROM ${baseImage} AS pruner\n\nWORKDIR /app\n\n${installPm}\n\nCOPY . .\n\n# Prune to only include necessary packages\nRUN ${turboCmd} prune ${turboPackage} --docker\n\n# Stage 2: Install dependencies\nFROM ${baseImage} AS deps\n\nWORKDIR /app\n\n${installPm}\n\n# Copy pruned lockfile and package.jsons\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\n\n# Install dependencies\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\n# Stage 3: Build\nFROM deps AS builder\n\nWORKDIR /app\n\n# Build-time args for encrypted secrets\nARG GKM_ENCRYPTED_CREDENTIALS=\"\"\nARG GKM_CREDENTIALS_IV=\"\"\n\n# Copy pruned source\nCOPY --from=pruner /app/out/full/ ./\n\n# Copy workspace root configs for turbo builds (turbo prune doesn't include root configs)\n# Using wildcard to make it optional for single-app projects\nCOPY --from=pruner /app/gkm.config.* ./\nCOPY --from=pruner /app/tsconfig.* ./\n\n# Write encrypted credentials for gkm build to embed\nRUN if [ -n \"$GKM_ENCRYPTED_CREDENTIALS\" ]; then \\\n mkdir -p ${appPath}/.gkm && \\\n echo \"$GKM_ENCRYPTED_CREDENTIALS\" > ${appPath}/.gkm/credentials.enc && \\\n echo \"$GKM_CREDENTIALS_IV\" > ${appPath}/.gkm/credentials.iv; \\\n fi\n\n# Build any workspace packages the app depends on.\n#\n# A no-op for a project whose dependencies come from a registry, and required\n# for one whose CLI is a sibling package: \\`turbo prune\\` honours .gitignore, so a\n# workspace package's \\`dist\\` never reaches the image and its bin has nothing to\n# import. \\`--if-present\\` is what makes the same Dockerfile correct for both.\nRUN ${pm.run} --if-present build\n\n# Build production server using gkm\nRUN cd ${appPath} && ${pm.exec} gkm build --provider server --production\n\n# Stage 4: Production\nFROM ${baseImage} AS runner\n\nWORKDIR /app\n\nRUN apk add --no-cache tini\n\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 hono\n\n# Copy bundled server\nCOPY --from=builder --chown=hono:nodejs /app/${appPath}/.gkm/server/dist/server.mjs ./\n\nENV NODE_ENV=production\nENV PORT=${port}\n\nHEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \\\\\n CMD wget -qO- http://localhost:${port}${healthCheckPath} > /dev/null 2>&1 || exit 1\n\nUSER hono\n\nEXPOSE ${port}\n\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"server.mjs\"]\n`;\n}\n\n/**\n * Options for entry-based Dockerfile generation.\n */\nexport interface EntryDockerfileOptions {\n\timageName: string;\n\tbaseImage: string;\n\tport: number;\n\t/** App path relative to workspace root */\n\tappPath: string;\n\t/** Entry file path relative to app path (e.g., './src/index.ts') */\n\tentry: string;\n\t/** Package name for turbo prune */\n\tturboPackage: string;\n\t/** Detected package manager */\n\tpackageManager: PackageManager;\n\t/** Health check path (default: '/health') */\n\thealthCheckPath?: string;\n}\n\n/**\n * Generate a Dockerfile for apps with a custom entry point.\n * Uses esbuild to bundle the entry point into dist/index.mjs with all dependencies.\n * This is used for apps that don't use gkm routes (e.g., Better Auth servers).\n * @internal Exported for testing\n */\nexport function generateEntryDockerfile(\n\toptions: EntryDockerfileOptions,\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\tappPath,\n\t\tentry,\n\t\tturboPackage,\n\t\tpackageManager,\n\t\thealthCheckPath = '/health',\n\t} = options;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\n\treturn `# syntax=docker/dockerfile:1\n# Entry-based Dockerfile with turbo prune + tsdown bundling\n\n# Stage 1: Prune monorepo\nFROM ${baseImage} AS pruner\n\nWORKDIR /app\n\n${installPm}\n\nCOPY . .\n\n# Prune to only include necessary packages\nRUN ${turboCmd} prune ${turboPackage} --docker\n\n# Stage 2: Install dependencies\nFROM ${baseImage} AS deps\n\nWORKDIR /app\n\n${installPm}\n\n# Copy pruned lockfile and package.jsons\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\n\n# Install dependencies\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\n# Stage 3: Build with tsdown\nFROM deps AS builder\n\nWORKDIR /app\n\n# Build-time args for encrypted secrets\nARG GKM_ENCRYPTED_CREDENTIALS=\"\"\nARG GKM_CREDENTIALS_IV=\"\"\n\n# Copy pruned source\nCOPY --from=pruner /app/out/full/ ./\n\n# Copy workspace root configs for turbo builds (turbo prune doesn't include root configs)\n# Using wildcard to make it optional for single-app projects\nCOPY --from=pruner /app/tsconfig.* ./\n\n# Write encrypted credentials for tsdown to embed via define\nRUN if [ -n \"$GKM_ENCRYPTED_CREDENTIALS\" ]; then \\\n mkdir -p ${appPath}/.gkm && \\\n echo \"$GKM_ENCRYPTED_CREDENTIALS\" > ${appPath}/.gkm/credentials.enc && \\\n echo \"$GKM_CREDENTIALS_IV\" > ${appPath}/.gkm/credentials.iv; \\\n fi\n\n# Bundle entry point with esbuild (outputs to dist/index.mjs)\n# Creates a fully standalone bundle with all dependencies included\n# Use define to embed credentials if present\nRUN cd ${appPath} && \\\n if [ -f .gkm/credentials.enc ]; then \\\n CREDS=$(cat .gkm/credentials.enc) && \\\n IV=$(cat .gkm/credentials.iv) && \\\n npx esbuild ${entry} --bundle --platform=node --target=node22 --format=esm \\\n --outfile=dist/index.mjs --packages=bundle \\\n --banner:js='import { createRequire } from \"module\"; const require = createRequire(import.meta.url);' \\\n --define:__GKM_ENCRYPTED_CREDENTIALS__=\"'\\\\\"$CREDS\\\\\"'\" \\\n --define:__GKM_CREDENTIALS_IV__=\"'\\\\\"$IV\\\\\"'\"; \\\n else \\\n npx esbuild ${entry} --bundle --platform=node --target=node22 --format=esm \\\n --outfile=dist/index.mjs --packages=bundle \\\n --banner:js='import { createRequire } from \"module\"; const require = createRequire(import.meta.url);'; \\\n fi\n\n# Stage 4: Production\nFROM ${baseImage} AS runner\n\nWORKDIR /app\n\nRUN apk add --no-cache tini\n\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 app\n\n# Copy bundled output only (no node_modules needed - fully bundled)\nCOPY --from=builder --chown=app:nodejs /app/${appPath}/dist/index.mjs ./\n\nENV NODE_ENV=production\nENV PORT=${port}\n\nHEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \\\\\n CMD wget -qO- http://localhost:${port}${healthCheckPath} > /dev/null 2>&1 || exit 1\n\nUSER app\n\nEXPOSE ${port}\n\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"node\", \"index.mjs\"]\n`;\n}\n\n/**\n * Generate a Dockerfile for Node-based SSR web frameworks (TanStack Start,\n * Remix). Builds with turbo, then runs the framework's production start\n * script via `npm start`.\n *\n * Public URL build args are declared so framework bundlers (Vite, Vinxi)\n * can inline them at build time. Per-framework prefix is the caller's job.\n * @internal Exported for testing\n */\nexport function generateNodeWebDockerfile(\n\toptions: FrontendDockerfileOptions,\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\tappPath,\n\t\tturboPackage,\n\t\tpackageManager,\n\t\tpublicUrlArgs = [],\n\t} = options;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\tconst startCmd =\n\t\tpackageManager === 'pnpm'\n\t\t\t? 'pnpm start'\n\t\t\t: `${pm.lockfile.startsWith('package-lock') ? 'npm' : packageManager} start`;\n\n\tconst argDecls = publicUrlArgs.map((a) => `ARG ${a}=\"\"`).join('\\n');\n\tconst envDecls = publicUrlArgs.map((a) => `ENV ${a}=$${a}`).join('\\n');\n\n\treturn `# syntax=docker/dockerfile:1\n# Node SSR web Dockerfile (TanStack Start / Remix) with turbo prune\n\nFROM ${baseImage} AS pruner\nWORKDIR /app\n${installPm}\nCOPY . .\nRUN ${turboCmd} prune ${turboPackage} --docker\n\nFROM ${baseImage} AS deps\nWORKDIR /app\n${installPm}\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\nFROM deps AS builder\nWORKDIR /app\n${argDecls}\n${envDecls}\nCOPY --from=pruner /app/out/full/ ./\nCOPY --from=pruner /app/tsconfig.* ./\nRUN ${turboCmd} run build --filter=${turboPackage}\n\nFROM ${baseImage} AS runner\nWORKDIR /app\nRUN apk add --no-cache tini\nRUN addgroup --system --gid 1001 nodejs && \\\\\n adduser --system --uid 1001 app\n\n# Copy the whole built workspace — frameworks differ on output paths\nCOPY --from=builder --chown=app:nodejs /app/ ./\n\nENV NODE_ENV=production\nENV PORT=${port}\nENV HOSTNAME=\"0.0.0.0\"\n\nUSER app\nEXPOSE ${port}\n\nENTRYPOINT [\"/sbin/tini\", \"--\"]\nCMD [\"sh\", \"-c\", \"cd ${appPath} && ${startCmd}\"]\n`;\n}\n\n/**\n * Generate a Dockerfile for Vite SPA apps. Builds the static bundle with\n * turbo, then serves it via nginx.\n * @internal Exported for testing\n */\nexport function generateViteStaticDockerfile(\n\toptions: FrontendDockerfileOptions,\n): string {\n\tconst {\n\t\tbaseImage,\n\t\tport,\n\t\tappPath,\n\t\tturboPackage,\n\t\tpackageManager,\n\t\tpublicUrlArgs = [],\n\t} = options;\n\n\tconst pm = getPmConfig(packageManager);\n\tconst installPm = pm.install ? `RUN ${pm.install}` : '';\n\tconst turboInstallCmd = getTurboInstallCmd(packageManager);\n\tconst turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';\n\n\tconst argDecls = publicUrlArgs.map((a) => `ARG ${a}=\"\"`).join('\\n');\n\tconst envDecls = publicUrlArgs.map((a) => `ENV ${a}=$${a}`).join('\\n');\n\n\treturn `# syntax=docker/dockerfile:1\n# Vite SPA Dockerfile — builds static assets, serves via nginx\n\nFROM ${baseImage} AS pruner\nWORKDIR /app\n${installPm}\nCOPY . .\nRUN ${turboCmd} prune ${turboPackage} --docker\n\nFROM ${baseImage} AS deps\nWORKDIR /app\n${installPm}\nCOPY --from=pruner /app/out/${pm.lockfile} ./\nCOPY --from=pruner /app/out/json/ ./\nRUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\\\\n ${turboInstallCmd}\n\nFROM deps AS builder\nWORKDIR /app\n${argDecls}\n${envDecls}\nCOPY --from=pruner /app/out/full/ ./\nCOPY --from=pruner /app/tsconfig.* ./\nRUN ${turboCmd} run build --filter=${turboPackage}\n\nFROM nginx:alpine AS runner\nCOPY --from=builder /app/${appPath}/dist /usr/share/nginx/html\nRUN printf 'server {\\\\n listen ${port};\\\\n root /usr/share/nginx/html;\\\\n index index.html;\\\\n location / { try_files $uri $uri/ /index.html; }\\\\n}\\\\n' \\\\\n > /etc/nginx/conf.d/default.conf\nEXPOSE ${port}\nCMD [\"nginx\", \"-g\", \"daemon off;\"]\n`;\n}\n","import { execSync } from 'node:child_process';\nimport { copyFileSync, existsSync, readFileSync, unlinkSync } from 'node:fs';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { basename, join } from 'node:path';\nimport { loadConfig, loadWorkspaceConfig } from '../config';\nimport { getPublicUrlArgNames } from '../deploy/domain.js';\nimport { derivedContainers } from '../reconcile/workspace.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport {\n\tgenerateDockerCompose,\n\tgenerateMinimalDockerCompose,\n\tgenerateWorkspaceCompose,\n} from './compose';\nimport {\n\tdetectPackageManager,\n\tfindLockfilePath,\n\tgenerateBackendDockerfile,\n\tgenerateDockerEntrypoint,\n\tgenerateDockerignore,\n\tgenerateEntryDockerfile,\n\tgenerateMultiStageDockerfile,\n\tgenerateNextjsDockerfile,\n\tgenerateNodeWebDockerfile,\n\tgenerateSlimDockerfile,\n\tgenerateViteStaticDockerfile,\n\thasTurboConfig,\n\tisMonorepo,\n\tresolveDockerConfig,\n} from './templates';\n\nexport {\n\tdetectPackageManager,\n\tfindLockfilePath,\n\thasTurboConfig,\n\tisMonorepo,\n} from './templates';\n\nconst logger = console;\n\nexport interface DockerOptions {\n\t/** Build Docker image after generating files */\n\tbuild?: boolean;\n\t/** Push image to registry after building */\n\tpush?: boolean;\n\t/** Image tag (default: 'latest') */\n\ttag?: string;\n\t/** Container registry URL */\n\tregistry?: string;\n\t/** Use slim Dockerfile (requires pre-built bundle from `gkm build --production`) */\n\tslim?: boolean;\n\t/** Enable turbo prune for monorepo optimization */\n\tturbo?: boolean;\n\t/** Package name for turbo prune (defaults to package.json name) */\n\tturboPackage?: string;\n}\n\nexport interface DockerGeneratedFiles {\n\tdockerfile: string;\n\tdockerCompose: string;\n\tdockerignore: string;\n\tentrypoint: string;\n}\n\n/**\n * Docker command implementation\n * Generates Dockerfile, docker-compose.yml, and related files\n *\n * Default: Multi-stage Dockerfile that builds from source inside Docker\n * --slim: Slim Dockerfile that copies pre-built bundle (requires prior build)\n */\nexport async function dockerCommand(\n\toptions: DockerOptions,\n): Promise<DockerGeneratedFiles | WorkspaceDockerResult> {\n\t// Load config with workspace detection\n\tconst loadedConfig = await loadWorkspaceConfig();\n\n\t// Route to workspace docker mode for multi-app workspaces\n\tif (loadedConfig.type === 'workspace') {\n\t\tlogger.log('📦 Detected workspace configuration');\n\t\treturn workspaceDockerCommand(loadedConfig.workspace, options);\n\t}\n\n\t// Single-app mode - use existing logic\n\tconst config = await loadConfig();\n\tconst dockerConfig = resolveDockerConfig(config);\n\n\t// Get health check path from production config\n\tconst serverConfig =\n\t\ttypeof config.providers?.server === 'object'\n\t\t\t? config.providers.server\n\t\t\t: undefined;\n\tconst healthCheckPath = serverConfig?.production?.healthCheck ?? '/health';\n\n\t// Determine Dockerfile type\n\t// Default: Multi-stage (builds inside Docker for reproducibility)\n\t// --slim: Requires pre-built bundle\n\tconst useSlim = options.slim === true;\n\n\tif (useSlim) {\n\t\t// Verify pre-built bundle exists for slim mode\n\t\tconst distDir = join(process.cwd(), '.gkm', 'server', 'dist');\n\t\tconst hasBuild = existsSync(join(distDir, 'server.mjs'));\n\n\t\tif (!hasBuild) {\n\t\t\tthrow new Error(\n\t\t\t\t'Slim Dockerfile requires a pre-built bundle. Run `gkm build --provider server --production` first, or omit --slim to use multi-stage build.',\n\t\t\t);\n\t\t}\n\t}\n\n\t// Generate Docker files\n\tconst dockerDir = join(process.cwd(), '.gkm', 'docker');\n\tawait mkdir(dockerDir, { recursive: true });\n\n\t// Detect package manager from lockfiles\n\tconst packageManager = detectPackageManager();\n\tconst inMonorepo = isMonorepo();\n\tconst hasTurbo = hasTurboConfig();\n\n\t// Auto-enable turbo for monorepos with turbo.json\n\tlet useTurbo = options.turbo ?? false;\n\tif (inMonorepo && !useSlim) {\n\t\tif (hasTurbo) {\n\t\t\tuseTurbo = true;\n\t\t\tlogger.log(' Detected monorepo with turbo.json - using turbo prune');\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t'Monorepo detected but turbo.json not found.\\n\\n' +\n\t\t\t\t\t'Docker builds in monorepos require Turborepo for proper dependency isolation.\\n\\n' +\n\t\t\t\t\t'To fix this:\\n' +\n\t\t\t\t\t' 1. Install turbo: pnpm add -Dw turbo\\n' +\n\t\t\t\t\t' 2. Create turbo.json in your monorepo root\\n' +\n\t\t\t\t\t' 3. Run this command again\\n\\n' +\n\t\t\t\t\t'See: https://turbo.build/repo/docs/guides/tools/docker',\n\t\t\t);\n\t\t}\n\t}\n\n\t// Get the actual package name from package.json for turbo prune\n\tlet turboPackage = options.turboPackage ?? dockerConfig.imageName;\n\tif (useTurbo && !options.turboPackage) {\n\t\ttry {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-require-imports\n\t\t\tconst pkg = require(`${process.cwd()}/package.json`);\n\t\t\tif (pkg.name) {\n\t\t\t\tturboPackage = pkg.name;\n\t\t\t\tlogger.log(` Turbo package: ${turboPackage}`);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Fall back to imageName\n\t\t}\n\t}\n\n\tconst templateOptions = {\n\t\timageName: dockerConfig.imageName,\n\t\tbaseImage: dockerConfig.baseImage,\n\t\tport: dockerConfig.port,\n\t\thealthCheckPath,\n\t\tprebuilt: useSlim,\n\t\tturbo: useTurbo,\n\t\tturboPackage,\n\t\tpackageManager,\n\t};\n\n\t// Generate Dockerfile\n\tconst dockerfile = useSlim\n\t\t? generateSlimDockerfile(templateOptions)\n\t\t: generateMultiStageDockerfile(templateOptions);\n\n\tconst dockerMode = useSlim ? 'slim' : useTurbo ? 'turbo' : 'multi-stage';\n\n\tconst dockerfilePath = join(dockerDir, 'Dockerfile');\n\tawait writeFile(dockerfilePath, dockerfile);\n\tlogger.log(\n\t\t`Generated: .gkm/docker/Dockerfile (${dockerMode}, ${packageManager})`,\n\t);\n\n\t// Generate docker-compose.yml\n\tconst composeOptions = {\n\t\timageName: dockerConfig.imageName,\n\t\tregistry: options.registry ?? dockerConfig.registry,\n\t\tport: dockerConfig.port,\n\t\thealthCheckPath,\n\t\tservices: dockerConfig.compose?.services ?? {},\n\t};\n\n\t// Check if there are any services configured\n\tconst hasServices = Array.isArray(composeOptions.services)\n\t\t? composeOptions.services.length > 0\n\t\t: Object.keys(composeOptions.services).length > 0;\n\n\tconst dockerCompose = hasServices\n\t\t? generateDockerCompose(composeOptions)\n\t\t: generateMinimalDockerCompose(composeOptions);\n\n\tconst composePath = join(dockerDir, 'docker-compose.yml');\n\tawait writeFile(composePath, dockerCompose);\n\tlogger.log('Generated: .gkm/docker/docker-compose.yml');\n\n\t// Generate .dockerignore in project root (Docker looks for it there)\n\tconst dockerignore = generateDockerignore();\n\tconst dockerignorePath = join(process.cwd(), '.dockerignore');\n\tawait writeFile(dockerignorePath, dockerignore);\n\tlogger.log('Generated: .dockerignore (project root)');\n\n\t// Generate docker-entrypoint.sh\n\tconst entrypoint = generateDockerEntrypoint();\n\tconst entrypointPath = join(dockerDir, 'docker-entrypoint.sh');\n\tawait writeFile(entrypointPath, entrypoint);\n\tlogger.log('Generated: .gkm/docker/docker-entrypoint.sh');\n\n\tconst result: DockerGeneratedFiles = {\n\t\tdockerfile: dockerfilePath,\n\t\tdockerCompose: composePath,\n\t\tdockerignore: dockerignorePath,\n\t\tentrypoint: entrypointPath,\n\t};\n\n\t// Build Docker image if requested\n\tif (options.build) {\n\t\tawait buildDockerImage(dockerConfig.imageName, options);\n\t}\n\n\t// Push Docker image if requested\n\tif (options.push) {\n\t\tawait pushDockerImage(dockerConfig.imageName, options);\n\t}\n\n\treturn result;\n}\n\n/**\n * Ensure lockfile exists in the build context\n * For monorepos, copies from workspace root if needed\n * Returns cleanup function if file was copied\n */\nfunction ensureLockfile(cwd: string): (() => void) | null {\n\tconst lockfilePath = findLockfilePath(cwd);\n\n\tif (!lockfilePath) {\n\t\tlogger.warn(\n\t\t\t'\\n⚠️ No lockfile found. Docker build may fail or use stale dependencies.',\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst lockfileName = basename(lockfilePath);\n\tconst localLockfile = join(cwd, lockfileName);\n\n\t// If lockfile exists locally (same directory), nothing to do\n\tif (lockfilePath === localLockfile) {\n\t\treturn null;\n\t}\n\n\tlogger.log(` Copying ${lockfileName} from monorepo root...`);\n\tcopyFileSync(lockfilePath, localLockfile);\n\n\t// Return cleanup function\n\treturn () => {\n\t\ttry {\n\t\t\tunlinkSync(localLockfile);\n\t\t} catch {\n\t\t\t// Ignore cleanup errors\n\t\t}\n\t};\n}\n\n/**\n * Build Docker image\n * Uses BuildKit for cache mount support\n */\nasync function buildDockerImage(\n\timageName: string,\n\toptions: DockerOptions,\n): Promise<void> {\n\tconst tag = options.tag ?? 'latest';\n\tconst registry = options.registry;\n\n\tconst fullImageName = registry\n\t\t? `${registry}/${imageName}:${tag}`\n\t\t: `${imageName}:${tag}`;\n\n\tlogger.log(`\\n🐳 Building Docker image: ${fullImageName}`);\n\n\tconst cwd = process.cwd();\n\n\t// Ensure lockfile exists (copy from monorepo root if needed)\n\tconst cleanup = ensureLockfile(cwd);\n\n\ttry {\n\t\t// Use BuildKit for cache mount support (required for --mount=type=cache)\n\t\texecSync(\n\t\t\t`DOCKER_BUILDKIT=1 docker build -f .gkm/docker/Dockerfile -t ${fullImageName} .`,\n\t\t\t{\n\t\t\t\tcwd,\n\t\t\t\tstdio: 'inherit',\n\t\t\t\tenv: { ...process.env, DOCKER_BUILDKIT: '1' },\n\t\t\t},\n\t\t);\n\t\tlogger.log(`✅ Docker image built: ${fullImageName}`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to build Docker image: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t} finally {\n\t\t// Clean up copied lockfile\n\t\tcleanup?.();\n\t}\n}\n\n/**\n * Push Docker image to registry\n */\nasync function pushDockerImage(\n\timageName: string,\n\toptions: DockerOptions,\n): Promise<void> {\n\tconst tag = options.tag ?? 'latest';\n\tconst registry = options.registry;\n\n\tif (!registry) {\n\t\tthrow new Error(\n\t\t\t'Registry is required to push Docker image. Use --registry or configure docker.registry in gkm.config.ts',\n\t\t);\n\t}\n\n\tconst fullImageName = `${registry}/${imageName}:${tag}`;\n\n\tlogger.log(`\\n🚀 Pushing Docker image: ${fullImageName}`);\n\n\ttry {\n\t\texecSync(`docker push ${fullImageName}`, {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t});\n\t\tlogger.log(`✅ Docker image pushed: ${fullImageName}`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to push Docker image: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n}\n\n/**\n * Result of generating Docker files for a single app in a workspace.\n */\nexport interface AppDockerResult {\n\tappName: string;\n\ttype: 'backend' | 'web' | 'mobile';\n\tdockerfile: string;\n\timageName: string;\n}\n\n/**\n * Result of workspace docker command.\n */\nexport interface WorkspaceDockerResult {\n\tapps: AppDockerResult[];\n\tdockerCompose: string;\n\tdockerignore: string;\n}\n\n/**\n * Get the package name from package.json in an app directory.\n */\nfunction getAppPackageName(appPath: string): string | undefined {\n\ttry {\n\t\tconst pkgPath = join(appPath, 'package.json');\n\t\tif (!existsSync(pkgPath)) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst content = readFileSync(pkgPath, 'utf-8');\n\t\tconst pkg = JSON.parse(content);\n\t\treturn pkg.name;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Generate Dockerfiles for all apps in a workspace.\n * @internal Exported for testing\n */\nexport async function workspaceDockerCommand(\n\tworkspace: NormalizedWorkspace,\n\toptions: DockerOptions,\n): Promise<WorkspaceDockerResult> {\n\tconst results: AppDockerResult[] = [];\n\tconst apps = Object.entries(workspace.apps);\n\n\tlogger.log(`\\n🐳 Generating Dockerfiles for workspace: ${workspace.name}`);\n\n\t// Create docker output directory\n\tconst dockerDir = join(workspace.root, '.gkm', 'docker');\n\tawait mkdir(dockerDir, { recursive: true });\n\n\t// Detect package manager\n\tconst packageManager = detectPackageManager(workspace.root);\n\tlogger.log(` Package manager: ${packageManager}`);\n\n\t// Generate Dockerfile for each app\n\tfor (const [appName, app] of apps) {\n\t\t// Mobile apps deploy via their own toolchain (e.g. EAS Build for Expo)\n\t\t// — no Docker image is produced.\n\t\tif (app.type === 'mobile') {\n\t\t\tlogger.log(\n\t\t\t\t`\\n 📱 Skipping Docker for ${appName} (mobile app — deploy via framework toolchain)`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst appPath = app.path;\n\t\tconst fullAppPath = join(workspace.root, appPath);\n\n\t\t// Get package name for turbo prune (use package.json name or app name)\n\t\tconst turboPackage = getAppPackageName(fullAppPath) ?? appName;\n\n\t\t// Determine image name\n\t\tconst imageName = appName;\n\n\t\tconst hasEntry = !!app.entry;\n\t\tconst buildType = hasEntry ? 'entry' : app.type;\n\t\tlogger.log(`\\n 📄 Generating Dockerfile for ${appName} (${buildType})`);\n\n\t\tlet dockerfile: string;\n\n\t\tif (app.type === 'web') {\n\t\t\tconst publicUrlArgs = getPublicUrlArgNames(app);\n\t\t\tconst webOpts = {\n\t\t\t\timageName,\n\t\t\t\tbaseImage: 'node:22-alpine',\n\t\t\t\tport: app.port,\n\t\t\t\tappPath,\n\t\t\t\tturboPackage,\n\t\t\t\tpackageManager,\n\t\t\t\tpublicUrlArgs,\n\t\t\t};\n\n\t\t\tswitch (app.framework) {\n\t\t\t\tcase 'vite':\n\t\t\t\t\tdockerfile = generateViteStaticDockerfile(webOpts);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'tanstack-start':\n\t\t\t\tcase 'remix':\n\t\t\t\t\tdockerfile = generateNodeWebDockerfile(webOpts);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t// nextjs (and any unspecified web framework — schema requires\n\t\t\t\t\t// a valid framework, so this is just a default).\n\t\t\t\t\tdockerfile = generateNextjsDockerfile(webOpts);\n\t\t\t}\n\t\t} else if (app.entry) {\n\t\t\t// Backend with custom entry point - use tsdown bundling\n\t\t\tdockerfile = generateEntryDockerfile({\n\t\t\t\timageName,\n\t\t\t\tbaseImage: 'node:22-alpine',\n\t\t\t\tport: app.port,\n\t\t\t\tappPath,\n\t\t\t\tentry: app.entry,\n\t\t\t\tturboPackage,\n\t\t\t\tpackageManager,\n\t\t\t\thealthCheckPath: '/health',\n\t\t\t});\n\t\t} else {\n\t\t\t// Backend with gkm routes - use gkm build\n\t\t\tdockerfile = generateBackendDockerfile({\n\t\t\t\timageName,\n\t\t\t\tbaseImage: 'node:22-alpine',\n\t\t\t\tport: app.port,\n\t\t\t\tappPath,\n\t\t\t\tturboPackage,\n\t\t\t\tpackageManager,\n\t\t\t\thealthCheckPath: '/health',\n\t\t\t});\n\t\t}\n\n\t\t// Write Dockerfile with app-specific name\n\t\tconst dockerfilePath = join(dockerDir, `Dockerfile.${appName}`);\n\t\tawait writeFile(dockerfilePath, dockerfile);\n\t\tlogger.log(` Generated: .gkm/docker/Dockerfile.${appName}`);\n\n\t\tresults.push({\n\t\t\tappName,\n\t\t\ttype: app.type,\n\t\t\tdockerfile: dockerfilePath,\n\t\t\timageName,\n\t\t});\n\t}\n\n\t// Generate shared .dockerignore\n\tconst dockerignore = generateDockerignore();\n\tconst dockerignorePath = join(workspace.root, '.dockerignore');\n\tawait writeFile(dockerignorePath, dockerignore);\n\tlogger.log(`\\n Generated: .dockerignore (workspace root)`);\n\n\t// Generate docker-compose.yml for workspace\n\tconst dockerCompose = generateWorkspaceCompose(workspace, {\n\t\tregistry: options.registry,\n\t\t// Which infrastructure this file describes is the manifest's answer, the\n\t\t// same one reconcile derives — not a boolean per service in config.\n\t\tcontainers: await derivedContainers(workspace, 'development'),\n\t});\n\tconst composePath = join(dockerDir, 'docker-compose.yml');\n\tawait writeFile(composePath, dockerCompose);\n\tlogger.log(` Generated: .gkm/docker/docker-compose.yml`);\n\n\t// Summary\n\tlogger.log(\n\t\t`\\n✅ Generated ${results.length} Dockerfile(s) + docker-compose.yml`,\n\t);\n\tlogger.log('\\n📋 Build commands:');\n\tfor (const result of results) {\n\t\tconst icon =\n\t\t\tresult.type === 'backend' ? '⚙️' : result.type === 'mobile' ? '📱' : '🌐';\n\t\tlogger.log(\n\t\t\t` ${icon} docker build -f .gkm/docker/Dockerfile.${result.appName} -t ${result.imageName} .`,\n\t\t);\n\t}\n\tlogger.log('\\n📋 Run all services:');\n\tlogger.log(' docker compose -f .gkm/docker/docker-compose.yml up --build');\n\n\treturn {\n\t\tapps: results,\n\t\tdockerCompose: composePath,\n\t\tdockerignore: dockerignorePath,\n\t};\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { scopedName } from '@geekmidas/manifest';\nimport type { GkmConfig } from '../config';\nimport { dockerCommand, findLockfilePath } from '../docker';\nimport type { DeployResult, DockerDeployConfig } from './types';\n\n/**\n * Get app name from package.json in the current working directory\n * Used for Dokploy app/project naming\n */\nexport function getAppNameFromCwd(): string | undefined {\n\tconst packageJsonPath = join(process.cwd(), 'package.json');\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn undefined;\n\t}\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n\t\tif (pkg.name) {\n\t\t\t// Strip org scope if present (e.g., @myorg/app -> app)\n\t\t\treturn pkg.name.replace(/^@[^/]+\\//, '');\n\t\t}\n\t} catch {\n\t\t// Ignore parse errors\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Get app name from package.json adjacent to the lockfile (project root)\n * Used for Docker image naming\n */\nexport function getAppNameFromPackageJson(): string | undefined {\n\tconst cwd = process.cwd();\n\n\t// Find the lockfile to determine the project root\n\tconst lockfilePath = findLockfilePath(cwd);\n\tif (!lockfilePath) {\n\t\treturn undefined;\n\t}\n\n\t// Use the package.json adjacent to the lockfile\n\tconst projectRoot = dirname(lockfilePath);\n\tconst packageJsonPath = join(projectRoot, 'package.json');\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn undefined;\n\t}\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n\t\tif (pkg.name) {\n\t\t\t// Strip org scope if present (e.g., @myorg/app -> app)\n\t\t\treturn pkg.name.replace(/^@[^/]+\\//, '');\n\t\t}\n\t} catch {\n\t\t// Ignore parse errors\n\t}\n\n\treturn undefined;\n}\n\nconst logger = console;\n\nexport interface DockerDeployOptions {\n\t/** Deployment stage */\n\tstage: string;\n\t/** Image tag */\n\ttag: string;\n\t/** Skip pushing to registry */\n\tskipPush?: boolean;\n\t/** Master key from build */\n\tmasterKey?: string;\n\t/** Docker config from gkm.config */\n\tconfig: DockerDeployConfig;\n\t/**\n\t * Build arguments to pass to docker build.\n\t * Format: ['KEY=value', 'KEY2=value2']\n\t */\n\tbuildArgs?: string[];\n\t/**\n\t * Public URL argument names for frontend Dockerfile generation.\n\t * Used to ensure the Dockerfile declares these as ARG/ENV.\n\t */\n\tpublicUrlArgs?: string[];\n}\n\n/**\n * Get the full image reference\n */\nexport function getImageRef(\n\tregistry: string | undefined,\n\timageName: string,\n\ttag: string,\n): string {\n\tif (registry) {\n\t\treturn `${registry}/${imageName}:${tag}`;\n\t}\n\treturn `${imageName}:${tag}`;\n}\n\n/**\n * Build Docker image\n * @param imageRef - Full image reference (registry/name:tag)\n * @param appName - Name of the app (used for Dockerfile.{appName} in workspaces)\n * @param buildArgs - Build arguments to pass to docker build\n */\nasync function buildImage(\n\timageRef: string,\n\t_appName?: string,\n\tbuildArgs?: string[],\n): Promise<void> {\n\tlogger.log(`\\n🔨 Building Docker image: ${imageRef}`);\n\n\t// Where the lockfile is no longer decides anything here: the image copies a\n\t// bundle that is already built, so a monorepo and a standalone app produce\n\t// the same Dockerfile and the same one-directory build context.\n\tconst cwd = process.cwd();\n\n\t// Generate appropriate Dockerfile\n\t// The bundle already exists: `gkm build` ran before this and produced a\n\t// self-contained `server.mjs`. So the image copies it rather than rebuilding\n\t// it, which is both faster and the only version that works from inside the\n\t// source monorepo — `turbo prune` honours .gitignore, so a sibling package's\n\t// `dist` never arrives, and rebuilding it in the image means bootstrapping\n\t// the whole workspace to produce a bundle we are holding.\n\tlogger.log(' Generating Dockerfile for the pre-built bundle...');\n\tawait dockerCommand({ slim: true });\n\n\t// Determine build context and Dockerfile path\n\t// For workspaces with multiple apps, use per-app Dockerfile (Dockerfile.api, etc.)\n\t// One file, not `Dockerfile.${appName}`: the suffix belonged to the\n\t// generate-every-app-at-once path, and this generates exactly one Dockerfile\n\t// for the app being built, immediately above.\n\t//\n\t// Absolute, because it is written under the *app* while the build may run\n\t// from elsewhere — where a relative `.gkm/docker/Dockerfile` resolves to a\n\t// path that does not exist, and `docker build` says only\n\t// `lstat .gkm: no such file or directory`.\n\tconst dockerfilePath = join(cwd, '.gkm', 'docker', 'Dockerfile');\n\n\t// The app's own directory, because the bundle is the only thing copied and\n\t// it lives there. A monorepo root context was for the build-inside-the-image\n\t// path, which this no longer takes.\n\tconst buildCwd = cwd;\n\n\t// Build the build args string\n\tconst buildArgsString =\n\t\tbuildArgs && buildArgs.length > 0\n\t\t\t? buildArgs.map((arg) => `--build-arg \"${arg}\"`).join(' ')\n\t\t\t: '';\n\n\ttry {\n\t\t// Build for linux/amd64 to ensure compatibility with most cloud servers\n\t\tconst cmd = [\n\t\t\t'DOCKER_BUILDKIT=1 docker build',\n\t\t\t'--platform linux/amd64',\n\t\t\t`-f ${dockerfilePath}`,\n\t\t\t`-t ${imageRef}`,\n\t\t\tbuildArgsString,\n\t\t\t'.',\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(' ');\n\n\t\texecSync(cmd, {\n\t\t\tcwd: buildCwd,\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, DOCKER_BUILDKIT: '1' },\n\t\t});\n\t\tlogger.log(`✅ Image built: ${imageRef}`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to build Docker image: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n}\n\n/**\n * Push Docker image to registry\n */\nasync function pushImage(imageRef: string): Promise<void> {\n\tlogger.log(`\\n☁️ Pushing image: ${imageRef}`);\n\n\ttry {\n\t\texecSync(`docker push ${imageRef}`, {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t});\n\t\tlogger.log(`✅ Image pushed: ${imageRef}`);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to push Docker image: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n}\n\n/**\n * Deploy using Docker (build and optionally push image)\n */\nexport async function deployDocker(\n\toptions: DockerDeployOptions,\n): Promise<DeployResult> {\n\tconst { stage, tag, skipPush, masterKey, config, buildArgs } = options;\n\n\t// imageName should always be set by resolveDockerConfig\n\tconst imageName = config.imageName!;\n\tconst imageRef = getImageRef(config.registry, imageName, tag);\n\n\t// Build image (pass appName for workspace Dockerfile selection)\n\tawait buildImage(imageRef, config.appName, buildArgs);\n\n\t// Push to registry if not skipped\n\tif (!skipPush) {\n\t\tif (!config.registry) {\n\t\t\tlogger.warn(\n\t\t\t\t'\\n⚠️ No registry configured. Use --skip-push or configure docker.registry in gkm.config.ts',\n\t\t\t);\n\t\t} else {\n\t\t\tawait pushImage(imageRef);\n\t\t}\n\t}\n\n\t// Output deployment info\n\tlogger.log('\\n✅ Docker deployment ready!');\n\tlogger.log(`\\n📋 Deployment details:`);\n\tlogger.log(` Image: ${imageRef}`);\n\tlogger.log(` Stage: ${stage}`);\n\n\tif (masterKey) {\n\t\tlogger.log(`\\n🔐 Deploy with this environment variable:`);\n\t\tlogger.log(` GKM_MASTER_KEY=${masterKey}`);\n\t\tlogger.log('\\n Example docker run:');\n\t\tlogger.log(` docker run -e GKM_MASTER_KEY=${masterKey} ${imageRef}`);\n\t}\n\n\treturn {\n\t\timageRef,\n\t\tmasterKey,\n\t};\n}\n\n/**\n * What one application is called on a provider.\n *\n * The same rule the constructs use, through the same `scopedName`: the\n * application beside `production-kitchen-sink-database` is\n * `production-kitchen-sink-api`, not `api`. Both deploy paths call this — the\n * workspace one named its applications by the bare app key, so a project held\n * an `api` and a `web` that every stage would collide on.\n *\n * The app id is dropped when it repeats the project, so a project named for its\n * one application is `production-kitchen-sink` rather than\n * `production-kitchen-sink-kitchen-sink`.\n */\nexport function applicationName(\n\tstage: string,\n\tproject: string,\n\tapp: string,\n): string {\n\treturn app === project\n\t\t? `${stage}-${project}`.toLowerCase()\n\t\t: scopedName([stage, project], app);\n}\n\n/**\n * Resolve Docker deploy config from gkm config.\n *\n * **`name` is the scope, exactly as it is in an SST config.** `sst.config.ts`\n * declares `name: 'kitchen-sink'` and every physical name is built from\n * `[stage, name]`; this is the same statement in the same place, so a construct\n * carries one name across providers rather than two that happen to match.\n *\n * A package.json name is the fallback, not the source. It used to be the source,\n * which put the *monorepo* name on the Dokploy project and the *package* name on\n * the application — two accidents of directory layout standing in for a\n * decision, neither of them scoped by stage.\n *\n * - `projectName` — the gkm config's `name`. The Dokploy project, and the `app`\n * half of every scoped name, the way `$app.name` is in SST.\n * - `appName` — the application within it, scoped `{stage}-{name}` by the\n * shared rule. Dropped to the project alone when the app *is* the project, so\n * a workspace named for its one app is not `…-kitchen-sink-kitchen-sink`.\n * - `imageName` — the Docker image, which is a different question: an image is\n * pushed to a registry under a name a human reads, and it carries no stage\n * because one image is deployed to several.\n */\nexport function resolveDockerConfig(\n\tconfig: GkmConfig,\n\tstage?: string,\n): DockerDeployConfig {\n\tconst projectName =\n\t\tconfig.name ?? getAppNameFromPackageJson() ?? getAppNameFromCwd() ?? 'app';\n\n\t// `api`, because that is the key the workspace projection gives a single-app\n\t// backend — the same key a workspace config would write. Asking the\n\t// filesystem instead is what put the *package* name here and the *monorepo*\n\t// name on the project: two accidents of layout, neither scoped.\n\tconst appId = 'api';\n\tconst appName =\n\t\tstage === undefined ? appId : applicationName(stage, projectName, appId);\n\n\t// The project's name, not the app key: an image is what somebody types after\n\t// `docker pull`, so `kitchen-sink` and not `api`. It carries no stage\n\t// either — one image is deployed to several, and the registry path already\n\t// scopes it.\n\tconst imageName = config.docker?.imageName ?? projectName;\n\n\treturn {\n\t\tregistry: config.docker?.registry,\n\t\timageName,\n\t\tprojectName,\n\t\tappName,\n\t};\n}\n","/**\n * Environment Variable Resolution for Dokploy Deployments\n *\n * Resolves sniffed environment variables to actual values during deployment.\n * Auto-supports common variables like DATABASE_URL, REDIS_URL, BETTER_AUTH_*,\n * and falls back to user-provided secrets.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport type { StageSecrets } from '../secrets/types';\nimport { stripPublicPrefix } from '../workspace/publicEnv';\nimport type { NormalizedAppConfig } from '../workspace/types';\nimport {\n\ttype DokployStageState,\n\tgetGeneratedSecret,\n\tsetGeneratedSecret,\n} from './state';\n\n/**\n * Context needed for environment variable resolution\n */\nexport interface EnvResolverContext {\n\t/** The app being deployed */\n\tapp: NormalizedAppConfig;\n\t/** The app name */\n\tappName: string;\n\t/** Deployment stage (production, staging, development) */\n\tstage: string;\n\t/** Deploy state (for persisting generated secrets) */\n\tstate: DokployStageState;\n\t/** Public hostname for this app */\n\tappHostname: string;\n\t/** All frontend app URLs (for BETTER_AUTH_TRUSTED_ORIGINS) */\n\tfrontendUrls: string[];\n\t/** User-provided secrets from secrets store */\n\tuserSecrets?: StageSecrets;\n\t/** Master key for runtime decryption (optional) */\n\tmasterKey?: string;\n\t/** URLs of deployed dependency apps (e.g., { auth: 'https://auth.example.com' }) */\n\tdependencyUrls?: Record<string, string>;\n}\n\n/**\n * Result of environment variable resolution\n */\nexport interface EnvResolutionResult {\n\t/** Successfully resolved environment variables */\n\tresolved: Record<string, string>;\n\t/** Environment variable names that could not be resolved */\n\tmissing: string[];\n}\n\n/**\n * Auto-supported environment variable names\n */\nexport const AUTO_SUPPORTED_VARS = [\n\t'PORT',\n\t'NODE_ENV',\n\t'STAGE',\n\t'DATABASE_URL',\n\t'REDIS_URL',\n\t'BETTER_AUTH_URL',\n\t'BETTER_AUTH_SECRET',\n\t'BETTER_AUTH_TRUSTED_ORIGINS',\n\t'GKM_MASTER_KEY',\n] as const;\n\nexport type AutoSupportedVar = (typeof AUTO_SUPPORTED_VARS)[number];\n\n/**\n * Check if a variable name is auto-supported\n */\nexport function isAutoSupportedVar(\n\tvarName: string,\n): varName is AutoSupportedVar {\n\treturn AUTO_SUPPORTED_VARS.includes(varName as AutoSupportedVar);\n}\n\n/**\n * Generate a secure random secret (64 hex characters = 32 bytes)\n */\nexport function generateSecret(): string {\n\treturn randomBytes(32).toString('hex');\n}\n\n/**\n * Get or generate a secret for an app.\n * If the secret already exists in state, returns it.\n * Otherwise generates a new one and stores it.\n */\nexport function getOrGenerateSecret(\n\tstate: DokployStageState,\n\tappName: string,\n\tsecretName: string,\n): string {\n\t// Check if already generated\n\tconst existing = getGeneratedSecret(state, appName, secretName);\n\tif (existing) {\n\t\treturn existing;\n\t}\n\n\t// Generate new secret\n\tconst generated = generateSecret();\n\n\t// Store in state for persistence\n\tsetGeneratedSecret(state, appName, secretName, generated);\n\n\treturn generated;\n}\n\n/**\n * Resolve a single environment variable\n */\nexport function resolveEnvVar(\n\tvarName: string,\n\tcontext: EnvResolverContext,\n): string | undefined {\n\t// Auto-supported variables\n\tswitch (varName) {\n\t\tcase 'PORT':\n\t\t\treturn String(context.app.port);\n\n\t\tcase 'NODE_ENV':\n\t\t\t// Always 'production' for deployed apps (gkm dev handles development mode)\n\t\t\treturn 'production';\n\n\t\tcase 'STAGE':\n\t\t\treturn context.stage;\n\n\t\tcase 'BETTER_AUTH_URL':\n\t\t\treturn `https://${context.appHostname}`;\n\n\t\tcase 'BETTER_AUTH_SECRET':\n\t\t\treturn getOrGenerateSecret(\n\t\t\t\tcontext.state,\n\t\t\t\tcontext.appName,\n\t\t\t\t'BETTER_AUTH_SECRET',\n\t\t\t);\n\n\t\tcase 'BETTER_AUTH_TRUSTED_ORIGINS':\n\t\t\tif (context.frontendUrls.length > 0) {\n\t\t\t\treturn context.frontendUrls.join(',');\n\t\t\t}\n\t\t\t// Fall through to check user secrets\n\t\t\tbreak;\n\n\t\tcase 'GKM_MASTER_KEY':\n\t\t\tif (context.masterKey) {\n\t\t\t\treturn context.masterKey;\n\t\t\t}\n\t\t\t// Fall through to check user secrets\n\t\t\tbreak;\n\t}\n\n\t// Check dependency URLs (e.g., AUTH_URL -> dependencyUrls.auth)\n\t// Also supports any known public prefix (NEXT_PUBLIC_, VITE_, ...) so\n\t// VITE_AUTH_URL and NEXT_PUBLIC_AUTH_URL both resolve to dependencyUrls.auth.\n\tif (context.dependencyUrls && varName.endsWith('_URL')) {\n\t\tconst stripped = stripPublicPrefix(varName) ?? varName;\n\t\tconst depName = stripped.slice(0, -4).toLowerCase();\n\n\t\tif (context.dependencyUrls[depName]) {\n\t\t\treturn context.dependencyUrls[depName];\n\t\t}\n\t}\n\n\t// Check user-provided secrets\n\tif (context.userSecrets) {\n\t\t// Check custom secrets first\n\t\tif (context.userSecrets.custom[varName]) {\n\t\t\treturn context.userSecrets.custom[varName];\n\t\t}\n\n\t\t// Check URLs (DATABASE_URL, REDIS_URL, RABBITMQ_URL)\n\t\tif (varName in context.userSecrets.urls) {\n\t\t\treturn context.userSecrets.urls[\n\t\t\t\tvarName as keyof typeof context.userSecrets.urls\n\t\t\t];\n\t\t}\n\n\t\t// Check service-specific vars\n\t\tif (\n\t\t\tvarName === 'POSTGRES_PASSWORD' &&\n\t\t\tcontext.userSecrets.services.postgres\n\t\t) {\n\t\t\treturn context.userSecrets.services.postgres.password;\n\t\t}\n\t\tif (varName === 'REDIS_PASSWORD' && context.userSecrets.services.redis) {\n\t\t\treturn context.userSecrets.services.redis.password;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Resolve all environment variables for an app\n */\nexport function resolveEnvVars(\n\trequiredVars: string[],\n\tcontext: EnvResolverContext,\n): EnvResolutionResult {\n\tconst resolved: Record<string, string> = {};\n\tconst missing: string[] = [];\n\n\tfor (const varName of requiredVars) {\n\t\tconst value = resolveEnvVar(varName, context);\n\t\tif (value !== undefined) {\n\t\t\tresolved[varName] = value;\n\t\t} else {\n\t\t\tmissing.push(varName);\n\t\t}\n\t}\n\n\treturn { resolved, missing };\n}\n\n/**\n * Format missing variables error message\n */\nexport function formatMissingVarsError(\n\tappName: string,\n\tmissing: string[],\n\tstage: string,\n): string {\n\tconst varList = missing.map((v) => ` - ${v}`).join('\\n');\n\treturn (\n\t\t`Deployment failed: ${appName} is missing required environment variables:\\n` +\n\t\t`${varList}\\n\\n` +\n\t\t`Add them with:\\n` +\n\t\t` gkm secrets:set <VAR_NAME> <value> --stage ${stage}\\n\\n` +\n\t\t`Or add them to the app's requiredEnv in gkm.config.ts to have them auto-resolved.`\n\t);\n}\n\n/**\n * Validate that all required environment variables can be resolved\n */\nexport function validateEnvVars(\n\trequiredVars: string[],\n\tcontext: EnvResolverContext,\n): { valid: boolean; missing: string[]; resolved: Record<string, string> } {\n\tconst { resolved, missing } = resolveEnvVars(requiredVars, context);\n\treturn {\n\t\tvalid: missing.length === 0,\n\t\tmissing,\n\t\tresolved,\n\t};\n}\n","/**\n * State Provider Interface\n *\n * Abstracts the storage backend for deployment state.\n * Built-in providers: LocalStateProvider, SSMStateProvider\n * Users can also supply custom implementations.\n */\n\nimport type { DokployStageState } from './state';\n\n/**\n * Interface for deployment state storage providers.\n *\n * Implementations must handle:\n * - Reading state for a stage (returns null if not found)\n * - Writing state for a stage (creates or updates)\n */\nexport interface StateProvider {\n\t/**\n\t * Read deployment state for a stage.\n\t *\n\t * @param stage - The deployment stage (e.g., 'development', 'production')\n\t * @returns The state object or null if not found\n\t */\n\tread(stage: string): Promise<DokployStageState | null>;\n\n\t/**\n\t * Write deployment state for a stage.\n\t *\n\t * @param stage - The deployment stage\n\t * @param state - The state object to persist\n\t */\n\twrite(stage: string, state: DokployStageState): Promise<void>;\n}\n\n/**\n * Valid AWS regions.\n */\nexport type AwsRegion =\n\t| 'us-east-1'\n\t| 'us-east-2'\n\t| 'us-west-1'\n\t| 'us-west-2'\n\t| 'af-south-1'\n\t| 'ap-east-1'\n\t| 'ap-south-1'\n\t| 'ap-south-2'\n\t| 'ap-southeast-1'\n\t| 'ap-southeast-2'\n\t| 'ap-southeast-3'\n\t| 'ap-southeast-4'\n\t| 'ap-northeast-1'\n\t| 'ap-northeast-2'\n\t| 'ap-northeast-3'\n\t| 'ca-central-1'\n\t| 'eu-central-1'\n\t| 'eu-central-2'\n\t| 'eu-west-1'\n\t| 'eu-west-2'\n\t| 'eu-west-3'\n\t| 'eu-south-1'\n\t| 'eu-south-2'\n\t| 'eu-north-1'\n\t| 'me-south-1'\n\t| 'me-central-1'\n\t| 'sa-east-1';\n\n/**\n * Local state provider config.\n */\nexport interface LocalStateConfig {\n\tprovider: 'local';\n}\n\n/**\n * SSM state provider config (requires region).\n */\nexport interface SSMStateConfig {\n\tprovider: 'ssm';\n\t/** AWS region (required for SSM provider) */\n\tregion: AwsRegion;\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile?: string;\n}\n\n/**\n * Custom state provider config.\n */\nexport interface CustomStateConfig {\n\t/** Custom StateProvider implementation */\n\tprovider: StateProvider;\n}\n\n/**\n * State configuration types.\n */\nexport type StateConfig = LocalStateConfig | SSMStateConfig | CustomStateConfig;\n\n/**\n * Check if value is a StateProvider implementation.\n */\nexport function isStateProvider(value: unknown): value is StateProvider {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\ttypeof (value as StateProvider).read === 'function' &&\n\t\ttypeof (value as StateProvider).write === 'function'\n\t);\n}\n\nexport interface CreateStateProviderOptions {\n\t/** State config from workspace */\n\tconfig?: StateConfig;\n\t/** Workspace root directory (for local provider) */\n\tworkspaceRoot: string;\n\t/** Workspace name (for SSM parameter path) */\n\tworkspaceName: string;\n}\n\n/**\n * Create a state provider based on configuration.\n *\n * - 'local': LocalStateProvider (default)\n * - 'ssm': CachedStateProvider with SSM as source of truth\n * - Custom: Use provided StateProvider implementation\n */\nexport async function createStateProvider(\n\toptions: CreateStateProviderOptions,\n): Promise<StateProvider> {\n\tconst { config, workspaceRoot, workspaceName } = options;\n\n\t// Default to local provider if no config\n\tif (!config) {\n\t\tconst { LocalStateProvider } = await import('./LocalStateProvider');\n\t\treturn new LocalStateProvider(workspaceRoot);\n\t}\n\n\t// Custom provider implementation\n\tif (isStateProvider(config.provider)) {\n\t\treturn config.provider;\n\t}\n\n\t// Built-in providers (discriminated by provider string)\n\tconst provider = config.provider;\n\n\tif (provider === 'local') {\n\t\tconst { LocalStateProvider } = await import('./LocalStateProvider');\n\t\treturn new LocalStateProvider(workspaceRoot);\n\t}\n\n\tif (provider === 'ssm') {\n\t\tif (!workspaceName) {\n\t\t\tthrow new Error(\n\t\t\t\t'Workspace name is required for SSM state provider. Set \"name\" in gkm.config.ts.',\n\t\t\t);\n\t\t}\n\n\t\tconst { LocalStateProvider } = await import('./LocalStateProvider');\n\t\tconst { SSMStateProvider } = await import('./SSMStateProvider');\n\t\tconst { CachedStateProvider } = await import('./CachedStateProvider');\n\n\t\tconst ssmConfig = config as SSMStateConfig;\n\t\tconst local = new LocalStateProvider(workspaceRoot);\n\t\tconst ssm = SSMStateProvider.create({\n\t\t\tworkspaceName,\n\t\t\tregion: ssmConfig.region,\n\t\t\tprofile: ssmConfig.profile,\n\t\t});\n\n\t\treturn new CachedStateProvider(ssm, local);\n\t}\n\n\t// Should never reach here - custom providers handled above\n\tthrow new Error(`Unknown state provider: ${JSON.stringify(config)}`);\n}\n","import { encryptSecrets } from '../secrets/encryption.js';\nimport { toEmbeddableSecrets } from '../secrets/storage.js';\nimport type {\n\tEmbeddableSecrets,\n\tEncryptedPayload,\n\tStageSecrets,\n} from '../secrets/types.js';\nimport type { SniffedEnvironment } from './sniffer.js';\n\n/**\n * Result of filtering secrets for an app.\n */\nexport interface FilteredAppSecrets {\n\tappName: string;\n\t/** Secrets filtered to only include what the app needs */\n\tsecrets: EmbeddableSecrets;\n\t/** List of required env vars that were found in secrets */\n\tfound: string[];\n\t/** List of required env vars that were NOT found in secrets */\n\tmissing: string[];\n}\n\n/**\n * Filter secrets to only include the env vars that an app requires.\n *\n * @param stageSecrets - All secrets for the stage\n * @param sniffedEnv - The sniffed environment requirements for the app\n * @returns Filtered secrets with found/missing tracking\n */\nexport function filterSecretsForApp(\n\tstageSecrets: StageSecrets,\n\tsniffedEnv: SniffedEnvironment,\n): FilteredAppSecrets {\n\t// Convert stage secrets to flat embeddable format\n\tconst allSecrets = toEmbeddableSecrets(stageSecrets);\n\tconst filtered: EmbeddableSecrets = {};\n\tconst found: string[] = [];\n\tconst missing: string[] = [];\n\n\t// Filter to only required env vars\n\tfor (const key of sniffedEnv.requiredEnvVars) {\n\t\tif (key in allSecrets) {\n\t\t\tfiltered[key] = allSecrets[key]!;\n\t\t\tfound.push(key);\n\t\t} else {\n\t\t\tmissing.push(key);\n\t\t}\n\t}\n\n\treturn {\n\t\tappName: sniffedEnv.appName,\n\t\tsecrets: filtered,\n\t\tfound: found.sort(),\n\t\tmissing: missing.sort(),\n\t};\n}\n\n/**\n * Result of encrypting secrets for an app.\n */\nexport interface EncryptedAppSecrets {\n\tappName: string;\n\t/** Encrypted payload with credentials and IV */\n\tpayload: EncryptedPayload;\n\t/** Master key for runtime decryption (hex encoded) */\n\tmasterKey: string;\n\t/** Number of secrets encrypted */\n\tsecretCount: number;\n\t/** List of required env vars that were NOT found in secrets */\n\tmissingSecrets: string[];\n}\n\n/**\n * Encrypt filtered secrets for an app.\n * Generates an ephemeral master key that should be injected into Dokploy.\n *\n * @param filteredSecrets - The filtered secrets for the app\n * @returns Encrypted payload with master key\n */\nexport function encryptSecretsForApp(\n\tfilteredSecrets: FilteredAppSecrets,\n): EncryptedAppSecrets {\n\tconst payload = encryptSecrets(filteredSecrets.secrets);\n\n\treturn {\n\t\tappName: filteredSecrets.appName,\n\t\tpayload,\n\t\tmasterKey: payload.masterKey,\n\t\tsecretCount: Object.keys(filteredSecrets.secrets).length,\n\t\tmissingSecrets: filteredSecrets.missing,\n\t};\n}\n\n/**\n * Filter and encrypt secrets for an app in one step.\n *\n * @param stageSecrets - All secrets for the stage\n * @param sniffedEnv - The sniffed environment requirements for the app\n * @returns Encrypted secrets with master key\n */\nexport function prepareSecretsForApp(\n\tstageSecrets: StageSecrets,\n\tsniffedEnv: SniffedEnvironment,\n): EncryptedAppSecrets {\n\tconst filtered = filterSecretsForApp(stageSecrets, sniffedEnv);\n\treturn encryptSecretsForApp(filtered);\n}\n\n/**\n * Prepare secrets for multiple apps.\n *\n * @param stageSecrets - All secrets for the stage\n * @param sniffedApps - Map of app name to sniffed environment\n * @returns Map of app name to encrypted secrets\n */\nexport function prepareSecretsForAllApps(\n\tstageSecrets: StageSecrets,\n\tsniffedApps: Map<string, SniffedEnvironment>,\n): Map<string, EncryptedAppSecrets> {\n\tconst results = new Map<string, EncryptedAppSecrets>();\n\n\tfor (const [appName, sniffedEnv] of sniffedApps) {\n\t\t// Only prepare secrets for apps that have required env vars\n\t\tif (sniffedEnv.requiredEnvVars.length > 0) {\n\t\t\tconst encrypted = prepareSecretsForApp(stageSecrets, sniffedEnv);\n\t\t\tresults.set(appName, encrypted);\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Report on secrets preparation status for all apps.\n */\nexport interface SecretsReport {\n\t/** Total number of apps processed */\n\ttotalApps: number;\n\t/** Apps with encrypted secrets */\n\tappsWithSecrets: string[];\n\t/** Apps without secrets (frontends or no env requirements) */\n\tappsWithoutSecrets: string[];\n\t/** Apps with missing secrets (warnings) */\n\tappsWithMissingSecrets: Array<{\n\t\tappName: string;\n\t\tmissing: string[];\n\t}>;\n}\n\n/**\n * Generate a report on secrets preparation.\n */\nexport function generateSecretsReport(\n\tencryptedApps: Map<string, EncryptedAppSecrets>,\n\tsniffedApps: Map<string, SniffedEnvironment>,\n): SecretsReport {\n\tconst appsWithSecrets: string[] = [];\n\tconst appsWithoutSecrets: string[] = [];\n\tconst appsWithMissingSecrets: Array<{ appName: string; missing: string[] }> =\n\t\t[];\n\n\tfor (const [appName, sniffedEnv] of sniffedApps) {\n\t\tif (sniffedEnv.requiredEnvVars.length === 0) {\n\t\t\tappsWithoutSecrets.push(appName);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst encrypted = encryptedApps.get(appName);\n\t\tif (encrypted) {\n\t\t\tappsWithSecrets.push(appName);\n\n\t\t\tif (encrypted.missingSecrets.length > 0) {\n\t\t\t\tappsWithMissingSecrets.push({\n\t\t\t\t\tappName,\n\t\t\t\t\tmissing: encrypted.missingSecrets,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\ttotalApps: sniffedApps.size,\n\t\tappsWithSecrets: appsWithSecrets.sort(),\n\t\tappsWithoutSecrets: appsWithoutSecrets.sort(),\n\t\tappsWithMissingSecrets,\n\t};\n}\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport type { SniffResult } from '@geekmidas/envkit/sniffer';\nimport { normalizeRoutes } from '../workspace/client-generator.js';\nimport { getPublicEnvPrefix } from '../workspace/publicEnv.js';\nimport type { NormalizedAppConfig } from '../workspace/types.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Resolve the tsx package path from the CLI package's dependencies.\n * This ensures tsx is available regardless of whether the target project has it installed.\n */\nfunction resolveTsxPath(): string {\n\tconst require = createRequire(import.meta.url);\n\treturn require.resolve('tsx');\n}\n\n/**\n * Resolve the path to a sniffer helper file.\n * Handles both dev (.ts with tsx) and production (.mjs from dist).\n *\n * In production: sniffer.ts is bundled into dist/index.mjs, but sniffer helper\n * files are output to dist/deploy/ as standalone modules for subprocess loading.\n *\n * In development: All files are in src/deploy/ and loaded via tsx.\n */\nfunction resolveSnifferFile(baseName: string): string {\n\t// Try deploy/ subdirectory first (production: bundled code is at dist/index.mjs,\n\t// but sniffer files are at dist/deploy/)\n\tconst deployMjsPath = resolve(__dirname, 'deploy', `${baseName}.mjs`);\n\tif (existsSync(deployMjsPath)) {\n\t\treturn deployMjsPath;\n\t}\n\n\t// Try same directory .mjs (production: if running from dist/deploy/ directly)\n\tconst mjsPath = resolve(__dirname, `${baseName}.mjs`);\n\tif (existsSync(mjsPath)) {\n\t\treturn mjsPath;\n\t}\n\n\t// Try same directory .ts (development with tsx: all files in src/deploy/)\n\tconst tsPath = resolve(__dirname, `${baseName}.ts`);\n\tif (existsSync(tsPath)) {\n\t\treturn tsPath;\n\t}\n\n\t// Fallback to .ts (will error if neither exists)\n\treturn tsPath;\n}\n\n// Re-export SniffResult for consumers\nexport type { SniffResult } from '@geekmidas/envkit/sniffer';\n\n/**\n * Result of sniffing an app's environment requirements.\n */\nexport interface SniffedEnvironment {\n\tappName: string;\n\t/**\n\t * All required environment variable names.\n\t * When `SniffAppOptions.markOptional` is true, optional variables are\n\t * suffixed with `?` (e.g. `PORT?`).\n\t */\n\trequiredEnvVars: string[];\n\t/**\n\t * Subset of variables that are optional (accessed via `.optional()` or\n\t * `.default()`). Always populated regardless of `markOptional`.\n\t */\n\toptionalEnvVars: string[];\n}\n\n/**\n * Options for sniffing an app's environment.\n */\nexport interface SniffAppOptions {\n\t/** Whether to log warnings for errors encountered during sniffing. Defaults to true. */\n\tlogWarnings?: boolean;\n\t/**\n\t * When true, optional variables (those with `.optional()` or `.default()`)\n\t * are suffixed with `?` in `requiredEnvVars` (e.g. `PORT?`).\n\t * Defaults to false.\n\t */\n\tmarkOptional?: boolean;\n}\n\n/**\n * Get required environment variables for an app.\n *\n * Detection strategy (in order):\n * 1. Frontend apps: Returns empty (no server secrets)\n * 2. Apps with `requiredEnv`: Uses explicit list from config\n * 3. Entry apps: Imports entry file in subprocess to capture config.parse() calls\n * 4. Route-based apps: Loads route files and calls getEnvironment() on each construct\n * 5. Apps with `envParser` (no routes): Runs SnifferEnvironmentParser to detect usage\n * 6. Apps with neither: Returns empty\n *\n * This function handles \"fire and forget\" async operations gracefully,\n * capturing errors and unhandled rejections without failing the build.\n *\n * @param app - The normalized app configuration\n * @param appName - The name of the app\n * @param workspacePath - Absolute path to the workspace root\n * @param options - Optional configuration for sniffing behavior\n * @returns The sniffed environment with required variables\n */\nexport async function sniffAppEnvironment(\n\tapp: NormalizedAppConfig,\n\tappName: string,\n\tworkspacePath: string,\n\toptions: SniffAppOptions = {},\n): Promise<SniffedEnvironment> {\n\tconst { logWarnings = true, markOptional = false } = options;\n\n\t// 1. Frontend apps - handle dependencies and config sniffing\n\tif (app.type === 'web' || app.type === 'mobile') {\n\t\t// Auto-generate {prefix}{DEP}_URL from dependencies, where prefix matches\n\t\t// the framework's public-var convention (NEXT_PUBLIC_, VITE_, ...).\n\t\t// For frameworks without a prefix (e.g. Remix), no dep var is required.\n\t\tconst publicPrefix = getPublicEnvPrefix(app.framework);\n\t\tconst depVars = publicPrefix\n\t\t\t? (app.dependencies ?? []).map(\n\t\t\t\t\t(dep) => `${publicPrefix}${dep.toUpperCase()}_URL`,\n\t\t\t\t)\n\t\t\t: [];\n\n\t\t// If config specified, sniff by importing the file(s)\n\t\t// The file calls .parse() at module load, which triggers sniffer to capture vars\n\t\tif (app.config) {\n\t\t\tconst sniffedVars: string[] = [];\n\t\t\tconst sniffedOptional: string[] = [];\n\n\t\t\t// Collect config paths to sniff\n\t\t\tconst configPaths: string[] = [];\n\t\t\tif (app.config.client) configPaths.push(app.config.client);\n\t\t\tif (app.config.server) configPaths.push(app.config.server);\n\n\t\t\t// Sniff each config file\n\t\t\tfor (const configPath of configPaths) {\n\t\t\t\tconst result = await sniffEntryFile(\n\t\t\t\t\tconfigPath,\n\t\t\t\t\tapp.path,\n\t\t\t\t\tworkspacePath,\n\t\t\t\t);\n\n\t\t\t\tif (logWarnings && result.error) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[sniffer] ${appName}: Config file \"${configPath}\" threw error during sniffing (env vars still captured): ${result.error.message}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tsniffedVars.push(...result.envVars);\n\t\t\t\tsniffedOptional.push(...result.optionalEnvVars);\n\t\t\t}\n\n\t\t\tconst optionalEnvVars = [...new Set(sniffedOptional)];\n\t\t\t// Combine: dependency vars + sniffed vars (deduplicated)\n\t\t\tconst allVars = applyMarkOptional(\n\t\t\t\t[...new Set([...depVars, ...sniffedVars])],\n\t\t\t\toptionalEnvVars,\n\t\t\t\tmarkOptional,\n\t\t\t);\n\t\t\treturn { appName, requiredEnvVars: allVars, optionalEnvVars };\n\t\t}\n\n\t\treturn { appName, requiredEnvVars: depVars, optionalEnvVars: [] };\n\t}\n\n\t// 2. Entry apps - import entry file in subprocess to trigger config.parse()\n\tif (app.entry) {\n\t\tconst result = await sniffEntryFile(app.entry, app.path, workspacePath);\n\n\t\tif (logWarnings && result.error) {\n\t\t\tconsole.warn(\n\t\t\t\t`[sniffer] ${appName}: Entry file threw error during sniffing (env vars still captured): ${result.error.message}`,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tappName,\n\t\t\trequiredEnvVars: applyMarkOptional(\n\t\t\t\tresult.envVars,\n\t\t\t\tresult.optionalEnvVars,\n\t\t\t\tmarkOptional,\n\t\t\t),\n\t\t\toptionalEnvVars: result.optionalEnvVars,\n\t\t};\n\t}\n\n\t// 4. Route-based apps - load routes and call getEnvironment() on each construct\n\tif (app.routes) {\n\t\tconst result = await sniffRouteFiles(\n\t\t\tnormalizeRoutes(app.routes),\n\t\t\tapp.path,\n\t\t\tworkspacePath,\n\t\t);\n\n\t\tif (logWarnings && result.error) {\n\t\t\tconsole.warn(\n\t\t\t\t`[sniffer] ${appName}: Route sniffing threw error (env vars still captured): ${result.error.message}`,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tappName,\n\t\t\trequiredEnvVars: applyMarkOptional(\n\t\t\t\tresult.envVars,\n\t\t\t\tresult.optionalEnvVars,\n\t\t\t\tmarkOptional,\n\t\t\t),\n\t\t\toptionalEnvVars: result.optionalEnvVars,\n\t\t};\n\t}\n\n\t// 5. Apps with envParser but no routes - run sniffer to detect env var usage\n\tif (app.envParser) {\n\t\tconst result = await sniffEnvParser(app.envParser, app.path, workspacePath);\n\n\t\t// Log any issues for debugging\n\t\tif (logWarnings) {\n\t\t\tif (result.error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[sniffer] ${appName}: envParser threw error during sniffing (env vars still captured): ${result.error.message}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (result.unhandledRejections.length > 0) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[sniffer] ${appName}: Fire-and-forget rejections during sniffing (suppressed): ${result.unhandledRejections.map((e) => e.message).join(', ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tappName,\n\t\t\trequiredEnvVars: applyMarkOptional(\n\t\t\t\tresult.envVars,\n\t\t\t\tresult.optionalEnvVars,\n\t\t\t\tmarkOptional,\n\t\t\t),\n\t\t\toptionalEnvVars: result.optionalEnvVars,\n\t\t};\n\t}\n\n\t// No env detection method available\n\treturn { appName, requiredEnvVars: [], optionalEnvVars: [] };\n}\n\n/**\n * Apply the `?` suffix to optional variables in an env var list.\n * Only modifies the list when `markOptional` is true and there are optional vars.\n */\nfunction applyMarkOptional(\n\tenvVars: string[],\n\toptionalEnvVars: string[],\n\tmarkOptional: boolean,\n): string[] {\n\tif (!markOptional || optionalEnvVars.length === 0) return envVars;\n\tconst optionalSet = new Set(optionalEnvVars);\n\treturn envVars.map((v) => (optionalSet.has(v) ? `${v}?` : v));\n}\n\n/**\n * Result from sniffing an entry file.\n */\ninterface EntrySniffResult {\n\tenvVars: string[];\n\toptionalEnvVars: string[];\n\terror?: Error;\n}\n\n/**\n * Sniff an entry file by importing it in a subprocess.\n *\n * Entry apps call `config.parse()` at module load time. To capture which\n * env vars are accessed, we:\n * 1. Spawn a subprocess with a module loader hook\n * 2. The loader intercepts `@geekmidas/envkit` and replaces EnvironmentParser\n * with SnifferEnvironmentParser\n * 3. Import the entry file (triggers config.parse())\n * 4. Capture and return the accessed env var names\n *\n * This approach provides process isolation - each app is sniffed in its own\n * subprocess, preventing module cache pollution.\n *\n * @param entryPath - Relative path to the entry file (e.g., './src/index.ts')\n * @param appPath - The app's path relative to workspace (e.g., 'apps/auth')\n * @param workspacePath - Absolute path to workspace root\n * @returns EntrySniffResult with env vars and optional error\n */\nasync function sniffEntryFile(\n\tentryPath: string,\n\tappPath: string,\n\tworkspacePath: string,\n): Promise<EntrySniffResult> {\n\tconst fullEntryPath = resolve(workspacePath, appPath, entryPath);\n\tconst loaderPath = resolveSnifferFile('sniffer-loader');\n\tconst workerPath = resolveSnifferFile('sniffer-worker');\n\n\treturn new Promise((resolvePromise) => {\n\t\tconst child = spawn(\n\t\t\t'node',\n\t\t\t['--import', loaderPath, workerPath, fullEntryPath],\n\t\t\t{\n\t\t\t\tcwd: resolve(workspacePath, appPath),\n\t\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t\t// Ensure tsx is available for TypeScript entry files\n\t\t\t\t\tNODE_OPTIONS: '--import=tsx',\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tlet stdout = '';\n\t\tlet stderr = '';\n\n\t\tchild.stdout.on('data', (data) => {\n\t\t\tstdout += data.toString();\n\t\t});\n\n\t\tchild.stderr.on('data', (data) => {\n\t\t\tstderr += data.toString();\n\t\t});\n\n\t\tchild.on('close', (code) => {\n\t\t\t// Try to parse the JSON output from the worker\n\t\t\ttry {\n\t\t\t\t// Find the last JSON object in stdout (worker may emit other output)\n\t\t\t\tconst jsonMatch = stdout.match(/\\{[^{}]*\"envVars\"[^{}]*\\}[^{]*$/);\n\t\t\t\tif (jsonMatch) {\n\t\t\t\t\tconst result = JSON.parse(jsonMatch[0]);\n\t\t\t\t\tresolvePromise({\n\t\t\t\t\t\tenvVars: result.envVars || [],\n\t\t\t\t\t\toptionalEnvVars: result.optionalEnvVars || [],\n\t\t\t\t\t\terror: result.error ? new Error(result.error) : undefined,\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// JSON parse failed\n\t\t\t}\n\n\t\t\t// If we couldn't parse the output, return empty with error info\n\t\t\tresolvePromise({\n\t\t\t\tenvVars: [],\n\t\t\t\toptionalEnvVars: [],\n\t\t\t\terror: new Error(\n\t\t\t\t\t`Failed to sniff entry file (exit code ${code}): ${stderr || stdout || 'No output'}`,\n\t\t\t\t),\n\t\t\t});\n\t\t});\n\n\t\tchild.on('error', (err) => {\n\t\t\tresolvePromise({\n\t\t\t\tenvVars: [],\n\t\t\t\toptionalEnvVars: [],\n\t\t\t\terror: err,\n\t\t\t});\n\t\t});\n\t});\n}\n\n/**\n * Sniff route files by loading constructs and calling getEnvironment().\n *\n * Route-based apps have endpoints, functions, crons, and subscribers that\n * use services. Each service's register() method accesses environment variables.\n *\n * This runs in a subprocess with tsx loader to properly handle TypeScript\n * compilation and path alias resolution (e.g., `src/...` imports).\n *\n * @param routes - Glob pattern(s) for route files\n * @param appPath - The app's path relative to workspace (e.g., 'apps/api')\n * @param workspacePath - Absolute path to workspace root\n * @returns EntrySniffResult with env vars and optional error\n */\nasync function sniffRouteFiles(\n\troutes: string | string[],\n\tappPath: string,\n\tworkspacePath: string,\n): Promise<EntrySniffResult> {\n\tconst fullAppPath = resolve(workspacePath, appPath);\n\tconst workerPath = resolveSnifferFile('sniffer-routes-worker');\n\tconst tsxPath = resolveTsxPath();\n\n\t// Convert array of patterns to first pattern (worker handles glob internally)\n\tconst routesArray = Array.isArray(routes) ? routes : [routes];\n\tconst pattern = routesArray[0];\n\tif (!pattern) {\n\t\treturn {\n\t\t\tenvVars: [],\n\t\t\toptionalEnvVars: [],\n\t\t\terror: new Error('No route patterns provided'),\n\t\t};\n\t}\n\n\treturn new Promise((resolvePromise) => {\n\t\tconst child = spawn(\n\t\t\t'node',\n\t\t\t['--import', tsxPath, workerPath, fullAppPath, pattern],\n\t\t\t{\n\t\t\t\tcwd: fullAppPath,\n\t\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tlet stdout = '';\n\t\tlet stderr = '';\n\n\t\tchild.stdout.on('data', (data) => {\n\t\t\tstdout += data.toString();\n\t\t});\n\n\t\tchild.stderr.on('data', (data) => {\n\t\t\tstderr += data.toString();\n\t\t});\n\n\t\tchild.on('close', (code) => {\n\t\t\t// Log any stderr output (import errors, etc.)\n\t\t\tif (stderr) {\n\t\t\t\tstderr\n\t\t\t\t\t.split('\\n')\n\t\t\t\t\t.filter((line) => line.trim())\n\t\t\t\t\t.forEach((line) => console.warn(line));\n\t\t\t}\n\n\t\t\t// Try to parse the JSON output from the worker\n\t\t\ttry {\n\t\t\t\t// Find the last JSON object in stdout (worker may emit other output)\n\t\t\t\tconst jsonMatch = stdout.match(/\\{[^{}]*\"envVars\"[^{}]*\\}[^{]*$/);\n\t\t\t\tif (jsonMatch) {\n\t\t\t\t\tconst result = JSON.parse(jsonMatch[0]);\n\t\t\t\t\tresolvePromise({\n\t\t\t\t\t\tenvVars: result.envVars || [],\n\t\t\t\t\t\toptionalEnvVars: result.optionalEnvVars || [],\n\t\t\t\t\t\terror: result.error ? new Error(result.error) : undefined,\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// JSON parse failed\n\t\t\t}\n\n\t\t\t// If we couldn't parse the output, return empty with error info\n\t\t\tresolvePromise({\n\t\t\t\tenvVars: [],\n\t\t\t\toptionalEnvVars: [],\n\t\t\t\terror: new Error(\n\t\t\t\t\t`Failed to sniff route files (exit code ${code}): ${stderr || stdout || 'No output'}`,\n\t\t\t\t),\n\t\t\t});\n\t\t});\n\n\t\tchild.on('error', (err) => {\n\t\t\tresolvePromise({\n\t\t\t\tenvVars: [],\n\t\t\t\toptionalEnvVars: [],\n\t\t\t\terror: err,\n\t\t\t});\n\t\t});\n\t});\n}\n\n/**\n * Run the SnifferEnvironmentParser on an envParser module to detect\n * which environment variables it accesses.\n *\n * This function handles \"fire and forget\" async operations by using\n * the shared sniffWithFireAndForget utility from @geekmidas/envkit.\n *\n * @param envParserPath - The envParser config (e.g., './src/config/env#envParser')\n * @param appPath - The app's path relative to workspace\n * @param workspacePath - Absolute path to workspace root\n * @returns SniffResult with env vars and any errors encountered\n */\nasync function sniffEnvParser(\n\tenvParserPath: string,\n\tappPath: string,\n\tworkspacePath: string,\n): Promise<SniffResult> {\n\t// Parse the envParser path: './src/config/env#envParser' or './src/config/env'\n\tconst [modulePath, exportName = 'default'] = envParserPath.split('#');\n\tif (!modulePath) {\n\t\treturn { envVars: [], optionalEnvVars: [], unhandledRejections: [] };\n\t}\n\n\t// Resolve the full path to the module\n\tconst fullPath = resolve(workspacePath, appPath, modulePath);\n\n\t// Dynamically import the sniffer utilities\n\tlet SnifferEnvironmentParser: any;\n\tlet sniffWithFireAndForget: any;\n\ttry {\n\t\tconst envkitModule = await import('@geekmidas/envkit/sniffer');\n\t\tSnifferEnvironmentParser = envkitModule.SnifferEnvironmentParser;\n\t\tsniffWithFireAndForget = envkitModule.sniffWithFireAndForget;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.warn(\n\t\t\t`[sniffer] Failed to import SnifferEnvironmentParser: ${message}`,\n\t\t);\n\t\treturn { envVars: [], optionalEnvVars: [], unhandledRejections: [] };\n\t}\n\n\tconst sniffer = new SnifferEnvironmentParser();\n\n\treturn sniffWithFireAndForget(sniffer, async () => {\n\t\t// Import the envParser module\n\t\tconst moduleUrl = pathToFileURL(fullPath).href;\n\t\tconst module = await import(moduleUrl);\n\n\t\t// Get the envParser function\n\t\tconst envParser = module[exportName];\n\t\tif (typeof envParser !== 'function') {\n\t\t\tconsole.warn(\n\t\t\t\t`[sniffer] Export \"${exportName}\" from \"${modulePath}\" is not a function`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// The envParser function typically creates and configures an EnvironmentParser.\n\t\t// We pass our sniffer which implements the same interface.\n\t\tconst result = envParser(sniffer);\n\n\t\t// If the result is a ConfigParser, call parse() to trigger env var access\n\t\tif (result && typeof result.parse === 'function') {\n\t\t\ttry {\n\t\t\t\tresult.parse();\n\t\t\t} catch {\n\t\t\t\t// Parsing may fail due to mock values, that's expected\n\t\t\t}\n\t\t}\n\t});\n}\n\n/**\n * Sniff environment requirements for multiple apps.\n *\n * @param apps - Map of app name to app config\n * @param workspacePath - Absolute path to workspace root\n * @param options - Optional configuration for sniffing behavior\n * @returns Map of app name to sniffed environment\n */\nexport async function sniffAllApps(\n\tapps: Record<string, NormalizedAppConfig>,\n\tworkspacePath: string,\n\toptions: SniffAppOptions = {},\n): Promise<Map<string, SniffedEnvironment>> {\n\tconst results = new Map<string, SniffedEnvironment>();\n\n\tfor (const [appName, app] of Object.entries(apps)) {\n\t\tconst sniffed = await sniffAppEnvironment(\n\t\t\tapp,\n\t\t\tappName,\n\t\t\tworkspacePath,\n\t\t\toptions,\n\t\t);\n\t\tresults.set(appName, sniffed);\n\t}\n\n\treturn results;\n}\n\n// Export for testing\nexport {\n\tsniffEnvParser as _sniffEnvParser,\n\tsniffEntryFile as _sniffEntryFile,\n\tsniffRouteFiles as _sniffRouteFiles,\n};\n","/**\n * Deploy Module\n *\n * Handles deployment of GKM workspaces to various providers (Docker, Dokploy).\n *\n * ## Per-App Database Credentials\n *\n * When deploying to Dokploy with Postgres, this module creates per-app database\n * users with isolated schemas. This follows the same pattern as local dev mode\n * (docker/postgres/init.sh).\n *\n * ### How It Works\n *\n * 1. **Provisioning**: Creates Postgres service with master credentials\n * 2. **User Creation**: For each backend app that needs DATABASE_URL:\n * - Generates a unique password (stored in deploy state)\n * - Creates a database user with that password\n * - Assigns schema permissions based on app name\n * 3. **Schema Assignment**:\n * - `api` app: Uses `public` schema (shared tables)\n * - Other apps (e.g., `auth`): Get their own schema with `search_path` set\n * 4. **Environment Injection**: Each app receives its own DATABASE_URL\n *\n * ### Security\n *\n * - External Postgres port is enabled only during user creation, then disabled\n * - Each app can only access its own schema\n * - Credentials are stored in `.gkm/deploy-{stage}.json` (gitignored)\n * - Subsequent deploys reuse existing credentials from state\n *\n * ### Example Flow\n *\n * ```\n * gkm deploy --stage production\n * ├─ Create Postgres (user: postgres, db: myproject)\n * ├─ Enable external port temporarily\n * ├─ Create user \"api\" → public schema\n * ├─ Create user \"auth\" → auth schema (search_path=auth)\n * ├─ Disable external port\n * ├─ Deploy \"api\" with DATABASE_URL=postgresql://api:xxx@postgres:5432/myproject\n * └─ Deploy \"auth\" with DATABASE_URL=postgresql://auth:yyy@postgres:5432/myproject\n * ```\n *\n * @module deploy\n */\n\nimport { createHash } from 'node:crypto';\nimport { stdin as input, stdout as output } from 'node:process';\nimport * as readline from 'node:readline/promises';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { Client as PgClient } from 'pg';\nimport {\n\tgetDokployCredentials,\n\tgetDokployRegistryId,\n\tstoreDokployCredentials,\n\tvalidateDokployToken,\n} from '../auth';\nimport { storeDokployRegistryId } from '../auth/credentials';\nimport { type GkmConfig, loadWorkspaceConfig } from '../config';\nimport { discover } from '../reconcile/discover.js';\nimport type { SqlClient, Statement } from '../reconcile/provision.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { readStageSecrets } from '../secrets/storage.js';\nimport { derivedApps } from '../workspace/derive.js';\nimport {\n\tgetAppBuildOrder,\n\tgetDeployTargetError,\n\tgetPublicEnvPrefix,\n\tisDeployTargetSupported,\n} from '../workspace/index.js';\nimport type {\n\tNormalizedAppConfig,\n\tNormalizedWorkspace,\n} from '../workspace/types.js';\nimport { applyDeclared, provisionDeclared } from './declared';\nimport { orchestrateDns, verifyDnsRecords } from './dns/index.js';\nimport { applicationName, deployDocker } from './docker';\nimport { DokployApi, type DokployApplication } from './dokploy-api';\nimport { isMainFrontendApp, resolveHost } from './domain.js';\nimport {\n\ttype EnvResolverContext,\n\tformatMissingVarsError,\n\tvalidateEnvVars,\n} from './env-resolver.js';\nimport type { DokployCluster } from './fromManifest';\nimport { createStateProvider } from './StateProvider.js';\nimport { generateSecretsReport, prepareSecretsForAllApps } from './secrets.js';\nimport { sniffAllApps } from './sniffer.js';\nimport {\n\tcreateEmptyState,\n\tgetApplicationId,\n\tgetBackupState,\n\tsetApplicationId,\n\tsetBackupState,\n\tsetPostgresBackupId,\n} from './state.js';\nimport type {\n\tAppDeployResult,\n\tDeployOptions,\n\tDeployProvider,\n\tDeployResult,\n\tDockerDeployConfig,\n\tDokployDeployConfig,\n\tWorkspaceDeployResult,\n} from './types';\n\nconst logger = console;\n\n/**\n * Prompt for input\n */\nasync function prompt(message: string, hidden = false): Promise<string> {\n\tif (!process.stdin.isTTY) {\n\t\tthrow new Error('Interactive input required. Please configure manually.');\n\t}\n\n\tif (hidden) {\n\t\tprocess.stdout.write(message);\n\t\treturn new Promise((resolve) => {\n\t\t\tlet value = '';\n\t\t\tconst onData = (char: Buffer) => {\n\t\t\t\tconst c = char.toString();\n\t\t\t\tif (c === '\\n' || c === '\\r') {\n\t\t\t\t\tprocess.stdin.setRawMode(false);\n\t\t\t\t\tprocess.stdin.pause();\n\t\t\t\t\tprocess.stdin.removeListener('data', onData);\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\tresolve(value);\n\t\t\t\t} else if (c === '\\u0003') {\n\t\t\t\t\tprocess.stdin.setRawMode(false);\n\t\t\t\t\tprocess.stdin.pause();\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t} else if (c === '\\u007F' || c === '\\b') {\n\t\t\t\t\tif (value.length > 0) value = value.slice(0, -1);\n\t\t\t\t} else {\n\t\t\t\t\tvalue += c;\n\t\t\t\t}\n\t\t\t};\n\t\t\tprocess.stdin.setRawMode(true);\n\t\t\tprocess.stdin.resume();\n\t\t\tprocess.stdin.on('data', onData);\n\t\t});\n\t}\n\n\tconst rl = readline.createInterface({ input, output });\n\ttry {\n\t\treturn await rl.question(message);\n\t} finally {\n\t\trl.close();\n\t}\n}\n\n/**\n * Docker compose services that can be provisioned\n */\ninterface DockerComposeServices {\n\tpostgres?: boolean;\n\tredis?: boolean;\n\trabbitmq?: boolean;\n}\n\n/**\n * Service URLs including both connection URLs and individual parameters\n */\ninterface ServiceUrls {\n\tDATABASE_URL?: string;\n\tDATABASE_HOST?: string;\n\tDATABASE_PORT?: string;\n\tDATABASE_NAME?: string;\n\tDATABASE_USER?: string;\n\tDATABASE_PASSWORD?: string;\n\tREDIS_URL?: string;\n\tREDIS_HOST?: string;\n\tREDIS_PORT?: string;\n\tREDIS_PASSWORD?: string;\n}\n\n/**\n * Result of Dokploy setup including provisioned service URLs\n */\ninterface DokploySetupResult {\n\tconfig: DokployDeployConfig;\n\tserviceUrls?: ServiceUrls;\n\t/**\n\t * The Dokploy environment the project's resources live in.\n\t *\n\t * Returned because the declared half needs it and had no way to ask: it was\n\t * computed here, used here, and dropped.\n\t */\n\tenvironmentId: string;\n}\n\n/**\n * Result from provisioning services\n */\nexport interface ProvisionServicesResult {\n\tserviceUrls: ServiceUrls;\n\tserviceIds: {\n\t\tpostgresId?: string;\n\t\tredisId?: string;\n\t};\n}\n\n/**\n * Wait for Postgres to be ready to accept connections.\n *\n * Polls the Postgres server until it accepts a connection or max retries reached.\n * Used after enabling the external port to ensure the database is accessible\n * before creating users.\n *\n * @param host - The Postgres server hostname\n * @param port - The external port (typically 5432)\n * @param user - Master database user (postgres)\n * @param password - Master database password\n * @param database - Database name to connect to\n * @param maxRetries - Maximum number of connection attempts (default: 30)\n * @param retryIntervalMs - Milliseconds between retries (default: 2000)\n * @throws Error if Postgres is not ready after maxRetries\n */\nasync function waitForPostgres(\n\thost: string,\n\tport: number,\n\tuser: string,\n\tpassword: string,\n\tdatabase: string,\n\tmaxRetries = 30,\n\tretryIntervalMs = 2000,\n): Promise<void> {\n\tfor (let i = 0; i < maxRetries; i++) {\n\t\ttry {\n\t\t\t// Bounded, because the interesting failure is not a refused connection\n\t\t\t// but a dropped one: while the container restarts around a port change,\n\t\t\t// the host drops the SYN rather than answering it, and an unbounded\n\t\t\t// connect waits out the OS timeout — a minute and a quarter — and then\n\t\t\t// reports ETIMEDOUT from inside a retry loop that never got to retry.\n\t\t\tconst client = new PgClient({\n\t\t\t\thost,\n\t\t\t\tport,\n\t\t\t\tuser,\n\t\t\t\tpassword,\n\t\t\t\tdatabase,\n\t\t\t\tconnectionTimeoutMillis: 5_000,\n\t\t\t});\n\t\t\t// A socket that dies *after* connecting emits on the client, and an\n\t\t\t// unhandled 'error' event takes the whole process down — which is how\n\t\t\t// a deploy went from \"retrying\" to a Node stack trace mid-run. The\n\t\t\t// retry loop below is the thing that decides what to do about it.\n\t\t\tclient.on('error', () => {});\n\t\t\tawait client.connect();\n\t\t\tawait client.end();\n\t\t\treturn;\n\t\t} catch {\n\t\t\tif (i < maxRetries - 1) {\n\t\t\t\tlogger.log(` Waiting for Postgres... (${i + 1}/${maxRetries})`);\n\t\t\t\tawait new Promise((r) => setTimeout(r, retryIntervalMs));\n\t\t\t}\n\t\t}\n\t}\n\tthrow new Error(`Postgres not ready after ${maxRetries} retries`);\n}\n\n/**\n * Run the manifest's DDL against a Dokploy Postgres.\n *\n * The cluster is only reachable from outside while an external port is\n * published, so this opens one, applies, and leaves it as it found it. The\n * alternative — running DDL from inside the network — needs a container to run\n * it in, which is what `DatabaseBootstrap` is on AWS and what a Dokploy target\n * has no equivalent for yet.\n *\n * The applier is the local target's, so every statement asks whether it is\n * needed first: a redeploy is free, and a half-applied run recovers by being\n * run again.\n */\n/**\n * A high port for one service, the same one every time.\n *\n * Derived from the service name rather than random so two deploys of the same\n * database agree and two different databases do not collide — and in the\n * ephemeral range, above anything a server is likely to have bound\n * deliberately.\n */\nfunction derivedPort(appName: string): number {\n\tconst digest = createHash('sha256').update(appName).digest();\n\n\treturn 49152 + (((digest[0]! << 8) | digest[1]!) % 16000);\n}\n\nasync function applyDeclaredStatements(\n\tapi: DokployApi,\n\tpostgres: DokployCluster,\n\tserverHostname: string,\n\tstatements: readonly Statement[],\n): Promise<number> {\n\t// Reuse whatever is already published, and otherwise pick a high port that\n\t// nothing on the host is likely to hold.\n\t//\n\t// 5432 was hardcoded, which fails the moment a server runs a second Postgres\n\t// — and this one runs fourteen. The error is `Port 5432 is already in use`,\n\t// from Docker rather than from anything the deploy could anticipate.\n\tconst existing = await api\n\t\t.getPostgres(postgres.postgresId)\n\t\t.then((current) => current.externalPort)\n\t\t.catch(() => null);\n\n\t// 5432 first, then a derived port — and the order matters more than it looks.\n\t//\n\t// A high port is the tidier choice on a host running several clusters, and it\n\t// is also the one a firewall almost certainly drops: a VPS typically permits\n\t// 22, 80, 443 and whatever was opened deliberately. Publishing 55337 here\n\t// produced thirty polite retries against a port nothing outside could ever\n\t// reach, while 5432 had worked minutes earlier.\n\t//\n\t// So: the conventional port, which is the one an operator has plausibly\n\t// allowed, and a derived fallback only when something already holds it.\n\t// Already published? Use it. Re-saving the port a container already holds is\n\t// rejected by Docker as a conflict with *itself*, which reads like the port\n\t// being taken by something else.\n\tlet externalPort = existing ?? undefined;\n\tconst opened = externalPort === undefined;\n\n\tif (externalPort === undefined) {\n\t\t// Ask the server what it has bound rather than guessing and retrying. A\n\t\t// port free from here can be held by a service in another project, and\n\t\t// the failure names a container the caller has never heard of.\n\t\tconst taken = await api.publishedPorts().catch(() => new Set<number>());\n\n\t\t// 5432 first when it is free: it is conventional, and therefore the port\n\t\t// an operator has plausibly allowed through the firewall. Being *free* and\n\t\t// being *reachable* are different questions and only the first has an API.\n\t\tconst candidates = [5432, derivedPort(postgres.appName)].filter(\n\t\t\t(port) => !taken.has(port),\n\t\t);\n\n\t\tfor (const candidate of candidates) {\n\t\t\ttry {\n\t\t\t\tawait api.savePostgresExternalPort(postgres.postgresId, candidate);\n\t\t\t\texternalPort = candidate;\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tif (!message.includes('already in use')) throw error;\n\n\t\t\t\tlogger.log(` Port ${candidate} is taken; trying another...`);\n\t\t\t}\n\t\t}\n\n\t\tif (externalPort === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not publish a port for ${postgres.appName}. ` +\n\t\t\t\t\t`In use on this server: ${[...taken].sort((a, b) => a - b).join(', ') || 'none reported'}. ` +\n\t\t\t\t\t`The role DDL needs to reach the cluster from here.`,\n\t\t\t);\n\t\t}\n\n\t\tlogger.log(` Publishing ${postgres.appName} on ${externalPort}...`);\n\t}\n\n\tawait api.deployPostgres(postgres.postgresId);\n\tawait waitForPostgres(\n\t\tserverHostname,\n\t\texternalPort,\n\t\tpostgres.databaseUser,\n\t\tpostgres.databasePassword,\n\t\tpostgres.databaseName,\n\t);\n\n\t// As the cluster master, which is the only credential that exists before any\n\t// role does — the same reason the AWS bootstrap connects as one.\n\tconst client: SqlClient = {\n\t\tasync query(database, sql, values) {\n\t\t\tconst connection = new PgClient({\n\t\t\t\thost: serverHostname,\n\t\t\t\tport: externalPort,\n\t\t\t\tuser: postgres.databaseUser,\n\t\t\t\tpassword: postgres.databasePassword,\n\t\t\t\tdatabase: database ?? postgres.databaseName,\n\t\t\t\tconnectionTimeoutMillis: 15_000,\n\t\t\t});\n\t\t\t// See `waitForPostgres`: the port is only reachable while published,\n\t\t\t// and the container restarts around that change, so the socket can\n\t\t\t// drop mid-statement. Swallowed here and surfaced by the awaited\n\t\t\t// call, which the retry can actually act on.\n\t\t\tconnection.on('error', () => {});\n\n\t\t\tawait connection.connect();\n\t\t\ttry {\n\t\t\t\tconst result = await connection.query(sql, values as never[]);\n\t\t\t\treturn result.rows;\n\t\t\t} finally {\n\t\t\t\tawait connection.end();\n\t\t\t}\n\t\t},\n\t};\n\n\t// Whatever happens below, the database does not stay exposed. Publishing a\n\t// port to run DDL is a means; leaving it published is a database on the\n\t// public internet, which is what the path this replaces did on every deploy.\n\tconst unpublish = async () => {\n\t\t// Only what this call opened. A port somebody published deliberately is\n\t\t// theirs, and closing it would be a deploy quietly changing how their\n\t\t// database is reached.\n\t\tif (!opened) return;\n\n\t\tawait api\n\t\t\t.savePostgresExternalPort(postgres.postgresId, null)\n\t\t\t.then(() => api.deployPostgres(postgres.postgresId))\n\t\t\t.catch(() => {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ⚠ Could not close external port ${externalPort} on ${postgres.appName} — close it in Dokploy.`,\n\t\t\t\t);\n\t\t\t});\n\t};\n\n\t// Attempt, then wait for the cluster and attempt again.\n\t//\n\t// Publishing an external port *restarts* the container, and a TCP connect\n\t// succeeds against an instance that is still settling — so a pass can die\n\t// partway with `terminating connection due to administrator command`, or\n\t// with the connection dropped outright while the port rule is rewritten.\n\t// Retrying is safe because the applier is convergent: every statement asks\n\t// whether it is needed, so a later pass reapplies nothing an earlier one\n\t// managed. Three passes rather than two because the first restart and the\n\t// settling after it are separate events, and hitting both in one run is\n\t// ordinary rather than exceptional.\n\tlet lastError: unknown;\n\n\ttry {\n\t\tfor (let attempt = 1; attempt <= 3; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn await applyDeclared(client, statements);\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error;\n\t\t\t\tif (attempt === 3) break;\n\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ⏳ Cluster still settling (${message}); retrying (${attempt}/2)...`,\n\t\t\t\t);\n\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10_000));\n\t\t\t\tawait waitForPostgres(\n\t\t\t\t\tserverHostname,\n\t\t\t\t\texternalPort,\n\t\t\t\t\tpostgres.databaseUser,\n\t\t\t\t\tpostgres.databasePassword,\n\t\t\t\t\tpostgres.databaseName,\n\t\t\t\t).catch(() => {});\n\t\t\t}\n\t\t}\n\n\t\tthrow lastError;\n\t} finally {\n\t\t// Whatever happened, the database does not stay exposed. A port opened to\n\t\t// run DDL and left open is a database on the public internet — which is\n\t\t// what a failure before this point used to leave behind, and how 64614\n\t\t// came to be stuck across three attempts.\n\t\tawait unpublish();\n\t}\n}\n\n/**\n * Get the server hostname from the Dokploy endpoint URL\n */\nfunction getServerHostname(endpoint: string): string {\n\tconst url = new URL(endpoint);\n\treturn url.hostname;\n}\n\n/**\n * Ensure Dokploy is fully configured, recovering/creating resources as needed\n */\n/**\n * The configured registry, among those the server already has.\n *\n * `docker.registry` is a host and optionally a namespace —\n * `ghcr.io/technanimals` — while Dokploy stores the host alone. Comparing the\n * first segment is what makes the two the same fact rather than two spellings\n * of it.\n */\nfunction matchingRegistry(\n\tregistries: readonly {\n\t\tregistryId: string;\n\t\tregistryName: string;\n\t\tregistryUrl: string;\n\t}[],\n\tconfigured: string | undefined,\n):\n\t| { registryId: string; registryName: string; registryUrl: string }\n\t| undefined {\n\tif (!configured) return undefined;\n\n\tconst host = configured.replace(/^https?:\\/\\//, '').split('/')[0];\n\n\treturn registries.find(\n\t\t(registry) =>\n\t\t\tregistry.registryUrl.replace(/^https?:\\/\\//, '').split('/')[0] === host,\n\t);\n}\n\nasync function _ensureDokploySetup(\n\tconfig: GkmConfig,\n\tdockerConfig: DockerDeployConfig,\n\tstage: string,\n\t_services?: DockerComposeServices,\n): Promise<DokploySetupResult> {\n\tlogger.log('\\n🔧 Checking Dokploy setup...');\n\n\t// Step 1: Ensure we have Dokploy credentials\n\tlet creds = await getDokployCredentials();\n\n\tif (!creds) {\n\t\tlogger.log(\"\\n📋 Dokploy credentials not found. Let's set them up.\");\n\t\tconst endpoint = await prompt(\n\t\t\t'Dokploy URL (e.g., https://dokploy.example.com): ',\n\t\t);\n\t\tconst normalizedEndpoint = endpoint.replace(/\\/$/, '');\n\n\t\ttry {\n\t\t\tnew URL(normalizedEndpoint);\n\t\t} catch {\n\t\t\tthrow new Error('Invalid URL format');\n\t\t}\n\n\t\tlogger.log(\n\t\t\t`\\nGenerate a token at: ${normalizedEndpoint}/settings/profile\\n`,\n\t\t);\n\t\tconst token = await prompt('API Token: ', true);\n\n\t\tlogger.log('\\nValidating credentials...');\n\t\tconst isValid = await validateDokployToken(normalizedEndpoint, token);\n\t\tif (!isValid) {\n\t\t\tthrow new Error('Invalid credentials. Please check your token.');\n\t\t}\n\n\t\tawait storeDokployCredentials(token, normalizedEndpoint);\n\t\tcreds = { token, endpoint: normalizedEndpoint };\n\t\tlogger.log('✓ Credentials saved');\n\t}\n\n\tconst api = new DokployApi({ baseUrl: creds.endpoint, token: creds.token });\n\n\t// Step 2: Check if we have config in gkm.config.ts\n\tconst existingConfig = config.providers?.dokploy;\n\tif (\n\t\texistingConfig &&\n\t\ttypeof existingConfig !== 'boolean' &&\n\t\texistingConfig.applicationId &&\n\t\texistingConfig.projectId\n\t) {\n\t\tlogger.log('✓ Dokploy config found in gkm.config.ts');\n\n\t\t// Verify the application still exists\n\t\ttry {\n\t\t\tconst projectDetails = await api.getProject(existingConfig.projectId);\n\t\t\tlogger.log('✓ Project verified');\n\n\t\t\t// Get registry ID from config first, then from local storage\n\t\t\tconst storedRegistryId =\n\t\t\t\texistingConfig.registryId ?? (await getDokployRegistryId());\n\n\t\t\t// Get environment ID for service provisioning (match by stage name)\n\t\t\tconst environments = projectDetails.environments ?? [];\n\t\t\tlet environment = environments.find(\n\t\t\t\t(e) => e.name.toLowerCase() === stage.toLowerCase(),\n\t\t\t);\n\n\t\t\t// Create environment if it doesn't exist for this stage\n\t\t\tif (!environment) {\n\t\t\t\tlogger.log(` Creating \"${stage}\" environment...`);\n\t\t\t\tenvironment = await api.createEnvironment(\n\t\t\t\t\texistingConfig.projectId,\n\t\t\t\t\tstage,\n\t\t\t\t);\n\t\t\t\tlogger.log(` ✓ Created environment: ${environment.environmentId}`);\n\t\t\t}\n\n\t\t\tconst environmentId = environment.environmentId;\n\n\t\t\treturn {\n\t\t\t\tconfig: {\n\t\t\t\t\tendpoint: existingConfig.endpoint,\n\t\t\t\t\tprojectId: existingConfig.projectId,\n\t\t\t\t\tapplicationId: existingConfig.applicationId,\n\t\t\t\t\tregistry: existingConfig.registry,\n\t\t\t\t\tregistryId: storedRegistryId ?? undefined,\n\t\t\t\t},\n\t\t\t\tenvironmentId,\n\t\t\t};\n\t\t} catch {\n\t\t\tlogger.log('⚠ Project not found, will recover...');\n\t\t}\n\t}\n\n\t// Step 3: Find or create project\n\tlogger.log('\\n📁 Looking for project...');\n\tconst projectName = dockerConfig.projectName!;\n\tconst projects = await api.listProjects();\n\tlet project = projects.find(\n\t\t(p) => p.name.toLowerCase() === projectName.toLowerCase(),\n\t);\n\n\tlet environmentId: string;\n\n\tif (project) {\n\t\tlogger.log(\n\t\t\t` Found existing project: ${project.name} (${project.projectId})`,\n\t\t);\n\n\t\t// Step 4: Get or create environment for existing project (match by stage)\n\t\tconst projectDetails = await api.getProject(project.projectId);\n\t\tconst environments = projectDetails.environments ?? [];\n\t\tconst matchingEnv = environments.find(\n\t\t\t(e) => e.name.toLowerCase() === stage.toLowerCase(),\n\t\t);\n\t\tif (matchingEnv) {\n\t\t\tenvironmentId = matchingEnv.environmentId;\n\t\t\tlogger.log(` Using environment: ${matchingEnv.name}`);\n\t\t} else {\n\t\t\tlogger.log(` Creating \"${stage}\" environment...`);\n\t\t\tconst env = await api.createEnvironment(project.projectId, stage);\n\t\t\tenvironmentId = env.environmentId;\n\t\t\tlogger.log(` ✓ Created environment: ${stage}`);\n\t\t}\n\t} else {\n\t\tlogger.log(` Creating project: ${projectName}`);\n\t\tconst result = await api.createProject(projectName);\n\t\tproject = result.project;\n\t\t// Rename the default environment to match stage if different\n\t\tif (result.environment.name.toLowerCase() !== stage.toLowerCase()) {\n\t\t\tlogger.log(` Creating \"${stage}\" environment...`);\n\t\t\tconst env = await api.createEnvironment(project.projectId, stage);\n\t\t\tenvironmentId = env.environmentId;\n\t\t} else {\n\t\t\tenvironmentId = result.environment.environmentId;\n\t\t}\n\t\tlogger.log(` ✓ Created project: ${project.projectId}`);\n\t\tlogger.log(` ✓ Using environment: ${stage}`);\n\t}\n\n\t// Step 5: Find or create application\n\tlogger.log('\\n📦 Looking for application...');\n\n\t// Scoped by `resolveDockerConfig` from the gkm config's `name`, through the\n\t// same `scopedName` the SST target builds every physical name with.\n\t//\n\t// It used to be the cwd package.json name, unscoped — so deploying `staging`\n\t// into the same project matched the production application by name and\n\t// redeployed it.\n\t//\n\t// Still one application for two surfaces: `Api` and `Auth` are both\n\t// `rest-api` declarations served by one container, so neither names it. That\n\t// needs the manifest read before applications are created — see §6b.\n\tconst appName = dockerConfig.appName!;\n\n\tlet applicationId: string;\n\n\t// Look it up by name, then create.\n\t//\n\t// This used to reuse an application only when its id was written into\n\t// `gkm.config.ts`, and create one unconditionally otherwise — so a project\n\t// whose config could not be rewritten got a *new* application on every\n\t// single deploy, silently, forever. The id in a source file was never the\n\t// right place for it either: the server already knows what it has, and the\n\t// name is what identifies it.\n\tif (\n\t\texistingConfig &&\n\t\ttypeof existingConfig !== 'boolean' &&\n\t\texistingConfig.applicationId\n\t) {\n\t\tapplicationId = existingConfig.applicationId;\n\t\tlogger.log(` Using application from config: ${applicationId}`);\n\t} else {\n\t\tconst { application, created } = await api.findOrCreateApplication(\n\t\t\tappName,\n\t\t\tproject.projectId,\n\t\t\tenvironmentId,\n\t\t);\n\n\t\tapplicationId = application.applicationId;\n\t\tlogger.log(\n\t\t\tcreated\n\t\t\t\t? ` ✓ Created application: ${applicationId}`\n\t\t\t\t: ` ✓ Found application: ${appName} (${applicationId})`,\n\t\t);\n\t}\n\n\t// Step 6: Ensure registry is set up\n\tlogger.log('\\n🐳 Checking registry...');\n\tlet registryId = await getDokployRegistryId();\n\n\tif (registryId) {\n\t\t// Verify stored registry still exists\n\t\ttry {\n\t\t\tconst registry = await api.getRegistry(registryId);\n\t\t\tlogger.log(` Using registry: ${registry.registryName}`);\n\t\t} catch {\n\t\t\tlogger.log(' ⚠ Stored registry not found, clearing...');\n\t\t\tregistryId = undefined;\n\t\t\tawait storeDokployRegistryId('');\n\t\t}\n\t}\n\n\tif (!registryId) {\n\t\tconst registries = await api.listRegistries();\n\n\t\tif (registries.length === 0) {\n\t\t\t// No registries exist\n\t\t\tif (dockerConfig.registry) {\n\t\t\t\tlogger.log(\" No registries found in Dokploy. Let's create one.\");\n\t\t\t\tlogger.log(` Registry URL: ${dockerConfig.registry}`);\n\n\t\t\t\tconst username = await prompt('Registry username: ');\n\t\t\t\tconst password = await prompt('Registry password/token: ', true);\n\n\t\t\t\tconst registry = await api.createRegistry(\n\t\t\t\t\t'Default Registry',\n\t\t\t\t\tdockerConfig.registry,\n\t\t\t\t\tusername,\n\t\t\t\t\tpassword,\n\t\t\t\t);\n\t\t\t\tregistryId = registry.registryId;\n\t\t\t\tawait storeDokployRegistryId(registryId);\n\t\t\t\tlogger.log(` ✓ Registry created: ${registryId}`);\n\t\t\t} else {\n\t\t\t\tlogger.log(\n\t\t\t\t\t' ⚠ No registry configured. Set docker.registry in gkm.config.ts',\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (matchingRegistry(registries, dockerConfig.registry)) {\n\t\t\t// The config already answered this. `docker.registry` names the host\n\t\t\t// images are pushed to, and a registry on the server with the same\n\t\t\t// host is the one to use — asking which would be asking a question\n\t\t\t// whose answer is written down, and it is what made a deploy\n\t\t\t// impossible without a terminal.\n\t\t\tconst matched = matchingRegistry(registries, dockerConfig.registry);\n\t\t\tif (!matched) throw new Error('unreachable');\n\n\t\t\tregistryId = matched.registryId;\n\t\t\tawait storeDokployRegistryId(registryId);\n\t\t\tlogger.log(\n\t\t\t\t` ✓ ${matched.registryName} (${matched.registryUrl}) — from docker.registry`,\n\t\t\t);\n\t\t} else {\n\t\t\t// Show available registries and let user select or create new\n\t\t\tlogger.log(' Available registries:');\n\t\t\tregistries.forEach((reg, i) => {\n\t\t\t\tlogger.log(` ${i + 1}. ${reg.registryName} (${reg.registryUrl})`);\n\t\t\t});\n\t\t\tif (dockerConfig.registry) {\n\t\t\t\tlogger.log(` ${registries.length + 1}. Create new registry`);\n\t\t\t}\n\n\t\t\tconst maxOption = dockerConfig.registry\n\t\t\t\t? registries.length + 1\n\t\t\t\t: registries.length;\n\t\t\tconst selection = await prompt(` Select registry (1-${maxOption}): `);\n\t\t\tconst index = parseInt(selection, 10) - 1;\n\n\t\t\tif (index >= 0 && index < registries.length) {\n\t\t\t\t// Selected existing registry\n\t\t\t\tregistryId = registries[index]!.registryId;\n\t\t\t\tawait storeDokployRegistryId(registryId);\n\t\t\t\tlogger.log(` ✓ Selected: ${registries[index]!.registryName}`);\n\t\t\t} else if (dockerConfig.registry && index === registries.length) {\n\t\t\t\t// Create new registry\n\t\t\t\tlogger.log(`\\n Creating new registry...`);\n\t\t\t\tlogger.log(` Registry URL: ${dockerConfig.registry}`);\n\n\t\t\t\tconst username = await prompt(' Registry username: ');\n\t\t\t\tconst password = await prompt(' Registry password/token: ', true);\n\n\t\t\t\tconst registry = await api.createRegistry(\n\t\t\t\t\tdockerConfig.registry.replace(/^https?:\\/\\//, ''),\n\t\t\t\t\tdockerConfig.registry,\n\t\t\t\t\tusername,\n\t\t\t\t\tpassword,\n\t\t\t\t);\n\t\t\t\tregistryId = registry.registryId;\n\t\t\t\tawait storeDokployRegistryId(registryId);\n\t\t\t\tlogger.log(` ✓ Registry created: ${registryId}`);\n\t\t\t} else {\n\t\t\t\tlogger.log(' ⚠ Invalid selection, skipping registry setup');\n\t\t\t}\n\t\t}\n\t}\n\n\t// Step 7: Build and save config\n\tconst dokployConfig: DokployDeployConfig = {\n\t\tendpoint: creds.endpoint,\n\t\tprojectId: project.projectId,\n\t\tapplicationId,\n\t\tregistryId: registryId ?? undefined,\n\t};\n\n\t// Update gkm.config.ts\n\t// Deliberately not written back into `gkm.config.ts`.\n\t//\n\t// It used to be, with a regex that matched to the first closing brace — so a\n\t// nested `domains: { … }` orphaned the tail and every key it did not itself\n\t// write was dropped. It corrupted a config once here.\n\t//\n\t// Nothing is lost by not writing: the project, application and registry are\n\t// found by name on the next run, and remembered in the deploy state, which is\n\t// the artefact whose job that is. Generated ids in a source file were only\n\t// ever a cache with no invalidation.\n\tlogger.log(` Project ${project.projectId} · application ${applicationId}`);\n\n\tlogger.log('\\n✅ Dokploy setup complete!');\n\tlogger.log(` Project: ${project.projectId}`);\n\tlogger.log(` Application: ${applicationId}`);\n\tif (registryId) {\n\t\tlogger.log(` Registry: ${registryId}`);\n\t}\n\n\treturn {\n\t\tconfig: dokployConfig,\n\t\tenvironmentId,\n\t};\n}\n\n/**\n * Generate image tag from stage and timestamp\n */\nexport function generateTag(stage: string): string {\n\tconst timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);\n\treturn `${stage}-${timestamp}`;\n}\n\n/**\n * Deploy all apps in a workspace to Dokploy.\n *\n * Two-phase orchestration:\n * - PHASE 1: Deploy backend apps (with encrypted secrets)\n * - PHASE 2: Deploy frontend apps (with public URLs from backends)\n *\n * Security model:\n * - Backend apps get encrypted secrets embedded at build time\n * - Only GKM_MASTER_KEY is injected as Dokploy env var\n * - Frontend apps get public URLs baked in at build time (no secrets)\n *\n * @internal Exported for testing\n */\n/**\n * The things this deploy creates a container for.\n *\n * A deploy unit is a *declaration*, not a config entry — and since that is now\n * true of every app, not just the ones being deployed, this is the workspace's\n * own derivation under the name the deploy phases call it by.\n */\nexport function deployUnits(\n\tmanifest: ConstructManifest,\n\tworkspace: NormalizedWorkspace,\n): Record<string, NormalizedAppConfig> {\n\treturn derivedApps(manifest, workspace);\n}\n\n/**\n * The variables an app cannot start without.\n *\n * The sniffer reports every key it saw in `requiredEnvVars` and says which of\n * them were read through `.optional()` or `.default()` in `optionalEnvVars`.\n * Reading only the first treats a key that is *absent by design* as a missing\n * secret — `AUTH_COOKIE_DOMAIN` is published only when a surface has a domain\n * to widen a cookie to, and one host has nothing to share it with.\n *\n * The same distinction `bundleServer` draws. It is drawn twice because the\n * bundle and the deploy each validate, and the deploy was the half still\n * failing on an answer that was correct.\n */\nfunction requiredOf(\n\trequirements:\n\t\t| { requiredEnvVars: string[]; optionalEnvVars: string[] }\n\t\t| undefined,\n): string[] {\n\tif (!requirements) return [];\n\n\tconst optional = new Set(requirements.optionalEnvVars);\n\n\treturn requirements.requiredEnvVars\n\t\t.map((name) => (name.endsWith('?') ? name.slice(0, -1) : name))\n\t\t.filter((name) => !optional.has(name));\n}\n\nexport async function workspaceDeployCommand(\n\tconfigured: NormalizedWorkspace,\n\toptions: DeployOptions,\n): Promise<WorkspaceDeployResult> {\n\tconst { provider, stage, tag, apps: selectedApps } = options;\n\n\t// What to deploy comes from the manifest.\n\t//\n\t// Discovered here rather than inside `provisionDeclared`, because the list of\n\t// things to build is the *declarations* — one `rest-api` is one server, one\n\t// `site` is one site — and the config only says how to run each. It read the\n\t// config before, which is why a declared site nobody had listed as an app was\n\t// silently never deployed, and why two surfaces in one app had to share a\n\t// container.\n\t//\n\t// A project that declares no surfaces keeps its configured apps, so adopting\n\t// constructs stays something you do a piece at a time.\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(configured),\n\t\tcwd: configured.root,\n\t});\n\tconst units = deployUnits(manifest, configured);\n\tconst workspace: NormalizedWorkspace =\n\t\tObject.keys(units).length > 0 ? { ...configured, apps: units } : configured;\n\n\tif (provider !== 'dokploy') {\n\t\tthrow new Error(\n\t\t\t`Workspace deployment only supports Dokploy. Got: ${provider}`,\n\t\t);\n\t}\n\n\tlogger.log(`\\n🚀 Deploying workspace \"${workspace.name}\" to Dokploy...`);\n\tlogger.log(` Stage: ${stage}`);\n\n\t// Generate tag if not provided\n\tconst imageTag = tag ?? generateTag(stage);\n\tlogger.log(` Tag: ${imageTag}`);\n\n\t// Get apps to deploy in dependency order\n\tconst buildOrder = getAppBuildOrder(workspace);\n\n\t// Filter to selected apps if specified\n\tlet appsToDeployNames = buildOrder;\n\tif (selectedApps && selectedApps.length > 0) {\n\t\t// Validate selected apps exist\n\t\tconst invalidApps = selectedApps.filter((name) => !workspace.apps[name]);\n\t\tif (invalidApps.length > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Unknown apps: ${invalidApps.join(', ')}\\n` +\n\t\t\t\t\t`Available apps: ${Object.keys(workspace.apps).join(', ')}`,\n\t\t\t);\n\t\t}\n\t\t// Keep only selected apps, but maintain dependency order\n\t\tappsToDeployNames = buildOrder.filter((name) =>\n\t\t\tselectedApps.includes(name),\n\t\t);\n\t\tlogger.log(` Deploying apps: ${appsToDeployNames.join(', ')}`);\n\t} else {\n\t\tlogger.log(` Deploying all apps: ${appsToDeployNames.join(', ')}`);\n\t}\n\n\t// Filter apps by deploy target\n\tconst dokployApps = appsToDeployNames.filter((name) => {\n\t\tconst app = workspace.apps[name]!;\n\t\tconst target = app.resolvedDeployTarget;\n\t\tif (!isDeployTargetSupported(target)) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠️ Skipping ${name}: ${getDeployTargetError(target, name)}`,\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t});\n\n\tif (dokployApps.length === 0) {\n\t\tthrow new Error(\n\t\t\t'No apps to deploy. All selected apps have unsupported deploy targets.',\n\t\t);\n\t}\n\n\tappsToDeployNames = dokployApps;\n\n\t// ==================================================================\n\t// PREFLIGHT: Load secrets and sniff environment requirements\n\t// ==================================================================\n\tlogger.log('\\n🔐 Loading secrets and analyzing environment requirements...');\n\n\t// Load secrets for this stage\n\tconst stageSecrets = await readStageSecrets(stage, workspace.root);\n\tif (!stageSecrets) {\n\t\tlogger.log(` ⚠️ No secrets found for stage \"${stage}\"`);\n\t\tlogger.log(\n\t\t\t` Run \"gkm secrets:init --stage ${stage}\" to create secrets`,\n\t\t);\n\t}\n\n\t// Sniff environment variables for all apps\n\tconst sniffedApps = await sniffAllApps(workspace.apps, workspace.root);\n\n\t// Prepare encrypted secrets for backend apps\n\tconst encryptedSecrets = stageSecrets\n\t\t? prepareSecretsForAllApps(stageSecrets, sniffedApps)\n\t\t: new Map();\n\n\t// Report on secrets preparation\n\tif (stageSecrets) {\n\t\tconst report = generateSecretsReport(encryptedSecrets, sniffedApps);\n\t\tif (report.appsWithSecrets.length > 0) {\n\t\t\tlogger.log(\n\t\t\t\t` ✓ Encrypted secrets for: ${report.appsWithSecrets.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (report.appsWithMissingSecrets.length > 0) {\n\t\t\tfor (const { appName, missing } of report.appsWithMissingSecrets) {\n\t\t\t\tlogger.log(` ⚠️ ${appName}: Missing secrets: ${missing.join(', ')}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// SETUP: Credentials, Project, Registry\n\t// ==================================================================\n\tlet creds = await getDokployCredentials();\n\tif (!creds) {\n\t\tlogger.log(\"\\n📋 Dokploy credentials not found. Let's set them up.\");\n\t\tconst endpoint = await prompt(\n\t\t\t'Dokploy URL (e.g., https://dokploy.example.com): ',\n\t\t);\n\t\tconst normalizedEndpoint = endpoint.replace(/\\/$/, '');\n\n\t\ttry {\n\t\t\tnew URL(normalizedEndpoint);\n\t\t} catch {\n\t\t\tthrow new Error('Invalid URL format');\n\t\t}\n\n\t\tlogger.log(\n\t\t\t`\\nGenerate a token at: ${normalizedEndpoint}/settings/profile\\n`,\n\t\t);\n\t\tconst token = await prompt('API Token: ', true);\n\n\t\tlogger.log('\\nValidating credentials...');\n\t\tconst isValid = await validateDokployToken(normalizedEndpoint, token);\n\t\tif (!isValid) {\n\t\t\tthrow new Error('Invalid credentials. Please check your token.');\n\t\t}\n\n\t\tawait storeDokployCredentials(token, normalizedEndpoint);\n\t\tcreds = { token, endpoint: normalizedEndpoint };\n\t\tlogger.log('✓ Credentials saved');\n\t}\n\n\tconst api = new DokployApi({ baseUrl: creds.endpoint, token: creds.token });\n\n\t// Find or create project for the workspace\n\tlogger.log('\\n📁 Setting up Dokploy project...');\n\tconst projectName = workspace.name;\n\tconst projects = await api.listProjects();\n\tlet project = projects.find(\n\t\t(p) => p.name.toLowerCase() === projectName.toLowerCase(),\n\t);\n\n\tlet environmentId: string;\n\n\tif (project) {\n\t\tlogger.log(` Found existing project: ${project.name}`);\n\t\tconst projectDetails = await api.getProject(project.projectId);\n\t\tconst environments = projectDetails.environments ?? [];\n\t\tconst matchingEnv = environments.find(\n\t\t\t(e) => e.name.toLowerCase() === stage.toLowerCase(),\n\t\t);\n\t\tif (matchingEnv) {\n\t\t\tenvironmentId = matchingEnv.environmentId;\n\t\t\tlogger.log(` Using environment: ${matchingEnv.name}`);\n\t\t} else {\n\t\t\tlogger.log(` Creating \"${stage}\" environment...`);\n\t\t\tconst env = await api.createEnvironment(project.projectId, stage);\n\t\t\tenvironmentId = env.environmentId;\n\t\t\tlogger.log(` ✓ Created environment: ${stage}`);\n\t\t}\n\t} else {\n\t\tlogger.log(` Creating project: ${projectName}`);\n\t\tconst result = await api.createProject(projectName);\n\t\tproject = result.project;\n\t\tif (result.environment.name.toLowerCase() !== stage.toLowerCase()) {\n\t\t\tlogger.log(` Creating \"${stage}\" environment...`);\n\t\t\tconst env = await api.createEnvironment(project.projectId, stage);\n\t\t\tenvironmentId = env.environmentId;\n\t\t} else {\n\t\t\tenvironmentId = result.environment.environmentId;\n\t\t}\n\t\tlogger.log(` ✓ Created project: ${project.projectId}`);\n\t}\n\n\t// ==================================================================\n\t// STATE: Create state provider and load deploy state\n\t// ==================================================================\n\tlogger.log('\\n📋 Loading deploy state...');\n\n\t// Create state provider based on workspace config\n\tconst stateProvider = await createStateProvider({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\n\tlet state = await stateProvider.read(stage);\n\n\tif (state) {\n\t\tlogger.log(` Found existing state for stage \"${stage}\"`);\n\t\t// Verify project ID matches (in case of recreation)\n\t\tif (state.projectId !== project.projectId) {\n\t\t\tlogger.log(` ⚠ Project ID changed, updating state`);\n\t\t\tstate.projectId = project.projectId;\n\t\t}\n\t\t// Verify environment ID matches (in case of recreation)\n\t\tif (state.environmentId !== environmentId) {\n\t\t\tlogger.log(` ⚠ Environment ID changed, updating state`);\n\t\t\tstate.environmentId = environmentId;\n\t\t}\n\t} else {\n\t\tlogger.log(` Creating new state for stage \"${stage}\"`);\n\t\tstate = createEmptyState(stage, project.projectId, environmentId);\n\t}\n\n\t// Get or set up registry\n\tlogger.log('\\n🐳 Checking registry...');\n\tlet registryId = await getDokployRegistryId();\n\tconst registry = workspace.deploy.dokploy?.registry;\n\n\tif (registryId) {\n\t\ttry {\n\t\t\tconst reg = await api.getRegistry(registryId);\n\t\t\tlogger.log(` Using registry: ${reg.registryName}`);\n\t\t} catch {\n\t\t\tlogger.log(' ⚠ Stored registry not found, clearing...');\n\t\t\tregistryId = undefined;\n\t\t\tawait storeDokployRegistryId('');\n\t\t}\n\t}\n\n\tif (!registryId) {\n\t\tconst registries = await api.listRegistries();\n\t\tif (registries.length > 0) {\n\t\t\tregistryId = registries[0]!.registryId;\n\t\t\tawait storeDokployRegistryId(registryId);\n\t\t\tlogger.log(` Using registry: ${registries[0]!.registryName}`);\n\t\t} else if (registry) {\n\t\t\tlogger.log(\" No registries found in Dokploy. Let's create one.\");\n\t\t\tlogger.log(` Registry URL: ${registry}`);\n\n\t\t\tconst username = await prompt('Registry username: ');\n\t\t\tconst password = await prompt('Registry password/token: ', true);\n\n\t\t\tconst reg = await api.createRegistry(\n\t\t\t\t'Default Registry',\n\t\t\t\tregistry,\n\t\t\t\tusername,\n\t\t\t\tpassword,\n\t\t\t);\n\t\t\tregistryId = reg.registryId;\n\t\t\tawait storeDokployRegistryId(registryId);\n\t\t\tlogger.log(` ✓ Registry created: ${registryId}`);\n\t\t} else {\n\t\t\tlogger.log(\n\t\t\t\t' ⚠ No registry configured. Set deploy.dokploy.registry in workspace config',\n\t\t\t);\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// Separate apps by type for two-phase deployment\n\t// ==================================================================\n\t// Mobile apps deploy via their own toolchain (e.g. EAS Build for Expo)\n\t// — skip them in the Dokploy deploy phases.\n\tconst skippedMobileApps = appsToDeployNames.filter(\n\t\t(name) => workspace.apps[name]!.type === 'mobile',\n\t);\n\tif (skippedMobileApps.length > 0) {\n\t\tlogger.log(\n\t\t\t`\\n📱 Skipping ${skippedMobileApps.length} mobile app(s) — deploy via framework toolchain: ${skippedMobileApps.join(', ')}`,\n\t\t);\n\t}\n\n\tconst backendApps = appsToDeployNames.filter(\n\t\t(name) => workspace.apps[name]!.type === 'backend',\n\t);\n\tconst frontendApps = appsToDeployNames.filter(\n\t\t(name) => workspace.apps[name]!.type === 'web',\n\t);\n\n\t// ==================================================================\n\t// Initialize per-app database users if Postgres is provisioned\n\t// ==================================================================\n\t// Read before the declared block below, which needs it to work out where a\n\t// surface will answer — that has to be known before any environment is\n\t// saved, and it used to be decided inside the app loop, which is too late.\n\tconst dokployConfig = workspace.deploy.dokploy;\n\n\t// ==================================================================\n\t// The declared half: everything the construct manifest says exists\n\t// ==================================================================\n\t// Separate from the block above on purpose. That one deploys *applications*\n\t// — images, registries, domains — which a project has whether or not it\n\t// declares anything. This is what exists because the app said so, and it is\n\t// skipped entirely for a project that has not adopted the model.\n\t//\n\t// It runs before any application environment is saved, because the URLs it\n\t// resolves are what those applications read.\n\tlet declaredEnv: Record<string, string> = {};\n\t// The clusters the manifest provisioned, for anything downstream that needs\n\t// one — a backup schedule names a database, and that database is now\n\t// declared rather than configured.\n\tlet declaredClusters: Record<string, DokployCluster> = {};\n\n\t{\n\t\tlogger.log('\\n📦 Provisioning declared constructs...');\n\n\t\t// Every surface answers on its process's address, so the address has to\n\t\t// exist before the manifest is walked. Computed here rather than in the\n\t\t// app loop, which is where it used to be decided and is too late.\n\t\tconst appUrls: Record<string, string> = {};\n\t\tfor (const appName of backendApps) {\n\t\t\tconst app = workspace.apps[appName];\n\t\t\tif (!app) continue;\n\n\t\t\tappUrls[appName] = `https://${resolveHost(\n\t\t\t\tappName,\n\t\t\t\tapp,\n\t\t\t\tstage,\n\t\t\t\tdokployConfig,\n\t\t\t\tfalse,\n\t\t\t)}`;\n\t\t}\n\n\t\tconst declared = await provisionDeclared({\n\t\t\tapi,\n\t\t\tworkspace,\n\t\t\tprojectId: project.projectId,\n\t\t\tenvironmentId: environmentId as string,\n\t\t\tstage,\n\t\t\tappUrls,\n\t\t\t// The one already discovered above, so a deploy reads the manifest once\n\t\t\t// and cannot act on two different versions of it.\n\t\t\tmanifest,\n\t\t});\n\n\t\tdeclaredEnv = declared.env;\n\t\tdeclaredClusters = declared.clusters;\n\n\t\tif (Object.keys(declaredEnv).length > 0) {\n\t\t\tlogger.log(\n\t\t\t\t` 🔌 Resolved ${Object.keys(declaredEnv).length} declared URL(s)`,\n\t\t\t);\n\t\t}\n\n\t\t// The DDL the provisioners deferred. Roles and tables need a connection\n\t\t// to a cluster that only exists once the calls above have been made —\n\t\t// which is why they were accumulated rather than run.\n\t\t//\n\t\t// Grouped by the cluster each belongs to, and *the manifest's* cluster\n\t\t// rather than whichever Postgres happens to be around: a project may also\n\t\t// have a legacy `services.postgres`, and applying a construct's roles to\n\t\t// that one would create them where nothing connects.\n\t\tif (declared.statements.length > 0) {\n\t\t\tconst serverHostname = getServerHostname(creds.endpoint);\n\n\t\t\tfor (const [databaseName, cluster] of Object.entries(declared.clusters)) {\n\t\t\t\tconst statements = declared.statements.filter(\n\t\t\t\t\t(statement) => statement.database === databaseName,\n\t\t\t\t);\n\t\t\t\tif (statements.length === 0) continue;\n\n\t\t\t\tconst created = await applyDeclaredStatements(\n\t\t\t\t\tapi,\n\t\t\t\t\tcluster,\n\t\t\t\t\tserverHostname,\n\t\t\t\t\tstatements,\n\t\t\t\t);\n\n\t\t\t\tlogger.log(\n\t\t\t\t\t` 🗄️ ${databaseName}: applied ${statements.length} statement(s), ${created} new`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// Provision backup destination if configured\n\t// ==================================================================\n\t// The first declared database. A workspace that declares two and wants both\n\t// backed up needs a per-database schedule, which the config has no way to\n\t// express yet — worth saying rather than backing up one and calling it done.\n\tconst backupCluster = Object.values(declaredClusters)[0];\n\n\tif (workspace.deploy?.backups && backupCluster) {\n\t\tlogger.log('\\n💾 Provisioning backup destination...');\n\n\t\tconst { provisionBackupDestination } = await import(\n\t\t\t'./backup-provisioner.js'\n\t\t);\n\n\t\tconst backupState = await provisionBackupDestination({\n\t\t\tapi,\n\t\t\tprojectId: project.projectId,\n\t\t\tprojectName: workspace.name,\n\t\t\tstage,\n\t\t\tconfig: workspace.deploy.backups,\n\t\t\texistingState: getBackupState(state),\n\t\t\tlogger,\n\t\t});\n\n\t\t// Save backup state\n\t\tsetBackupState(state, backupState);\n\n\t\t// Create backup schedule for postgres if not already configured\n\t\tif (!backupState.postgresBackupId) {\n\t\t\tconst backupSchedule = workspace.deploy.backups.schedule ?? '0 2 * * *';\n\t\t\tconst backupRetention = workspace.deploy.backups.retention ?? 30;\n\n\t\t\tlogger.log(' Creating postgres backup schedule...');\n\t\t\tconst backup = await api.createPostgresBackup({\n\t\t\t\tschedule: backupSchedule,\n\t\t\t\tprefix: `${stage}/postgres`,\n\t\t\t\tdestinationId: backupState.destinationId,\n\t\t\t\tdatabase: backupCluster.databaseName,\n\t\t\t\tpostgresId: backupCluster.postgresId,\n\t\t\t\tenabled: true,\n\t\t\t\tkeepLatestCount: backupRetention,\n\t\t\t});\n\t\t\tsetPostgresBackupId(state, backup.backupId);\n\t\t\tlogger.log(` ✓ Postgres backup schedule created (${backupSchedule})`);\n\t\t} else {\n\t\t\tlogger.log(' ✓ Using existing postgres backup schedule');\n\t\t}\n\t}\n\n\t// Track deployed app public URLs for frontend builds\n\tconst publicUrls: Record<string, string> = {};\n\tconst results: AppDeployResult[] = [];\n\n\t// Track domain IDs and hostnames for DNS orchestration\n\tconst appHostnames = new Map<string, string>(); // appName -> hostname\n\tconst appDomainIds = new Map<string, string>(); // appName -> domainId\n\n\t// ==================================================================\n\t// PRE-COMPUTE: Frontend URLs for BETTER_AUTH_TRUSTED_ORIGINS\n\t// ==================================================================\n\tconst frontendUrls: string[] = [];\n\tfor (const appName of frontendApps) {\n\t\tconst app = workspace.apps[appName]!;\n\t\tconst isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);\n\t\tconst hostname = resolveHost(\n\t\t\tappName,\n\t\t\tapp,\n\t\t\tstage,\n\t\t\tdokployConfig,\n\t\t\tisMainFrontend,\n\t\t);\n\t\tfrontendUrls.push(`https://${hostname}`);\n\t}\n\n\t// ==================================================================\n\t// PHASE 1: Deploy backend apps (with encrypted secrets)\n\t// ==================================================================\n\tif (backendApps.length > 0) {\n\t\tlogger.log('\\n📦 PHASE 1: Deploying backend applications...');\n\n\t\tfor (const appName of backendApps) {\n\t\t\tconst app = workspace.apps[appName]!;\n\n\t\t\tlogger.log(`\\n ⚙️ Deploying ${appName}...`);\n\n\t\t\ttry {\n\t\t\t\t// Use simple app name - project already provides namespace\n\t\t\t\t// Scoped exactly as a construct is, and by the same function. It\n\t\t\t\t// used to be the bare app key, so a project held an `api` and a\n\t\t\t\t// `web` that every stage would collide on.\n\t\t\t\tconst dokployAppName = applicationName(stage, workspace.name, appName);\n\n\t\t\t\t// Check state for cached application ID\n\t\t\t\tlet application: DokployApplication | null = null;\n\t\t\t\tconst cachedAppId = getApplicationId(state, appName);\n\n\t\t\t\tif (cachedAppId) {\n\t\t\t\t\tlogger.log(` Using cached ID: ${cachedAppId}`);\n\t\t\t\t\tapplication = await api.getApplication(cachedAppId);\n\t\t\t\t\tif (application) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` ✓ Application found: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.log(` ⚠ Cached ID invalid, will create new`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If not found by ID, use findOrCreate\n\t\t\t\tif (!application) {\n\t\t\t\t\tconst result = await api.findOrCreateApplication(\n\t\t\t\t\t\tdokployAppName,\n\t\t\t\t\t\tproject.projectId,\n\t\t\t\t\t\tenvironmentId,\n\t\t\t\t\t);\n\t\t\t\t\tapplication = result.application;\n\n\t\t\t\t\tif (result.created) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` Created application: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` Found existing application: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Store application ID in state\n\t\t\t\tsetApplicationId(state, appName, application.applicationId);\n\n\t\t\t\t// Get encrypted secrets for this app\n\t\t\t\tconst appSecrets = encryptedSecrets.get(appName);\n\t\t\t\tconst buildArgs: string[] = [];\n\n\t\t\t\tif (appSecrets && appSecrets.secretCount > 0) {\n\t\t\t\t\tbuildArgs.push(\n\t\t\t\t\t\t`GKM_ENCRYPTED_CREDENTIALS=${appSecrets.payload.encrypted}`,\n\t\t\t\t\t);\n\t\t\t\t\tbuildArgs.push(`GKM_CREDENTIALS_IV=${appSecrets.payload.iv}`);\n\t\t\t\t\tlogger.log(` Encrypted ${appSecrets.secretCount} secrets`);\n\t\t\t\t}\n\n\t\t\t\t// Build Docker image with encrypted secrets\n\t\t\t\tconst imageName = `${workspace.name}-${appName}`;\n\t\t\t\tconst imageRef = registry\n\t\t\t\t\t? `${registry}/${imageName}:${imageTag}`\n\t\t\t\t\t: `${imageName}:${imageTag}`;\n\n\t\t\t\tlogger.log(` Building Docker image: ${imageRef}`);\n\n\t\t\t\tawait deployDocker({\n\t\t\t\t\tstage,\n\t\t\t\t\ttag: imageTag,\n\t\t\t\t\tskipPush: false,\n\t\t\t\t\tconfig: {\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\timageName,\n\t\t\t\t\t\tappName,\n\t\t\t\t\t},\n\t\t\t\t\tbuildArgs,\n\t\t\t\t});\n\n\t\t\t\t// Compute hostname first (needed for BETTER_AUTH_URL)\n\t\t\t\tconst backendHost = resolveHost(\n\t\t\t\t\tappName,\n\t\t\t\t\tapp,\n\t\t\t\t\tstage,\n\t\t\t\t\tdokployConfig,\n\t\t\t\t\tfalse, // Backend apps are not main frontend\n\t\t\t\t);\n\n\t\t\t\t// Build dependency URLs from already-deployed apps\n\t\t\t\tconst dependencyUrls: Record<string, string> = {};\n\t\t\t\tif (app.dependencies) {\n\t\t\t\t\tfor (const dep of app.dependencies) {\n\t\t\t\t\t\tif (publicUrls[dep]) {\n\t\t\t\t\t\t\tdependencyUrls[dep] = publicUrls[dep];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Build env resolver context\n\t\t\t\tconst envContext: EnvResolverContext = {\n\t\t\t\t\tapp,\n\t\t\t\t\tappName,\n\t\t\t\t\tstage,\n\t\t\t\t\tstate,\n\t\t\t\t\tappHostname: backendHost,\n\t\t\t\t\tfrontendUrls,\n\t\t\t\t\tuserSecrets: stageSecrets ?? undefined,\n\t\t\t\t\tmasterKey: appSecrets?.masterKey,\n\t\t\t\t\tdependencyUrls,\n\t\t\t\t};\n\n\t\t\t\t// Resolve all required environment variables\n\t\t\t\t// Always include PORT, NODE_ENV, STAGE even if not explicitly required\n\t\t\t\tconst appRequirements = sniffedApps.get(appName);\n\t\t\t\tconst sniffedVars = requiredOf(appRequirements);\n\t\t\t\tconst requiredVars = [\n\t\t\t\t\t...new Set(['PORT', 'NODE_ENV', 'STAGE', ...sniffedVars]),\n\t\t\t\t];\n\t\t\t\tconst { valid, missing, resolved } = validateEnvVars(\n\t\t\t\t\trequiredVars,\n\t\t\t\t\tenvContext,\n\t\t\t\t);\n\n\t\t\t\tif (!valid) {\n\t\t\t\t\tthrow new Error(formatMissingVarsError(appName, missing, stage));\n\t\t\t\t}\n\n\t\t\t\t// Declared URLs win over anything sniffed or stored, which is the\n\t\t\t\t// same precedence the local target applies: the manifest is the\n\t\t\t\t// statement of what exists, and a value left over from before it\n\t\t\t\t// was declared is exactly the drift this replaces.\n\t\t\t\t//\n\t\t\t\t// They are merged *after* validation rather than added to the\n\t\t\t\t// required list, because the sniffer cannot see them — a construct\n\t\t\t\t// reads its own key inside `@geekmidas/constructs`, so requiring\n\t\t\t\t// them would fail every app that declares anything.\n\t\t\t\tconst withDeclared = { ...resolved, ...declaredEnv };\n\n\t\t\t\t// Build env vars string for Dokploy\n\t\t\t\tconst envVars: string[] = Object.entries(withDeclared).map(\n\t\t\t\t\t([key, value]) => `${key}=${value}`,\n\t\t\t\t);\n\n\t\t\t\tif (Object.keys(withDeclared).length > 0) {\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` Resolved ${Object.keys(withDeclared).length} env vars: ${Object.keys(withDeclared).sort().join(', ')}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Configure and deploy application in Dokploy\n\t\t\t\tawait api.saveDockerProvider(application.applicationId, imageRef, {\n\t\t\t\t\tregistryId,\n\t\t\t\t});\n\n\t\t\t\tawait api.saveApplicationEnv(\n\t\t\t\t\tapplication.applicationId,\n\t\t\t\t\tenvVars.join('\\n'),\n\t\t\t\t);\n\n\t\t\t\tlogger.log(` Deploying to Dokploy...`);\n\t\t\t\tawait api.deployApplication(application.applicationId);\n\n\t\t\t\t// Check if domain already exists (backendHost computed above)\n\t\t\t\tconst existingDomains = await api.getDomainsByApplicationId(\n\t\t\t\t\tapplication.applicationId,\n\t\t\t\t);\n\t\t\t\tconst existingDomain = existingDomains.find(\n\t\t\t\t\t(d) => d.host === backendHost,\n\t\t\t\t);\n\n\t\t\t\tif (existingDomain) {\n\t\t\t\t\t// Domain already exists\n\t\t\t\t\tappHostnames.set(appName, backendHost);\n\t\t\t\t\tappDomainIds.set(appName, existingDomain.domainId);\n\t\t\t\t\tpublicUrls[appName] = `https://${backendHost}`;\n\t\t\t\t\tlogger.log(` ✓ Domain: https://${backendHost} (existing)`);\n\t\t\t\t} else {\n\t\t\t\t\t// Create new domain\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst domain = await api.createDomain({\n\t\t\t\t\t\t\thost: backendHost,\n\t\t\t\t\t\t\tport: app.port,\n\t\t\t\t\t\t\thttps: true,\n\t\t\t\t\t\t\tcertificateType: 'letsencrypt',\n\t\t\t\t\t\t\tapplicationId: application.applicationId,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tappHostnames.set(appName, backendHost);\n\t\t\t\t\t\tappDomainIds.set(appName, domain.domainId);\n\t\t\t\t\t\tpublicUrls[appName] = `https://${backendHost}`;\n\t\t\t\t\t\tlogger.log(` ✓ Domain: https://${backendHost} (created)`);\n\t\t\t\t\t} catch (domainError) {\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\tdomainError instanceof Error\n\t\t\t\t\t\t\t\t? domainError.message\n\t\t\t\t\t\t\t\t: 'Unknown error';\n\t\t\t\t\t\tlogger.log(` ⚠ Domain creation failed: ${message}`);\n\t\t\t\t\t\tappHostnames.set(appName, backendHost);\n\t\t\t\t\t\tpublicUrls[appName] = `https://${backendHost}`;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tresults.push({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: app.type,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tapplicationId: application.applicationId,\n\t\t\t\t\timageRef,\n\t\t\t\t});\n\n\t\t\t\tlogger.log(` ✓ ${appName} deployed successfully`);\n\t\t\t} catch (error) {\n\t\t\t\tconst message =\n\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error';\n\t\t\t\tlogger.log(` ✗ Failed to deploy ${appName}: ${message}`);\n\n\t\t\t\tresults.push({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: app.type,\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: message,\n\t\t\t\t});\n\n\t\t\t\t// Abort on backend failure to prevent incomplete deployment\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Backend deployment failed for ${appName}. Aborting to prevent partial deployment.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// PHASE 2: Deploy frontend apps (with public URLs from backends)\n\t// ==================================================================\n\tif (frontendApps.length > 0) {\n\t\tlogger.log('\\n🌐 PHASE 2: Deploying frontend applications...');\n\n\t\tfor (const appName of frontendApps) {\n\t\t\tconst app = workspace.apps[appName]!;\n\n\t\t\tlogger.log(`\\n 🌐 Deploying ${appName}...`);\n\n\t\t\ttry {\n\t\t\t\t// Use simple app name - project already provides namespace\n\t\t\t\t// Scoped exactly as a construct is, and by the same function. It\n\t\t\t\t// used to be the bare app key, so a project held an `api` and a\n\t\t\t\t// `web` that every stage would collide on.\n\t\t\t\tconst dokployAppName = applicationName(stage, workspace.name, appName);\n\n\t\t\t\t// Check state for cached application ID\n\t\t\t\tlet application: DokployApplication | null = null;\n\t\t\t\tconst cachedAppId = getApplicationId(state, appName);\n\n\t\t\t\tif (cachedAppId) {\n\t\t\t\t\tlogger.log(` Using cached ID: ${cachedAppId}`);\n\t\t\t\t\tapplication = await api.getApplication(cachedAppId);\n\t\t\t\t\tif (application) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` ✓ Application found: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.log(` ⚠ Cached ID invalid, will create new`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If not found by ID, use findOrCreate\n\t\t\t\tif (!application) {\n\t\t\t\t\tconst result = await api.findOrCreateApplication(\n\t\t\t\t\t\tdokployAppName,\n\t\t\t\t\t\tproject.projectId,\n\t\t\t\t\t\tenvironmentId,\n\t\t\t\t\t);\n\t\t\t\t\tapplication = result.application;\n\n\t\t\t\t\tif (result.created) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` Created application: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` Found existing application: ${application.applicationId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Store application ID in state\n\t\t\t\tsetApplicationId(state, appName, application.applicationId);\n\n\t\t\t\t// Build dependency URLs for frontend (same pattern as backend)\n\t\t\t\tconst dependencyUrls: Record<string, string> = {};\n\t\t\t\tif (app.dependencies) {\n\t\t\t\t\tfor (const dep of app.dependencies) {\n\t\t\t\t\t\tif (publicUrls[dep]) {\n\t\t\t\t\t\t\tdependencyUrls[dep] = publicUrls[dep];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Compute hostname for this frontend app\n\t\t\t\tconst isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);\n\t\t\t\tconst frontendHost = resolveHost(\n\t\t\t\t\tappName,\n\t\t\t\t\tapp,\n\t\t\t\t\tstage,\n\t\t\t\t\tdokployConfig,\n\t\t\t\t\tisMainFrontend,\n\t\t\t\t);\n\n\t\t\t\t// Build env context for frontend\n\t\t\t\tconst envContext: EnvResolverContext = {\n\t\t\t\t\tapp,\n\t\t\t\t\tappName,\n\t\t\t\t\tstage,\n\t\t\t\t\tstate,\n\t\t\t\t\tappHostname: frontendHost,\n\t\t\t\t\tfrontendUrls: [],\n\t\t\t\t\tuserSecrets: stageSecrets ?? undefined,\n\t\t\t\t\tdependencyUrls,\n\t\t\t\t};\n\n\t\t\t\t// Resolve all env vars BEFORE Docker build (public-prefixed vars\n\t\t\t\t// must be present at bundler build time so they get inlined).\n\t\t\t\tconst sniffedVars = requiredOf(sniffedApps.get(appName));\n\t\t\t\tconst { valid, missing, resolved } = validateEnvVars(\n\t\t\t\t\tsniffedVars,\n\t\t\t\t\tenvContext,\n\t\t\t\t);\n\n\t\t\t\tif (!valid) {\n\t\t\t\t\tthrow new Error(formatMissingVarsError(appName, missing, stage));\n\t\t\t\t}\n\n\t\t\t\tif (Object.keys(resolved).length > 0) {\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` Resolved ${Object.keys(resolved).length} env vars: ${Object.keys(resolved).join(', ')}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Build args: only the framework's public-prefixed vars get baked\n\t\t\t\t// into the bundle. Server-only vars stay as runtime env.\n\t\t\t\tconst publicPrefix = getPublicEnvPrefix(app.framework);\n\t\t\t\tconst buildArgs: string[] = [];\n\t\t\t\tconst publicUrlArgNames: string[] = [];\n\n\t\t\t\tif (publicPrefix) {\n\t\t\t\t\tfor (const [key, value] of Object.entries(resolved)) {\n\t\t\t\t\t\tif (key.startsWith(publicPrefix)) {\n\t\t\t\t\t\t\tbuildArgs.push(`${key}=${value}`);\n\t\t\t\t\t\t\tpublicUrlArgNames.push(key);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (buildArgs.length > 0) {\n\t\t\t\t\tlogger.log(` Build args: ${publicUrlArgNames.join(', ')}`);\n\t\t\t\t}\n\n\t\t\t\t// Build Docker image with public-prefixed vars as build args\n\t\t\t\tconst imageName = `${workspace.name}-${appName}`;\n\t\t\t\tconst imageRef = registry\n\t\t\t\t\t? `${registry}/${imageName}:${imageTag}`\n\t\t\t\t\t: `${imageName}:${imageTag}`;\n\n\t\t\t\tlogger.log(` Building Docker image: ${imageRef}`);\n\n\t\t\t\tawait deployDocker({\n\t\t\t\t\tstage,\n\t\t\t\t\ttag: imageTag,\n\t\t\t\t\tskipPush: false,\n\t\t\t\t\tconfig: {\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\timageName,\n\t\t\t\t\t\tappName,\n\t\t\t\t\t},\n\t\t\t\t\tbuildArgs,\n\t\t\t\t\t// Pass arg names for Dockerfile ARG generation\n\t\t\t\t\tpublicUrlArgs: publicUrlArgNames,\n\t\t\t\t});\n\n\t\t\t\t// Prepare runtime environment variables\n\t\t\t\tconst envVars: string[] = [\n\t\t\t\t\t`NODE_ENV=production`,\n\t\t\t\t\t`PORT=${app.port}`,\n\t\t\t\t\t`STAGE=${stage}`,\n\t\t\t\t];\n\n\t\t\t\t// Add all resolved vars as runtime env (for SSR and server components)\n\t\t\t\tfor (const [key, value] of Object.entries(resolved)) {\n\t\t\t\t\tenvVars.push(`${key}=${value}`);\n\t\t\t\t}\n\n\t\t\t\t// Configure and deploy application in Dokploy\n\t\t\t\tawait api.saveDockerProvider(application.applicationId, imageRef, {\n\t\t\t\t\tregistryId,\n\t\t\t\t});\n\n\t\t\t\tawait api.saveApplicationEnv(\n\t\t\t\t\tapplication.applicationId,\n\t\t\t\t\tenvVars.join('\\n'),\n\t\t\t\t);\n\n\t\t\t\tlogger.log(` Deploying to Dokploy...`);\n\t\t\t\tawait api.deployApplication(application.applicationId);\n\n\t\t\t\t// Check if domain already exists (frontendHost computed earlier for env context)\n\t\t\t\tconst existingFrontendDomains = await api.getDomainsByApplicationId(\n\t\t\t\t\tapplication.applicationId,\n\t\t\t\t);\n\t\t\t\tconst existingFrontendDomain = existingFrontendDomains.find(\n\t\t\t\t\t(d) => d.host === frontendHost,\n\t\t\t\t);\n\n\t\t\t\tif (existingFrontendDomain) {\n\t\t\t\t\t// Domain already exists\n\t\t\t\t\tappHostnames.set(appName, frontendHost);\n\t\t\t\t\tappDomainIds.set(appName, existingFrontendDomain.domainId);\n\t\t\t\t\tpublicUrls[appName] = `https://${frontendHost}`;\n\t\t\t\t\tlogger.log(` ✓ Domain: https://${frontendHost} (existing)`);\n\t\t\t\t} else {\n\t\t\t\t\t// Create new domain\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst domain = await api.createDomain({\n\t\t\t\t\t\t\thost: frontendHost,\n\t\t\t\t\t\t\tport: app.port,\n\t\t\t\t\t\t\thttps: true,\n\t\t\t\t\t\t\tcertificateType: 'letsencrypt',\n\t\t\t\t\t\t\tapplicationId: application.applicationId,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tappHostnames.set(appName, frontendHost);\n\t\t\t\t\t\tappDomainIds.set(appName, domain.domainId);\n\t\t\t\t\t\tpublicUrls[appName] = `https://${frontendHost}`;\n\t\t\t\t\t\tlogger.log(` ✓ Domain: https://${frontendHost} (created)`);\n\t\t\t\t\t} catch (domainError) {\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\tdomainError instanceof Error\n\t\t\t\t\t\t\t\t? domainError.message\n\t\t\t\t\t\t\t\t: 'Unknown error';\n\t\t\t\t\t\tlogger.log(` ⚠ Domain creation failed: ${message}`);\n\t\t\t\t\t\tappHostnames.set(appName, frontendHost);\n\t\t\t\t\t\tpublicUrls[appName] = `https://${frontendHost}`;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tresults.push({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: app.type,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tapplicationId: application.applicationId,\n\t\t\t\t\timageRef,\n\t\t\t\t});\n\n\t\t\t\tlogger.log(` ✓ ${appName} deployed successfully`);\n\t\t\t} catch (error) {\n\t\t\t\tconst message =\n\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error';\n\t\t\t\tlogger.log(` ✗ Failed to deploy ${appName}: ${message}`);\n\n\t\t\t\tresults.push({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: app.type,\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: message,\n\t\t\t\t});\n\t\t\t\t// Don't abort on frontend failures - continue with other frontends\n\t\t\t}\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// STATE: Save deploy state\n\t// ==================================================================\n\tlogger.log('\\n📋 Saving deploy state...');\n\tawait stateProvider.write(stage, state);\n\tlogger.log(' ✓ State saved');\n\n\t// ==================================================================\n\t// DNS: Create DNS records, verify propagation, and validate for SSL\n\t// ==================================================================\n\tconst dnsConfig = workspace.deploy.dns;\n\tif (dnsConfig && appHostnames.size > 0) {\n\t\tconst dnsResult = await orchestrateDns(\n\t\t\tappHostnames,\n\t\t\tdnsConfig,\n\t\t\tcreds.endpoint,\n\t\t);\n\n\t\t// Verify DNS records resolve correctly (with state caching)\n\t\tif (dnsResult?.serverIp && appHostnames.size > 0) {\n\t\t\tawait verifyDnsRecords(appHostnames, dnsResult.serverIp, state);\n\n\t\t\t// Save state again to persist DNS verification results\n\t\t\tawait stateProvider.write(stage, state);\n\t\t}\n\n\t\t// Validate domains to trigger SSL certificate generation\n\t\tif (dnsResult?.success && appHostnames.size > 0) {\n\t\t\tlogger.log('\\n🔒 Validating domains for SSL certificates...');\n\t\t\tfor (const [appName, hostname] of appHostnames) {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await api.validateDomain(hostname);\n\t\t\t\t\tif (result.isValid) {\n\t\t\t\t\t\tlogger.log(` ✓ ${appName}: ${hostname} → ${result.resolvedIp}`);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.log(` ⚠ ${appName}: ${hostname} not valid`);\n\t\t\t\t\t}\n\t\t\t\t} catch (validationError) {\n\t\t\t\t\tconst message =\n\t\t\t\t\t\tvalidationError instanceof Error\n\t\t\t\t\t\t\t? validationError.message\n\t\t\t\t\t\t\t: 'Unknown error';\n\t\t\t\t\tlogger.log(` ⚠ ${appName}: validation failed - ${message}`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// ==================================================================\n\t// Summary\n\t// ==================================================================\n\tconst successCount = results.filter((r) => r.success).length;\n\tconst failedCount = results.filter((r) => !r.success).length;\n\n\tlogger.log(`\\n${'─'.repeat(50)}`);\n\tlogger.log(`\\n✅ Workspace deployment complete!`);\n\tlogger.log(` Project: ${project.projectId}`);\n\tlogger.log(` Successful: ${successCount}`);\n\tif (failedCount > 0) {\n\t\tlogger.log(` Failed: ${failedCount}`);\n\t}\n\n\t// Print deployed URLs\n\tif (Object.keys(publicUrls).length > 0) {\n\t\tlogger.log('\\n 📡 Deployed URLs:');\n\t\tfor (const [name, url] of Object.entries(publicUrls)) {\n\t\t\tlogger.log(` ${name}: ${url}`);\n\t\t}\n\t}\n\n\treturn {\n\t\tapps: results,\n\t\tprojectId: project.projectId,\n\t\tsuccessCount,\n\t\tfailedCount,\n\t};\n}\n\n/**\n * Main deploy command\n */\nexport async function deployCommand(\n\toptions: DeployOptions,\n): Promise<DeployResult | WorkspaceDeployResult> {\n\t// Load config with workspace detection\n\tconst loadedConfig = await loadWorkspaceConfig();\n\n\t// One path, whatever the config was written as.\n\t//\n\t// `defineConfig` is sugar over a one-app workspace, and `processConfig`\n\t// already projects it into the same `NormalizedWorkspace` a `defineWorkspace`\n\t// produces — so branching here meant a single-app project silently got less.\n\t// Domains were the clearest case: `createDomain` is only reached from this\n\t// function, so a single-app deploy provisioned everything, pushed an image,\n\t// started a container, and left nothing routing to it.\n\treturn workspaceDeployCommand(loadedConfig.workspace, options);\n}\n\nexport type { DeployOptions, DeployProvider, DeployResult };\n","import { existsSync } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tgetDokployCredentials,\n\tgetDokployRegistryId,\n\tgetDokployToken,\n\tstoreDokployRegistryId,\n} from '../auth';\nimport { DokployApi } from './dokploy-api';\nimport type { DokployDeployConfig } from './types';\n\nconst logger = console;\n\nexport interface DeployInitOptions {\n\t/** Dokploy endpoint URL (optional if logged in) */\n\tendpoint?: string;\n\t/** Project name (creates new or uses existing) */\n\tprojectName: string;\n\t/** Application name */\n\tappName: string;\n\t/** Use existing project ID instead of creating/finding */\n\tprojectId?: string;\n\t/** Registry ID in Dokploy (optional, uses stored if available) */\n\tregistryId?: string;\n}\n\nexport interface RegistrySetupOptions {\n\t/** Dokploy endpoint URL (optional if logged in) */\n\tendpoint?: string;\n\t/** Registry name (for display in Dokploy) */\n\tregistryName: string;\n\t/** Registry URL (e.g., ghcr.io, docker.io) */\n\tregistryUrl: string;\n\t/** Registry username */\n\tusername: string;\n\t/** Registry password or token */\n\tpassword: string;\n\t/** Image prefix (optional, e.g., org-name) */\n\timagePrefix?: string;\n}\n\n/**\n * Get the Dokploy API token from stored credentials or environment\n */\nasync function getApiToken(): Promise<string> {\n\tconst token = await getDokployToken();\n\tif (!token) {\n\t\tthrow new Error(\n\t\t\t'Dokploy credentials not found.\\n' +\n\t\t\t\t'Run \"gkm login --service dokploy\" to authenticate, or set DOKPLOY_API_TOKEN.',\n\t\t);\n\t}\n\treturn token;\n}\n\n/**\n * Get Dokploy endpoint from options or stored credentials\n */\nasync function getEndpoint(providedEndpoint?: string): Promise<string> {\n\tif (providedEndpoint) {\n\t\treturn providedEndpoint;\n\t}\n\n\tconst stored = await getDokployCredentials();\n\tif (stored) {\n\t\treturn stored.endpoint;\n\t}\n\n\tthrow new Error(\n\t\t'Dokploy endpoint not specified.\\n' +\n\t\t\t'Either run \"gkm login --service dokploy\" first, or provide --endpoint.',\n\t);\n}\n\n/**\n * Create a Dokploy API client\n */\nasync function createApi(endpoint: string): Promise<DokployApi> {\n\tconst token = await getApiToken();\n\treturn new DokployApi({ baseUrl: endpoint, token });\n}\n\n/**\n * Update gkm.config.ts with Dokploy configuration\n */\n/**\n * Whether the file's `dokploy` block is more than a flat literal.\n *\n * A spread, a `process.env`, a ternary or a nested object all defeat the\n * single-level regex below — the first two by being expressions it would\n * stringify away, the last by ending the match at the wrong brace.\n */\nfunction unsafeToRewrite(content: string): boolean {\n\tconst start = content.indexOf('dokploy:');\n\tif (start === -1) return false;\n\n\t// Far enough to see the block without parsing the file: the regex only ever\n\t// looked this far itself.\n\tconst block = content.slice(start, content.indexOf('\\n\\t},', start) + 1);\n\n\treturn /\\.\\.\\.|process\\.env|\\?\\s|`|\\{[^}]*\\{/s.test(block);\n}\n\nexport async function updateConfig(\n\tconfig: DokployDeployConfig,\n\tcwd: string = process.cwd(),\n): Promise<void> {\n\tconst configPath = join(cwd, 'gkm.config.ts');\n\n\tif (!existsSync(configPath)) {\n\t\tlogger.warn(\n\t\t\t'\\n gkm.config.ts not found. Add this configuration manually:\\n',\n\t\t);\n\t\tlogger.log(` providers: {`);\n\t\tlogger.log(` dokploy: {`);\n\t\tlogger.log(` endpoint: '${config.endpoint}',`);\n\t\tlogger.log(` projectId: '${config.projectId}',`);\n\t\tlogger.log(` applicationId: '${config.applicationId}',`);\n\t\tlogger.log(` },`);\n\t\tlogger.log(` },`);\n\t\treturn;\n\t}\n\n\tconst content = await readFile(configPath, 'utf-8');\n\n\t// Refuse rather than corrupt.\n\t//\n\t// The rewrite below is `replace(/dokploy:\\s*\\{[^}]*\\}/s, …)` — it matches to\n\t// the *first* closing brace and replaces the whole block. That is fine for a\n\t// flat literal and destructive for anything else: a nested `domains: { … }`\n\t// ends the match early and leaves the tail of the old block orphaned, and\n\t// every key this function does not itself write is dropped on the floor,\n\t// including the registry and the domains a deploy needs.\n\t//\n\t// It cost a corrupted config to find, so it says what it would have done and\n\t// leaves the file alone. The ids are not lost by skipping: they are\n\t// rediscovered by name on the next run and remembered in the state file,\n\t// which is the thing whose job that is.\n\tif (unsafeToRewrite(content)) {\n\t\tlogger.warn(\n\t\t\t'\\n gkm.config.ts holds a dokploy block this cannot safely rewrite —' +\n\t\t\t\t'\\n it contains an expression or a nested object. Leaving it alone.',\n\t\t);\n\t\tlogger.log(`\\n Discovered, if you want them written down:`);\n\t\tlogger.log(` projectId: '${config.projectId}'`);\n\t\tlogger.log(` applicationId: '${config.applicationId}'`);\n\t\tif (config.registryId) {\n\t\t\tlogger.log(` registryId: '${config.registryId}'`);\n\t\t}\n\t\treturn;\n\t}\n\n\t// Check if providers.dokploy already exists\n\tif (content.includes('dokploy:') && content.includes('applicationId:')) {\n\t\tlogger.log('\\n Dokploy config already exists in gkm.config.ts');\n\t\tlogger.log(' Updating with new values...');\n\t}\n\n\t// Build the dokploy config string\n\tconst registryLine = config.registryId\n\t\t? `\\n\\t\\t\\tregistryId: '${config.registryId}',`\n\t\t: '';\n\tconst dokployConfigStr = `dokploy: {\n\t\t\tendpoint: '${config.endpoint}',\n\t\t\tprojectId: '${config.projectId}',\n\t\t\tapplicationId: '${config.applicationId}',${registryLine}\n\t\t}`;\n\n\t// Try to add or update the dokploy config\n\tlet newContent: string;\n\n\tif (content.includes('providers:')) {\n\t\t// Add dokploy to existing providers\n\t\tif (content.includes('dokploy:')) {\n\t\t\t// Update existing dokploy config (handle multi-line with registryId)\n\t\t\tnewContent = content.replace(/dokploy:\\s*\\{[^}]*\\}/s, dokployConfigStr);\n\t\t} else {\n\t\t\t// Add dokploy to providers\n\t\t\tnewContent = content.replace(\n\t\t\t\t/providers:\\s*\\{/,\n\t\t\t\t`providers: {\\n\\t\\t${dokployConfigStr},`,\n\t\t\t);\n\t\t}\n\t} else {\n\t\t// Add providers section before the closing of defineConfig\n\t\tnewContent = content.replace(\n\t\t\t/}\\s*\\)\\s*;?\\s*$/,\n\t\t\t`\n\tproviders: {\n\t\t${dokployConfigStr},\n\t},\n});`,\n\t\t);\n\t}\n\n\tawait writeFile(configPath, newContent);\n\tlogger.log('\\n ✓ Updated gkm.config.ts with Dokploy configuration');\n}\n\n/**\n * Initialize Dokploy deployment configuration\n */\nexport async function deployInitCommand(\n\toptions: DeployInitOptions,\n): Promise<DokployDeployConfig> {\n\tconst {\n\t\tprojectName,\n\t\tappName,\n\t\tprojectId: existingProjectId,\n\t\tregistryId,\n\t} = options;\n\n\tconst endpoint = await getEndpoint(options.endpoint);\n\tconst api = await createApi(endpoint);\n\n\tlogger.log(`\\n🚀 Initializing Dokploy deployment...`);\n\tlogger.log(` Endpoint: ${endpoint}`);\n\n\t// Step 1: Find or create project\n\tlet projectId: string;\n\n\tif (existingProjectId) {\n\t\tprojectId = existingProjectId;\n\t\tlogger.log(`\\n📁 Using existing project: ${projectId}`);\n\t} else {\n\t\tlogger.log(`\\n📁 Looking for project: ${projectName}`);\n\n\t\tconst projects = await api.listProjects();\n\t\tconst existingProject = projects.find(\n\t\t\t(p) => p.name.toLowerCase() === projectName.toLowerCase(),\n\t\t);\n\n\t\tif (existingProject) {\n\t\t\tprojectId = existingProject.projectId;\n\t\t\tlogger.log(` Found existing project: ${projectId}`);\n\t\t} else {\n\t\t\tlogger.log(` Creating new project...`);\n\t\t\tconst result = await api.createProject(projectName);\n\t\t\tprojectId = result.project.projectId;\n\t\t\tlogger.log(` ✓ Created project: ${projectId}`);\n\t\t}\n\t}\n\n\t// Step 2: Get project to find environment\n\tconst project = await api.getProject(projectId);\n\tlet environmentId: string;\n\n\tconst firstEnv = project.environments?.[0];\n\tif (firstEnv) {\n\t\tenvironmentId = firstEnv.environmentId;\n\t} else {\n\t\t// Create a default environment\n\t\tlogger.log(` Creating production environment...`);\n\t\tconst env = await api.createEnvironment(projectId, 'production');\n\t\tenvironmentId = env.environmentId;\n\t}\n\n\t// Step 3: Create application\n\tlogger.log(`\\n📦 Creating application: ${appName}`);\n\tconst application = await api.createApplication(\n\t\tappName,\n\t\tprojectId,\n\t\tenvironmentId,\n\t);\n\tlogger.log(` ✓ Created application: ${application.applicationId}`);\n\n\t// Step 4: Configure registry if provided\n\tif (registryId) {\n\t\tlogger.log(`\\n🔧 Configuring registry: ${registryId}`);\n\t\tawait api.updateApplication(application.applicationId, { registryId });\n\t\tlogger.log(` ✓ Registry configured`);\n\t} else {\n\t\t// List available registries\n\t\ttry {\n\t\t\tconst registries = await api.listRegistries();\n\t\t\tif (registries.length > 0) {\n\t\t\t\tlogger.log(`\\n📋 Available registries:`);\n\t\t\t\tfor (const reg of registries) {\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` - ${reg.registryName}: ${reg.registryUrl} (${reg.registryId})`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlogger.log(`\\n To use a registry, run with --registry-id <id>`);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore registry listing errors\n\t\t}\n\t}\n\n\t// Step 5: Build config\n\tconst config: DokployDeployConfig = {\n\t\tendpoint,\n\t\tprojectId,\n\t\tapplicationId: application.applicationId,\n\t};\n\n\t// Step 6: Update gkm.config.ts\n\tawait updateConfig(config);\n\n\tlogger.log(`\\n✅ Dokploy deployment initialized!`);\n\tlogger.log(`\\n📋 Configuration:`);\n\tlogger.log(` Project ID: ${projectId}`);\n\tlogger.log(` Application ID: ${application.applicationId}`);\n\tlogger.log(`\\n🔗 View in Dokploy: ${endpoint}/project/${projectId}`);\n\tlogger.log(`\\n📝 Next steps:`);\n\tlogger.log(` 1. Initialize secrets: gkm secrets:init --stage production`);\n\tlogger.log(` 2. Deploy: gkm deploy --provider dokploy --stage production`);\n\n\treturn config;\n}\n\n/**\n * List available Dokploy resources\n */\nexport async function deployListCommand(options: {\n\tendpoint?: string;\n\tresource: 'projects' | 'registries';\n}): Promise<void> {\n\tconst endpoint = await getEndpoint(options.endpoint);\n\tconst api = await createApi(endpoint);\n\n\tconst { resource } = options;\n\n\tif (resource === 'projects') {\n\t\tlogger.log(`\\n📁 Projects in ${endpoint}:`);\n\t\tconst projects = await api.listProjects();\n\n\t\tif (projects.length === 0) {\n\t\t\tlogger.log(' No projects found');\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const project of projects) {\n\t\t\tlogger.log(`\\n ${project.name} (${project.projectId})`);\n\t\t\tif (project.description) {\n\t\t\t\tlogger.log(` ${project.description}`);\n\t\t\t}\n\t\t}\n\t} else if (resource === 'registries') {\n\t\tlogger.log(`\\n🐳 Registries in ${endpoint}:`);\n\t\tconst registries = await api.listRegistries();\n\n\t\tif (registries.length === 0) {\n\t\t\tlogger.log(' No registries configured');\n\t\t\tlogger.log(' Run \"gkm registry:setup\" to configure a registry');\n\t\t\treturn;\n\t\t}\n\n\t\tconst storedRegistryId = await getDokployRegistryId();\n\n\t\tfor (const registry of registries) {\n\t\t\tconst isDefault = registry.registryId === storedRegistryId;\n\t\t\tconst marker = isDefault ? ' (default)' : '';\n\t\t\tlogger.log(\n\t\t\t\t`\\n ${registry.registryName}${marker} (${registry.registryId})`,\n\t\t\t);\n\t\t\tlogger.log(` URL: ${registry.registryUrl}`);\n\t\t\tlogger.log(` Username: ${registry.username}`);\n\t\t\tif (registry.imagePrefix) {\n\t\t\t\tlogger.log(` Prefix: ${registry.imagePrefix}`);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Setup a Docker registry in Dokploy\n */\nexport async function registrySetupCommand(\n\toptions: RegistrySetupOptions,\n): Promise<string> {\n\tconst { registryName, registryUrl, username, password, imagePrefix } =\n\t\toptions;\n\n\tconst endpoint = await getEndpoint(options.endpoint);\n\tconst api = await createApi(endpoint);\n\n\tlogger.log(`\\n🐳 Setting up Docker registry in Dokploy...`);\n\tlogger.log(` Endpoint: ${endpoint}`);\n\n\t// Check if registry with same URL already exists\n\tconst existingRegistries = await api.listRegistries();\n\tconst existing = existingRegistries.find(\n\t\t(r) =>\n\t\t\tr.registryUrl === registryUrl ||\n\t\t\tr.registryName.toLowerCase() === registryName.toLowerCase(),\n\t);\n\n\tlet registryId: string;\n\n\tif (existing) {\n\t\tlogger.log(`\\n📋 Found existing registry: ${existing.registryName}`);\n\t\tlogger.log(` Updating credentials...`);\n\n\t\tawait api.updateRegistry(existing.registryId, {\n\t\t\tregistryName,\n\t\t\tusername,\n\t\t\tpassword,\n\t\t\timagePrefix,\n\t\t});\n\n\t\tregistryId = existing.registryId;\n\t\tlogger.log(` ✓ Registry updated: ${registryId}`);\n\t} else {\n\t\tlogger.log(`\\n📦 Creating registry: ${registryName}`);\n\n\t\tconst registry = await api.createRegistry(\n\t\t\tregistryName,\n\t\t\tregistryUrl,\n\t\t\tusername,\n\t\t\tpassword,\n\t\t\t{ imagePrefix },\n\t\t);\n\n\t\tregistryId = registry.registryId;\n\t\tlogger.log(` ✓ Registry created: ${registryId}`);\n\t}\n\n\t// Store registry ID in credentials\n\tawait storeDokployRegistryId(registryId);\n\tlogger.log(`\\n💾 Saved registry ID to ~/.gkm/credentials.json`);\n\n\tlogger.log(`\\n✅ Registry setup complete!`);\n\tlogger.log(`\\n📋 Registry Details:`);\n\tlogger.log(` ID: ${registryId}`);\n\tlogger.log(` Name: ${registryName}`);\n\tlogger.log(` URL: ${registryUrl}`);\n\tlogger.log(` Username: ${username}`);\n\tif (imagePrefix) {\n\t\tlogger.log(` Prefix: ${imagePrefix}`);\n\t}\n\n\tlogger.log(\n\t\t`\\n📝 The registry ID is now stored and will be used automatically`,\n\t);\n\tlogger.log(` when deploying with \"gkm deploy --provider dokploy\"`);\n\n\treturn registryId;\n}\n\n/**\n * Use an existing registry (set as default)\n */\nexport async function registryUseCommand(options: {\n\tendpoint?: string;\n\tregistryId: string;\n}): Promise<void> {\n\tconst { registryId } = options;\n\n\tconst endpoint = await getEndpoint(options.endpoint);\n\tconst api = await createApi(endpoint);\n\n\tlogger.log(`\\n🔧 Setting default registry...`);\n\n\t// Verify the registry exists\n\ttry {\n\t\tconst registry = await api.getRegistry(registryId);\n\t\tlogger.log(` Found registry: ${registry.registryName}`);\n\t} catch {\n\t\tthrow new Error(\n\t\t\t`Registry not found: ${registryId}\\n` +\n\t\t\t\t'Run \"gkm deploy:list registries\" to see available registries.',\n\t\t);\n\t}\n\n\t// Store registry ID in credentials\n\tawait storeDokployRegistryId(registryId);\n\n\tlogger.log(`\\n✅ Default registry set: ${registryId}`);\n\tlogger.log(` This registry will be used for future deployments.`);\n}\n","/**\n * State Management CLI Commands\n *\n * Commands for managing deployment state across local and remote providers.\n */\n\nimport { loadWorkspaceConfig } from '../config';\nimport { CachedStateProvider } from './CachedStateProvider';\nimport { createStateProvider } from './StateProvider';\nimport type { DokployStageState } from './state';\n\nexport interface StateCommandOptions {\n\tstage: string;\n}\n\n/**\n * Pull state from remote to local.\n * `gkm state:pull --stage=<stage>`\n */\nexport async function statePullCommand(\n\toptions: StateCommandOptions,\n): Promise<void> {\n\tconst { workspace } = await loadWorkspaceConfig();\n\n\tif (!workspace.state || workspace.state.provider === 'local') {\n\t\tconsole.error('No remote state provider configured.');\n\t\tconsole.error('Add a remote provider in gkm.config.ts:');\n\t\tconsole.error(' state: { provider: \"ssm\", region: \"us-east-1\" }');\n\t\tprocess.exit(1);\n\t}\n\n\tconst provider = await createStateProvider({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\n\tif (!(provider instanceof CachedStateProvider)) {\n\t\tconsole.error('State provider does not support pull operation.');\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(`Pulling state for stage: ${options.stage}...`);\n\tconst state = await provider.pull(options.stage);\n\n\tif (state) {\n\t\tconsole.log('State pulled successfully.');\n\t\tprintStateSummary(state);\n\t} else {\n\t\tconsole.log('No remote state found for this stage.');\n\t}\n}\n\n/**\n * Push local state to remote.\n * `gkm state:push --stage=<stage>`\n */\nexport async function statePushCommand(\n\toptions: StateCommandOptions,\n): Promise<void> {\n\tconst { workspace } = await loadWorkspaceConfig();\n\n\tif (!workspace.state || workspace.state.provider === 'local') {\n\t\tconsole.error('No remote state provider configured.');\n\t\tconsole.error('Add a remote provider in gkm.config.ts:');\n\t\tconsole.error(' state: { provider: \"ssm\", region: \"us-east-1\" }');\n\t\tprocess.exit(1);\n\t}\n\n\tconst provider = await createStateProvider({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\n\tif (!(provider instanceof CachedStateProvider)) {\n\t\tconsole.error('State provider does not support push operation.');\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(`Pushing state for stage: ${options.stage}...`);\n\tconst state = await provider.push(options.stage);\n\n\tif (state) {\n\t\tconsole.log('State pushed successfully.');\n\t\tprintStateSummary(state);\n\t} else {\n\t\tconsole.log('No local state found for this stage.');\n\t}\n}\n\n/**\n * Show current state.\n * `gkm state:show --stage=<stage>`\n */\nexport async function stateShowCommand(\n\toptions: StateCommandOptions & { json?: boolean },\n): Promise<void> {\n\tconst { workspace } = await loadWorkspaceConfig();\n\n\tconst provider = await createStateProvider({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\n\tconst state = await provider.read(options.stage);\n\n\tif (!state) {\n\t\tconsole.log(`No state found for stage: ${options.stage}`);\n\t\treturn;\n\t}\n\n\tif (options.json) {\n\t\tconsole.log(JSON.stringify(state, null, 2));\n\t} else {\n\t\tprintStateDetails(state);\n\t}\n}\n\n/**\n * Compare local and remote state.\n * `gkm state:diff --stage=<stage>`\n */\nexport async function stateDiffCommand(\n\toptions: StateCommandOptions,\n): Promise<void> {\n\tconst { workspace } = await loadWorkspaceConfig();\n\n\tif (!workspace.state || workspace.state.provider === 'local') {\n\t\tconsole.error('No remote state provider configured.');\n\t\tconsole.error('Diff requires a remote provider to compare against.');\n\t\tprocess.exit(1);\n\t}\n\n\tconst provider = await createStateProvider({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\n\tif (!(provider instanceof CachedStateProvider)) {\n\t\tconsole.error('State provider does not support diff operation.');\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(`Comparing state for stage: ${options.stage}...\\n`);\n\tconst { local, remote } = await provider.diff(options.stage);\n\n\tif (!local && !remote) {\n\t\tconsole.log('No state found (local or remote).');\n\t\treturn;\n\t}\n\n\tif (!local) {\n\t\tconsole.log('Local: (none)');\n\t} else {\n\t\tconsole.log(`Local: Last deployed ${local.lastDeployedAt}`);\n\t}\n\n\tif (!remote) {\n\t\tconsole.log('Remote: (none)');\n\t} else {\n\t\tconsole.log(`Remote: Last deployed ${remote.lastDeployedAt}`);\n\t}\n\n\tconsole.log('');\n\n\t// Compare applications\n\tconst localApps = local?.applications ?? {};\n\tconst remoteApps = remote?.applications ?? {};\n\tconst allApps = new Set([\n\t\t...Object.keys(localApps),\n\t\t...Object.keys(remoteApps),\n\t]);\n\n\tif (allApps.size > 0) {\n\t\tconsole.log('Applications:');\n\t\tfor (const app of allApps) {\n\t\t\tconst localId = localApps[app];\n\t\t\tconst remoteId = remoteApps[app];\n\n\t\t\tif (localId === remoteId) {\n\t\t\t\tconsole.log(` ${app}: ${localId ?? '(none)'}`);\n\t\t\t} else if (!localId) {\n\t\t\t\tconsole.log(` ${app}: (none) -> ${remoteId} [REMOTE ONLY]`);\n\t\t\t} else if (!remoteId) {\n\t\t\t\tconsole.log(` ${app}: ${localId} -> (none) [LOCAL ONLY]`);\n\t\t\t} else {\n\t\t\t\tconsole.log(\n\t\t\t\t\t` ${app}: ${localId} (local) != ${remoteId} (remote) [MISMATCH]`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Compare services\n\tconst localServices = local?.services ?? {};\n\tconst remoteServices = remote?.services ?? {};\n\n\tif (\n\t\tObject.keys(localServices).length > 0 ||\n\t\tObject.keys(remoteServices).length > 0\n\t) {\n\t\tconsole.log('\\nServices:');\n\t\tconst serviceKeys = new Set([\n\t\t\t...Object.keys(localServices),\n\t\t\t...Object.keys(remoteServices),\n\t\t]);\n\n\t\tfor (const key of serviceKeys) {\n\t\t\tconst localVal = localServices[key as keyof typeof localServices];\n\t\t\tconst remoteVal = remoteServices[key as keyof typeof remoteServices];\n\n\t\t\tif (localVal === remoteVal) {\n\t\t\t\tconsole.log(` ${key}: ${localVal ?? '(none)'}`);\n\t\t\t} else {\n\t\t\t\tconsole.log(\n\t\t\t\t\t` ${key}: ${localVal ?? '(none)'} (local) != ${remoteVal ?? '(none)'} (remote)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction printStateSummary(state: DokployStageState): void {\n\tconst appCount = Object.keys(state.applications).length;\n\tconst hasPostgres = !!state.services.postgresId;\n\tconst hasRedis = !!state.services.redisId;\n\n\tconsole.log(` Stage: ${state.stage}`);\n\tconsole.log(` Applications: ${appCount}`);\n\tconsole.log(` Postgres: ${hasPostgres ? 'configured' : 'none'}`);\n\tconsole.log(` Redis: ${hasRedis ? 'configured' : 'none'}`);\n\tconsole.log(` Last deployed: ${state.lastDeployedAt}`);\n}\n\nfunction printStateDetails(state: DokployStageState): void {\n\tconsole.log(`Stage: ${state.stage}`);\n\tconsole.log(`Environment ID: ${state.environmentId}`);\n\tconsole.log(`Last Deployed: ${state.lastDeployedAt}`);\n\tconsole.log('');\n\n\tconsole.log('Applications:');\n\tconst apps = Object.entries(state.applications);\n\tif (apps.length === 0) {\n\t\tconsole.log(' (none)');\n\t} else {\n\t\tfor (const [name, id] of apps) {\n\t\t\tconsole.log(` ${name}: ${id}`);\n\t\t}\n\t}\n\tconsole.log('');\n\n\tconsole.log('Services:');\n\tif (!state.services.postgresId && !state.services.redisId) {\n\t\tconsole.log(' (none)');\n\t} else {\n\t\tif (state.services.postgresId) {\n\t\t\tconsole.log(` Postgres: ${state.services.postgresId}`);\n\t\t}\n\t\tif (state.services.redisId) {\n\t\t\tconsole.log(` Redis: ${state.services.redisId}`);\n\t\t}\n\t}\n\n\tif (state.dnsVerified && Object.keys(state.dnsVerified).length > 0) {\n\t\tconsole.log('');\n\t\tconsole.log('DNS Verified:');\n\t\tfor (const [hostname, info] of Object.entries(state.dnsVerified)) {\n\t\t\tconsole.log(` ${hostname}: ${info.serverIp} (${info.verifiedAt})`);\n\t\t}\n\t}\n}\n","import { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\n// Load package.json - handles both bundled (flat dist/) and source (nested src/init/)\nfunction loadPackageJson(): { version: string } {\n\ttry {\n\t\t// Try flat dist path first (../package.json from dist/)\n\t\treturn require('../package.json');\n\t} catch {\n\t\t// Fall back to nested source path (../../package.json from src/init/)\n\t\treturn require('../../package.json');\n\t}\n}\n\nconst pkg = loadPackageJson();\n\n/**\n * CLI version resolved from package.json at runtime\n */\nexport const CLI_VERSION = `~${pkg.version}`;\n\n/**\n * Package versions for @geekmidas packages\n *\n * AUTO-GENERATED (except CLI) - Do not edit manually\n * Run: pnpm --filter @geekmidas/cli sync-versions\n */\nexport const GEEKMIDAS_VERSIONS = {\n\t'@geekmidas/audit': '~9.0.2',\n\t'@geekmidas/auth': '~9.0.2',\n\t'@geekmidas/cache': '~9.0.2',\n\t'@geekmidas/client': '~9.0.2',\n\t'@geekmidas/cloud': '~9.0.2',\n\t'@geekmidas/constructs': '~9.0.2',\n\t'@geekmidas/db': '~9.0.2',\n\t'@geekmidas/emailkit': '~9.0.2',\n\t'@geekmidas/envkit': '~9.0.2',\n\t'@geekmidas/errors': '~9.0.2',\n\t'@geekmidas/events': '~9.0.2',\n\t'@geekmidas/logger': '~9.0.2',\n\t'@geekmidas/manifest': '~9.0.2',\n\t'@geekmidas/rate-limit': '~9.0.2',\n\t'@geekmidas/schema': '~9.0.2',\n\t'@geekmidas/services': '~9.0.2',\n\t'@geekmidas/storage': '~9.0.2',\n\t'@geekmidas/studio': '~9.0.2',\n\t'@geekmidas/telescope': '~9.0.2',\n\t'@geekmidas/testkit': '~9.0.2',\n\t'@geekmidas/cli': CLI_VERSION,\n} as const;\n\nexport type GeekmidasPackage = keyof typeof GEEKMIDAS_VERSIONS;\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate auth app files for fullstack template\n * Uses better-auth with magic link authentication\n */\nexport function generateAuthAppFiles(\n\toptions: TemplateOptions,\n): GeneratedFile[] {\n\tif (!options.monorepo || options.template !== 'fullstack') {\n\t\treturn [];\n\t}\n\n\tconst packageName = `@${options.name}/auth`;\n\tconst modelsPackage = `@${options.name}/models`;\n\n\t// package.json for auth app\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\tscripts: {\n\t\t\tdev: 'gkm dev --entry ./src/index.ts',\n\t\t\tbuild: 'tsc',\n\t\t\tstart: 'node dist/index.js',\n\t\t\ttypecheck: 'tsc --noEmit',\n\t\t\t'db:migrate': 'gkm exec -- npx @better-auth/cli migrate',\n\t\t\t'db:generate': 'gkm exec -- npx @better-auth/cli generate',\n\t\t},\n\t\tdependencies: {\n\t\t\t[modelsPackage]: 'workspace:*',\n\t\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t\t'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],\n\t\t\t'@hono/node-server': '~1.13.0',\n\t\t\t'better-auth': '~1.2.0',\n\t\t\thono: '~4.8.0',\n\t\t\tkysely: '~0.27.0',\n\t\t\tpg: '~8.13.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t\t'@types/node': '~22.0.0',\n\t\t\t'@types/pg': '~8.11.0',\n\t\t\ttsx: '~4.20.0',\n\t\t\ttypescript: '~5.8.2',\n\t\t},\n\t};\n\n\t// tsconfig.json for auth app\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\tbaseUrl: '.',\n\t\t\tpaths: {\n\t\t\t\t'~/*': ['./src/*'],\n\t\t\t\t[`@${options.name}/*`]: ['../../packages/*/src'],\n\t\t\t},\n\t\t},\n\t\tinclude: ['src/**/*.ts'],\n\t\texclude: ['node_modules', 'dist'],\n\t};\n\n\t// src/config/env.ts\n\tconst envTs = `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { EnvironmentParser } from '@geekmidas/envkit';\n\nexport const envParser = new EnvironmentParser({ ...process.env, ...Credentials });\n\n// Global config - only minimal shared values\n// Service-specific config should be parsed where needed\nexport const config = envParser\n .create((get) => ({\n nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),\n stage: get('STAGE').enum(['development', 'staging', 'production']).default('development'),\n }))\n .parse();\n`;\n\n\t// src/config/logger.ts\n\tconst loggerTs = `import { createLogger } from '@geekmidas/logger/${options.loggerType}';\n\nexport const logger = createLogger();\n`;\n\n\t// src/auth.ts - better-auth instance with magic link\n\tconst authTs = `import { betterAuth } from 'better-auth';\nimport { magicLink } from 'better-auth/plugins';\nimport pg from 'pg';\nimport { envParser } from './config/env.ts';\nimport { logger } from './config/logger.ts';\n\n// Parse auth-specific config (no defaults - values from secrets)\nconst authConfig = envParser\n .create((get) => ({\n databaseUrl: get('DATABASE_URL').string(),\n baseUrl: get('BETTER_AUTH_URL').string(),\n trustedOrigins: get('BETTER_AUTH_TRUSTED_ORIGINS').string(),\n secret: get('BETTER_AUTH_SECRET').string(),\n }))\n .parse();\n\nexport const auth = betterAuth({\n database: new pg.Pool({\n connectionString: authConfig.databaseUrl,\n }),\n baseURL: authConfig.baseUrl,\n trustedOrigins: authConfig.trustedOrigins.split(','),\n secret: authConfig.secret,\n plugins: [\n magicLink({\n sendMagicLink: async ({ email, url }) => {\n // TODO: Implement email sending using @geekmidas/emailkit\n // For development, log the magic link\n logger.info({ email, url }, 'Magic link generated');\n console.log('\\\\n================================');\n console.log('MAGIC LINK FOR:', email);\n console.log(url);\n console.log('================================\\\\n');\n },\n expiresIn: 300, // 5 minutes\n }),\n ],\n emailAndPassword: {\n enabled: false, // Only magic link for now\n },\n});\n\nexport type Auth = typeof auth;\n`;\n\n\t// src/index.ts - Hono app entry point\n\tconst indexTs = `import { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { serve } from '@hono/node-server';\nimport { auth } from './auth.ts';\nimport { envParser } from './config/env.ts';\nimport { logger } from './config/logger.ts';\n\n// Parse server config (no defaults - values from secrets)\nconst serverConfig = envParser\n .create((get) => ({\n port: get('PORT').string().transform(Number),\n trustedOrigins: get('BETTER_AUTH_TRUSTED_ORIGINS').string(),\n }))\n .parse();\n\nconst app = new Hono();\n\n// CORS must be registered before routes\napp.use(\n '/api/auth/*',\n cors({\n origin: serverConfig.trustedOrigins.split(','),\n allowHeaders: ['Content-Type', 'Authorization'],\n allowMethods: ['POST', 'GET', 'OPTIONS'],\n credentials: true,\n }),\n);\n\n// Health check endpoint\napp.get('/health', (c) => {\n return c.json({\n status: 'ok',\n service: 'auth',\n timestamp: new Date().toISOString(),\n });\n});\n\n// Mount better-auth handler\napp.on(['POST', 'GET'], '/api/auth/*', (c) => {\n return auth.handler(c.req.raw);\n});\n\nlogger.info({ port: serverConfig.port }, 'Starting auth server');\n\nserve({\n fetch: app.fetch,\n port: serverConfig.port,\n}, (info) => {\n logger.info({ port: info.port }, 'Auth server running');\n});\n`;\n\n\t// .gitignore for auth app\n\tconst gitignore = `node_modules/\ndist/\n.env.local\n*.log\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'apps/auth/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/src/config/env.ts',\n\t\t\tcontent: envTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/src/config/logger.ts',\n\t\t\tcontent: loggerTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/src/auth.ts',\n\t\t\tcontent: authTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/src/index.ts',\n\t\t\tcontent: indexTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/auth/.gitignore',\n\t\t\tcontent: gitignore,\n\t\t},\n\t];\n}\n","/**\n * What a scaffolded project declares, and the keys that derive from it.\n *\n * One place, because three generators need the same three facts: the template\n * writes `new KyselyDatabase<Database, 'Acme'>('Acme')`, the test setup reads\n * `ACME_OWNER_URL` to migrate, and Studio reads `ACME_URL` to connect. Deriving\n * all of them from `@geekmidas/manifest`'s own helpers is what keeps a\n * scaffolded project agreeing with the runtime that discovers it — the same\n * reason the constructs own both faces at runtime.\n */\n\nimport { canonicalId, provideKey, serviceKey } from '@geekmidas/manifest';\nimport type { GeneratedFile } from './templates/index.js';\n\n/** The glob every generated config points at. One glob, every kind. */\nexport const CONSTRUCTS_GLOB = './src/constructs/**/*.ts';\n\nexport interface ScaffoldedConstruct {\n\t/** The canonical id — what the construct is declared under. */\n\tid: string;\n\t/** The key it is reached under in a handler's service record. */\n\tservice: string;\n\t/** The runtime URL key the target publishes. */\n\turlKey: string;\n}\n\nexport interface ScaffoldedDatabase extends ScaffoldedConstruct {\n\t/** The DDL role's URL — what migrations connect as. */\n\townerUrlKey: string;\n}\n\n/**\n * The database a project named `name` declares.\n *\n * Named after the project rather than `Db`, so two apps in one workspace can\n * declare one each without colliding on an id or on an env key.\n */\nexport function databaseFor(name: string): ScaffoldedDatabase {\n\tconst id = canonicalId(name);\n\n\treturn {\n\t\tid,\n\t\tservice: serviceKey(id),\n\t\turlKey: provideKey(id, 'url'),\n\t\townerUrlKey: provideKey(id, 'ownerUrl'),\n\t};\n}\n\n/** The bucket a project named `name` declares. */\nexport function storageFor(name: string): ScaffoldedConstruct {\n\tconst id = canonicalId(`${name}-uploads`);\n\n\treturn { id, service: serviceKey(id), urlKey: provideKey(id, 'url') };\n}\n\n/** The mail sender a project named `name` declares. */\nexport function emailFor(name: string): ScaffoldedConstruct {\n\tconst id = canonicalId(`${name}-mail`);\n\n\treturn { id, service: serviceKey(id), urlKey: provideKey(id, 'url') };\n}\n\n/** The cache a project named `name` declares. */\nexport function cacheFor(name: string): ScaffoldedConstruct {\n\tconst id = canonicalId(`${name}-cache`);\n\n\treturn { id, service: serviceKey(id), urlKey: provideKey(id, 'url') };\n}\n\n/**\n * The files a project's declared database needs.\n *\n * Shared by every template, so a worker and an API declare the same database\n * the same way — and so the schema type, the migration, and the id the test\n * setup reads all come from one place.\n */\nexport function databaseFiles(name: string): GeneratedFile[] {\n\tconst db = databaseFor(name);\n\n\treturn [\n\t\t{\n\t\t\tpath: 'src/constructs/database.ts',\n\t\t\tcontent: `import { KyselyDatabase } from '@geekmidas/constructs/database/kysely';\nimport type { Generated } from 'kysely';\n\n/** Your database schema. Add tables here. */\nexport interface Database {\n users: {\n id: Generated<string>;\n name: string;\n email: string;\n created_at: Generated<Date>;\n };\n}\n\n/**\n * The app's database, declared once.\n *\n * The container, the database inside it, its roles and schema, and\n * \\`${db.urlKey}\\` all derive from this line — \\`gkm dev\\` reconciles them\n * before the server starts, which is why nothing lists \\`postgres\\` anywhere.\n *\n * Both type arguments or neither: TypeScript has no partial type-argument\n * inference, so passing only \\`Database\\` would leave the name at \\`string\\`\n * and widen the service key away from \\`${db.service}\\`.\n */\nexport const database = new KyselyDatabase<Database, '${db.id}'>('${db.id}');\n`,\n\t\t},\n\t\t{\n\t\t\t// The table the scaffolded endpoints and factories expect. Applied by\n\t\t\t// `gkm exec -- pnpm kysely migrate:latest`, or by the test setup.\n\t\t\tpath: 'src/db/migrations/001_create_users.ts',\n\t\t\tcontent: `import type { Kysely } from 'kysely';\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n await db.schema\n .createTable('users')\n .addColumn('id', 'uuid', (col) =>\n col.primaryKey().defaultTo(db.fn('gen_random_uuid')),\n )\n .addColumn('name', 'varchar(255)', (col) => col.notNull())\n .addColumn('email', 'varchar(255)', (col) => col.notNull().unique())\n .addColumn('created_at', 'timestamptz', (col) =>\n col.notNull().defaultTo(db.fn('now')),\n )\n .execute();\n\n await db.schema\n .createIndex('users_email_idx')\n .on('users')\n .column('email')\n .execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n await db.schema.dropTable('users').execute();\n}\n`,\n\t\t},\n\t];\n}\n","import { CONSTRUCTS_GLOB } from '../constructs.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\n\n/**\n * Vitest config content with globalSetup for database-enabled apps\n */\nconst vitestConfigContent = `import { defineConfig } from 'vitest/config';\nimport tsconfigPaths from 'vite-tsconfig-paths';\n\nexport default defineConfig({\n plugins: [tsconfigPaths()],\n test: {\n environment: 'node',\n globalSetup: './test/globalSetup.ts',\n },\n});\n`;\n\n/**\n * Generate configuration files (gkm.config.ts, tsconfig.json, biome.json, turbo.json)\n */\nexport function generateConfigFiles(\n\toptions: TemplateOptions,\n\ttemplate: TemplateConfig,\n): GeneratedFile[] {\n\tconst { telescope, studio, routesStructure } = options;\n\tconst isServerless = template.name === 'serverless';\n\tconst hasWorker = template.name === 'worker';\n\tconst isFullstack = options.template === 'fullstack';\n\n\t// Get routes glob pattern based on structure\n\tconst getRoutesGlob = () => {\n\t\tswitch (routesStructure) {\n\t\t\tcase 'centralized-endpoints':\n\t\t\t\treturn './src/endpoints/**/*.ts';\n\t\t\tcase 'centralized-routes':\n\t\t\t\treturn './src/routes/**/*.ts';\n\t\t\tcase 'domain-based':\n\t\t\t\treturn './src/**/routes/*.ts';\n\t\t}\n\t};\n\n\t// For fullstack template, generate workspace config at root\n\t// Single app config is still generated for non-fullstack monorepo setups\n\tif (isFullstack) {\n\t\t// Workspace config is generated in monorepo.ts for fullstack\n\t\treturn generateSingleAppConfigFiles(options, template, {\n\t\t\ttelescope,\n\t\t\tstudio,\n\t\t\troutesStructure,\n\t\t\tisServerless,\n\t\t\thasWorker,\n\t\t\tgetRoutesGlob,\n\t\t});\n\t}\n\n\t// Build gkm.config.ts for single-app\n\tlet gkmConfig = `import { defineConfig } from '@geekmidas/cli/config';\n\nexport default defineConfig({${\n\t\toptions.monorepo\n\t\t\t? ''\n\t\t\t: `\n // One glob, every kind. What reconcile reads to derive the containers this\n // app needs: a declared database is why a Postgres exists at all, so nothing\n // lists \\`postgres\\` anywhere.\n constructs: '${CONSTRUCTS_GLOB}',`\n\t}\n routes: '${getRoutesGlob()}',\n envParser: './src/config/env#envParser',\n logger: './src/config/logger#logger',`;\n\n\tif (isServerless || hasWorker) {\n\t\tgkmConfig += `\n functions: './src/functions/**/*.ts',`;\n\t}\n\n\t// What no construct implies. `db` and `storage` are deliberately absent —\n\t// the declared KyselyDatabase and ObjectStorage are what bring up the\n\t// Postgres and the MinIO, and reconcile ignores those keys rather than\n\t// obeying them so the two cannot disagree. What is left is a backend\n\t// selection.\n\tif (!options.monorepo) {\n\t\tconst { cache, mail, events } = options.services;\n\n\t\tif (cache || mail || events) {\n\t\t\tgkmConfig += `\n services: {`;\n\t\t\tif (cache) {\n\t\t\t\tgkmConfig += `\n cache: true,`;\n\t\t\t}\n\t\t\tif (mail) {\n\t\t\t\tgkmConfig += `\n mail: true,`;\n\t\t\t}\n\t\t\tif (events) {\n\t\t\t\tgkmConfig += `\n events: '${events}',`;\n\t\t\t}\n\t\t\tgkmConfig += `\n },`;\n\t\t}\n\t}\n\n\tif (hasWorker) {\n\t\tgkmConfig += `\n crons: './src/crons/**/*.ts',\n subscribers: './src/subscribers/**/*.ts',`;\n\t}\n\n\tif (telescope) {\n\t\tgkmConfig += `\n telescope: {\n enabled: true,\n path: '/__telescope',\n },`;\n\t}\n\n\tif (studio) {\n\t\tgkmConfig += `\n studio: './src/config/studio#studio',`;\n\t}\n\n\t// Always add openapi config (output path is fixed to .gkm/openapi.ts)\n\tgkmConfig += `\n openapi: {\n enabled: true,\n },`;\n\n\tgkmConfig += `\n});\n`;\n\n\t// Build tsconfig.json - extends root for monorepo, standalone for non-monorepo\n\t// Using noEmit: true since typecheck is done via turbo\n\tconst tsConfig = options.monorepo\n\t\t? {\n\t\t\t\textends: '../../tsconfig.json',\n\t\t\t\tcompilerOptions: {\n\t\t\t\t\tnoEmit: true,\n\t\t\t\t\tallowImportingTsExtensions: true,\n\t\t\t\t\tbaseUrl: '.',\n\t\t\t\t\tpaths: {\n\t\t\t\t\t\t'~/*': ['./src/*'],\n\t\t\t\t\t\t[`@${options.name}/*`]: ['../../packages/*/src'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tinclude: ['src/**/*.ts'],\n\t\t\t\texclude: ['node_modules', 'dist'],\n\t\t\t}\n\t\t: {\n\t\t\t\tcompilerOptions: {\n\t\t\t\t\ttarget: 'ES2022',\n\t\t\t\t\tmodule: 'NodeNext',\n\t\t\t\t\tmoduleResolution: 'NodeNext',\n\t\t\t\t\tlib: ['ES2022'],\n\t\t\t\t\tstrict: true,\n\t\t\t\t\tesModuleInterop: true,\n\t\t\t\t\tskipLibCheck: true,\n\t\t\t\t\tforceConsistentCasingInFileNames: true,\n\t\t\t\t\tresolveJsonModule: true,\n\t\t\t\t\tnoEmit: true,\n\t\t\t\t\tallowImportingTsExtensions: true,\n\t\t\t\t},\n\t\t\t\tinclude: ['src/**/*.ts'],\n\t\t\t\texclude: ['node_modules', 'dist'],\n\t\t\t};\n\n\t// Skip biome.json and turbo.json for monorepo (they're at root)\n\tif (options.monorepo) {\n\t\tconst files: GeneratedFile[] = [\n\t\t\t{\n\t\t\t\tpath: 'gkm.config.ts',\n\t\t\t\tcontent: gkmConfig,\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: 'tsconfig.json',\n\t\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t\t},\n\t\t];\n\n\t\tif (options.database) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'vitest.config.ts',\n\t\t\t\tcontent: vitestConfigContent,\n\t\t\t});\n\t\t}\n\n\t\treturn files;\n\t}\n\n\t// Build biome.json\n\tconst biomeConfig = {\n\t\t$schema: 'https://biomejs.dev/schemas/2.3.0/schema.json',\n\t\tvcs: {\n\t\t\tenabled: true,\n\t\t\tclientKind: 'git',\n\t\t\tuseIgnoreFile: true,\n\t\t},\n\t\torganizeImports: {\n\t\t\tenabled: true,\n\t\t},\n\t\tformatter: {\n\t\t\tenabled: true,\n\t\t\tindentStyle: 'space',\n\t\t\tindentWidth: 2,\n\t\t\tlineWidth: 80,\n\t\t},\n\t\tjavascript: {\n\t\t\tformatter: {\n\t\t\t\tquoteStyle: 'single',\n\t\t\t\ttrailingCommas: 'all',\n\t\t\t\tsemicolons: 'always',\n\t\t\t\tarrowParentheses: 'always',\n\t\t\t},\n\t\t},\n\t\tlinter: {\n\t\t\tenabled: true,\n\t\t\trules: {\n\t\t\t\trecommended: true,\n\t\t\t\tcorrectness: {\n\t\t\t\t\tnoUnusedImports: 'error',\n\t\t\t\t\tnoUnusedVariables: 'error',\n\t\t\t\t},\n\t\t\t\tstyle: {\n\t\t\t\t\tnoNonNullAssertion: 'off',\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tfiles: {\n\t\t\tignore: ['node_modules', 'dist', '.gkm', 'coverage'],\n\t\t},\n\t};\n\n\t// Build turbo.json\n\tconst turboConfig = {\n\t\t$schema: 'https://turbo.build/schema.json',\n\t\ttasks: {\n\t\t\tbuild: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: ['dist/**'],\n\t\t\t},\n\t\t\tdev: {\n\t\t\t\tcache: false,\n\t\t\t\tpersistent: true,\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\tcache: false,\n\t\t\t},\n\t\t\t'test:once': {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: ['coverage/**'],\n\t\t\t},\n\t\t\ttypecheck: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t\tlint: {\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t\tfmt: {\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t},\n\t};\n\n\tconst files: GeneratedFile[] = [\n\t\t{\n\t\t\tpath: 'gkm.config.ts',\n\t\t\tcontent: gkmConfig,\n\t\t},\n\t\t{\n\t\t\tpath: 'tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'biome.json',\n\t\t\tcontent: `${JSON.stringify(biomeConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'turbo.json',\n\t\t\tcontent: `${JSON.stringify(turboConfig, null, 2)}\\n`,\n\t\t},\n\t];\n\n\tif (options.database) {\n\t\tfiles.push({\n\t\t\tpath: 'vitest.config.ts',\n\t\t\tcontent: vitestConfigContent,\n\t\t});\n\t}\n\n\treturn files;\n}\n\n/**\n * Helper to generate config files for API app in fullstack template\n * (workspace config is at root, so no gkm.config.ts for app)\n */\ninterface ConfigHelperOptions {\n\ttelescope: boolean;\n\tstudio: boolean;\n\troutesStructure: string;\n\tisServerless: boolean;\n\thasWorker: boolean;\n\tgetRoutesGlob: () => string;\n}\n\nfunction generateSingleAppConfigFiles(\n\toptions: TemplateOptions,\n\t_template: TemplateConfig,\n\t_helpers: ConfigHelperOptions,\n): GeneratedFile[] {\n\t// For fullstack, only generate tsconfig.json for the API app\n\t// The workspace gkm.config.ts is generated in monorepo.ts\n\t// Using noEmit: true since typecheck is done via turbo\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\tbaseUrl: '.',\n\t\t\tpaths: {\n\t\t\t\t'~/*': ['./src/*'],\n\t\t\t\t[`@${options.name}/*`]: ['../../packages/*/src'],\n\t\t\t},\n\t\t},\n\t\tinclude: ['src/**/*.ts'],\n\t\texclude: ['node_modules', 'dist'],\n\t};\n\n\tconst files: GeneratedFile[] = [\n\t\t{\n\t\t\tpath: 'tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t];\n\n\tif (options.database) {\n\t\tfiles.push({\n\t\t\tpath: 'vitest.config.ts',\n\t\t\tcontent: vitestConfigContent,\n\t\t});\n\t}\n\n\treturn files;\n}\n","import type { EventsBackend } from '../../types.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\n\nexport interface DatabaseAppConfig {\n\tname: string;\n\tpassword: string;\n}\n\n/**\n * Generate docker-compose.yml based on template and options\n */\nexport function generateDockerFiles(\n\toptions: TemplateOptions,\n\ttemplate: TemplateConfig,\n\tdbApps?: DatabaseAppConfig[],\n): GeneratedFile[] {\n\tconst { database } = options;\n\tconst isServerless = template.name === 'serverless';\n\tconst hasWorker = template.name === 'worker';\n\tconst isFullstack = options.template === 'fullstack';\n\n\tconst services: string[] = [];\n\tconst volumes: string[] = [];\n\tconst files: GeneratedFile[] = [];\n\n\t// PostgreSQL database\n\tif (database) {\n\t\tconst initVolume =\n\t\t\tisFullstack && dbApps?.length\n\t\t\t\t? `\n - ./docker/postgres/init.sh:/docker-entrypoint-initdb.d/init.sh:ro`\n\t\t\t\t: '';\n\n\t\tconst envFile =\n\t\t\tisFullstack && dbApps?.length\n\t\t\t\t? `\n env_file:\n - ./docker/.env`\n\t\t\t\t: '';\n\n\t\tservices.push(` postgres:\n image: postgres:18-alpine\n container_name: ${options.name}-postgres\n restart: unless-stopped${envFile}\n environment:\n POSTGRES_USER: \\${POSTGRES_USER:-postgres}\n POSTGRES_PASSWORD: \\${POSTGRES_PASSWORD:-postgres}\n POSTGRES_DB: \\${POSTGRES_DB:-${options.name.replace(/-/g, '_')}_dev}\n ports:\n - '\\${POSTGRES_HOST_PORT:-5432}:5432'\n volumes:\n - dbdata:/var/lib/postgresql${initVolume}\n healthcheck:\n test: ['CMD-SHELL', 'pg_isready -U $$POSTGRES_USER']\n interval: 5s\n timeout: 5s\n retries: 5`);\n\t\tvolumes.push(' dbdata:');\n\n\t\t// Generate PostgreSQL init script and .env for fullstack template\n\t\tif (isFullstack && dbApps?.length) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'docker/postgres/init.sh',\n\t\t\t\tcontent: generatePostgresInitScript(dbApps, options.services?.events),\n\t\t\t});\n\n\t\t\t// Generate .env file for docker-compose (contains db passwords)\n\t\t\tfiles.push({\n\t\t\t\tpath: 'docker/.env',\n\t\t\t\tcontent: generateDockerEnv(dbApps, options.services?.events),\n\t\t\t});\n\t\t}\n\t}\n\n\t// Redis - different setup for serverless vs standard\n\tif (isServerless) {\n\t\t// Use serverless-redis-http for Lambda compatibility\n\t\tservices.push(` redis:\n image: redis:7-alpine\n container_name: ${options.name}-redis\n restart: unless-stopped\n ports:\n - '\\${REDIS_HOST_PORT:-6379}:6379'\n volumes:\n - redis_data:/data\n healthcheck:\n test: ['CMD', 'redis-cli', 'ping']\n interval: 5s\n timeout: 5s\n retries: 5\n\n serverless-redis:\n image: hiett/serverless-redis-http:latest\n container_name: ${options.name}-serverless-redis\n restart: unless-stopped\n ports:\n - '\\${SRH_HOST_PORT:-8079}:80'\n environment:\n SRH_MODE: env\n SRH_TOKEN: local_dev_token\n SRH_CONNECTION_STRING: redis://redis:6379\n depends_on:\n redis:\n condition: service_healthy`);\n\t\tvolumes.push(' redis_data:');\n\t} else {\n\t\t// Standard Redis for non-serverless templates\n\t\tservices.push(` redis:\n image: redis:7-alpine\n container_name: ${options.name}-redis\n restart: unless-stopped\n ports:\n - '\\${REDIS_HOST_PORT:-6379}:6379'\n volumes:\n - redis_data:/data\n healthcheck:\n test: ['CMD', 'redis-cli', 'ping']\n interval: 5s\n timeout: 5s\n retries: 5`);\n\t\tvolumes.push(' redis_data:');\n\t}\n\n\t// RabbitMQ for worker template\n\tif (hasWorker) {\n\t\tservices.push(` rabbitmq:\n image: rabbitmq:3-management-alpine\n container_name: ${options.name}-rabbitmq\n restart: unless-stopped\n ports:\n - '\\${RABBITMQ_HOST_PORT:-5672}:5672'\n - '\\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672'\n environment:\n RABBITMQ_DEFAULT_USER: guest\n RABBITMQ_DEFAULT_PASS: guest\n volumes:\n - rabbitmq_data:/var/lib/rabbitmq\n healthcheck:\n test: ['CMD', 'rabbitmq-diagnostics', 'check_running']\n interval: 10s\n timeout: 5s\n retries: 5`);\n\t\tvolumes.push(' rabbitmq_data:');\n\t}\n\n\t// Mailpit for email testing\n\tif (options.services?.mail) {\n\t\tservices.push(` mailpit:\n image: axllent/mailpit:latest\n container_name: ${options.name}-mailpit\n restart: unless-stopped\n ports:\n - '\\${SMTP_PORT:-1025}:1025'\n - '\\${MAILPIT_PORT:-8025}:8025'\n environment:\n MP_SMTP_AUTH: \\${SMTP_USER:-${options.name}}:\\${SMTP_PASS:-${options.name}}`);\n\t}\n\n\t// MinIO for S3-compatible object storage\n\tif (options.services?.storage) {\n\t\tservices.push(` minio:\n image: quay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z\n container_name: ${options.name}-minio\n restart: unless-stopped\n entrypoint: sh\n command: -c 'mkdir -p /data/\\${STORAGE_BUCKET:-${options.name}} && /usr/bin/docker-entrypoint.sh server --console-address \":9001\" /data'\n environment:\n MINIO_ROOT_USER: \\${STORAGE_ACCESS_KEY_ID:-${options.name}}\n MINIO_ROOT_PASSWORD: \\${STORAGE_SECRET_ACCESS_KEY:-${options.name}}\n ports:\n - '\\${MINIO_API_HOST_PORT:-9000}:9000'\n - '\\${MINIO_CONSOLE_HOST_PORT:-9001}:9001'\n volumes:\n - minio_data:/data\n healthcheck:\n test: ['CMD', 'mc', 'ready', 'local']\n interval: 10s\n timeout: 5s\n retries: 5`);\n\t\tvolumes.push(' minio_data:');\n\t}\n\n\t// LocalStack for SNS events\n\tif (options.services?.events === 'sns') {\n\t\tservices.push(` localstack:\n image: floci/floci:latest\n container_name: ${options.name}-localstack\n restart: unless-stopped\n environment:\n AWS_DEFAULT_REGION: \\${AWS_REGION:-us-east-1}\n AWS_ACCESS_KEY_ID: \\${AWS_ACCESS_KEY_ID:-localstack}\n AWS_SECRET_ACCESS_KEY: \\${AWS_SECRET_ACCESS_KEY:-localstack}\n ports:\n - '\\${LOCALSTACK_PORT:-4566}:4566'\n volumes:\n - localstack_data:/var/lib/localstack\n healthcheck:\n test: ['CMD', 'curl', '-f', 'http://localhost:4566/_localstack/health']\n interval: 10s\n timeout: 5s\n retries: 5`);\n\t\tvolumes.push(' localstack_data:');\n\t}\n\n\t// RabbitMQ for rabbitmq events (when not already added by worker template)\n\tif (options.services?.events === 'rabbitmq' && !hasWorker) {\n\t\tservices.push(` rabbitmq:\n image: rabbitmq:3-management-alpine\n container_name: ${options.name}-rabbitmq\n restart: unless-stopped\n ports:\n - '\\${RABBITMQ_HOST_PORT:-5672}:5672'\n - '\\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672'\n environment:\n RABBITMQ_DEFAULT_USER: guest\n RABBITMQ_DEFAULT_PASS: guest\n volumes:\n - rabbitmq_data:/var/lib/rabbitmq\n healthcheck:\n test: ['CMD', 'rabbitmq-diagnostics', 'check_running']\n interval: 10s\n timeout: 5s\n retries: 5`);\n\t\tif (!volumes.includes(' rabbitmq_data:')) {\n\t\t\tvolumes.push(' rabbitmq_data:');\n\t\t}\n\t}\n\n\t// Build docker-compose.yml\n\tlet dockerCompose = `# Use \"gkm dev\" or \"gkm test\" to start services.\n# Running \"docker compose up\" directly will not inject secrets or resolve ports.\nservices:\n${services.join('\\n\\n')}\n`;\n\n\tif (volumes.length > 0) {\n\t\tdockerCompose += `\nvolumes:\n${volumes.join('\\n')}\n`;\n\t}\n\n\t// Add docker-compose.yml to files\n\tfiles.push({\n\t\tpath: 'docker-compose.yml',\n\t\tcontent: dockerCompose,\n\t});\n\n\treturn files;\n}\n\n/**\n * Generate .env file for docker-compose with database passwords\n */\nfunction generateDockerEnv(\n\tapps: DatabaseAppConfig[],\n\t_eventsBackend?: EventsBackend,\n): string {\n\tconst envVars = apps.map((app) => {\n\t\tconst envVar = `${app.name.toUpperCase()}_DB_PASSWORD`;\n\t\treturn `${envVar}=${app.password}`;\n\t});\n\n\t// Always include pgboss password for the dedicated pgboss user\n\tenvVars.push(`PGBOSS_DB_PASSWORD=pgboss-dev-password`);\n\n\treturn `# Auto-generated docker environment file\n# Contains database passwords for docker-compose postgres init\n# This file is gitignored - do not commit to version control\n${envVars.join('\\n')}\n`;\n}\n\n/**\n * Generate PostgreSQL init shell script that creates per-app users with separate schemas.\n * Uses idempotent DO blocks so the script can be re-run safely.\n * - api user: uses public schema\n * - auth user: uses auth schema with search_path=auth\n * - pgboss user: uses pgboss schema (when events === 'pgboss')\n */\nfunction generatePostgresInitScript(\n\tapps: DatabaseAppConfig[],\n\t_eventsBackend?: EventsBackend,\n): string {\n\tconst userCreations = apps.map((app) => {\n\t\tconst userName = app.name.replace(/-/g, '_');\n\t\tconst envVar = `${app.name.toUpperCase()}_DB_PASSWORD`;\n\t\tconst isApi = app.name === 'api';\n\t\tconst schemaName = isApi ? 'public' : userName;\n\n\t\tif (isApi) {\n\t\t\treturn `\n# Create ${app.name} user (uses public schema) - idempotent\necho \"Creating user ${userName}...\"\npsql -v ON_ERROR_STOP=1 --username \"$POSTGRES_USER\" --dbname \"$POSTGRES_DB\" <<-EOSQL\n DO \\\\$\\\\$\n BEGIN\n IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${userName}') THEN\n CREATE USER ${userName} WITH PASSWORD '$${envVar}';\n ELSE\n ALTER USER ${userName} WITH PASSWORD '$${envVar}';\n END IF;\n END\n \\\\$\\\\$;\n GRANT ALL ON SCHEMA public TO ${userName};\n ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${userName};\n ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ${userName};\nEOSQL\n`;\n\t\t}\n\t\treturn `\n# Create ${app.name} user with dedicated schema - idempotent\necho \"Creating user ${userName} with schema ${schemaName}...\"\npsql -v ON_ERROR_STOP=1 --username \"$POSTGRES_USER\" --dbname \"$POSTGRES_DB\" <<-EOSQL\n DO \\\\$\\\\$\n BEGIN\n IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${userName}') THEN\n CREATE USER ${userName} WITH PASSWORD '$${envVar}';\n ELSE\n ALTER USER ${userName} WITH PASSWORD '$${envVar}';\n END IF;\n END\n \\\\$\\\\$;\n CREATE SCHEMA IF NOT EXISTS ${schemaName} AUTHORIZATION ${userName};\n ALTER USER ${userName} SET search_path TO ${schemaName};\n GRANT USAGE ON SCHEMA ${schemaName} TO ${userName};\n GRANT ALL ON ALL TABLES IN SCHEMA ${schemaName} TO ${userName};\n GRANT ALL ON ALL SEQUENCES IN SCHEMA ${schemaName} TO ${userName};\n ALTER DEFAULT PRIVILEGES IN SCHEMA ${schemaName} GRANT ALL ON TABLES TO ${userName};\n ALTER DEFAULT PRIVILEGES IN SCHEMA ${schemaName} GRANT ALL ON SEQUENCES TO ${userName};\nEOSQL\n`;\n\t});\n\n\t// Always add pgboss user and schema.\n\t// pgboss reuses the same database with a dedicated user/schema,\n\t// so subscribers work out of the box without explicit events config.\n\tconst pgbossBlock = `\n# Create pgboss user with dedicated schema - idempotent\necho \"Creating pgboss user and schema...\"\npsql -v ON_ERROR_STOP=1 --username \"$POSTGRES_USER\" --dbname \"$POSTGRES_DB\" <<-EOSQL\n DO \\\\$\\\\$\n BEGIN\n IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'pgboss') THEN\n CREATE USER pgboss WITH PASSWORD '$PGBOSS_DB_PASSWORD';\n ELSE\n ALTER USER pgboss WITH PASSWORD '$PGBOSS_DB_PASSWORD';\n END IF;\n END\n \\\\$\\\\$;\n CREATE SCHEMA IF NOT EXISTS pgboss AUTHORIZATION pgboss;\n ALTER USER pgboss SET search_path TO pgboss;\n GRANT USAGE ON SCHEMA pgboss TO pgboss;\n GRANT ALL ON ALL TABLES IN SCHEMA pgboss TO pgboss;\n GRANT ALL ON ALL SEQUENCES IN SCHEMA pgboss TO pgboss;\n ALTER DEFAULT PRIVILEGES IN SCHEMA pgboss GRANT ALL ON TABLES TO pgboss;\n ALTER DEFAULT PRIVILEGES IN SCHEMA pgboss GRANT ALL ON SEQUENCES TO pgboss;\nEOSQL\n`;\n\n\t// Add extensions\n\tconst extensions = `\n# Create extensions\necho \"Creating extensions...\"\npsql -v ON_ERROR_STOP=1 --username \"$POSTGRES_USER\" --dbname \"$POSTGRES_DB\" <<-EOSQL\n CREATE EXTENSION IF NOT EXISTS pg_trgm;\n CREATE EXTENSION IF NOT EXISTS citext;\nEOSQL\n`;\n\n\treturn `#!/bin/bash\nset -e\n\n# Auto-generated PostgreSQL init script (idempotent - safe to re-run)\n# Creates per-app users with separate schemas in a single database\n# - api: uses public schema\n# - auth: uses auth schema (search_path=auth)\n# - pgboss: uses pgboss schema for event processing\n${extensions}\n${userCreations.join('\\n')}${pgbossBlock}\necho \"Database initialization complete!\"\n`;\n}\n","import type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\n\n/**\n * Generate environment-related files (.gitignore only).\n * Note: .env files are no longer generated. Use `gkm secrets:init` to initialize\n * encrypted secrets stored in `.gkm/secrets/{stage}.json` with keys stored at\n * `~/.gkm/{project-name}/{stage}.key`.\n */\nexport function generateEnvFiles(\n\toptions: TemplateOptions,\n\t_template: TemplateConfig,\n): GeneratedFile[] {\n\tconst files: GeneratedFile[] = [];\n\n\t// Only add .gitignore for non-monorepo (monorepo has it at root)\n\tif (!options.monorepo) {\n\t\tconst gitignore = `# Dependencies\nnode_modules/\n\n# Build output\ndist/\n.gkm/\n\n# Environment (legacy - use gkm secrets instead)\n.env\n.env.local\n.env.*.local\n\n# IDE\n.idea/\n.vscode/\n*.swp\n*.swo\n\n# OS\n.DS_Store\nThumbs.db\n\n# Logs\n*.log\nnpm-debug.log*\nyarn-debug.log*\npnpm-debug.log*\n\n# Test coverage\ncoverage/\n\n# TypeScript cache\n*.tsbuildinfo\n`;\n\t\tfiles.push({\n\t\t\tpath: '.gitignore',\n\t\t\tcontent: gitignore,\n\t\t});\n\t}\n\n\treturn files;\n}\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate an Expo mobile app for the fullstack template.\n *\n * Mirrors the structure of rezgo/apps/app: NativeWind for styling,\n * expo-router for navigation, better-auth + magic link, React Query\n * for data, all wired into the toolbox's typed API client.\n */\nexport function generateExpoAppFiles(\n\toptions: TemplateOptions,\n): GeneratedFile[] {\n\tif (!options.monorepo || options.template !== 'fullstack') {\n\t\treturn [];\n\t}\n\n\tconst packageName = `@${options.name}/app`;\n\tconst apiPackage = `@${options.name}/api`;\n\tconst modelsPackage = `@${options.name}/models`;\n\t// Expo bundle id / scheme — kept simple; the user can rename later.\n\tconst slug = options.name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\tconst scheme = slug.replace(/-/g, '');\n\tconst bundleId = `com.${scheme}.app`;\n\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tmain: 'expo-router/entry',\n\t\tprivate: true,\n\t\tscripts: {\n\t\t\tdev: 'gkm exec -- expo start -c',\n\t\t\tios: 'expo start --ios',\n\t\t\tandroid: 'expo start --android',\n\t\t\tweb: 'expo start --web',\n\t\t\ttypecheck: 'tsc --noEmit',\n\t\t},\n\t\tdependencies: {\n\t\t\t[apiPackage]: 'workspace:*',\n\t\t\t[modelsPackage]: 'workspace:*',\n\t\t\t'@better-auth/expo': '~1.2.0',\n\t\t\t'@geekmidas/client': GEEKMIDAS_VERSIONS['@geekmidas/client'],\n\t\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t\t'@react-navigation/native': '^7.1.0',\n\t\t\t'@tanstack/react-query': '~5.80.0',\n\t\t\t'better-auth': '~1.2.0',\n\t\t\texpo: '~55.0.0',\n\t\t\t'expo-constants': '~55.0.0',\n\t\t\t'expo-dev-client': '~55.0.0',\n\t\t\t'expo-linking': '~55.0.0',\n\t\t\t'expo-router': '~55.0.0',\n\t\t\t'expo-secure-store': '~14.2.0',\n\t\t\t'expo-splash-screen': '~55.0.0',\n\t\t\t'expo-status-bar': '~55.0.0',\n\t\t\tnativewind: '~4.2.0',\n\t\t\treact: '19.2.0',\n\t\t\t'react-dom': '19.2.0',\n\t\t\t'react-native': '0.83.6',\n\t\t\t'react-native-gesture-handler': '~2.30.0',\n\t\t\t'react-native-reanimated': '~4.2.0',\n\t\t\t'react-native-safe-area-context': '~5.6.0',\n\t\t\t'react-native-screens': '~4.23.0',\n\t\t\t'react-native-web': '~0.21.0',\n\t\t\ttailwindcss: '~3.4.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@babel/core': '^7.25.0',\n\t\t\t'@types/react': '~19.0.0',\n\t\t\ttypescript: '~5.8.2',\n\t\t},\n\t};\n\n\tconst appConfig = `import type { ConfigContext, ExpoConfig } from '@expo/config';\n\n/**\n * Expo runtime config. Public env vars (\\`EXPO_PUBLIC_*\\`) are inlined\n * at build time; everything else is read at runtime from \\`extra\\`.\n */\nexport default function config(_context: ConfigContext): ExpoConfig {\n return {\n name: '${options.name}',\n slug: '${slug}',\n version: '0.0.1',\n orientation: 'portrait',\n icon: './assets/icon.png',\n scheme: '${scheme}',\n userInterfaceStyle: 'automatic',\n newArchEnabled: true,\n ios: {\n supportsTablet: true,\n bundleIdentifier: '${bundleId}',\n },\n android: {\n package: '${bundleId}',\n adaptiveIcon: {\n foregroundImage: './assets/adaptive-icon.png',\n backgroundColor: '#ffffff',\n },\n edgeToEdgeEnabled: true,\n },\n web: {\n output: 'static',\n },\n plugins: [\n 'expo-router',\n 'expo-secure-store',\n [\n 'expo-splash-screen',\n {\n image: './assets/splash.png',\n imageWidth: 200,\n resizeMode: 'contain',\n backgroundColor: '#ffffff',\n },\n ],\n ],\n experiments: {\n typedRoutes: true,\n },\n };\n}\n`;\n\n\tconst easJson = {\n\t\tcli: {\n\t\t\tversion: '>= 10.2.1',\n\t\t\tappVersionSource: 'remote',\n\t\t},\n\t\tbuild: {\n\t\t\tdev: {\n\t\t\t\tdevelopmentClient: true,\n\t\t\t\tdistribution: 'internal',\n\t\t\t\tenvironment: 'development',\n\t\t\t\tenv: {\n\t\t\t\t\tEXPO_PUBLIC_API_URL: 'http://localhost:3000',\n\t\t\t\t\tEXPO_PUBLIC_AUTH_URL: 'http://localhost:3002',\n\t\t\t\t},\n\t\t\t},\n\t\t\tpreview: {\n\t\t\t\tdistribution: 'internal',\n\t\t\t\tenvironment: 'preview',\n\t\t\t\tenv: {\n\t\t\t\t\tEXPO_PUBLIC_API_URL: 'https://api.example.com',\n\t\t\t\t\tEXPO_PUBLIC_AUTH_URL: 'https://auth.example.com',\n\t\t\t\t},\n\t\t\t},\n\t\t\tproduction: {\n\t\t\t\tautoIncrement: true,\n\t\t\t\tenvironment: 'production',\n\t\t\t\tenv: {\n\t\t\t\t\tEXPO_PUBLIC_API_URL: 'https://api.example.com',\n\t\t\t\t\tEXPO_PUBLIC_AUTH_URL: 'https://auth.example.com',\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n\n\tconst babelConfig = `module.exports = function (api) {\n api.cache(true);\n return {\n presets: [\n ['babel-preset-expo', { jsxImportSource: 'nativewind' }],\n 'nativewind/babel',\n ],\n };\n};\n`;\n\n\tconst metroConfig = `const { getDefaultConfig } = require('expo/metro-config');\nconst { withNativeWind } = require('nativewind/metro');\n\nconst config = getDefaultConfig(__dirname);\n\nmodule.exports = withNativeWind(config, { input: './global.css' });\n`;\n\n\tconst tsConfig = {\n\t\textends: 'expo/tsconfig.base',\n\t\tcompilerOptions: {\n\t\t\tstrict: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\ttypes: ['nativewind/types'],\n\t\t\tpaths: {\n\t\t\t\t'@/*': ['./*'],\n\t\t\t\t[modelsPackage]: ['../../packages/models/src'],\n\t\t\t\t[`${modelsPackage}/*`]: ['../../packages/models/src/*'],\n\t\t\t\t[`${apiPackage}/client`]: ['../../apps/api/.gkm/openapi.ts'],\n\t\t\t},\n\t\t},\n\t\tinclude: [\n\t\t\t'**/*.ts',\n\t\t\t'**/*.tsx',\n\t\t\t'.expo/types/**/*.ts',\n\t\t\t'expo-env.d.ts',\n\t\t\t'nativewind-env.d.ts',\n\t\t],\n\t};\n\n\tconst tailwindConfig = `import type { Config } from 'tailwindcss';\n\nexport default {\n content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],\n presets: [require('nativewind/preset')],\n theme: {\n extend: {},\n },\n plugins: [],\n} satisfies Config;\n`;\n\n\tconst globalCss = `@tailwind base;\n@tailwind components;\n@tailwind utilities;\n`;\n\n\tconst expoEnvDts = `/// <reference types=\"expo/types\" />\\n`;\n\n\tconst nativewindEnvDts = `/// <reference types=\"nativewind/types\" />\\n`;\n\n\tconst configTs = `import { EnvironmentParser } from '@geekmidas/envkit';\n\n/**\n * Public app config. EXPO_PUBLIC_* vars are inlined into the bundle at\n * build time, so they're safe to read in client code.\n */\nconst envParser = new EnvironmentParser({\n EXPO_PUBLIC_API_URL: process.env.EXPO_PUBLIC_API_URL,\n EXPO_PUBLIC_AUTH_URL: process.env.EXPO_PUBLIC_AUTH_URL,\n});\n\nexport const config = envParser\n .create((get) => ({\n apiUrl: get('EXPO_PUBLIC_API_URL').string(),\n authUrl: get('EXPO_PUBLIC_AUTH_URL').string(),\n }))\n .parse();\n`;\n\n\tconst queryClientTs = `import { QueryClient } from '@tanstack/react-query';\n\nexport const queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 60 * 1000,\n },\n },\n});\n`;\n\n\tconst authClientTs = `import { expoClient } from '@better-auth/expo/client';\nimport { magicLinkClient } from 'better-auth/client/plugins';\nimport { createAuthClient } from 'better-auth/react';\nimport * as SecureStore from 'expo-secure-store';\n\nimport { config } from '@/config.ts';\n\nexport const STORAGE_PREFIX = '${scheme}';\nexport const COOKIE_STORE_KEY = \\`\\${STORAGE_PREFIX}_cookie\\`;\n\nexport const authClient = createAuthClient({\n baseURL: config.authUrl,\n plugins: [\n expoClient({\n scheme: '${scheme}',\n storagePrefix: STORAGE_PREFIX,\n storage: SecureStore,\n }),\n magicLinkClient(),\n ],\n});\n\nexport const { signIn, useSession } = authClient;\n\nexport async function signOut() {\n try {\n await authClient.signOut();\n } finally {\n await SecureStore.deleteItemAsync(COOKIE_STORE_KEY);\n }\n}\n`;\n\n\tconst apiTs = `import { createApi } from '${apiPackage}/client';\n\nimport { config } from '@/config.ts';\nimport { authClient } from './auth-client.ts';\nimport { queryClient } from './query-client.ts';\n\nexport function createAppApi(options?: { headers?: Record<string, string> }) {\n return createApi({\n baseURL: config.apiUrl,\n queryClient,\n headers: options?.headers,\n onRequest: (cfg) => {\n const cookie = authClient.getCookie();\n const next = { ...cfg, credentials: 'omit' as const };\n if (cookie) {\n return {\n ...next,\n headers: { ...(next.headers ?? {}), Cookie: cookie },\n };\n }\n return next;\n },\n });\n}\n`;\n\n\tconst apiContextTsx = `import { createContext, type ReactNode, use } from 'react';\nimport type { createAppApi } from './api.ts';\n\nexport type Api = ReturnType<typeof createAppApi>;\n\nconst ApiContext = createContext<Api | null>(null);\n\nexport function ApiProvider({\n api,\n children,\n}: {\n api: Api;\n children: ReactNode;\n}) {\n return <ApiContext.Provider value={api}>{children}</ApiContext.Provider>;\n}\n\nexport function useApi(): Api {\n const api = use(ApiContext);\n if (!api) {\n throw new Error('useApi must be used within an ApiProvider');\n }\n return api;\n}\n`;\n\n\tconst layoutTsx = `import '../global.css';\n\nimport { QueryClientProvider } from '@tanstack/react-query';\nimport { Stack, useRouter, useSegments } from 'expo-router';\nimport { StatusBar } from 'expo-status-bar';\nimport { useEffect, useMemo } from 'react';\nimport 'react-native-reanimated';\n\nimport { authClient } from '@/lib/auth-client.ts';\nimport { ApiProvider } from '@/lib/api-context.tsx';\nimport { createAppApi } from '@/lib/api.ts';\nimport { queryClient } from '@/lib/query-client.ts';\n\nfunction AuthGate({ children }: { children: React.ReactNode }) {\n const router = useRouter();\n const segments = useSegments();\n const { data: session, isPending } = authClient.useSession();\n\n useEffect(() => {\n if (isPending) return;\n const onAuthRoute = segments[0] === 'login';\n if (!session && !onAuthRoute) {\n router.replace('/login');\n } else if (session && onAuthRoute) {\n router.replace('/');\n }\n }, [isPending, session, segments, router]);\n\n return <>{children}</>;\n}\n\nexport default function RootLayout() {\n const api = useMemo(() => createAppApi(), []);\n\n return (\n <QueryClientProvider client={queryClient}>\n <ApiProvider api={api}>\n <AuthGate>\n <Stack screenOptions={{ headerShown: false }}>\n <Stack.Screen name=\"index\" />\n <Stack.Screen name=\"login\" />\n </Stack>\n </AuthGate>\n <StatusBar style=\"auto\" />\n </ApiProvider>\n </QueryClientProvider>\n );\n}\n`;\n\n\tconst indexTsx = `import { Pressable, Text, View } from 'react-native';\nimport { SafeAreaView } from 'react-native-safe-area-context';\n\nimport { signOut, useSession } from '@/lib/auth-client.ts';\nimport { useApi } from '@/lib/api-context.tsx';\n\nexport default function Home() {\n const { data: session } = useSession();\n const api = useApi();\n const { data: health } = api.useQuery('GET /health', {});\n\n return (\n <SafeAreaView className=\"flex-1 bg-white\">\n <View className=\"flex-1 items-center justify-center gap-4 px-6\">\n <Text className=\"text-3xl font-bold text-slate-900\">\n Welcome to ${options.name}\n </Text>\n {session ? (\n <Text className=\"text-sm text-slate-500\">\n Signed in as {session.user.email}\n </Text>\n ) : null}\n {health ? (\n <Text className=\"text-xs text-slate-400\">\n API: {JSON.stringify(health)}\n </Text>\n ) : null}\n <Pressable\n onPress={signOut}\n className=\"mt-4 rounded-lg bg-slate-900 px-6 py-3\"\n >\n <Text className=\"font-semibold text-white\">Sign out</Text>\n </Pressable>\n </View>\n </SafeAreaView>\n );\n}\n`;\n\n\tconst loginTsx = `import * as Linking from 'expo-linking';\nimport { useState } from 'react';\nimport {\n KeyboardAvoidingView,\n Platform,\n Pressable,\n Text,\n TextInput,\n View,\n} from 'react-native';\nimport { SafeAreaView } from 'react-native-safe-area-context';\n\nimport { authClient } from '@/lib/auth-client.ts';\n\nconst EMAIL_RE = /^[^\\\\s@]+@[^\\\\s@]+\\\\.[^\\\\s@]+$/;\n\nexport default function LoginScreen() {\n const [email, setEmail] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [sent, setSent] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n async function handleSubmit() {\n if (!EMAIL_RE.test(email.trim())) {\n setError('Enter a valid email address');\n return;\n }\n setError(null);\n setSubmitting(true);\n\n const callbackURL = Linking.createURL('/').replace(/^(\\\\w+):\\\\/\\\\/\\\\//, '$1://');\n try {\n const result = await authClient.signIn.magicLink({\n email: email.trim(),\n callbackURL,\n });\n if (result.error) {\n setError(result.error.message ?? 'Could not send magic link');\n return;\n }\n setSent(true);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Something went wrong');\n } finally {\n setSubmitting(false);\n }\n }\n\n if (sent) {\n return (\n <SafeAreaView className=\"flex-1 bg-white\">\n <View className=\"flex-1 items-center justify-center px-6\">\n <Text className=\"text-2xl font-bold text-slate-900\">\n Check your inbox\n </Text>\n <Text className=\"mt-3 text-center text-slate-500\">\n We sent a sign-in link to {email.trim()}\n </Text>\n </View>\n </SafeAreaView>\n );\n }\n\n return (\n <SafeAreaView className=\"flex-1 bg-white\">\n <KeyboardAvoidingView\n behavior={Platform.OS === 'ios' ? 'padding' : undefined}\n className=\"flex-1\"\n >\n <View className=\"flex-1 justify-center px-6\">\n <Text className=\"text-3xl font-bold text-slate-900\">Sign in</Text>\n <Text className=\"mt-1 text-sm text-slate-500\">\n We'll email you a one-time sign-in link.\n </Text>\n\n <Text className=\"mt-6 text-xs font-semibold uppercase text-slate-500\">\n Email\n </Text>\n <TextInput\n value={email}\n onChangeText={(v) => {\n setEmail(v);\n if (error) setError(null);\n }}\n autoCapitalize=\"none\"\n autoCorrect={false}\n keyboardType=\"email-address\"\n placeholder=\"you@example.com\"\n placeholderTextColor=\"#94a3b8\"\n editable={!submitting}\n className=\"mt-2 rounded-lg border border-slate-300 px-4 py-3\"\n />\n {error ? (\n <Text className=\"mt-2 text-sm text-red-600\">{error}</Text>\n ) : null}\n\n <Pressable\n onPress={handleSubmit}\n disabled={submitting}\n className=\"mt-6 items-center rounded-lg bg-slate-900 py-4 disabled:opacity-60\"\n >\n <Text className=\"font-semibold text-white\">\n {submitting ? 'Sending…' : 'Email me a sign-in link'}\n </Text>\n </Pressable>\n </View>\n </KeyboardAvoidingView>\n </SafeAreaView>\n );\n}\n`;\n\n\tconst gitignore = `node_modules/\n.expo/\ndist/\n*.log\n.env*.local\nios/\nandroid/\n.eas/\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'apps/app/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{ path: 'apps/app/app.config.ts', content: appConfig },\n\t\t{\n\t\t\tpath: 'apps/app/eas.json',\n\t\t\tcontent: `${JSON.stringify(easJson, null, 2)}\\n`,\n\t\t},\n\t\t{ path: 'apps/app/babel.config.js', content: babelConfig },\n\t\t{ path: 'apps/app/metro.config.js', content: metroConfig },\n\t\t{\n\t\t\tpath: 'apps/app/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{ path: 'apps/app/tailwind.config.ts', content: tailwindConfig },\n\t\t{ path: 'apps/app/global.css', content: globalCss },\n\t\t{ path: 'apps/app/expo-env.d.ts', content: expoEnvDts },\n\t\t{ path: 'apps/app/nativewind-env.d.ts', content: nativewindEnvDts },\n\t\t{ path: 'apps/app/config.ts', content: configTs },\n\t\t{ path: 'apps/app/lib/query-client.ts', content: queryClientTs },\n\t\t{ path: 'apps/app/lib/auth-client.ts', content: authClientTs },\n\t\t{ path: 'apps/app/lib/api.ts', content: apiTs },\n\t\t{ path: 'apps/app/lib/api-context.tsx', content: apiContextTsx },\n\t\t{ path: 'apps/app/app/_layout.tsx', content: layoutTsx },\n\t\t{ path: 'apps/app/app/index.tsx', content: indexTsx },\n\t\t{ path: 'apps/app/app/login.tsx', content: loginTsx },\n\t\t{ path: 'apps/app/.gitignore', content: gitignore },\n\t];\n}\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\n\n/**\n * Generate packages/models for shared Zod schemas (monorepo only)\n */\nexport function generateModelsPackage(\n\toptions: TemplateOptions,\n): GeneratedFile[] {\n\tif (!options.monorepo) {\n\t\treturn [];\n\t}\n\n\t// Package name based on project name\n\tconst packageName = `@${options.name}/models`;\n\n\t// package.json for models\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\texports: {\n\t\t\t'./*': './src/*.ts',\n\t\t},\n\t\tscripts: {\n\t\t\ttypecheck: 'tsc --noEmit',\n\t\t},\n\t\tdependencies: {},\n\t\tdevDependencies: {\n\t\t\ttypescript: '~5.8.2',\n\t\t},\n\t};\n\n\t// tsconfig.json for models - extends root config\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t},\n\t\tinclude: ['src/**/*.ts'],\n\t\texclude: ['node_modules', 'dist'],\n\t};\n\n\t// common.ts - shared utility schemas\n\tconst commonTs = `import { z } from 'zod';\n\n// ============================================\n// Common Schemas\n// ============================================\n\nexport const IdSchema = z.uuid();\n\nexport const IdParamsSchema = z.object({\n id: IdSchema,\n});\n\nexport const TimestampsSchema = z.object({\n createdAt: z.coerce.date(),\n updatedAt: z.coerce.date(),\n});\n\nexport const PaginationSchema = z.object({\n page: z.coerce.number().int().positive().default(1),\n limit: z.coerce.number().int().positive().max(100).default(20),\n});\n\nexport const PaginatedResponseSchema = <T extends z.ZodTypeAny>(itemSchema: T) =>\n z.object({\n items: z.array(itemSchema),\n total: z.number(),\n page: z.number(),\n limit: z.number(),\n totalPages: z.number(),\n });\n\n// ============================================\n// Type Exports\n// ============================================\n\nexport type Id = z.infer<typeof IdSchema>;\nexport type IdParams = z.infer<typeof IdParamsSchema>;\nexport type Timestamps = z.infer<typeof TimestampsSchema>;\nexport type Pagination = z.infer<typeof PaginationSchema>;\n`;\n\n\t// user.ts - user-related schemas\n\tconst userTs = `import { z } from 'zod';\nimport { IdSchema, TimestampsSchema } from './common.ts';\n\n// ============================================\n// User Schemas\n// ============================================\n\nexport const UserSchema = z.object({\n id: IdSchema,\n email: z.email(),\n name: z.string().min(1).max(100),\n ...TimestampsSchema.shape,\n});\n\nexport const CreateUserSchema = UserSchema.omit({\n id: true,\n createdAt: true,\n updatedAt: true,\n});\n\nexport const UpdateUserSchema = CreateUserSchema.partial();\n\n// ============================================\n// Response Schemas\n// ============================================\n\nexport const UserResponseSchema = UserSchema.pick({\n id: true,\n name: true,\n email: true,\n});\n\nexport const ListUsersResponseSchema = z.object({\n users: z.array(UserSchema.pick({ id: true, name: true })),\n});\n\n// ============================================\n// Type Exports\n// ============================================\n\nexport type User = z.infer<typeof UserSchema>;\nexport type CreateUser = z.infer<typeof CreateUserSchema>;\nexport type UpdateUser = z.infer<typeof UpdateUserSchema>;\nexport type UserResponse = z.infer<typeof UserResponseSchema>;\nexport type ListUsersResponse = z.infer<typeof ListUsersResponseSchema>;\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'packages/models/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/models/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/models/src/common.ts',\n\t\t\tcontent: commonTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/models/src/user.ts',\n\t\t\tcontent: userTs,\n\t\t},\n\t];\n}\n","import type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate monorepo root files (pnpm-workspace.yaml, root package.json, etc.)\n */\nexport function generateMonorepoFiles(\n\toptions: TemplateOptions,\n\t_template: TemplateConfig,\n): GeneratedFile[] {\n\tif (!options.monorepo) {\n\t\treturn [];\n\t}\n\n\tconst isFullstack = options.template === 'fullstack';\n\n\t// Root package.json for monorepo\n\tconst rootPackageJson = {\n\t\tname: options.name,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\tpackageManager: 'pnpm@10.13.1',\n\t\tscripts: {\n\t\t\tdev: isFullstack ? 'gkm dev' : 'turbo dev',\n\t\t\tbuild: isFullstack ? 'gkm build' : 'turbo build',\n\t\t\ttest: isFullstack ? 'gkm test' : 'turbo test',\n\t\t\t'test:once': isFullstack ? 'gkm test --run' : 'turbo test:once',\n\t\t\ttypecheck: 'turbo typecheck',\n\t\t\tlint: 'biome lint .',\n\t\t\tfmt: 'biome format . --write',\n\t\t\t'fmt:check': 'biome format .',\n\t\t\t...(isFullstack\n\t\t\t\t? { storybook: 'pnpm --filter ./packages/ui storybook' }\n\t\t\t\t: {}),\n\t\t\t...(options.deployTarget === 'dokploy'\n\t\t\t\t? { deploy: 'gkm deploy --provider dokploy --stage production' }\n\t\t\t\t: {}),\n\t\t},\n\t\tdependencies: {\n\t\t\tzod: '~4.1.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@biomejs/biome': '~2.3.0',\n\t\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t\tesbuild: '~0.27.0',\n\t\t\ttsx: '~4.20.0',\n\t\t\tturbo: '~2.3.0',\n\t\t\ttypescript: '~5.8.2',\n\t\t\tvitest: '~4.0.0',\n\t\t},\n\t};\n\n\t// pnpm-workspace.yaml - detect folder structure from apiPath\n\tconst apiPathParts = options.apiPath.split('/');\n\tconst appsFolder = apiPathParts[0] || 'apps';\n\n\tconst pnpmWorkspace = `packages:\n - '${appsFolder}/*'\n - 'packages/*'\n`;\n\n\t// Root biome.json\n\tconst biomeConfig = {\n\t\t$schema: 'https://biomejs.dev/schemas/2.3.0/schema.json',\n\t\tvcs: {\n\t\t\tenabled: true,\n\t\t\tclientKind: 'git',\n\t\t\tuseIgnoreFile: true,\n\t\t},\n\t\torganizeImports: {\n\t\t\tenabled: true,\n\t\t},\n\t\tformatter: {\n\t\t\tenabled: true,\n\t\t\tindentStyle: 'space',\n\t\t\tindentWidth: 2,\n\t\t\tlineWidth: 80,\n\t\t},\n\t\tjavascript: {\n\t\t\tformatter: {\n\t\t\t\tquoteStyle: 'single',\n\t\t\t\ttrailingCommas: 'all',\n\t\t\t\tsemicolons: 'always',\n\t\t\t\tarrowParentheses: 'always',\n\t\t\t},\n\t\t},\n\t\tlinter: {\n\t\t\tenabled: true,\n\t\t\trules: {\n\t\t\t\trecommended: true,\n\t\t\t\tcorrectness: {\n\t\t\t\t\tnoUnusedImports: 'error',\n\t\t\t\t\tnoUnusedVariables: 'error',\n\t\t\t\t},\n\t\t\t\tstyle: {\n\t\t\t\t\tnoNonNullAssertion: 'off',\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tfiles: {\n\t\t\tignore: ['node_modules', 'dist', '.gkm', 'coverage'],\n\t\t},\n\t};\n\n\t// Root turbo.json\n\tconst turboConfig = {\n\t\t$schema: 'https://turbo.build/schema.json',\n\t\ttasks: {\n\t\t\tbuild: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: ['dist/**'],\n\t\t\t},\n\t\t\tdev: {\n\t\t\t\tcache: false,\n\t\t\t\tpersistent: true,\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\tcache: false,\n\t\t\t},\n\t\t\t'test:once': {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: ['coverage/**'],\n\t\t\t},\n\t\t\ttypecheck: {\n\t\t\t\tdependsOn: ['^build'],\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t\tlint: {\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t\tfmt: {\n\t\t\t\toutputs: [],\n\t\t\t},\n\t\t},\n\t};\n\n\t// Root .gitignore\n\tconst gitignore = `# Dependencies\nnode_modules/\n\n# Build output\ndist/\n.gkm/\n\n# Environment\n.env\n.env.local\n.env.*.local\ndocker/.env\n\n# IDE\n.idea/\n*.swp\n*.swo\n\n# OS\n.DS_Store\nThumbs.db\n\n# Logs\n*.log\nnpm-debug.log*\nyarn-debug.log*\npnpm-debug.log*\n\n# Test coverage\ncoverage/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Turbo\n.turbo/\n`;\n\n\t// Root tsconfig.json - base config for all packages\n\t// Using turbo typecheck to run tsc --noEmit in each app/package\n\tconst tsConfig = {\n\t\tcompilerOptions: {\n\t\t\ttarget: 'ES2022',\n\t\t\tmodule: 'NodeNext',\n\t\t\tmoduleResolution: 'NodeNext',\n\t\t\tlib: ['ES2022'],\n\t\t\tstrict: true,\n\t\t\tesModuleInterop: true,\n\t\t\tskipLibCheck: true,\n\t\t\tforceConsistentCasingInFileNames: true,\n\t\t\tresolveJsonModule: true,\n\t\t},\n\t\texclude: ['node_modules', 'dist'],\n\t};\n\n\t// Vitest config for workspace\n\tconst vitestConfig = `import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n test: {\n globals: true,\n environment: 'node',\n include: ['apps/**/*.{test,spec}.ts', 'packages/**/*.{test,spec}.ts'],\n exclude: ['**/node_modules/**', '**/dist/**'],\n coverage: {\n provider: 'v8',\n reporter: ['text', 'json', 'html'],\n exclude: ['**/node_modules/**', '**/dist/**', '**/*.d.ts'],\n },\n },\n});\n`;\n\n\t// VSCode settings for consistent development experience\n\tconst vscodeSettings = {\n\t\t'search.exclude': {\n\t\t\t'**/.sst': true,\n\t\t\t'**/.gkm': true,\n\t\t\t'**/.turbo': true,\n\t\t},\n\t\t'editor.formatOnSave': true,\n\t\t'editor.defaultFormatter': 'biomejs.biome',\n\t\t'editor.codeActionsOnSave': {\n\t\t\t'source.fixAll.biome': 'always',\n\t\t\t'source.organizeImports.biome': 'always',\n\t\t\t'source.organizeImports': 'always',\n\t\t},\n\t\t'[typescriptreact]': {\n\t\t\t'editor.defaultFormatter': 'biomejs.biome',\n\t\t},\n\t\t'[typescript]': {\n\t\t\t'editor.defaultFormatter': 'biomejs.biome',\n\t\t},\n\t\t'[javascript]': {\n\t\t\t'editor.defaultFormatter': 'biomejs.biome',\n\t\t},\n\t\t'[json]': {\n\t\t\t'editor.defaultFormatter': 'biomejs.biome',\n\t\t},\n\t\t'cSpell.words': [\n\t\t\t'betterauth',\n\t\t\t'dokploy',\n\t\t\t'envkit',\n\t\t\t'geekmidas',\n\t\t\t'healthcheck',\n\t\t\t'kysely',\n\t\t\t'testkit',\n\t\t\t'timestamptz',\n\t\t\t'turborepo',\n\t\t\toptions.name,\n\t\t],\n\t};\n\n\t// VSCode extensions recommendations\n\tconst vscodeExtensions = {\n\t\trecommendations: [\n\t\t\t'biomejs.biome',\n\t\t\t'streetsidesoftware.code-spell-checker',\n\t\t\t'dbaeumer.vscode-eslint',\n\t\t\t'ms-azuretools.vscode-docker',\n\t\t],\n\t};\n\n\tconst files: GeneratedFile[] = [\n\t\t{\n\t\t\tpath: 'package.json',\n\t\t\tcontent: `${JSON.stringify(rootPackageJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'pnpm-workspace.yaml',\n\t\t\tcontent: pnpmWorkspace,\n\t\t},\n\t\t{\n\t\t\tpath: 'tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'biome.json',\n\t\t\tcontent: `${JSON.stringify(biomeConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'turbo.json',\n\t\t\tcontent: `${JSON.stringify(turboConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'vitest.config.ts',\n\t\t\tcontent: vitestConfig,\n\t\t},\n\t\t{\n\t\t\tpath: '.gitignore',\n\t\t\tcontent: gitignore,\n\t\t},\n\t\t{\n\t\t\tpath: '.vscode/settings.json',\n\t\t\tcontent: `${JSON.stringify(vscodeSettings, null, '\\t')}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: '.vscode/extensions.json',\n\t\t\tcontent: `${JSON.stringify(vscodeExtensions, null, '\\t')}\\n`,\n\t\t},\n\t];\n\n\t// Add workspace config for fullstack template\n\tif (isFullstack) {\n\t\tfiles.push({\n\t\t\tpath: 'gkm.config.ts',\n\t\t\tcontent: generateWorkspaceConfig(options),\n\t\t});\n\t}\n\n\treturn files;\n}\n\n/**\n * Generate the frontend app entry for the workspace config based on the\n * selected `frontendFramework`. Defaults to Next.js for backwards compat.\n */\nfunction generateFrontendAppEntry(options: TemplateOptions): string {\n\tswitch (options.frontendFramework) {\n\t\tcase 'tanstack-start':\n\t\t\treturn `web: {\n type: 'web',\n framework: 'tanstack-start',\n path: 'apps/web',\n port: 3001,\n dependencies: ['api', 'auth'],\n config: {\n client: './src/config/client.ts',\n server: './src/config/server.ts',\n },\n },`;\n\t\tcase 'expo':\n\t\t\treturn `app: {\n type: 'mobile',\n framework: 'expo',\n path: 'apps/app',\n port: 8081,\n dependencies: ['api', 'auth'],\n },`;\n\t\tdefault:\n\t\t\treturn `web: {\n type: 'web',\n framework: 'nextjs',\n path: 'apps/web',\n port: 3001,\n dependencies: ['api', 'auth'],\n config: {\n client: './src/config/client.ts',\n server: './src/config/server.ts',\n },\n },`;\n\t}\n}\n\n/**\n * Generate gkm.config.ts with defineWorkspace for fullstack template\n */\nfunction generateWorkspaceConfig(options: TemplateOptions): string {\n\tconst { telescope, services, deployTarget, routesStructure } = options;\n\n\t// Get routes glob pattern\n\tconst getRoutesGlob = (): string => {\n\t\tswitch (routesStructure) {\n\t\t\tcase 'centralized-endpoints':\n\t\t\t\treturn './src/endpoints/**/*.ts';\n\t\t\tcase 'centralized-routes':\n\t\t\t\treturn './src/routes/**/*.ts';\n\t\t\tcase 'domain-based':\n\t\t\t\treturn './src/**/routes/*.ts';\n\t\t}\n\t};\n\n\tlet config = `import { defineWorkspace } from '@geekmidas/cli/config';\n\nexport default defineWorkspace({\n name: '${options.name}',\n apps: {\n api: {\n type: 'backend',\n path: 'apps/api',\n port: 3000,\n routes: '${getRoutesGlob()}',\n envParser: './src/config/env#envParser',\n logger: './src/config/logger#logger',\n dependencies: ['auth'],`;\n\n\tif (telescope) {\n\t\tconfig += `\n telescope: {\n enabled: true,\n path: '/__telescope',\n },`;\n\t}\n\n\tconfig += `\n openapi: {\n enabled: true,\n },\n },\n auth: {\n type: 'backend',\n path: 'apps/auth',\n port: 3002,\n entry: './src/index.ts',\n framework: 'better-auth',\n envParser: './src/config/env#envParser',\n logger: './src/config/logger#logger',\n },\n ${generateFrontendAppEntry(options)}\n },\n shared: {\n packages: ['packages/*'],\n models: {\n path: 'packages/models',\n schema: 'zod',\n },\n },`;\n\n\t// Add services if any are selected\n\tif (\n\t\tservices.db ||\n\t\tservices.cache ||\n\t\tservices.mail ||\n\t\tservices.storage ||\n\t\tservices.events\n\t) {\n\t\tconfig += `\n services: {`;\n\t\tif (services.db) {\n\t\t\tconfig += `\n db: true,`;\n\t\t}\n\t\tif (services.cache) {\n\t\t\tconfig += `\n cache: true,`;\n\t\t}\n\t\tif (services.mail) {\n\t\t\tconfig += `\n mail: true,`;\n\t\t}\n\t\tif (services.storage) {\n\t\t\tconfig += `\n storage: true,`;\n\t\t}\n\t\tif (services.events) {\n\t\t\tconfig += `\n events: '${services.events}',`;\n\t\t}\n\t\tconfig += `\n },`;\n\t}\n\n\t// Add deploy config if dokploy is selected\n\tif (deployTarget === 'dokploy') {\n\t\tconfig += `\n deploy: {\n default: 'dokploy',\n },`;\n\t}\n\n\t// Always enable secrets for workspace dev environment\n\tconfig += `\n secrets: {\n enabled: true,\n },`;\n\n\tconfig += `\n});\n`;\n\n\treturn config;\n}\n","import {\n\tcacheFor,\n\tdatabaseFiles,\n\tdatabaseFor,\n\temailFor,\n\tstorageFor,\n} from '../constructs.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from './index.js';\n\nexport const apiTemplate: TemplateConfig = {\n\tname: 'api',\n\tdescription: 'Full API with auth, database, services',\n\n\tdependencies: {\n\t\t'@geekmidas/audit': GEEKMIDAS_VERSIONS['@geekmidas/audit'],\n\t\t'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],\n\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t'@geekmidas/events': GEEKMIDAS_VERSIONS['@geekmidas/events'],\n\t\t'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],\n\t\t'@geekmidas/rate-limit': GEEKMIDAS_VERSIONS['@geekmidas/rate-limit'],\n\t\t'@geekmidas/schema': GEEKMIDAS_VERSIONS['@geekmidas/schema'],\n\t\t'@geekmidas/services': GEEKMIDAS_VERSIONS['@geekmidas/services'],\n\t\t'@geekmidas/errors': GEEKMIDAS_VERSIONS['@geekmidas/errors'],\n\t\t'@geekmidas/auth': GEEKMIDAS_VERSIONS['@geekmidas/auth'],\n\t\t'@hono/node-server': '~1.14.1',\n\t\thono: '~4.8.2',\n\t\tpino: '~9.6.0',\n\t\tzod: '~4.1.0',\n\t},\n\n\tdevDependencies: {\n\t\t'@biomejs/biome': '~2.3.0',\n\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t'@types/node': '~22.0.0',\n\t\tesbuild: '~0.27.0',\n\t\ttsx: '~4.20.0',\n\t\tturbo: '~2.3.0',\n\t\ttypescript: '~5.8.2',\n\t\tvitest: '~4.0.0',\n\t},\n\n\tscripts: {\n\t\tdev: 'gkm dev',\n\t\tbuild: 'gkm build',\n\t\ttest: 'vitest',\n\t\t'test:once': 'vitest run',\n\t\ttypecheck: 'tsc --noEmit',\n\t\tlint: 'biome lint .',\n\t\tfmt: 'biome format . --write',\n\t\t'fmt:check': 'biome format .',\n\t},\n\n\tfiles: (options: TemplateOptions): GeneratedFile[] => {\n\t\tconst { loggerType, routesStructure, monorepo, name, services } = options;\n\n\t\t// The ids and env keys the scaffolded constructs own. Derived, so the\n\t\t// files below and the runtime that discovers them cannot disagree.\n\t\tconst bucket = storageFor(name);\n\t\tconst kv = cacheFor(name);\n\t\tconst db = databaseFor(name);\n\t\tconst mail = emailFor(name);\n\n\t\t// Single-app projects have no `~/*` alias, so what a generated file\n\t\t// imports depends on where it will sit.\n\t\tconst src = (path: string) => (monorepo ? `~/${path}` : `./${path}`);\n\n\t\t// Whether this app declares its infrastructure.\n\t\t//\n\t\t// Single-app projects do: the config gets a `constructs` glob, reconcile\n\t\t// derives their containers, and the handler reaches them by edge. The\n\t\t// fullstack workspace does not yet — its auth app's database role is\n\t\t// created by `docker/postgres/init.sh` and its URL comes from a\n\t\t// per-app secret, neither of which reconcile knows about, so declaring\n\t\t// only the API's half would leave auth pointing at a container that is\n\t\t// no longer the one running.\n\t\tconst declares = !monorepo;\n\n\t\tconst loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';\n\nexport const logger = createLogger();\n`;\n\n\t\t// Models package import path for monorepo\n\t\tconst modelsImport = monorepo ? `@${name}/models` : null;\n\n\t\t// Get route path based on structure\n\t\tconst getRoutePath = (file: string) => {\n\t\t\tswitch (routesStructure) {\n\t\t\t\tcase 'centralized-endpoints':\n\t\t\t\t\treturn `src/endpoints/${file}`;\n\t\t\t\tcase 'centralized-routes':\n\t\t\t\t\treturn `src/routes/${file}`;\n\t\t\t\tcase 'domain-based': {\n\t\t\t\t\tconst parts = file.split('/');\n\t\t\t\t\tif (parts.length === 1) {\n\t\t\t\t\t\treturn `src/${file.replace('.ts', '')}/routes/index.ts`;\n\t\t\t\t\t}\n\t\t\t\t\treturn `src/${parts[0]}/routes/${parts.slice(1).join('/')}`;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Where an endpoint file reaches the surface from — one level deeper when\n\t\t// the routes are nested by domain.\n\t\tconst apiImport =\n\t\t\troutesStructure === 'domain-based'\n\t\t\t\t? '../../constructs/api.js'\n\t\t\t\t: '../constructs/api.js';\n\n\t\tconst files: GeneratedFile[] = [\n\t\t\t// The application's HTTP surface. Endpoints are built from it, so each\n\t\t\t// one carries the logger and environment parser its adaptor needs —\n\t\t\t// nothing has to be named in config and nothing printed into the\n\t\t\t// generated handler.\n\t\t\t{\n\t\t\t\tpath: 'src/constructs/api.ts',\n\t\t\t\tcontent: `import { RestApi } from '@geekmidas/constructs/rest-api';\nimport { logger } from '../config/logger.ts';\n\nexport const api = new RestApi('Api', {\n // Typed out rather than omitted: an API that ships open because a field was\n // left off is the one default worth refusing to have.\n defaultAuthorizer: 'none',\n\n // The actual logger, not a path to one. Every endpoint built from this\n // surface runs with it, and so does the generated entry.\n logger,\n});\n`,\n\t\t\t},\n\n\t\t\t// src/config/env.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/env.ts',\n\t\t\t\tcontent: `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { EnvironmentParser } from '@geekmidas/envkit';\n\nexport const envParser = new EnvironmentParser({ ...process.env, ...Credentials });\n\n// Global config - only minimal shared values\n// Service-specific config should be parsed in each service\nexport const config = envParser\n .create((get) => ({\n nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),\n stage: get('STAGE').enum(['development', 'staging', 'production']).default('development'),\n }))\n .parse();\n`,\n\t\t\t},\n\n\t\t\t// src/config/logger.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/logger.ts',\n\t\t\t\tcontent: loggerContent,\n\t\t\t},\n\n\t\t\t// health endpoint\n\t\t\t{\n\t\t\t\tpath: getRoutePath('health.ts'),\n\t\t\t\tcontent: monorepo\n\t\t\t\t\t? `import { z } from 'zod';\nimport { router } from '~/router.ts';\n\nexport const healthEndpoint = router\n .get('/health')\n .output(z.object({\n status: z.string(),\n timestamp: z.string(),\n }))\n .handle(async () => ({\n status: 'ok',\n timestamp: new Date().toISOString(),\n }));\n`\n\t\t\t\t\t: `import { z } from 'zod';\nimport { router } from './router.ts';\n\nexport const healthEndpoint = router\n .get('/health')\n .output(z.object({\n status: z.string(),\n timestamp: z.string(),\n }))\n .handle(async () => ({\n status: 'ok',\n timestamp: new Date().toISOString(),\n }));\n`,\n\t\t\t},\n\n\t\t\t// users endpoints\n\t\t\t{\n\t\t\t\tpath: getRoutePath('users/list.ts'),\n\t\t\t\tcontent: modelsImport\n\t\t\t\t\t? `import { ListUsersResponseSchema } from '${modelsImport}/user';\nimport { router } from '${src('router.ts')}';\n\nexport const listUsersEndpoint = router\n .get('/users')\n .output(ListUsersResponseSchema)\n${\n\toptions.database\n\t\t? ` // \\`db\\` is here because the router named the database construct.\n .handle(async ({ db }) => ({\n users: await db.selectFrom('users').select(['id', 'name']).execute(),\n }));\n`\n\t\t: ` .handle(async () => ({\n users: [\n { id: '550e8400-e29b-41d4-a716-446655440001', name: 'Alice' },\n { id: '550e8400-e29b-41d4-a716-446655440002', name: 'Bob' },\n ],\n }));\n`\n}`\n\t\t\t\t\t: `import { z } from 'zod';\nimport { router } from '${src('router.ts')}';\n\nconst UserSchema = z.object({\n id: z.string(),\n name: z.string(),\n});\n\nexport const listUsersEndpoint = router\n .get('/users')\n .output(z.object({\n users: z.array(UserSchema),\n }))\n${\n\toptions.database\n\t\t? ` // \\`db\\` is here because the router named the database construct.\n .handle(async ({ db }) => ({\n users: await db.selectFrom('users').select(['id', 'name']).execute(),\n }));\n`\n\t\t: ` .handle(async () => ({\n users: [\n { id: '1', name: 'Alice' },\n { id: '2', name: 'Bob' },\n ],\n }));\n`\n}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tpath: getRoutePath('users/get.ts'),\n\t\t\t\tcontent: modelsImport\n\t\t\t\t\t? `import { IdParamsSchema } from '${modelsImport}/common';\nimport { UserResponseSchema } from '${modelsImport}/user';\nimport { router } from '${src('router.ts')}';\n\nexport const getUserEndpoint = router\n .get('/users/:id')\n .params(IdParamsSchema)\n .output(UserResponseSchema)\n .handle(async ({ params }) => ({\n id: params.id,\n name: 'Alice',\n email: 'alice@example.com',\n }));\n`\n\t\t\t\t\t: `import { z } from 'zod';\nimport { router } from '${src('router.ts')}';\n\nexport const getUserEndpoint = router\n .get('/users/:id')\n .params(z.object({ id: z.string() }))\n .output(z.object({\n id: z.string(),\n name: z.string(),\n email: z.email(),\n }))\n .handle(async ({ params }) => ({\n id: params.id,\n name: 'Alice',\n email: 'alice@example.com',\n }));\n`,\n\t\t\t},\n\t\t];\n\n\t\t// Add auth service for monorepo (calls auth app for session)\n\t\tif (options.monorepo) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/services/auth.ts',\n\t\t\t\tcontent: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';\n\nexport interface Session {\n user: {\n id: string;\n email: string;\n name: string;\n };\n}\n\nexport interface AuthClient {\n getSession: (cookie: string) => Promise<Session | null>;\n}\n\nexport const authService = {\n serviceName: 'auth' as const,\n async register({ envParser, context }: ServiceRegisterOptions) {\n const logger = context.getLogger();\n\n const config = envParser\n .create((get) => ({\n url: get('AUTH_URL').string(),\n }))\n .parse();\n\n logger.info({ authUrl: config.url }, 'Auth service configured');\n\n return {\n getSession: async (cookie: string): Promise<Session | null> => {\n const res = await fetch(\\`\\${config.url}/api/auth/get-session\\`, {\n headers: { cookie },\n });\n if (!res.ok) return null;\n return res.json();\n },\n };\n },\n} satisfies Service<'auth', AuthClient>;\n`,\n\t\t\t});\n\n\t\t\t// Add router with session\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/router.ts',\n\t\t\t\tcontent: `import { UnauthorizedError } from '@geekmidas/errors';\nimport { api } from './constructs/api.ts';${\n\t\t\t\t\toptions.database\n\t\t\t\t\t\t? `\nimport { database } from './constructs/database.ts';`\n\t\t\t\t\t\t: ''\n\t\t\t\t}\nimport { authService, type Session } from './services/auth.ts';\nimport { logger } from './config/logger.ts';\n\n/**\n * The shared endpoint factory — no session required.\n *${\n\t\toptions.database\n\t\t\t? `\n * Naming the database construct is what puts \\`db\\` in every handler built from\n * this router. Depend on other constructs per endpoint with \\`.dependsOn([…])\\`.`\n\t\t\t: `\n * Depend on constructs per endpoint with \\`.dependsOn([…])\\`.`\n }\n */\nexport const router = api.endpoints${options.database ? '.database(database)' : ''};\n\n// The auth client available, but the session not enforced.\nexport const r = router.services([authService]);\n\n// Requires an active session — throws when there is none.\nexport const sessionRouter = r.session<Session>(async ({ services, header }) => {\n const cookie = header('cookie') || '';\n const session = await services.auth.getSession(cookie);\n\n if (!session?.user) {\n throw new UnauthorizedError('No active session');\n }\n\n return session;\n});\n`,\n\t\t\t});\n\n\t\t\t// Add protected endpoint example\n\t\t\tfiles.push({\n\t\t\t\tpath: getRoutePath('profile.ts'),\n\t\t\t\tcontent: `import { z } from 'zod';\nimport { sessionRouter } from '~/router.ts';\n\nexport const profileEndpoint = sessionRouter\n .get('/profile')\n .output(z.object({\n id: z.string(),\n email: z.string(),\n name: z.string(),\n }))\n .handle(async ({ session }) => session.user);\n`,\n\t\t\t});\n\t\t}\n\n\t\t// The non-monorepo router. The monorepo one is written above, with the\n\t\t// session extractor its auth app needs.\n\t\tif (!monorepo) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/router.ts',\n\t\t\t\tcontent: `import { api } from './constructs/api.ts';${\n\t\t\t\t\toptions.database\n\t\t\t\t\t\t? `\nimport { database } from './constructs/database.ts';`\n\t\t\t\t\t\t: ''\n\t\t\t\t}\n\n/**\n * The shared endpoint factory.\n *${\n\t\toptions.database\n\t\t\t? `\n * Naming the database construct is what puts \\`db\\` in every handler built from\n * this router. Depend on other constructs per endpoint with \\`.dependsOn([…])\\`.`\n\t\t\t: `\n * Depend on constructs per endpoint with \\`.dependsOn([…])\\`.`\n }\n */\nexport const router = api.endpoints${options.database ? '.database(database)' : ''};\n`,\n\t\t\t});\n\t\t}\n\n\t\t// The database — a construct, not a hand-written service.\n\t\tif (options.database && declares) {\n\t\t\tfiles.push(...databaseFiles(name));\n\t\t}\n\n\t\t// Object storage — MinIO locally, S3 deployed, one declaration for both.\n\t\tif (services.storage && declares) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/constructs/storage.ts',\n\t\t\t\tcontent: `import { ObjectStorage } from '@geekmidas/constructs/object-storage';\n\n/**\n * A bucket, declared once.\n *\n * Reach it from an endpoint with \\`.dependsOn([uploads])\\`, which is what makes\n * \\`services.${bucket.service}\\` exist and type — and what tells the deploy\n * target to grant that handler S3 access, and nothing else.\n */\nexport const uploads = new ObjectStorage('${bucket.id}');\n`,\n\t\t\t});\n\t\t}\n\n\t\t// Mail. Mailpit locally, SES/Resend/SMTP deployed — one client either\n\t\t// way, because every backend speaks SMTP.\n\t\tif (services.mail && declares) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/constructs/email.ts',\n\t\t\t\tcontent: `import { Email } from '@geekmidas/constructs/email';\n\n/**\n * Outbound mail, declared once.\n *\n * Add React templates to \\`templates\\` and \\`sendTemplate\\` becomes typed\n * against them. Reach it with \\`.dependsOn([email])\\` for\n * \\`services.${mail.service}\\`; who delivers it is \\`services.mail\\` in\n * \\`gkm.config.ts\\`.\n */\nexport const email = new Email('${mail.id}', { templates: {} });\n`,\n\t\t\t});\n\t\t}\n\n\t\t// A cache. Where it lives when deployed is `services.cache` in the\n\t\t// config; the application code is the same either way.\n\t\tif (services.cache && declares) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/constructs/cache.ts',\n\t\t\t\tcontent: `import { Cache } from '@geekmidas/constructs/cache';\n\n/**\n * A cache, declared once.\n *\n * Reach it with \\`.dependsOn([cache])\\` for \\`services.${kv.service}\\`. This\n * form says the app caches and leaves *where* to the deployment: which backend\n * serves it — Upstash, ElastiCache, or a database — is \\`services.cache\\` in\n * \\`gkm.config.ts\\`, because the same code caches into any of them.\n *\n * To say it caches in a particular database instead, declare it from that\n * database — \\`database.cache()\\` — and entries become a table in it, in its\n * schema and reached by its role, which config can no longer move.\n */\nexport const cache = new Cache('${kv.id}');\n`,\n\t\t\t});\n\t\t}\n\n\t\t// The workspace path, until its auth app declares its own half.\n\t\tif (options.database && !declares) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/services/database.ts',\n\t\t\t\tcontent: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';\nimport { Kysely, PostgresDialect } from 'kysely';\nimport pg from 'pg';\n\n// Define your database schema\nexport interface Database {\n users: {\n id: string;\n name: string;\n email: string;\n created_at: Date;\n };\n}\n\nexport const databaseService = {\n serviceName: 'database' as const,\n async register({ envParser, context }: ServiceRegisterOptions) {\n const logger = context.getLogger();\n logger.info('Connecting to database');\n\n const config = envParser\n .create((get) => ({\n url: get('DATABASE_URL').string(),\n }))\n .parse();\n\n const db = new Kysely<Database>({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: config.url }),\n }),\n });\n\n logger.info('Database connection established');\n return db;\n },\n} satisfies Service<'database', Kysely<Database>>;\n`,\n\t\t\t});\n\t\t}\n\n\t\t// Add Telescope config if enabled\n\t\tif (options.telescope) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/telescope.ts',\n\t\t\t\tcontent: `import { Telescope } from '@geekmidas/telescope';\nimport { InMemoryStorage } from '@geekmidas/telescope/storage/memory';\n\nexport const telescope = new Telescope({\n storage: new InMemoryStorage({ maxEntries: 100 }),\n enabled: process.env.NODE_ENV === 'development',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\t// Add Studio config if enabled (requires database)\n\t\tif (options.studio && options.database) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/studio.ts',\n\t\t\t\tcontent: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';\nimport { Kysely, PostgresDialect } from 'kysely';\nimport pg from 'pg';\nimport type { Database } from '${src(declares ? 'constructs/database.ts' : 'services/database.ts')}';\nimport { envParser } from '${src('config/env.ts')}';\n\nconst studioConfig = envParser\n .create((get) => ({\n databaseUrl: get('${declares ? db.urlKey : 'DATABASE_URL'}').string(),\n }))\n .parse();\n\n// Create a Kysely instance for Studio\nconst db = new Kysely<Database>({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: studioConfig.databaseUrl }),\n }),\n});\n\nexport const studio = new Studio<Database>({\n monitoring: {\n storage: new InMemoryMonitoringStorage({ maxEntries: 100 }),\n },\n data: {\n db,\n cursor: { field: 'id', direction: Direction.Desc },\n },\n enabled: process.env.NODE_ENV === 'development',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\treturn files;\n\t},\n};\n","import { databaseFiles, databaseFor } from '../constructs.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from './index.js';\n\nexport const minimalTemplate: TemplateConfig = {\n\tname: 'minimal',\n\tdescription: 'Basic health endpoint',\n\n\tdependencies: {\n\t\t'@geekmidas/audit': GEEKMIDAS_VERSIONS['@geekmidas/audit'],\n\t\t'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],\n\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],\n\t\t'@geekmidas/rate-limit': GEEKMIDAS_VERSIONS['@geekmidas/rate-limit'],\n\t\t'@geekmidas/schema': GEEKMIDAS_VERSIONS['@geekmidas/schema'],\n\t\t'@hono/node-server': '~1.14.1',\n\t\thono: '~4.8.2',\n\t\tpino: '~9.6.0',\n\t\tzod: '~4.1.0',\n\t},\n\n\tdevDependencies: {\n\t\t'@biomejs/biome': '~2.3.0',\n\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t'@types/node': '~22.0.0',\n\t\tesbuild: '~0.27.0',\n\t\ttsx: '~4.20.0',\n\t\tturbo: '~2.3.0',\n\t\ttypescript: '~5.8.2',\n\t\tvitest: '~4.0.0',\n\t},\n\n\tscripts: {\n\t\tdev: 'gkm dev',\n\t\tbuild: 'gkm build',\n\t\ttest: 'vitest',\n\t\t'test:once': 'vitest run',\n\t\ttypecheck: 'tsc --noEmit',\n\t\tlint: 'biome lint .',\n\t\tfmt: 'biome format . --write',\n\t\t'fmt:check': 'biome format .',\n\t},\n\n\tfiles: (options: TemplateOptions): GeneratedFile[] => {\n\t\tconst { loggerType, routesStructure, name } = options;\n\n\t\t// The id and env keys the scaffolded database owns.\n\t\tconst db = databaseFor(name);\n\n\t\tconst loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';\n\nexport const logger = createLogger();\n`;\n\n\t\t// Get route path based on structure\n\t\tconst getRoutePath = (file: string) => {\n\t\t\tswitch (routesStructure) {\n\t\t\t\tcase 'centralized-endpoints':\n\t\t\t\t\treturn `src/endpoints/${file}`;\n\t\t\t\tcase 'centralized-routes':\n\t\t\t\t\treturn `src/routes/${file}`;\n\t\t\t\tcase 'domain-based':\n\t\t\t\t\treturn `src/${file.replace('.ts', '')}/routes/index.ts`;\n\t\t\t}\n\t\t};\n\n\t\t// Where an endpoint file reaches the surface from — one level deeper when\n\t\t// the routes are nested by domain.\n\t\tconst apiImport =\n\t\t\troutesStructure === 'domain-based'\n\t\t\t\t? '../../constructs/api.js'\n\t\t\t\t: '../constructs/api.js';\n\n\t\tconst files: GeneratedFile[] = [\n\t\t\t// The application's HTTP surface. Endpoints are built from it, so each\n\t\t\t// one carries the logger and environment parser its adaptor needs —\n\t\t\t// nothing has to be named in config and nothing printed into the\n\t\t\t// generated handler.\n\t\t\t{\n\t\t\t\tpath: 'src/constructs/api.ts',\n\t\t\t\tcontent: `import { RestApi } from '@geekmidas/constructs/rest-api';\nimport { logger } from '../config/logger.ts';\n\nexport const api = new RestApi('Api', {\n // Typed out rather than omitted: an API that ships open because a field was\n // left off is the one default worth refusing to have.\n defaultAuthorizer: 'none',\n\n // The actual logger, not a path to one. Every endpoint built from this\n // surface runs with it, and so does the generated entry.\n logger,\n});\n`,\n\t\t\t},\n\n\t\t\t// src/config/env.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/env.ts',\n\t\t\t\tcontent: `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { EnvironmentParser } from '@geekmidas/envkit';\n\nexport const envParser = new EnvironmentParser({ ...process.env, ...Credentials });\n\n// Global config - only minimal shared values\n// Service-specific config should be parsed in each service\nexport const config = envParser\n .create((get) => ({\n nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),\n stage: get('STAGE').enum(['development', 'staging', 'production']).default('development'),\n }))\n .parse();\n`,\n\t\t\t},\n\n\t\t\t// src/config/logger.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/logger.ts',\n\t\t\t\tcontent: loggerContent,\n\t\t\t},\n\n\t\t\t// health endpoint\n\t\t\t{\n\t\t\t\tpath: getRoutePath('health.ts'),\n\t\t\t\tcontent: `import { z } from 'zod';\nimport { api } from '${apiImport}';\n\nexport const healthEndpoint = api\n .get('/health')\n .output(z.object({\n status: z.string(),\n timestamp: z.string(),\n }))\n .handle(async () => ({\n status: 'ok',\n timestamp: new Date().toISOString(),\n }));\n`,\n\t\t\t},\n\t\t];\n\n\t\t// The database — a construct, not a hand-written service.\n\t\tif (options.database) {\n\t\t\tfiles.push(...databaseFiles(name));\n\t\t}\n\n\t\t// Add Telescope config if enabled\n\t\tif (options.telescope) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/telescope.ts',\n\t\t\t\tcontent: `import { Telescope } from '@geekmidas/telescope';\nimport { InMemoryStorage } from '@geekmidas/telescope/storage/memory';\n\nexport const telescope = new Telescope({\n storage: new InMemoryStorage({ maxEntries: 100 }),\n enabled: process.env.NODE_ENV === 'development',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\t// Add Studio config if enabled (requires database)\n\t\tif (options.studio && options.database) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/studio.ts',\n\t\t\t\tcontent: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';\nimport { Kysely, PostgresDialect } from 'kysely';\nimport pg from 'pg';\nimport type { Database } from './constructs/database.ts';\nimport { envParser } from './config/env.ts';\n\n// The key the database construct publishes — not a hand-written DATABASE_URL.\nconst studioConfig = envParser\n .create((get) => ({\n databaseUrl: get('${db.urlKey}').string(),\n }))\n .parse();\n\n// Create a Kysely instance for Studio\nconst db = new Kysely<Database>({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: studioConfig.databaseUrl }),\n }),\n});\n\nexport const studio = new Studio<Database>({\n monitoring: {\n storage: new InMemoryMonitoringStorage({ maxEntries: 100 }),\n },\n data: {\n db,\n cursor: { field: 'id', direction: Direction.Desc },\n },\n enabled: process.env.NODE_ENV === 'development',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\treturn files;\n\t},\n};\n","import { databaseFiles } from '../constructs.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from './index.js';\n\nexport const serverlessTemplate: TemplateConfig = {\n\tname: 'serverless',\n\tdescription: 'AWS Lambda handlers',\n\n\tdependencies: {\n\t\t'@geekmidas/audit': GEEKMIDAS_VERSIONS['@geekmidas/audit'],\n\t\t'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],\n\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],\n\t\t'@geekmidas/cloud': GEEKMIDAS_VERSIONS['@geekmidas/cloud'],\n\t\t'@geekmidas/rate-limit': GEEKMIDAS_VERSIONS['@geekmidas/rate-limit'],\n\t\t'@geekmidas/schema': GEEKMIDAS_VERSIONS['@geekmidas/schema'],\n\t\t'@hono/node-server': '~1.14.1',\n\t\thono: '~4.8.2',\n\t\tpino: '~9.6.0',\n\t},\n\n\tdevDependencies: {\n\t\t'@biomejs/biome': '~2.3.0',\n\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t'@types/aws-lambda': '~8.10.92',\n\t\t'@types/node': '~22.0.0',\n\t\ttsx: '~4.20.0',\n\t\tturbo: '~2.3.0',\n\t\ttypescript: '~5.8.2',\n\t\tvitest: '~4.0.0',\n\t},\n\n\tscripts: {\n\t\tdev: 'gkm dev',\n\t\tbuild: 'gkm build --provider aws-apigatewayv2',\n\t\ttest: 'vitest',\n\t\t'test:once': 'vitest run',\n\t\ttypecheck: 'tsc --noEmit',\n\t\tlint: 'biome lint .',\n\t\tfmt: 'biome format . --write',\n\t\t'fmt:check': 'biome format .',\n\t},\n\n\tfiles: (options: TemplateOptions): GeneratedFile[] => {\n\t\tconst { loggerType, routesStructure, name } = options;\n\n\t\tconst loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';\n\nexport const logger = createLogger();\n`;\n\n\t\t// Get route path based on structure\n\t\tconst getRoutePath = (file: string) => {\n\t\t\tswitch (routesStructure) {\n\t\t\t\tcase 'centralized-endpoints':\n\t\t\t\t\treturn `src/endpoints/${file}`;\n\t\t\t\tcase 'centralized-routes':\n\t\t\t\t\treturn `src/routes/${file}`;\n\t\t\t\tcase 'domain-based':\n\t\t\t\t\treturn `src/${file.replace('.ts', '')}/routes/index.ts`;\n\t\t\t}\n\t\t};\n\n\t\t// Where an endpoint file reaches the surface from — one level deeper when\n\t\t// the routes are nested by domain.\n\t\tconst apiImport =\n\t\t\troutesStructure === 'domain-based'\n\t\t\t\t? '../../constructs/api.js'\n\t\t\t\t: '../constructs/api.js';\n\n\t\tconst files: GeneratedFile[] = [\n\t\t\t// The application's HTTP surface. Endpoints are built from it, so each\n\t\t\t// one carries the logger and environment parser its adaptor needs —\n\t\t\t// nothing has to be named in config and nothing printed into the\n\t\t\t// generated handler.\n\t\t\t{\n\t\t\t\tpath: 'src/constructs/api.ts',\n\t\t\t\tcontent: `import { RestApi } from '@geekmidas/constructs/rest-api';\nimport { logger } from '../config/logger.ts';\n\nexport const api = new RestApi('Api', {\n // Typed out rather than omitted: an API that ships open because a field was\n // left off is the one default worth refusing to have.\n defaultAuthorizer: 'none',\n\n // The actual logger, not a path to one. Every endpoint built from this\n // surface runs with it, and so does the generated entry.\n logger,\n});\n`,\n\t\t\t},\n\n\t\t\t// src/config/env.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/env.ts',\n\t\t\t\tcontent: `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { EnvironmentParser } from '@geekmidas/envkit';\n\nexport const envParser = new EnvironmentParser({ ...process.env, ...Credentials });\n\n// Global config - only minimal shared values\n// Service-specific config should be parsed in each service\nexport const config = envParser\n .create((get) => ({\n nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),\n stage: get('STAGE').enum(['dev', 'staging', 'prod']).default('dev'),\n }))\n .parse();\n`,\n\t\t\t},\n\n\t\t\t// src/config/logger.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/logger.ts',\n\t\t\t\tcontent: loggerContent,\n\t\t\t},\n\n\t\t\t// health endpoint\n\t\t\t{\n\t\t\t\tpath: getRoutePath('health.ts'),\n\t\t\t\tcontent: `import { z } from 'zod';\nimport { api } from '${apiImport}';\n\nexport const healthEndpoint = api\n .get('/health')\n .output(z.object({\n status: z.string(),\n timestamp: z.string(),\n region: z.string(),\n }))\n .handle(async () => ({\n status: 'ok',\n timestamp: new Date().toISOString(),\n region: process.env.AWS_REGION || 'local',\n }));\n`,\n\t\t\t},\n\n\t\t\t// src/functions/hello.ts\n\t\t\t{\n\t\t\t\tpath: 'src/functions/hello.ts',\n\t\t\t\tcontent: `import { f } from '@geekmidas/constructs/functions';\nimport { z } from 'zod';\n\nexport const helloFunction = f\n .input(z.object({ name: z.string() }))\n .output(z.object({ message: z.string() }))\n .handle(async ({ input }) => ({\n message: \\`Hello, \\${input.name}!\\`,\n }));\n`,\n\t\t\t},\n\t\t];\n\n\t\t// The database, when this project has one. A `pgboss` events backend\n\t\t// implies one, which is why a worker gets here without asking for it.\n\t\tif (options.database) {\n\t\t\tfiles.push(...databaseFiles(name));\n\t\t}\n\n\t\t// Add Telescope config if enabled\n\t\tif (options.telescope) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/telescope.ts',\n\t\t\t\tcontent: `import { Telescope } from '@geekmidas/telescope';\nimport { InMemoryStorage } from '@geekmidas/telescope/storage/memory';\n\n// Note: For production Lambda, consider using a persistent storage\nexport const telescope = new Telescope({\n storage: new InMemoryStorage({ maxEntries: 50 }),\n enabled: process.env.STAGE === 'dev',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\treturn files;\n\t},\n};\n","import { databaseFiles } from '../constructs.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from './index.js';\n\nexport const workerTemplate: TemplateConfig = {\n\tname: 'worker',\n\tdescription: 'Background job processing',\n\n\tdependencies: {\n\t\t'@geekmidas/audit': GEEKMIDAS_VERSIONS['@geekmidas/audit'],\n\t\t'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],\n\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t'@geekmidas/events': GEEKMIDAS_VERSIONS['@geekmidas/events'],\n\t\t'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],\n\t\t'@geekmidas/rate-limit': GEEKMIDAS_VERSIONS['@geekmidas/rate-limit'],\n\t\t'@geekmidas/schema': GEEKMIDAS_VERSIONS['@geekmidas/schema'],\n\t\t'@hono/node-server': '~1.14.1',\n\t\thono: '~4.8.2',\n\t\tpino: '~9.6.0',\n\t},\n\n\tdevDependencies: {\n\t\t'@biomejs/biome': '~2.3.0',\n\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t'@types/node': '~22.0.0',\n\t\ttsx: '~4.20.0',\n\t\tturbo: '~2.3.0',\n\t\ttypescript: '~5.8.2',\n\t\tvitest: '~4.0.0',\n\t},\n\n\tscripts: {\n\t\tdev: 'gkm dev',\n\t\tbuild: 'gkm build',\n\t\ttest: 'vitest',\n\t\t'test:once': 'vitest run',\n\t\ttypecheck: 'tsc --noEmit',\n\t\tlint: 'biome lint .',\n\t\tfmt: 'biome format . --write',\n\t\t'fmt:check': 'biome format .',\n\t},\n\n\tfiles: (options: TemplateOptions): GeneratedFile[] => {\n\t\tconst { loggerType, routesStructure, name } = options;\n\n\t\tconst loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';\n\nexport const logger = createLogger();\n`;\n\n\t\t// Get route path based on structure\n\t\tconst getRoutePath = (file: string) => {\n\t\t\tswitch (routesStructure) {\n\t\t\t\tcase 'centralized-endpoints':\n\t\t\t\t\treturn `src/endpoints/${file}`;\n\t\t\t\tcase 'centralized-routes':\n\t\t\t\t\treturn `src/routes/${file}`;\n\t\t\t\tcase 'domain-based':\n\t\t\t\t\treturn `src/${file.replace('.ts', '')}/routes/index.ts`;\n\t\t\t}\n\t\t};\n\n\t\t// Where an endpoint file reaches the surface from — one level deeper when\n\t\t// the routes are nested by domain.\n\t\tconst apiImport =\n\t\t\troutesStructure === 'domain-based'\n\t\t\t\t? '../../constructs/api.js'\n\t\t\t\t: '../constructs/api.js';\n\n\t\tconst files: GeneratedFile[] = [\n\t\t\t// The application's HTTP surface. Endpoints are built from it, so each\n\t\t\t// one carries the logger and environment parser its adaptor needs —\n\t\t\t// nothing has to be named in config and nothing printed into the\n\t\t\t// generated handler.\n\t\t\t{\n\t\t\t\tpath: 'src/constructs/api.ts',\n\t\t\t\tcontent: `import { RestApi } from '@geekmidas/constructs/rest-api';\nimport { logger } from '../config/logger.ts';\n\nexport const api = new RestApi('Api', {\n // Typed out rather than omitted: an API that ships open because a field was\n // left off is the one default worth refusing to have.\n defaultAuthorizer: 'none',\n\n // The actual logger, not a path to one. Every endpoint built from this\n // surface runs with it, and so does the generated entry.\n logger,\n});\n`,\n\t\t\t},\n\n\t\t\t// src/config/env.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/env.ts',\n\t\t\t\tcontent: `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { EnvironmentParser } from '@geekmidas/envkit';\n\nexport const envParser = new EnvironmentParser({ ...process.env, ...Credentials });\n\n// Global config - only minimal shared values\n// Service-specific config should be parsed in each service\nexport const config = envParser\n .create((get) => ({\n nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),\n stage: get('STAGE').enum(['development', 'staging', 'production']).default('development'),\n }))\n .parse();\n`,\n\t\t\t},\n\n\t\t\t// src/config/logger.ts\n\t\t\t{\n\t\t\t\tpath: 'src/config/logger.ts',\n\t\t\t\tcontent: loggerContent,\n\t\t\t},\n\n\t\t\t// health endpoint\n\t\t\t{\n\t\t\t\tpath: getRoutePath('health.ts'),\n\t\t\t\tcontent: `import { z } from 'zod';\nimport { api } from '${apiImport}';\n\nexport const healthEndpoint = api\n .get('/health')\n .output(z.object({\n status: z.string(),\n timestamp: z.string(),\n }))\n .handle(async () => ({\n status: 'ok',\n timestamp: new Date().toISOString(),\n }));\n`,\n\t\t\t},\n\n\t\t\t// src/events/types.ts\n\t\t\t{\n\t\t\t\tpath: 'src/events/types.ts',\n\t\t\t\tcontent: `import type { PublishableMessage } from '@geekmidas/events';\n\n// Define your event types here\nexport type AppEvents =\n | PublishableMessage<'user.created', { userId: string; email: string }>\n | PublishableMessage<'user.updated', { userId: string; changes: Record<string, unknown> }>\n | PublishableMessage<'order.placed', { orderId: string; userId: string; total: number }>;\n`,\n\t\t\t},\n\n\t\t\t// src/events/publisher.ts\n\t\t\t{\n\t\t\t\tpath: 'src/events/publisher.ts',\n\t\t\t\tcontent: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';\nimport { Publisher, type EventPublisher } from '@geekmidas/events';\nimport type { AppEvents } from './types.ts';\n\nexport const eventsPublisherService = {\n serviceName: 'events' as const,\n async register({ envParser, context }: ServiceRegisterOptions) {\n const logger = context.getLogger();\n logger.info('Connecting to message broker');\n\n const config = envParser\n .create((get) => ({\n url: get('RABBITMQ_URL').string().default('amqp://localhost:5672'),\n }))\n .parse();\n\n const publisher = await Publisher.fromConnectionString<AppEvents>(\n \\`rabbitmq://\\${config.url.replace('amqp://', '')}?exchange=events\\`\n );\n\n logger.info('Message broker connection established');\n return publisher;\n },\n} satisfies Service<'events', EventPublisher<AppEvents>>;\n`,\n\t\t\t},\n\n\t\t\t// src/subscribers/user-events.ts\n\t\t\t{\n\t\t\t\tpath: 'src/subscribers/user-events.ts',\n\t\t\t\tcontent: `import { s } from '@geekmidas/constructs/subscribers';\nimport { eventsPublisherService } from '~/events/publisher.ts';\n\nexport const userEventsSubscriber = s\n .publisher(eventsPublisherService)\n .subscribe(['user.created', 'user.updated'])\n .handle(async ({ event, logger }) => {\n logger.info({ type: event.type, payload: event.payload }, 'Processing user event');\n\n switch (event.type) {\n case 'user.created':\n // Handle user creation\n logger.info({ userId: event.payload.userId }, 'New user created');\n break;\n case 'user.updated':\n // Handle user update\n logger.info({ userId: event.payload.userId }, 'User updated');\n break;\n }\n });\n`,\n\t\t\t},\n\n\t\t\t// src/crons/cleanup.ts\n\t\t\t{\n\t\t\t\tpath: 'src/crons/cleanup.ts',\n\t\t\t\tcontent: `import { cron } from '@geekmidas/constructs/crons';\n\n// Run every day at midnight\nexport const cleanupCron = cron('0 0 * * *')\n .handle(async ({ logger }) => {\n logger.info('Running cleanup job');\n\n // Add your cleanup logic here\n // e.g., delete old sessions, clean up temp files, etc.\n\n logger.info('Cleanup job completed');\n });\n`,\n\t\t\t},\n\t\t];\n\n\t\t// The database, when this project has one. A `pgboss` events backend\n\t\t// implies one, which is why a worker gets here without asking for it.\n\t\tif (options.database) {\n\t\t\tfiles.push(...databaseFiles(name));\n\t\t}\n\n\t\t// Add Telescope config if enabled\n\t\tif (options.telescope) {\n\t\t\tfiles.push({\n\t\t\t\tpath: 'src/config/telescope.ts',\n\t\t\t\tcontent: `import { Telescope } from '@geekmidas/telescope';\nimport { InMemoryStorage } from '@geekmidas/telescope/storage/memory';\n\nexport const telescope = new Telescope({\n storage: new InMemoryStorage({ maxEntries: 100 }),\n enabled: process.env.NODE_ENV === 'development',\n});\n`,\n\t\t\t});\n\t\t}\n\n\t\treturn files;\n\t},\n};\n","import { apiTemplate } from './api.js';\nimport { minimalTemplate } from './minimal.js';\nimport { serverlessTemplate } from './serverless.js';\nimport { workerTemplate } from './worker.js';\n\n/**\n * OpenAPI output path (fixed, not configurable)\n */\nexport const OPENAPI_OUTPUT_PATH = './.gkm/openapi.ts';\n\n/**\n * Endpoints output path (generated by gkm dev/build)\n */\nexport const ENDPOINTS_OUTPUT_PATH = './.gkm/server/endpoints.ts';\n\n/**\n * Logger implementation type\n */\nexport type LoggerType = 'pino' | 'console';\n\n/**\n * Routes structure pattern\n */\nexport type RoutesStructure =\n\t| 'centralized-endpoints'\n\t| 'centralized-routes'\n\t| 'domain-based';\n\n/**\n * Package manager type\n */\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';\n\n/**\n * Deploy target type\n */\nexport type DeployTarget = 'dokploy' | 'none';\n\n/**\n * Services selection\n */\nexport interface ServicesSelection {\n\tdb: boolean;\n\tcache: boolean;\n\tmail: boolean;\n\tstorage: boolean;\n\tevents?: import('../../types.js').EventsBackend;\n}\n\n/**\n * Frontend framework for the fullstack template's client app.\n * Limited to the three with first-class scaffolds; the wider\n * `FrontendFramework` type also covers `vite` and `remix` which\n * users can configure manually after init.\n */\nexport type FullstackFrontendFramework = 'nextjs' | 'tanstack-start' | 'expo';\n\n/**\n * Options collected from user prompts\n */\nexport interface TemplateOptions {\n\tname: string;\n\ttemplate: TemplateName;\n\ttelescope: boolean;\n\tdatabase: boolean;\n\tstudio: boolean;\n\tloggerType: LoggerType;\n\troutesStructure: RoutesStructure;\n\tmonorepo: boolean;\n\t/** Path for the API app in monorepo (e.g., 'apps/api') */\n\tapiPath: string;\n\t/** Selected package manager */\n\tpackageManager: PackageManager;\n\t/** Deploy target */\n\tdeployTarget: DeployTarget;\n\t/** Services selection */\n\tservices: ServicesSelection;\n\t/** Frontend framework (fullstack template only) */\n\tfrontendFramework?: FullstackFrontendFramework;\n}\n\n/**\n * A file to be generated\n */\nexport interface GeneratedFile {\n\tpath: string;\n\tcontent: string;\n}\n\n/**\n * Template configuration\n */\nexport interface TemplateConfig {\n\tname: TemplateName;\n\tdescription: string;\n\tdependencies: Record<string, string>;\n\tdevDependencies: Record<string, string>;\n\tscripts: Record<string, string>;\n\tfiles: (options: TemplateOptions) => GeneratedFile[];\n}\n\nexport type TemplateName =\n\t| 'minimal'\n\t| 'api'\n\t| 'serverless'\n\t| 'worker'\n\t| 'fullstack';\n\n/**\n * All available templates\n */\nexport const templates: Record<\n\tExclude<TemplateName, 'fullstack'>,\n\tTemplateConfig\n> = {\n\tminimal: minimalTemplate,\n\tapi: apiTemplate,\n\tserverless: serverlessTemplate,\n\tworker: workerTemplate,\n};\n\n/**\n * Template choices for prompts (Story 1.11 simplified to api + fullstack)\n */\nexport const templateChoices = [\n\t{\n\t\ttitle: 'API',\n\t\tvalue: 'api' as TemplateName,\n\t\tdescription: 'Single backend API with endpoints',\n\t},\n\t{\n\t\ttitle: 'Fullstack',\n\t\tvalue: 'fullstack' as TemplateName,\n\t\tdescription: 'Monorepo with API + Next.js + shared models',\n\t},\n];\n\n/**\n * All template choices (includes advanced options)\n */\nexport const allTemplateChoices = [\n\t{\n\t\ttitle: 'Minimal',\n\t\tvalue: 'minimal' as TemplateName,\n\t\tdescription: 'Basic health endpoint',\n\t},\n\t{\n\t\ttitle: 'API',\n\t\tvalue: 'api' as TemplateName,\n\t\tdescription: 'Full API with auth, database, services',\n\t},\n\t{\n\t\ttitle: 'Fullstack',\n\t\tvalue: 'fullstack' as TemplateName,\n\t\tdescription: 'Monorepo with API + Next.js + shared models',\n\t},\n\t{\n\t\ttitle: 'Serverless',\n\t\tvalue: 'serverless' as TemplateName,\n\t\tdescription: 'AWS Lambda handlers',\n\t},\n\t{\n\t\ttitle: 'Worker',\n\t\tvalue: 'worker' as TemplateName,\n\t\tdescription: 'Background job processing',\n\t},\n];\n\n/**\n * Frontend framework choices for the fullstack template prompt.\n */\nexport const frontendFrameworkChoices = [\n\t{\n\t\ttitle: 'Next.js',\n\t\tvalue: 'nextjs' as FullstackFrontendFramework,\n\t\tdescription: 'React server components, app router (default)',\n\t},\n\t{\n\t\ttitle: 'TanStack Start',\n\t\tvalue: 'tanstack-start' as FullstackFrontendFramework,\n\t\tdescription: 'File-based router on Vite + Nitro',\n\t},\n\t{\n\t\ttitle: 'Expo',\n\t\tvalue: 'expo' as FullstackFrontendFramework,\n\t\tdescription: 'React Native mobile app (iOS + Android)',\n\t},\n];\n\n/**\n * Logger type choices for prompts\n */\nexport const loggerTypeChoices = [\n\t{\n\t\ttitle: 'Pino',\n\t\tvalue: 'pino' as LoggerType,\n\t\tdescription: 'Fast JSON logger for production (recommended)',\n\t},\n\t{\n\t\ttitle: 'Console',\n\t\tvalue: 'console' as LoggerType,\n\t\tdescription: 'Simple console logger for development',\n\t},\n];\n\n/**\n * Routes structure choices for prompts\n */\nexport const routesStructureChoices = [\n\t{\n\t\ttitle: 'Centralized (endpoints)',\n\t\tvalue: 'centralized-endpoints' as RoutesStructure,\n\t\tdescription: 'src/endpoints/**/*.ts',\n\t},\n\t{\n\t\ttitle: 'Centralized (routes)',\n\t\tvalue: 'centralized-routes' as RoutesStructure,\n\t\tdescription: 'src/routes/**/*.ts',\n\t},\n\t{\n\t\ttitle: 'Domain-based',\n\t\tvalue: 'domain-based' as RoutesStructure,\n\t\tdescription: 'src/**/routes/*.ts (e.g., src/users/routes/list.ts)',\n\t},\n];\n\n/**\n * Package manager choices for prompts\n */\nexport const packageManagerChoices = [\n\t{\n\t\ttitle: 'pnpm',\n\t\tvalue: 'pnpm' as PackageManager,\n\t\tdescription: 'Fast, disk space efficient (recommended)',\n\t},\n\t{\n\t\ttitle: 'npm',\n\t\tvalue: 'npm' as PackageManager,\n\t\tdescription: 'Node.js default package manager',\n\t},\n\t{\n\t\ttitle: 'yarn',\n\t\tvalue: 'yarn' as PackageManager,\n\t\tdescription: 'Yarn package manager',\n\t},\n\t{\n\t\ttitle: 'bun',\n\t\tvalue: 'bun' as PackageManager,\n\t\tdescription: 'Fast JavaScript runtime and package manager',\n\t},\n];\n\n/**\n * Deploy target choices for prompts\n */\nexport const deployTargetChoices = [\n\t{\n\t\ttitle: 'Dokploy',\n\t\tvalue: 'dokploy' as DeployTarget,\n\t\tdescription: 'Deploy to Dokploy (Docker-based hosting)',\n\t},\n\t{\n\t\ttitle: 'Configure later',\n\t\tvalue: 'none' as DeployTarget,\n\t\tdescription: 'Skip deployment setup for now',\n\t},\n];\n\n/**\n * Services choices for multi-select prompt\n */\n/**\n * What the project declares — and therefore which containers it runs.\n *\n * Each choice scaffolds a construct rather than a container: the container is\n * what `gkm dev` derives from the declaration, which is why the descriptions\n * name the construct and the titles name the thing you will see running.\n */\nexport const servicesChoices = [\n\t{\n\t\ttitle: 'PostgreSQL',\n\t\tvalue: 'db',\n\t\tdescription: 'A declared database — KyselyDatabase construct',\n\t},\n\t{\n\t\ttitle: 'Redis',\n\t\tvalue: 'cache',\n\t\tdescription: 'A declared cache — Cache construct',\n\t},\n\t{\n\t\ttitle: 'Mailpit',\n\t\tvalue: 'mail',\n\t\tdescription: 'Declared outbound mail — Email construct',\n\t},\n\t{\n\t\ttitle: 'MinIO',\n\t\tvalue: 'storage',\n\t\tdescription: 'A declared bucket — ObjectStorage construct',\n\t},\n];\n\n/**\n * Event backend choices for prompts\n */\nexport const eventsBackendChoices = [\n\t{\n\t\ttitle: 'pg-boss',\n\t\tvalue: 'pgboss' as const,\n\t\tdescription:\n\t\t\t'PostgreSQL-based job queue (reuses postgres, no extra container)',\n\t},\n\t{\n\t\ttitle: 'SNS/SQS',\n\t\tvalue: 'sns' as const,\n\t\tdescription: 'AWS SNS+SQS via LocalStack for local dev',\n\t},\n\t{\n\t\ttitle: 'RabbitMQ',\n\t\tvalue: 'rabbitmq' as const,\n\t\tdescription: 'AMQP message broker',\n\t},\n\t{\n\t\ttitle: 'None',\n\t\tvalue: undefined,\n\t\tdescription: 'Skip event backend',\n\t},\n];\n\n/**\n * Get a template by name\n */\nexport function getTemplate(name: TemplateName): TemplateConfig | null {\n\tif (name === 'fullstack') {\n\t\t// Fullstack template is handled specially, uses api template as base\n\t\treturn templates.api;\n\t}\n\tconst template = templates[name];\n\tif (!template) {\n\t\tthrow new Error(`Unknown template: ${name}`);\n\t}\n\treturn template;\n}\n\n/**\n * Check if a template is the fullstack monorepo template\n */\nexport function isFullstackTemplate(name: TemplateName): boolean {\n\treturn name === 'fullstack';\n}\n","import {\n\tENDPOINTS_OUTPUT_PATH,\n\ttype GeneratedFile,\n\tOPENAPI_OUTPUT_PATH,\n\ttype TemplateConfig,\n\ttype TemplateOptions,\n} from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate package.json with dependencies based on template and options\n */\nexport function generatePackageJson(\n\toptions: TemplateOptions,\n\ttemplate: TemplateConfig,\n): GeneratedFile[] {\n\tconst { name, telescope, database, studio, monorepo, services } = options;\n\n\t// Start with template dependencies\n\tconst dependencies = { ...template.dependencies };\n\tconst devDependencies = { ...template.devDependencies };\n\tconst scripts = { ...template.scripts };\n\n\t// Add optional dependencies based on user choices\n\tif (telescope) {\n\t\tdependencies['@geekmidas/telescope'] =\n\t\t\tGEEKMIDAS_VERSIONS['@geekmidas/telescope'];\n\t}\n\n\tif (studio) {\n\t\tdependencies['@geekmidas/studio'] = GEEKMIDAS_VERSIONS['@geekmidas/studio'];\n\t}\n\n\t// Only a project that declares constructs needs them. The workspace path\n\t// still reaches its infrastructure through hand-written services.\n\tconst declares = !monorepo;\n\n\t// Every construct resolves its env key through the manifest, so declaring\n\t// one at all is what needs this — not any particular kind.\n\tif (\n\t\tdeclares &&\n\t\t(database || services.storage || services.cache || services.mail)\n\t) {\n\t\tdependencies['@geekmidas/manifest'] =\n\t\t\tGEEKMIDAS_VERSIONS['@geekmidas/manifest'];\n\t}\n\n\t// A construct hands back a client from the package that owns it, and each of\n\t// those is an optional peer — an app that declares no bucket resolves no S3\n\t// SDK. Installed here because the app declared one.\n\tif (declares && services.storage) {\n\t\tdependencies['@geekmidas/storage'] =\n\t\t\tGEEKMIDAS_VERSIONS['@geekmidas/storage'];\n\t}\n\n\tif (declares && services.cache) {\n\t\tdependencies['@geekmidas/cache'] = GEEKMIDAS_VERSIONS['@geekmidas/cache'];\n\t}\n\n\tif (declares && services.mail) {\n\t\tdependencies['@geekmidas/emailkit'] =\n\t\t\tGEEKMIDAS_VERSIONS['@geekmidas/emailkit'];\n\t}\n\n\tif (database) {\n\t\tdependencies['@geekmidas/db'] = GEEKMIDAS_VERSIONS['@geekmidas/db'];\n\t\tdependencies.kysely = '~0.28.2';\n\t\tdependencies.pg = '~8.16.0';\n\t\tdevDependencies['@types/pg'] = '~8.15.0';\n\t\tdevDependencies['@geekmidas/testkit'] =\n\t\t\tGEEKMIDAS_VERSIONS['@geekmidas/testkit'];\n\t\tdevDependencies['@faker-js/faker'] = '~9.8.0';\n\t\tdevDependencies['kysely-ctl'] = '~0.10.0';\n\t\tdevDependencies['vite-tsconfig-paths'] = '~5.1.0';\n\t}\n\n\t// For monorepo apps, remove biome/turbo/esbuild (they're at root) and lint/fmt scripts\n\t// zod is at root level for monorepos\n\tif (monorepo) {\n\t\tdelete devDependencies['@biomejs/biome'];\n\t\tdelete devDependencies.turbo;\n\t\tdelete devDependencies.esbuild;\n\t\tdelete dependencies.zod;\n\t\tdelete scripts.lint;\n\t\tdelete scripts.fmt;\n\t\tdelete scripts['fmt:check'];\n\n\t\t// Add models package as dependency\n\t\tdependencies[`@${name}/models`] = 'workspace:*';\n\t}\n\n\t// Sort dependencies alphabetically\n\tconst sortObject = (obj: Record<string, string>) =>\n\t\tObject.fromEntries(\n\t\t\tObject.entries(obj).sort(([a], [b]) => a.localeCompare(b)),\n\t\t);\n\n\t// For monorepo, derive package name from apiPath (e.g., apps/api -> @name/api)\n\tlet packageName = name;\n\tif (monorepo && options.apiPath) {\n\t\tconst pathParts = options.apiPath.split('/');\n\t\tconst appName = pathParts[pathParts.length - 1] || 'api';\n\t\tpackageName = `@${name}/${appName}`;\n\t}\n\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\texports: {\n\t\t\t'./client': OPENAPI_OUTPUT_PATH,\n\t\t\t'./endpoints': ENDPOINTS_OUTPUT_PATH,\n\t\t},\n\t\tscripts,\n\t\tdependencies: sortObject(dependencies),\n\t\tdevDependencies: sortObject(devDependencies),\n\t};\n\n\treturn [\n\t\t{\n\t\t\tpath: 'package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t];\n}\n","import type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\n\n/**\n * Generate source files from template\n */\nexport function generateSourceFiles(\n\toptions: TemplateOptions,\n\ttemplate: TemplateConfig,\n): GeneratedFile[] {\n\treturn template.files(options);\n}\n","import { databaseFor } from '../constructs.js';\nimport type {\n\tGeneratedFile,\n\tTemplateConfig,\n\tTemplateOptions,\n} from '../templates/index.js';\n\n/**\n * Generate test infrastructure files when database is enabled.\n * Includes transaction-isolated test config, global setup with migrations,\n * factory system with builders/seeds, and an example spec.\n */\nexport function generateTestFiles(\n\toptions: TemplateOptions,\n\t_template: TemplateConfig,\n): GeneratedFile[] {\n\tif (!options.database) {\n\t\treturn [];\n\t}\n\n\t// The keys the declared database publishes. Migrations connect as the owner\n\t// role — the one that may create, alter, and drop; a handler is never given\n\t// it, which is the security property the role split exists for.\n\tconst db = databaseFor(options.name);\n\n\t// Single-app projects have no `~/*` alias, so what a test file imports\n\t// depends on where it will sit.\n\tconst declares = !options.monorepo;\n\tconst schema = declares\n\t\t? '../src/constructs/database.ts'\n\t\t: '~/services/database.ts';\n\n\t// Which key holds the URL: the one the construct publishes, or the one the\n\t// workspace's per-app secret sets.\n\tconst runtimeUrl = declares ? db.urlKey : 'DATABASE_URL';\n\tconst ownerUrl = declares ? db.ownerUrlKey : 'DATABASE_URL';\n\n\treturn [\n\t\t// kysely.config.ts - Kysely CLI configuration for migrations\n\t\t{\n\t\t\tpath: 'kysely.config.ts',\n\t\t\tcontent: `import { Credentials } from '@geekmidas/envkit/credentials';\nimport { PostgresDialect } from 'kysely';\nimport { defineConfig } from 'kysely-ctl';\nimport pg from 'pg';\n\n// The owner role's URL — the one that may create, alter, and drop. Both keys\n// are published by the declared database construct; run this under\n// \\`gkm exec -- pnpm kysely migrate:latest\\` so they are injected.\nconst url = Credentials.${ownerUrl} ?? Credentials.${runtimeUrl};\n\nexport default defineConfig({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: url }),\n }),\n migrations: {\n migrationFolder: './src/db/migrations',\n },\n});\n`,\n\t\t},\n\n\t\t// test/config.ts - Wraps vitest `it` with transaction auto-rollback\n\t\t{\n\t\t\tpath: 'test/config.ts',\n\t\t\tcontent: `import { it as itVitest } from 'vitest';\nimport { Kysely, PostgresDialect } from 'kysely';\nimport pg from 'pg';\nimport { wrapVitestKyselyTransaction } from '@geekmidas/testkit/kysely';\nimport type { Database } from '${schema}';\n\nconst connection = new Kysely<Database>({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: process.env.${runtimeUrl} }),\n }),\n});\n\nexport const it = wrapVitestKyselyTransaction<Database>(itVitest, {\n connection,\n});\n`,\n\t\t},\n\n\t\t// test/globalSetup.ts - Creates the test database and runs migrations\n\t\t{\n\t\t\tpath: 'test/globalSetup.ts',\n\t\t\tcontent: `import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { Credentials } from '@geekmidas/envkit/credentials';\nimport { PostgresKyselyMigrator } from '@geekmidas/testkit/kysely';\nimport { Kysely, PostgresDialect } from 'kysely';\nimport { FileMigrationProvider } from 'kysely/migration';\nimport pg from 'pg';\n\nexport default async function globalSetup() {\n // \\`gkm test\\` reconciles the test stage before this runs: the container is\n // up, the database exists, and its roles are created. What is left is the\n // schema, which is what migrations are for.\n const databaseUrl = Credentials.${ownerUrl} ?? Credentials.${runtimeUrl}!;\n const migrationFolder = path.resolve(import.meta.dirname, '../src/db/migrations');\n\n const db = new Kysely({\n dialect: new PostgresDialect({\n pool: new pg.Pool({ connectionString: databaseUrl }),\n }),\n });\n\n const migrator = new PostgresKyselyMigrator({\n uri: databaseUrl,\n db,\n provider: new FileMigrationProvider({\n fs,\n path,\n migrationFolder,\n }),\n });\n\n const teardown = await migrator.start();\n return teardown;\n}\n`,\n\t\t},\n\n\t\t// test/factory/index.ts - Factory aggregator\n\t\t{\n\t\t\tpath: 'test/factory/index.ts',\n\t\t\tcontent: `import type { Kysely } from 'kysely';\nimport { KyselyFactory } from '@geekmidas/testkit/kysely';\nimport type { Database } from '${schema}';\nimport { usersBuilder } from './users.ts';\n\nconst builders = { users: usersBuilder };\nconst seeds = {};\n\nexport function createFactory(db: Kysely<Database>) {\n return new KyselyFactory<Database, typeof builders, typeof seeds>(\n builders,\n seeds,\n db,\n );\n}\n\nexport type Factory = ReturnType<typeof createFactory>;\n`,\n\t\t},\n\n\t\t// test/factory/users.ts - Example builder\n\t\t{\n\t\t\tpath: 'test/factory/users.ts',\n\t\t\tcontent: `import { KyselyFactory } from '@geekmidas/testkit/kysely';\nimport type { Database } from '${schema}';\n\nexport const usersBuilder = KyselyFactory.createBuilder<Database, 'users'>(\n 'users',\n ({ faker }) => ({\n id: faker.string.uuid(),\n name: faker.person.fullName(),\n email: faker.internet.email(),\n created_at: new Date(),\n }),\n);\n`,\n\t\t},\n\n\t\t// test/example.spec.ts - Example test showing usage\n\t\t{\n\t\t\tpath: 'test/example.spec.ts',\n\t\t\tcontent: `import { describe, expect } from 'vitest';\nimport { it } from './config.ts';\n\ndescribe('example', () => {\n it('should have a working test setup', async ({ db }) => {\n // db is a transaction-wrapped Kysely instance\n // All changes are automatically rolled back after the test\n expect(db).toBeDefined();\n });\n});\n`,\n\t\t},\n\t];\n}\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\n\n/**\n * Generate UI package files for fullstack template\n * Based on @geekmidas/ui with shadcn/ui, Tailwind CSS v4, and Storybook\n */\nexport function generateUiPackageFiles(\n\toptions: TemplateOptions,\n): GeneratedFile[] {\n\tif (!options.monorepo || options.template !== 'fullstack') {\n\t\treturn [];\n\t}\n\n\tconst packageName = `@${options.name}/ui`;\n\n\t// package.json for UI package\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\texports: {\n\t\t\t'.': './src/index.ts',\n\t\t\t'./components': './src/components/index.ts',\n\t\t\t'./lib/utils': './src/lib/utils.ts',\n\t\t\t'./styles': './src/styles/globals.css',\n\t\t},\n\t\tscripts: {\n\t\t\t'ts:check': 'tsc --noEmit',\n\t\t\tstorybook: 'storybook dev -p 6006',\n\t\t\t'build:storybook': 'storybook build -o dist/storybook',\n\t\t},\n\t\tdependencies: {\n\t\t\t'@radix-ui/react-dialog': '~1.1.4',\n\t\t\t'@radix-ui/react-label': '~2.1.2',\n\t\t\t'@radix-ui/react-separator': '~1.1.2',\n\t\t\t'@radix-ui/react-slot': '~1.2.4',\n\t\t\t'@radix-ui/react-tabs': '~1.1.2',\n\t\t\t'@radix-ui/react-tooltip': '~1.1.6',\n\t\t\t'class-variance-authority': '~0.7.1',\n\t\t\tclsx: '^2.1.1',\n\t\t\t'lucide-react': '~0.562.0',\n\t\t\t'tailwind-merge': '~3.4.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@storybook/addon-a11y': '^8.4.7',\n\t\t\t'@storybook/addon-essentials': '^8.4.7',\n\t\t\t'@storybook/addon-interactions': '^8.4.7',\n\t\t\t'@storybook/react': '^8.4.7',\n\t\t\t'@storybook/react-vite': '^8.4.7',\n\t\t\t'@tailwindcss/vite': '^4.0.0',\n\t\t\t'@types/react': '^19.0.0',\n\t\t\t'@types/react-dom': '^19.0.0',\n\t\t\treact: '^19.0.0',\n\t\t\t'react-dom': '^19.0.0',\n\t\t\tstorybook: '^8.4.7',\n\t\t\ttailwindcss: '^4.0.0',\n\t\t\ttypescript: '^5.8.2',\n\t\t\tvite: '^6.0.0',\n\t\t},\n\t\tpeerDependencies: {\n\t\t\treact: '>=18.0.0',\n\t\t\t'react-dom': '>=18.0.0',\n\t\t\ttailwindcss: '>=4.0.0',\n\t\t},\n\t};\n\n\t// tsconfig.json for UI package\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tjsx: 'react-jsx',\n\t\t\tlib: ['ES2023', 'DOM', 'DOM.Iterable'],\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\tbaseUrl: '.',\n\t\t\tpaths: {\n\t\t\t\t'~/*': ['./src/*'],\n\t\t\t},\n\t\t},\n\t\tinclude: ['src/**/*'],\n\t\texclude: ['node_modules', 'dist', '**/*.stories.tsx'],\n\t};\n\n\t// components.json (shadcn config)\n\tconst componentsJson = {\n\t\t$schema: 'https://ui.shadcn.com/schema.json',\n\t\tstyle: 'new-york',\n\t\trsc: false,\n\t\ttsx: true,\n\t\ttailwind: {\n\t\t\tconfig: '',\n\t\t\tcss: 'src/styles/globals.css',\n\t\t\tbaseColor: 'neutral',\n\t\t\tcssVariables: true,\n\t\t\tprefix: '',\n\t\t},\n\t\taliases: {\n\t\t\tcomponents: '~/components',\n\t\t\tutils: '~/lib/utils',\n\t\t\tui: '~/components/ui',\n\t\t\tlib: '~/lib',\n\t\t\thooks: '~/hooks',\n\t\t},\n\t\ticonLibrary: 'lucide',\n\t};\n\n\t// .storybook/main.ts\n\tconst storybookMain = `import type { StorybookConfig } from '@storybook/react-vite';\n\nconst config: StorybookConfig = {\n stories: ['../src/**/*.stories.@(ts|tsx)'],\n addons: [\n '@storybook/addon-essentials',\n '@storybook/addon-interactions',\n '@storybook/addon-a11y',\n ],\n framework: {\n name: '@storybook/react-vite',\n options: {},\n },\n docs: {\n autodocs: 'tag',\n },\n viteFinal: async (config) => {\n // Add Tailwind CSS v4 plugin\n const tailwindcss = await import('@tailwindcss/vite');\n config.plugins = config.plugins || [];\n config.plugins.push(tailwindcss.default());\n return config;\n },\n};\n\nexport default config;\n`;\n\n\t// .storybook/preview.ts\n\tconst storybookPreview = `import type { Preview } from '@storybook/react';\nimport '../src/styles/globals.css';\n\nconst preview: Preview = {\n parameters: {\n backgrounds: {\n default: 'dark',\n values: [\n { name: 'dark', value: '#171717' },\n { name: 'surface', value: '#1c1c1c' },\n { name: 'light', value: '#fafafa' },\n ],\n },\n controls: {\n matchers: {\n color: /(background|color)$/i,\n date: /Date$/i,\n },\n },\n layout: 'centered',\n },\n};\n\nexport default preview;\n`;\n\n\t// src/styles/globals.css (Tailwind v4 format)\n\tconst globalsCss = `@import \"tailwindcss\";\n\n@theme {\n --color-background: hsl(var(--background));\n --color-foreground: hsl(var(--foreground));\n --color-card: hsl(var(--card));\n --color-card-foreground: hsl(var(--card-foreground));\n --color-popover: hsl(var(--popover));\n --color-popover-foreground: hsl(var(--popover-foreground));\n --color-primary: hsl(var(--primary));\n --color-primary-foreground: hsl(var(--primary-foreground));\n --color-secondary: hsl(var(--secondary));\n --color-secondary-foreground: hsl(var(--secondary-foreground));\n --color-muted: hsl(var(--muted));\n --color-muted-foreground: hsl(var(--muted-foreground));\n --color-accent: hsl(var(--accent));\n --color-accent-foreground: hsl(var(--accent-foreground));\n --color-destructive: hsl(var(--destructive));\n --color-destructive-foreground: hsl(var(--destructive-foreground));\n --color-border: hsl(var(--border));\n --color-input: hsl(var(--input));\n --color-ring: hsl(var(--ring));\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n}\n\n@layer base {\n :root {\n --background: 0 0% 100%;\n --foreground: 0 0% 3.9%;\n --card: 0 0% 100%;\n --card-foreground: 0 0% 3.9%;\n --popover: 0 0% 100%;\n --popover-foreground: 0 0% 3.9%;\n --primary: 160 84% 39%;\n --primary-foreground: 0 0% 98%;\n --secondary: 0 0% 96.1%;\n --secondary-foreground: 0 0% 9%;\n --muted: 0 0% 96.1%;\n --muted-foreground: 0 0% 45.1%;\n --accent: 0 0% 96.1%;\n --accent-foreground: 0 0% 9%;\n --destructive: 0 84.2% 60.2%;\n --destructive-foreground: 0 0% 98%;\n --border: 0 0% 89.8%;\n --input: 0 0% 89.8%;\n --ring: 160 84% 39%;\n --radius: 0.5rem;\n }\n\n .dark {\n --background: 0 0% 9%;\n --foreground: 0 0% 98%;\n --card: 0 0% 11%;\n --card-foreground: 0 0% 98%;\n --popover: 0 0% 11%;\n --popover-foreground: 0 0% 98%;\n --primary: 160 84% 52%;\n --primary-foreground: 0 0% 9%;\n --secondary: 0 0% 15%;\n --secondary-foreground: 0 0% 98%;\n --muted: 0 0% 15%;\n --muted-foreground: 0 0% 64%;\n --accent: 0 0% 15%;\n --accent-foreground: 0 0% 98%;\n --destructive: 0 62.8% 50.6%;\n --destructive-foreground: 0 0% 98%;\n --border: 0 0% 18%;\n --input: 0 0% 18%;\n --ring: 160 84% 52%;\n }\n}\n\n@layer base {\n * {\n @apply border-border;\n }\n body {\n @apply bg-background text-foreground;\n }\n}\n`;\n\n\t// src/lib/utils.ts\n\tconst utilsTs = `import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n`;\n\n\t// src/components/ui/button.tsx\n\tconst buttonTsx = `import { Slot } from '@radix-ui/react-slot';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst buttonVariants = cva(\n 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',\n {\n variants: {\n variant: {\n default:\n 'bg-primary text-primary-foreground shadow hover:bg-primary/90',\n destructive:\n 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',\n outline:\n 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',\n secondary:\n 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',\n ghost: 'hover:bg-accent hover:text-accent-foreground',\n link: 'text-primary underline-offset-4 hover:underline',\n },\n size: {\n default: 'h-9 px-4 py-2',\n sm: 'h-8 rounded-md px-3 text-xs',\n lg: 'h-10 rounded-md px-8',\n icon: 'h-9 w-9',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : 'button';\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n );\n },\n);\nButton.displayName = 'Button';\n\nexport { Button, buttonVariants };\n`;\n\n\t// src/components/ui/button/button.stories.tsx\n\tconst buttonStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Button } from '~/components/ui/button';\n\nconst meta: Meta<typeof Button> = {\n title: 'Components/Button',\n component: Button,\n tags: ['autodocs'],\n argTypes: {\n variant: {\n control: 'select',\n options: ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'],\n },\n size: {\n control: 'select',\n options: ['default', 'sm', 'lg', 'icon'],\n },\n },\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Button>;\n\nexport const Default: Story = {\n args: {\n children: 'Button',\n variant: 'default',\n size: 'default',\n },\n};\n\nexport const Secondary: Story = {\n args: {\n children: 'Secondary',\n variant: 'secondary',\n },\n};\n\nexport const Destructive: Story = {\n args: {\n children: 'Destructive',\n variant: 'destructive',\n },\n};\n\nexport const Outline: Story = {\n args: {\n children: 'Outline',\n variant: 'outline',\n },\n};\n\nexport const Ghost: Story = {\n args: {\n children: 'Ghost',\n variant: 'ghost',\n },\n};\n\nexport const Link: Story = {\n args: {\n children: 'Link',\n variant: 'link',\n },\n};\n`;\n\n\t// src/components/ui/input.tsx\n\tconst inputTsx = `import * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(\n ({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n 'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',\n className,\n )}\n ref={ref}\n {...props}\n />\n );\n },\n);\nInput.displayName = 'Input';\n\nexport { Input };\n`;\n\n\t// src/components/ui/card.tsx\n\tconst cardTsx = `import * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst Card = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'rounded-xl border bg-card text-card-foreground shadow',\n className,\n )}\n {...props}\n />\n));\nCard.displayName = 'Card';\n\nconst CardHeader = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex flex-col space-y-1.5 p-6', className)}\n {...props}\n />\n));\nCardHeader.displayName = 'CardHeader';\n\nconst CardTitle = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('font-semibold leading-none tracking-tight', className)}\n {...props}\n />\n));\nCardTitle.displayName = 'CardTitle';\n\nconst CardDescription = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n));\nCardDescription.displayName = 'CardDescription';\n\nconst CardContent = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />\n));\nCardContent.displayName = 'CardContent';\n\nconst CardFooter = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center p-6 pt-0', className)}\n {...props}\n />\n));\nCardFooter.displayName = 'CardFooter';\n\nexport { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };\n`;\n\n\t// src/components/ui/input/input.stories.tsx\n\tconst inputStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Input } from '~/components/ui/input';\n\nconst meta: Meta<typeof Input> = {\n title: 'Components/Input',\n component: Input,\n tags: ['autodocs'],\n argTypes: {\n type: {\n control: 'select',\n options: ['text', 'email', 'password', 'number', 'search', 'tel', 'url'],\n },\n disabled: {\n control: 'boolean',\n },\n },\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Input>;\n\nexport const Default: Story = {\n args: {\n placeholder: 'Enter text...',\n },\n};\n\nexport const Email: Story = {\n args: {\n type: 'email',\n placeholder: 'Enter email...',\n },\n};\n\nexport const Password: Story = {\n args: {\n type: 'password',\n placeholder: 'Enter password...',\n },\n};\n\nexport const Disabled: Story = {\n args: {\n placeholder: 'Disabled input',\n disabled: true,\n },\n};\n\nexport const WithValue: Story = {\n args: {\n defaultValue: 'Hello World',\n },\n};\n`;\n\n\t// src/components/ui/card/card.stories.tsx\n\tconst cardStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Button } from '~/components/ui/button';\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from '~/components/ui/card';\nimport { Input } from '~/components/ui/input';\n\nconst meta: Meta<typeof Card> = {\n title: 'Components/Card',\n component: Card,\n tags: ['autodocs'],\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Card>;\n\nexport const Default: Story = {\n render: () => (\n <Card className=\"w-[350px]\">\n <CardHeader>\n <CardTitle>Card Title</CardTitle>\n <CardDescription>Card description goes here.</CardDescription>\n </CardHeader>\n <CardContent>\n <p>Card content goes here.</p>\n </CardContent>\n </Card>\n ),\n};\n\nexport const WithFooter: Story = {\n render: () => (\n <Card className=\"w-[350px]\">\n <CardHeader>\n <CardTitle>Create Account</CardTitle>\n <CardDescription>Enter your details below.</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <Input placeholder=\"Email\" type=\"email\" />\n <Input placeholder=\"Password\" type=\"password\" />\n </CardContent>\n <CardFooter className=\"flex justify-between\">\n <Button variant=\"outline\">Cancel</Button>\n <Button>Create</Button>\n </CardFooter>\n </Card>\n ),\n};\n\nexport const Simple: Story = {\n render: () => (\n <Card className=\"w-[350px] p-6\">\n <p>Simple card with just content.</p>\n </Card>\n ),\n};\n`;\n\n\t// src/components/ui/label/index.tsx\n\tconst labelTsx = `import * as LabelPrimitive from '@radix-ui/react-label';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst labelVariants = cva(\n 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',\n);\n\nconst Label = React.forwardRef<\n React.ElementRef<typeof LabelPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &\n VariantProps<typeof labelVariants>\n>(({ className, ...props }, ref) => (\n <LabelPrimitive.Root\n ref={ref}\n className={cn(labelVariants(), className)}\n {...props}\n />\n));\nLabel.displayName = LabelPrimitive.Root.displayName;\n\nexport { Label };\n`;\n\n\t// src/components/ui/label/label.stories.tsx\n\tconst labelStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Input } from '~/components/ui/input';\nimport { Label } from '~/components/ui/label';\n\nconst meta: Meta<typeof Label> = {\n title: 'Components/Label',\n component: Label,\n tags: ['autodocs'],\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Label>;\n\nexport const Default: Story = {\n args: {\n children: 'Label',\n },\n};\n\nexport const WithInput: Story = {\n render: () => (\n <div className=\"grid w-full max-w-sm items-center gap-1.5\">\n <Label htmlFor=\"email\">Email</Label>\n <Input type=\"email\" id=\"email\" placeholder=\"Email\" />\n </div>\n ),\n};\n\nexport const Disabled: Story = {\n render: () => (\n <div className=\"grid w-full max-w-sm items-center gap-1.5\">\n <Label htmlFor=\"disabled\" className=\"peer-disabled:cursor-not-allowed peer-disabled:opacity-70\">\n Disabled\n </Label>\n <Input type=\"text\" id=\"disabled\" placeholder=\"Disabled\" disabled className=\"peer\" />\n </div>\n ),\n};\n`;\n\n\t// src/components/ui/badge/index.tsx\n\tconst badgeTsx = `import { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst badgeVariants = cva(\n 'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',\n {\n variants: {\n variant: {\n default:\n 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',\n secondary:\n 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',\n destructive:\n 'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',\n outline: 'text-foreground',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n },\n);\n\nexport interface BadgeProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof badgeVariants> {}\n\nfunction Badge({ className, variant, ...props }: BadgeProps) {\n return (\n <div className={cn(badgeVariants({ variant }), className)} {...props} />\n );\n}\n\nexport { Badge, badgeVariants };\n`;\n\n\t// src/components/ui/badge/badge.stories.tsx\n\tconst badgeStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Badge } from '~/components/ui/badge';\n\nconst meta: Meta<typeof Badge> = {\n title: 'Components/Badge',\n component: Badge,\n tags: ['autodocs'],\n argTypes: {\n variant: {\n control: 'select',\n options: ['default', 'secondary', 'destructive', 'outline'],\n },\n },\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Badge>;\n\nexport const Default: Story = {\n args: {\n children: 'Badge',\n variant: 'default',\n },\n};\n\nexport const Secondary: Story = {\n args: {\n children: 'Secondary',\n variant: 'secondary',\n },\n};\n\nexport const Destructive: Story = {\n args: {\n children: 'Destructive',\n variant: 'destructive',\n },\n};\n\nexport const Outline: Story = {\n args: {\n children: 'Outline',\n variant: 'outline',\n },\n};\n`;\n\n\t// src/components/ui/separator/index.tsx\n\tconst separatorTsx = `import * as SeparatorPrimitive from '@radix-ui/react-separator';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst Separator = React.forwardRef<\n React.ElementRef<typeof SeparatorPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>\n>(\n (\n { className, orientation = 'horizontal', decorative = true, ...props },\n ref,\n ) => (\n <SeparatorPrimitive.Root\n ref={ref}\n decorative={decorative}\n orientation={orientation}\n className={cn(\n 'shrink-0 bg-border',\n orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',\n className,\n )}\n {...props}\n />\n ),\n);\nSeparator.displayName = SeparatorPrimitive.Root.displayName;\n\nexport { Separator };\n`;\n\n\t// src/components/ui/separator/separator.stories.tsx\n\tconst separatorStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Separator } from '~/components/ui/separator';\n\nconst meta: Meta<typeof Separator> = {\n title: 'Components/Separator',\n component: Separator,\n tags: ['autodocs'],\n argTypes: {\n orientation: {\n control: 'select',\n options: ['horizontal', 'vertical'],\n },\n },\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Separator>;\n\nexport const Horizontal: Story = {\n render: () => (\n <div className=\"w-[300px]\">\n <div className=\"space-y-1\">\n <h4 className=\"text-sm font-medium leading-none\">Radix Primitives</h4>\n <p className=\"text-sm text-muted-foreground\">\n An open-source UI component library.\n </p>\n </div>\n <Separator className=\"my-4\" />\n <div className=\"flex h-5 items-center space-x-4 text-sm\">\n <div>Blog</div>\n <Separator orientation=\"vertical\" />\n <div>Docs</div>\n <Separator orientation=\"vertical\" />\n <div>Source</div>\n </div>\n </div>\n ),\n};\n\nexport const Vertical: Story = {\n render: () => (\n <div className=\"flex h-5 items-center space-x-4 text-sm\">\n <div>Blog</div>\n <Separator orientation=\"vertical\" />\n <div>Docs</div>\n <Separator orientation=\"vertical\" />\n <div>Source</div>\n </div>\n ),\n};\n`;\n\n\t// src/components/ui/tabs/index.tsx\n\tconst tabsTsx = `import * as TabsPrimitive from '@radix-ui/react-tabs';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst Tabs = TabsPrimitive.Root;\n\nconst TabsList = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.List\n ref={ref}\n className={cn(\n 'inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground',\n className,\n )}\n {...props}\n />\n));\nTabsList.displayName = TabsPrimitive.List.displayName;\n\nconst TabsTrigger = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n 'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow',\n className,\n )}\n {...props}\n />\n));\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName;\n\nconst TabsContent = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Content\n ref={ref}\n className={cn(\n 'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n className,\n )}\n {...props}\n />\n));\nTabsContent.displayName = TabsPrimitive.Content.displayName;\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent };\n`;\n\n\t// src/components/ui/tabs/tabs.stories.tsx\n\tconst tabsStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/ui/tabs';\nimport { Button } from '~/components/ui/button';\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '~/components/ui/card';\nimport { Input } from '~/components/ui/input';\nimport { Label } from '~/components/ui/label';\n\nconst meta: Meta<typeof Tabs> = {\n title: 'Components/Tabs',\n component: Tabs,\n tags: ['autodocs'],\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Tabs>;\n\nexport const Default: Story = {\n render: () => (\n <Tabs defaultValue=\"account\" className=\"w-[400px]\">\n <TabsList>\n <TabsTrigger value=\"account\">Account</TabsTrigger>\n <TabsTrigger value=\"password\">Password</TabsTrigger>\n </TabsList>\n <TabsContent value=\"account\">\n <Card>\n <CardHeader>\n <CardTitle>Account</CardTitle>\n <CardDescription>\n Make changes to your account here. Click save when you're done.\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"name\">Name</Label>\n <Input id=\"name\" defaultValue=\"Pedro Duarte\" />\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor=\"username\">Username</Label>\n <Input id=\"username\" defaultValue=\"@peduarte\" />\n </div>\n </CardContent>\n <CardFooter>\n <Button>Save changes</Button>\n </CardFooter>\n </Card>\n </TabsContent>\n <TabsContent value=\"password\">\n <Card>\n <CardHeader>\n <CardTitle>Password</CardTitle>\n <CardDescription>\n Change your password here. After saving, you'll be logged out.\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"current\">Current password</Label>\n <Input id=\"current\" type=\"password\" />\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor=\"new\">New password</Label>\n <Input id=\"new\" type=\"password\" />\n </div>\n </CardContent>\n <CardFooter>\n <Button>Save password</Button>\n </CardFooter>\n </Card>\n </TabsContent>\n </Tabs>\n ),\n};\n`;\n\n\t// src/components/ui/tooltip/index.tsx\n\tconst tooltipTsx = `import * as TooltipPrimitive from '@radix-ui/react-tooltip';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst TooltipProvider = TooltipPrimitive.Provider;\n\nconst Tooltip = TooltipPrimitive.Root;\n\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n 'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n className,\n )}\n {...props}\n />\n </TooltipPrimitive.Portal>\n));\nTooltipContent.displayName = TooltipPrimitive.Content.displayName;\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n`;\n\n\t// src/components/ui/tooltip/tooltip.stories.tsx\n\tconst tooltipStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '~/components/ui/tooltip';\nimport { Button } from '~/components/ui/button';\n\nconst meta: Meta<typeof Tooltip> = {\n title: 'Components/Tooltip',\n component: Tooltip,\n tags: ['autodocs'],\n decorators: [\n (Story) => (\n <TooltipProvider>\n <Story />\n </TooltipProvider>\n ),\n ],\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Tooltip>;\n\nexport const Default: Story = {\n render: () => (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\">Hover me</Button>\n </TooltipTrigger>\n <TooltipContent>\n <p>Add to library</p>\n </TooltipContent>\n </Tooltip>\n ),\n};\n\nexport const Positions: Story = {\n render: () => (\n <div className=\"flex gap-4\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\">Top</Button>\n </TooltipTrigger>\n <TooltipContent side=\"top\">\n <p>Top tooltip</p>\n </TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\">Bottom</Button>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\">\n <p>Bottom tooltip</p>\n </TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\">Left</Button>\n </TooltipTrigger>\n <TooltipContent side=\"left\">\n <p>Left tooltip</p>\n </TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\">Right</Button>\n </TooltipTrigger>\n <TooltipContent side=\"right\">\n <p>Right tooltip</p>\n </TooltipContent>\n </Tooltip>\n </div>\n ),\n};\n`;\n\n\t// src/components/ui/dialog/index.tsx\n\tconst dialogTsx = `import * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { X } from 'lucide-react';\nimport * as React from 'react';\n\nimport { cn } from '~/lib/utils';\n\nconst Dialog = DialogPrimitive.Root;\n\nconst DialogTrigger = DialogPrimitive.Trigger;\n\nconst DialogPortal = DialogPrimitive.Portal;\n\nconst DialogClose = DialogPrimitive.Close;\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n 'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n className,\n )}\n {...props}\n />\n));\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName;\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',\n className,\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n));\nDialogContent.displayName = DialogPrimitive.Content.displayName;\n\nconst DialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n 'flex flex-col space-y-1.5 text-center sm:text-left',\n className,\n )}\n {...props}\n />\n);\nDialogHeader.displayName = 'DialogHeader';\n\nconst DialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',\n className,\n )}\n {...props}\n />\n);\nDialogFooter.displayName = 'DialogFooter';\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\n 'text-lg font-semibold leading-none tracking-tight',\n className,\n )}\n {...props}\n />\n));\nDialogTitle.displayName = DialogPrimitive.Title.displayName;\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n));\nDialogDescription.displayName = DialogPrimitive.Description.displayName;\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogTrigger,\n DialogClose,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n};\n`;\n\n\t// src/components/ui/dialog/dialog.stories.tsx\n\tconst dialogStories = `import type { Meta, StoryObj } from '@storybook/react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '~/components/ui/dialog';\nimport { Button } from '~/components/ui/button';\nimport { Input } from '~/components/ui/input';\nimport { Label } from '~/components/ui/label';\n\nconst meta: Meta<typeof Dialog> = {\n title: 'Components/Dialog',\n component: Dialog,\n tags: ['autodocs'],\n};\n\nexport default meta;\ntype Story = StoryObj<typeof Dialog>;\n\nexport const Default: Story = {\n render: () => (\n <Dialog>\n <DialogTrigger asChild>\n <Button variant=\"outline\">Edit Profile</Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-[425px]\">\n <DialogHeader>\n <DialogTitle>Edit profile</DialogTitle>\n <DialogDescription>\n Make changes to your profile here. Click save when you're done.\n </DialogDescription>\n </DialogHeader>\n <div className=\"grid gap-4 py-4\">\n <div className=\"grid grid-cols-4 items-center gap-4\">\n <Label htmlFor=\"name\" className=\"text-right\">\n Name\n </Label>\n <Input id=\"name\" defaultValue=\"Pedro Duarte\" className=\"col-span-3\" />\n </div>\n <div className=\"grid grid-cols-4 items-center gap-4\">\n <Label htmlFor=\"username\" className=\"text-right\">\n Username\n </Label>\n <Input id=\"username\" defaultValue=\"@peduarte\" className=\"col-span-3\" />\n </div>\n </div>\n <DialogFooter>\n <Button type=\"submit\">Save changes</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n ),\n};\n\nexport const Alert: Story = {\n render: () => (\n <Dialog>\n <DialogTrigger asChild>\n <Button variant=\"destructive\">Delete Account</Button>\n </DialogTrigger>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Are you absolutely sure?</DialogTitle>\n <DialogDescription>\n This action cannot be undone. This will permanently delete your\n account and remove your data from our servers.\n </DialogDescription>\n </DialogHeader>\n <DialogFooter>\n <Button variant=\"outline\">Cancel</Button>\n <Button variant=\"destructive\">Delete</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n ),\n};\n`;\n\n\t// src/components/ui/index.ts\n\tconst componentsUiIndex = `export { Button, type ButtonProps, buttonVariants } from './button.tsx';\nexport { Input } from './input.tsx';\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardDescription,\n CardContent,\n} from './card.tsx';\nexport { Label } from './label.tsx';\nexport { Badge, type BadgeProps, badgeVariants } from './badge.tsx';\nexport { Separator } from './separator.tsx';\nexport { Tabs, TabsList, TabsTrigger, TabsContent } from './tabs.tsx';\nexport {\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n TooltipProvider,\n} from './tooltip.tsx';\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogTrigger,\n DialogClose,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n} from './dialog.tsx';\n`;\n\n\t// Rename component files to index.tsx (same content)\n\tconst buttonIndexTsx = buttonTsx;\n\tconst inputIndexTsx = inputTsx;\n\tconst cardIndexTsx = cardTsx;\n\n\t// src/components/index.ts\n\tconst componentsIndex = `export * from './ui/index.ts';\n`;\n\n\t// src/index.ts\n\tconst indexTs = `// @${options.name}/ui - Shared UI component library\n\n// shadcn/ui components\nexport * from './components/index.ts';\n\n// Utilities\nexport { cn } from './lib/utils.ts';\n`;\n\n\t// .gitignore for UI package\n\tconst gitignore = `node_modules/\ndist/\nstorybook-static/\n*.log\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'packages/ui/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/components.json',\n\t\t\tcontent: `${JSON.stringify(componentsJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/.storybook/main.ts',\n\t\t\tcontent: storybookMain,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/.storybook/preview.ts',\n\t\t\tcontent: storybookPreview,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/styles/globals.css',\n\t\t\tcontent: globalsCss,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/lib/utils.ts',\n\t\t\tcontent: utilsTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/button/index.tsx',\n\t\t\tcontent: buttonIndexTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/button/button.stories.tsx',\n\t\t\tcontent: buttonStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/input/index.tsx',\n\t\t\tcontent: inputIndexTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/input/input.stories.tsx',\n\t\t\tcontent: inputStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/card/index.tsx',\n\t\t\tcontent: cardIndexTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/card/card.stories.tsx',\n\t\t\tcontent: cardStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/label/index.tsx',\n\t\t\tcontent: labelTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/label/label.stories.tsx',\n\t\t\tcontent: labelStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/badge/index.tsx',\n\t\t\tcontent: badgeTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/badge/badge.stories.tsx',\n\t\t\tcontent: badgeStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/separator/index.tsx',\n\t\t\tcontent: separatorTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/separator/separator.stories.tsx',\n\t\t\tcontent: separatorStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/tabs/index.tsx',\n\t\t\tcontent: tabsTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/tabs/tabs.stories.tsx',\n\t\t\tcontent: tabsStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/tooltip/index.tsx',\n\t\t\tcontent: tooltipTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/tooltip/tooltip.stories.tsx',\n\t\t\tcontent: tooltipStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/dialog/index.tsx',\n\t\t\tcontent: dialogTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/dialog/dialog.stories.tsx',\n\t\t\tcontent: dialogStories,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/ui/index.ts',\n\t\t\tcontent: componentsUiIndex,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/components/index.ts',\n\t\t\tcontent: componentsIndex,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/src/index.ts',\n\t\t\tcontent: indexTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'packages/ui/.gitignore',\n\t\t\tcontent: gitignore,\n\t\t},\n\t];\n}\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate Next.js web app files for fullstack template\n */\nexport function generateWebAppFiles(options: TemplateOptions): GeneratedFile[] {\n\tif (!options.monorepo || options.template !== 'fullstack') {\n\t\treturn [];\n\t}\n\n\tconst packageName = `@${options.name}/web`;\n\tconst apiPackage = `@${options.name}/api`;\n\tconst modelsPackage = `@${options.name}/models`;\n\tconst uiPackage = `@${options.name}/ui`;\n\n\t// package.json for web app\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\tscripts: {\n\t\t\tdev: 'gkm exec -- next dev --turbopack',\n\t\t\tbuild: 'gkm exec -- next build',\n\t\t\tstart: 'next start',\n\t\t\ttypecheck: 'tsc --noEmit',\n\t\t},\n\t\tdependencies: {\n\t\t\t[apiPackage]: 'workspace:*',\n\t\t\t[modelsPackage]: 'workspace:*',\n\t\t\t[uiPackage]: 'workspace:*',\n\t\t\t'@geekmidas/client': GEEKMIDAS_VERSIONS['@geekmidas/client'],\n\t\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t\t'@tanstack/react-query': '~5.80.0',\n\t\t\t'better-auth': '~1.2.0',\n\t\t\tnext: '~16.1.0',\n\t\t\treact: '~19.2.0',\n\t\t\t'react-dom': '~19.2.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t\t'@tailwindcss/postcss': '^4.0.0',\n\t\t\t'@types/node': '~22.0.0',\n\t\t\t'@types/react': '~19.0.0',\n\t\t\t'@types/react-dom': '~19.0.0',\n\t\t\ttailwindcss: '^4.0.0',\n\t\t\ttsx: '~4.20.0',\n\t\t\ttypescript: '~5.8.2',\n\t\t},\n\t};\n\n\t// next.config.ts\n\tconst nextConfig = `import type { NextConfig } from 'next';\n\nconst nextConfig: NextConfig = {\n output: 'standalone',\n reactStrictMode: true,\n transpilePackages: ['${apiPackage}', '${modelsPackage}', '${uiPackage}'],\n};\n\nexport default nextConfig;\n`;\n\n\t// postcss.config.mjs for Tailwind v4\n\tconst postcssConfig = `export default {\n plugins: {\n '@tailwindcss/postcss': {},\n },\n};\n`;\n\n\t// tsconfig.json for Next.js\n\t// Note: Next.js handles compilation, so noEmit: true\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tlib: ['dom', 'dom.iterable', 'ES2022'],\n\t\t\tallowJs: true,\n\t\t\tskipLibCheck: true,\n\t\t\tstrict: true,\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\tesModuleInterop: true,\n\t\t\tmodule: 'ESNext',\n\t\t\tmoduleResolution: 'bundler',\n\t\t\tresolveJsonModule: true,\n\t\t\tisolatedModules: true,\n\t\t\tjsx: 'preserve',\n\t\t\tincremental: true,\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'next',\n\t\t\t\t},\n\t\t\t],\n\t\t\tbaseUrl: '.',\n\t\t\tpaths: {\n\t\t\t\t'~/*': ['./src/*', '../../packages/ui/src/*'],\n\t\t\t\t[`${apiPackage}/client`]: ['../../apps/api/.gkm/openapi.ts'],\n\t\t\t\t[`${modelsPackage}`]: ['../../packages/models/src'],\n\t\t\t\t[`${modelsPackage}/*`]: ['../../packages/models/src/*'],\n\t\t\t\t[`${uiPackage}`]: ['../../packages/ui/src'],\n\t\t\t\t[`${uiPackage}/*`]: ['../../packages/ui/src/*'],\n\t\t\t},\n\t\t},\n\t\tinclude: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],\n\t\texclude: ['node_modules'],\n\t\treferences: [\n\t\t\t{ path: '../../apps/api' },\n\t\t\t{ path: '../../packages/ui' },\n\t\t\t{ path: '../../packages/models' },\n\t\t],\n\t};\n\n\t// Query client singleton for browser, fresh instance for server\n\tconst queryClientTs = `import { QueryClient } from '@tanstack/react-query';\n\nfunction makeQueryClient() {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 60 * 1000,\n },\n },\n });\n}\n\nlet browserQueryClient: QueryClient | undefined = undefined;\n\nexport function getQueryClient() {\n if (typeof window === 'undefined') {\n // Server: always make a new query client\n return makeQueryClient();\n }\n // Browser: reuse existing query client\n if (!browserQueryClient) browserQueryClient = makeQueryClient();\n return browserQueryClient;\n}\n`;\n\n\t// Client config - NEXT_PUBLIC_* vars (available in browser)\n\tconst clientConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';\n\n// Client config - only NEXT_PUBLIC_* vars (available in browser)\n// These values are inlined at build time by Next.js\nconst envParser = new EnvironmentParser({\n NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,\n NEXT_PUBLIC_AUTH_URL: process.env.NEXT_PUBLIC_AUTH_URL,\n});\n\nexport const clientConfig = envParser\n .create((get) => ({\n apiUrl: get('NEXT_PUBLIC_API_URL').string(),\n authUrl: get('NEXT_PUBLIC_AUTH_URL').string(),\n }))\n .parse();\n`;\n\n\t// Server config - server-only vars (not available in browser)\n\tconst serverConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';\n\n// Server config - all env vars (server-side only, not exposed to browser)\n// Access these only in Server Components, Route Handlers, or Server Actions\nconst envParser = new EnvironmentParser({ ...process.env });\n\nexport const serverConfig = envParser\n .create((get) => ({\n // Add server-only secrets here\n // Example: stripeSecretKey: get('STRIPE_SECRET_KEY').string(),\n }))\n .parse();\n`;\n\n\t// Auth client for better-auth\n\tconst authClientTs = `import { createAuthClient } from 'better-auth/react';\nimport { magicLinkClient } from 'better-auth/client/plugins';\nimport { clientConfig } from '~/config/client.ts';\n\nexport const authClient = createAuthClient({\n baseURL: clientConfig.authUrl,\n plugins: [magicLinkClient()],\n});\n\nexport const { signIn, signUp, signOut, useSession, magicLink } = authClient;\n`;\n\n\t// Providers using shared QueryClient\n\tconst providersTsx = `'use client';\n\nimport { QueryClientProvider } from '@tanstack/react-query';\nimport { getQueryClient } from '~/lib/query-client.ts';\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n const queryClient = getQueryClient();\n\n return (\n <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>\n );\n}\n`;\n\n\t// API client setup - imports directly from the API package export\n\tconst apiIndexTs = `import { createApi } from '${apiPackage}/client';\nimport { getQueryClient } from '~/lib/query-client.ts';\nimport { clientConfig } from '~/config/client.ts';\n\nexport const api = createApi({\n baseURL: clientConfig.apiUrl,\n queryClient: getQueryClient(),\n});\n`;\n\n\t// globals.css that imports UI package styles\n\tconst globalsCss = `@import '${uiPackage}/styles';\n`;\n\n\t// App layout\n\tconst layoutTsx = `import type { Metadata } from 'next';\nimport { Providers } from './providers.tsx';\nimport './globals.css';\n\nexport const metadata: Metadata = {\n title: '${options.name}',\n description: 'Created with gkm init',\n};\n\nexport default function RootLayout({\n children,\n}: {\n children: React.ReactNode;\n}) {\n return (\n <html lang=\"en\">\n <body>\n <Providers>{children}</Providers>\n </body>\n </html>\n );\n}\n`;\n\n\t// Home page with API example using UI components\n\tconst pageTsx = `import { api } from '~/api/index.ts';\nimport { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from '${uiPackage}/components';\n\nexport default async function Home() {\n // Type-safe API call using the generated client\n const health = await api('GET /health').catch(() => null);\n\n return (\n <main className=\"min-h-screen bg-background p-8\">\n <div className=\"mx-auto max-w-4xl space-y-8\">\n <div className=\"space-y-2\">\n <h1 className=\"text-4xl font-bold tracking-tight\">Welcome to ${options.name}</h1>\n <p className=\"text-muted-foreground\">Your fullstack application is ready.</p>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>API Status</CardTitle>\n <CardDescription>Connection to your backend API</CardDescription>\n </CardHeader>\n <CardContent>\n {health ? (\n <pre className=\"rounded-lg bg-muted p-4 text-sm\">\n {JSON.stringify(health, null, 2)}\n </pre>\n ) : (\n <p className=\"text-destructive\">Unable to connect to API</p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Next Steps</CardTitle>\n <CardDescription>Get started with your project</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <ul className=\"list-inside list-disc space-y-2 text-muted-foreground\">\n <li>Run <code className=\"rounded bg-muted px-1\">gkm openapi</code> to generate typed API client</li>\n <li>Edit <code className=\"rounded bg-muted px-1\">apps/web/src/app/page.tsx</code> to customize this page</li>\n <li>Add API routes in <code className=\"rounded bg-muted px-1\">apps/api/src/endpoints/</code></li>\n <li>Add UI components with <code className=\"rounded bg-muted px-1\">npx shadcn@latest add</code> in packages/ui</li>\n </ul>\n <div className=\"flex gap-4\">\n <Button>Get Started</Button>\n <Button variant=\"outline\">Documentation</Button>\n </div>\n </CardContent>\n </Card>\n </div>\n </main>\n );\n}\n`;\n\n\t// .gitignore for Next.js\n\tconst gitignore = `.next/\nnode_modules/\n.env.local\n*.log\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'apps/web/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/next.config.ts',\n\t\t\tcontent: nextConfig,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/postcss.config.mjs',\n\t\t\tcontent: postcssConfig,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/app/globals.css',\n\t\t\tcontent: globalsCss,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/app/layout.tsx',\n\t\t\tcontent: layoutTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/app/providers.tsx',\n\t\t\tcontent: providersTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/app/page.tsx',\n\t\t\tcontent: pageTsx,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/config/client.ts',\n\t\t\tcontent: clientConfigTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/config/server.ts',\n\t\t\tcontent: serverConfigTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/lib/query-client.ts',\n\t\t\tcontent: queryClientTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/lib/auth-client.ts',\n\t\t\tcontent: authClientTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/src/api/index.ts',\n\t\t\tcontent: apiIndexTs,\n\t\t},\n\t\t{\n\t\t\tpath: 'apps/web/.gitignore',\n\t\t\tcontent: gitignore,\n\t\t},\n\t];\n}\n","import type { GeneratedFile, TemplateOptions } from '../templates/index.js';\nimport { GEEKMIDAS_VERSIONS } from '../versions.js';\n\n/**\n * Generate TanStack Start web app files for the fullstack template.\n *\n * Mirrors the Next.js scaffold (apps/web) but on Vite + TanStack Start +\n * TanStack Router with the `VITE_` env-var convention.\n */\nexport function generateTanStackWebFiles(\n\toptions: TemplateOptions,\n): GeneratedFile[] {\n\tif (!options.monorepo || options.template !== 'fullstack') {\n\t\treturn [];\n\t}\n\n\tconst packageName = `@${options.name}/web`;\n\tconst apiPackage = `@${options.name}/api`;\n\tconst modelsPackage = `@${options.name}/models`;\n\tconst uiPackage = `@${options.name}/ui`;\n\n\tconst packageJson = {\n\t\tname: packageName,\n\t\tversion: '0.0.1',\n\t\tprivate: true,\n\t\ttype: 'module',\n\t\tscripts: {\n\t\t\tdev: 'gkm exec -- vite dev',\n\t\t\tbuild: 'gkm exec -- vite build',\n\t\t\tstart: 'node .output/server/index.mjs',\n\t\t\ttypecheck: 'tsc --noEmit',\n\t\t},\n\t\tdependencies: {\n\t\t\t[apiPackage]: 'workspace:*',\n\t\t\t[modelsPackage]: 'workspace:*',\n\t\t\t[uiPackage]: 'workspace:*',\n\t\t\t'@geekmidas/client': GEEKMIDAS_VERSIONS['@geekmidas/client'],\n\t\t\t'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],\n\t\t\t'@tanstack/react-query': '~5.80.0',\n\t\t\t'@tanstack/react-router': '^1.87.0',\n\t\t\t'@tanstack/react-start': '^1.87.0',\n\t\t\t'better-auth': '~1.2.0',\n\t\t\treact: '~19.2.0',\n\t\t\t'react-dom': '~19.2.0',\n\t\t},\n\t\tdevDependencies: {\n\t\t\t'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],\n\t\t\t'@tailwindcss/vite': '^4.0.0',\n\t\t\t'@tanstack/router-plugin': '^1.87.0',\n\t\t\t'@types/node': '~22.0.0',\n\t\t\t'@types/react': '~19.0.0',\n\t\t\t'@types/react-dom': '~19.0.0',\n\t\t\t'@vitejs/plugin-react': '^4.3.4',\n\t\t\ttailwindcss: '^4.0.0',\n\t\t\ttsx: '~4.20.0',\n\t\t\ttypescript: '~5.8.2',\n\t\t\tvite: '^7.0.0',\n\t\t\t'vite-tsconfig-paths': '~5.1.0',\n\t\t},\n\t};\n\n\tconst viteConfig = `import { Credentials } from '@geekmidas/envkit/credentials';\nimport tailwindcss from '@tailwindcss/vite';\nimport { tanstackStart } from '@tanstack/react-start/plugin/vite';\nimport viteReact from '@vitejs/plugin-react';\nimport { defineConfig } from 'vite';\nimport tsconfigPaths from 'vite-tsconfig-paths';\n\nexport default defineConfig({\n server: {\n port: Number(Credentials.PORT ?? 3001),\n },\n // VITE_* vars are inlined automatically; no manual loadEnv needed.\n envPrefix: 'VITE_',\n plugins: [\n tsconfigPaths({ root: import.meta.dirname }),\n tailwindcss(),\n tanstackStart(),\n viteReact(),\n ],\n});\n`;\n\n\tconst tsConfig = {\n\t\textends: '../../tsconfig.json',\n\t\tcompilerOptions: {\n\t\t\tlib: ['dom', 'dom.iterable', 'ES2022'],\n\t\t\tallowJs: true,\n\t\t\tskipLibCheck: true,\n\t\t\tstrict: true,\n\t\t\tnoEmit: true,\n\t\t\tallowImportingTsExtensions: true,\n\t\t\tesModuleInterop: true,\n\t\t\tmodule: 'ESNext',\n\t\t\tmoduleResolution: 'bundler',\n\t\t\tresolveJsonModule: true,\n\t\t\tisolatedModules: true,\n\t\t\tjsx: 'preserve',\n\t\t\tincremental: true,\n\t\t\tbaseUrl: '.',\n\t\t\ttypes: ['vite/client'],\n\t\t\tpaths: {\n\t\t\t\t'~/*': ['./src/*', '../../packages/ui/src/*'],\n\t\t\t\t[modelsPackage]: ['../../packages/models/src'],\n\t\t\t\t[`${modelsPackage}/*`]: ['../../packages/models/src/*'],\n\t\t\t\t[uiPackage]: ['../../packages/ui/src'],\n\t\t\t\t[`${uiPackage}/*`]: ['../../packages/ui/src/*'],\n\t\t\t\t[`${apiPackage}/client`]: ['../../apps/api/.gkm/openapi.ts'],\n\t\t\t},\n\t\t},\n\t\tinclude: ['**/*.ts', '**/*.tsx', 'src/routeTree.gen.ts'],\n\t\texclude: ['node_modules', '.output', '.vinxi', 'dist'],\n\t};\n\n\tconst queryClientTs = `import { QueryClient } from '@tanstack/react-query';\n\nfunction makeQueryClient() {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 60 * 1000,\n },\n },\n });\n}\n\nlet browserQueryClient: QueryClient | undefined = undefined;\n\nexport function getQueryClient() {\n if (typeof window === 'undefined') {\n return makeQueryClient();\n }\n if (!browserQueryClient) browserQueryClient = makeQueryClient();\n return browserQueryClient;\n}\n`;\n\n\tconst clientConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';\n\n// Client config - VITE_* vars are inlined at build time.\nconst envParser = new EnvironmentParser({\n VITE_API_URL: import.meta.env.VITE_API_URL,\n VITE_AUTH_URL: import.meta.env.VITE_AUTH_URL,\n});\n\nexport const clientConfig = envParser\n .create((get) => ({\n apiUrl: get('VITE_API_URL').string(),\n authUrl: get('VITE_AUTH_URL').string(),\n }))\n .parse();\n`;\n\n\tconst serverConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';\n\n// Server config - all env vars (server-side only, not exposed to browser).\n// Access these only in TanStack Start server functions / server routes.\nconst envParser = new EnvironmentParser({ ...process.env });\n\nexport const serverConfig = envParser\n .create((_get) => ({\n // Add server-only secrets here\n // Example: stripeSecretKey: _get('STRIPE_SECRET_KEY').string(),\n }))\n .parse();\n`;\n\n\tconst authClientTs = `import { magicLinkClient } from 'better-auth/client/plugins';\nimport { createAuthClient } from 'better-auth/react';\nimport { clientConfig } from '~/config/client.ts';\n\nexport const authClient = createAuthClient({\n baseURL: clientConfig.authUrl,\n plugins: [magicLinkClient()],\n});\n\nexport const { signIn, signUp, signOut, useSession, magicLink } = authClient;\n`;\n\n\tconst apiIndexTs = `import { createApi } from '${apiPackage}/client';\nimport { clientConfig } from '~/config/client.ts';\nimport { getQueryClient } from '~/lib/query-client.ts';\n\nexport function createAppApi(options?: { headers?: Record<string, string> }) {\n return createApi({\n baseURL: clientConfig.apiUrl,\n queryClient: getQueryClient(),\n headers: options?.headers,\n onRequest: (config) => ({ ...config, credentials: 'include' }),\n });\n}\n\nexport const api = createAppApi();\n`;\n\n\tconst routerTs = `import { createRouter } from '@tanstack/react-router';\nimport { routeTree } from './routeTree.gen.ts';\n\nexport function getRouter() {\n return createRouter({\n routeTree,\n scrollRestoration: true,\n defaultPreload: 'intent',\n });\n}\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: ReturnType<typeof getRouter>;\n }\n}\n`;\n\n\t// Placeholder routeTree.gen — TanStack's plugin regenerates this on dev.\n\tconst routeTreeGen = `// This file is auto-generated by @tanstack/router-plugin.\n// It will be overwritten on the next \\`vite dev\\` run.\nimport { rootRouteWithContext } from '@tanstack/react-router';\n\nexport const routeTree = rootRouteWithContext()({\n // Routes are populated by the plugin from src/routes/**.tsx\n} as never);\n`;\n\n\tconst rootRouteTsx = `/// <reference types=\"vite/client\" />\nimport '~/styles/globals.css';\n\nimport { QueryClientProvider } from '@tanstack/react-query';\nimport {\n HeadContent,\n Outlet,\n Scripts,\n createRootRoute,\n} from '@tanstack/react-router';\nimport type { ReactNode } from 'react';\nimport { getQueryClient } from '~/lib/query-client.ts';\n\nexport const Route = createRootRoute({\n head: () => ({\n meta: [\n { charSet: 'utf-8' },\n { name: 'viewport', content: 'width=device-width, initial-scale=1' },\n { title: '${options.name}' },\n ],\n }),\n component: RootComponent,\n});\n\nfunction RootComponent() {\n const queryClient = getQueryClient();\n return (\n <RootDocument>\n <QueryClientProvider client={queryClient}>\n <Outlet />\n </QueryClientProvider>\n </RootDocument>\n );\n}\n\nfunction RootDocument({ children }: { children: ReactNode }) {\n return (\n <html lang=\"en\">\n <head>\n <HeadContent />\n </head>\n <body>\n {children}\n <Scripts />\n </body>\n </html>\n );\n}\n`;\n\n\tconst indexRouteTsx = `import { createFileRoute } from '@tanstack/react-router';\nimport { api } from '~/api/index.ts';\n\nexport const Route = createFileRoute('/')({\n component: HomeComponent,\n});\n\nfunction HomeComponent() {\n const { data: health } = api.useQuery('GET /health', {});\n\n return (\n <main className=\"min-h-screen bg-background p-8\">\n <div className=\"mx-auto max-w-4xl space-y-8\">\n <div className=\"space-y-2\">\n <h1 className=\"text-4xl font-bold tracking-tight\">\n Welcome to ${options.name}\n </h1>\n <p className=\"text-muted-foreground\">\n Your TanStack Start app is ready.\n </p>\n </div>\n\n <section className=\"rounded-lg border p-6\">\n <h2 className=\"text-xl font-semibold\">API Status</h2>\n {health ? (\n <pre className=\"mt-4 rounded bg-muted p-4 text-sm\">\n {JSON.stringify(health, null, 2)}\n </pre>\n ) : (\n <p className=\"mt-2 text-muted-foreground\">Connecting…</p>\n )}\n </section>\n </div>\n </main>\n );\n}\n`;\n\n\tconst globalsCss = `@import '${uiPackage}/styles';\n\n@source \"../..\";\n@source \"../../../../packages/ui/src\";\n`;\n\n\tconst gitignore = `.output/\n.vinxi/\nnode_modules/\n.env.local\n*.log\nsrc/routeTree.gen.ts\n`;\n\n\treturn [\n\t\t{\n\t\t\tpath: 'apps/web/package.json',\n\t\t\tcontent: `${JSON.stringify(packageJson, null, 2)}\\n`,\n\t\t},\n\t\t{ path: 'apps/web/vite.config.ts', content: viteConfig },\n\t\t{\n\t\t\tpath: 'apps/web/tsconfig.json',\n\t\t\tcontent: `${JSON.stringify(tsConfig, null, 2)}\\n`,\n\t\t},\n\t\t{ path: 'apps/web/src/lib/query-client.ts', content: queryClientTs },\n\t\t{ path: 'apps/web/src/lib/auth-client.ts', content: authClientTs },\n\t\t{ path: 'apps/web/src/config/client.ts', content: clientConfigTs },\n\t\t{ path: 'apps/web/src/config/server.ts', content: serverConfigTs },\n\t\t{ path: 'apps/web/src/api/index.ts', content: apiIndexTs },\n\t\t{ path: 'apps/web/src/router.tsx', content: routerTs },\n\t\t{ path: 'apps/web/src/routeTree.gen.ts', content: routeTreeGen },\n\t\t{ path: 'apps/web/src/routes/__root.tsx', content: rootRouteTsx },\n\t\t{ path: 'apps/web/src/routes/index.tsx', content: indexRouteTsx },\n\t\t{ path: 'apps/web/src/styles/globals.css', content: globalsCss },\n\t\t{ path: 'apps/web/.gitignore', content: gitignore },\n\t];\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, parse } from 'node:path';\nimport fg from 'fast-glob';\nimport { parse as parseYaml } from 'yaml';\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';\n\n/**\n * Detect the package manager being used based on lockfiles or npm_config_user_agent\n */\nexport function detectPackageManager(\n\tcwd: string = process.cwd(),\n): PackageManager {\n\t// Check for lockfiles in cwd\n\tif (existsSync(join(cwd, 'pnpm-lock.yaml'))) return 'pnpm';\n\tif (existsSync(join(cwd, 'yarn.lock'))) return 'yarn';\n\tif (existsSync(join(cwd, 'bun.lockb'))) return 'bun';\n\tif (existsSync(join(cwd, 'package-lock.json'))) return 'npm';\n\n\t// Check npm_config_user_agent (set when running via npx/pnpm dlx/etc)\n\tconst userAgent = process.env.npm_config_user_agent || '';\n\tif (userAgent.includes('pnpm')) return 'pnpm';\n\tif (userAgent.includes('yarn')) return 'yarn';\n\tif (userAgent.includes('bun')) return 'bun';\n\n\treturn 'npm';\n}\n\n/**\n * Validate project name for npm package naming conventions\n */\nexport function validateProjectName(name: string): boolean | string {\n\tif (!name) {\n\t\treturn 'Project name is required';\n\t}\n\n\t// Check for valid npm package name characters\n\tif (!/^[a-z0-9-_@/.]+$/i.test(name)) {\n\t\treturn 'Project name can only contain letters, numbers, hyphens, underscores, @, /, and .';\n\t}\n\n\t// Check for reserved names\n\tconst reserved = ['node_modules', '.git', 'package.json', 'src'];\n\tif (reserved.includes(name.toLowerCase())) {\n\t\treturn `\"${name}\" is a reserved name`;\n\t}\n\n\treturn true;\n}\n\n/**\n * Check if a directory already exists at the target path\n */\nexport function checkDirectoryExists(\n\tname: string,\n\tcwd: string = process.cwd(),\n): boolean | string {\n\tconst targetPath = join(cwd, name);\n\tif (existsSync(targetPath)) {\n\t\treturn `Directory \"${name}\" already exists`;\n\t}\n\treturn true;\n}\n\n/**\n * Get the install command for a package manager\n */\nexport function getInstallCommand(pkgManager: PackageManager): string {\n\tswitch (pkgManager) {\n\t\tcase 'pnpm':\n\t\t\treturn 'pnpm install';\n\t\tcase 'yarn':\n\t\t\treturn 'yarn';\n\t\tcase 'bun':\n\t\t\treturn 'bun install';\n\t\tdefault:\n\t\t\treturn 'npm install';\n\t}\n}\n\n/**\n * Get the dev command for a package manager\n */\nexport function getRunCommand(\n\tpkgManager: PackageManager,\n\tscript: string,\n): string {\n\tswitch (pkgManager) {\n\t\tcase 'pnpm':\n\t\t\treturn `pnpm ${script}`;\n\t\tcase 'yarn':\n\t\t\treturn `yarn ${script}`;\n\t\tcase 'bun':\n\t\t\treturn `bun run ${script}`;\n\t\tdefault:\n\t\t\treturn `npm run ${script}`;\n\t}\n}\n\nconst lockfileByPm: Record<PackageManager, string> = {\n\tpnpm: 'pnpm-lock.yaml',\n\tyarn: 'yarn.lock',\n\tnpm: 'package-lock.json',\n\tbun: 'bun.lockb',\n};\n\n/**\n * Find the workspace/project root by walking up from cwd.\n * Checks for PM-specific workspace config, package.json#workspaces,\n * and lockfiles.\n */\nexport function findWorkspaceRoot(cwd: string, pm: PackageManager): string {\n\tlet dir = cwd;\n\tconst root = parse(dir).root;\n\tconst lockfile = lockfileByPm[pm];\n\n\twhile (dir !== root) {\n\t\tif (pm === 'pnpm' && existsSync(join(dir, 'pnpm-workspace.yaml'))) {\n\t\t\treturn dir;\n\t\t}\n\n\t\tconst pkgJsonPath = join(dir, 'package.json');\n\t\tif (existsSync(pkgJsonPath)) {\n\t\t\ttry {\n\t\t\t\tconst pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));\n\t\t\t\tif (pkg.workspaces) return dir;\n\t\t\t} catch {\n\t\t\t\t// ignore malformed package.json\n\t\t\t}\n\t\t}\n\n\t\tif (existsSync(join(dir, lockfile))) {\n\t\t\treturn dir;\n\t\t}\n\n\t\tdir = dirname(dir);\n\t}\n\n\treturn cwd;\n}\n\n/**\n * Get workspace package glob patterns from pnpm-workspace.yaml\n * or package.json#workspaces.\n */\nexport function getWorkspaceGlobs(root: string): string[] {\n\tconst pnpmWorkspacePath = join(root, 'pnpm-workspace.yaml');\n\tif (existsSync(pnpmWorkspacePath)) {\n\t\tconst content = readFileSync(pnpmWorkspacePath, 'utf-8');\n\t\tconst parsed = parseYaml(content);\n\t\treturn parsed?.packages ?? [];\n\t}\n\n\tconst rootPkgJsonPath = join(root, 'package.json');\n\tif (existsSync(rootPkgJsonPath)) {\n\t\tconst pkg = JSON.parse(readFileSync(rootPkgJsonPath, 'utf-8'));\n\t\tif (Array.isArray(pkg.workspaces)) {\n\t\t\treturn pkg.workspaces;\n\t\t}\n\t\tif (pkg.workspaces?.packages) {\n\t\t\treturn pkg.workspaces.packages;\n\t\t}\n\t}\n\n\treturn [];\n}\n\n/**\n * Find all package.json files across a workspace.\n * Returns the root package.json plus all workspace member package.json paths.\n */\nexport function findWorkspacePackages(\n\tcwd: string,\n\tpm: PackageManager,\n): string[] {\n\tconst workspaceRoot = findWorkspaceRoot(cwd, pm);\n\tconst results: string[] = [];\n\n\tconst rootPkgJson = join(workspaceRoot, 'package.json');\n\tif (existsSync(rootPkgJson)) {\n\t\tresults.push(rootPkgJson);\n\t}\n\n\tconst globs = getWorkspaceGlobs(workspaceRoot);\n\n\tfor (const glob of globs) {\n\t\tconst pattern = `${glob}/package.json`;\n\t\tconst matches = fg.sync(pattern, {\n\t\t\tcwd: workspaceRoot,\n\t\t\tabsolute: true,\n\t\t\tignore: ['**/node_modules/**'],\n\t\t});\n\t\tresults.push(...matches);\n\t}\n\n\treturn [...new Set(results)];\n}\n","import { execSync } from 'node:child_process';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport prompts from 'prompts';\nimport { createStageSecrets } from '../secrets/generator.js';\nimport { getKeyPath } from '../secrets/keystore.js';\nimport { writeStageSecrets } from '../secrets/storage.js';\nimport {\n\tgenerateDbPassword,\n\tgenerateDbUrl,\n} from '../setup/fullstack-secrets.js';\nimport type { ComposeServiceName, EventsBackend } from '../types.js';\nimport { generateAuthAppFiles } from './generators/auth.js';\nimport { generateConfigFiles } from './generators/config.js';\nimport {\n\ttype DatabaseAppConfig,\n\tgenerateDockerFiles,\n} from './generators/docker.js';\nimport { generateEnvFiles } from './generators/env.js';\nimport { generateExpoAppFiles } from './generators/mobile-expo.js';\nimport { generateModelsPackage } from './generators/models.js';\nimport { generateMonorepoFiles } from './generators/monorepo.js';\nimport { generatePackageJson } from './generators/package.js';\nimport { generateSourceFiles } from './generators/source.js';\nimport { generateTestFiles } from './generators/test.js';\nimport { generateUiPackageFiles } from './generators/ui.js';\nimport { generateWebAppFiles } from './generators/web.js';\nimport { generateTanStackWebFiles } from './generators/web-tanstack.js';\nimport {\n\ttype DeployTarget,\n\tdeployTargetChoices,\n\teventsBackendChoices,\n\ttype FullstackFrontendFramework,\n\tfrontendFrameworkChoices,\n\tgetTemplate,\n\tisFullstackTemplate,\n\tloggerTypeChoices,\n\ttype PackageManager,\n\tpackageManagerChoices,\n\troutesStructureChoices,\n\ttype ServicesSelection,\n\tservicesChoices,\n\ttype TemplateName,\n\ttype TemplateOptions,\n\ttemplateChoices,\n} from './templates/index.js';\nimport {\n\tcheckDirectoryExists,\n\tdetectPackageManager,\n\tgetInstallCommand,\n\tgetRunCommand,\n\tvalidateProjectName,\n} from './utils.js';\n\nexport interface InitOptions {\n\t/** Project name */\n\tname?: string;\n\t/** Template to use */\n\ttemplate?: TemplateName;\n\t/** Skip dependency installation */\n\tskipInstall?: boolean;\n\t/** Use defaults for all prompts */\n\tyes?: boolean;\n\t/** Force monorepo setup (deprecated, use fullstack template) */\n\tmonorepo?: boolean;\n\t/** API app path in monorepo */\n\tapiPath?: string;\n\t/** Package manager to use */\n\tpm?: PackageManager;\n}\n\n/**\n * Main init command - scaffolds a new project\n */\nexport async function initCommand(\n\tprojectName?: string,\n\toptions: InitOptions = {},\n): Promise<void> {\n\tconst cwd = process.cwd();\n\tconst detectedPkgManager = detectPackageManager(cwd);\n\n\t// Handle Ctrl+C gracefully\n\tprompts.override({});\n\tconst onCancel = () => {\n\t\tprocess.exit(0);\n\t};\n\n\t// Gather answers via prompts\n\tconst answers = await prompts(\n\t\t[\n\t\t\t{\n\t\t\t\ttype: projectName || options.name ? null : 'text',\n\t\t\t\tname: 'name',\n\t\t\t\tmessage: 'Project name:',\n\t\t\t\tinitial: 'my-app',\n\t\t\t\tvalidate: (value: string) => {\n\t\t\t\t\tconst nameValid = validateProjectName(value);\n\t\t\t\t\tif (nameValid !== true) return nameValid;\n\t\t\t\t\tconst dirValid = checkDirectoryExists(value, cwd);\n\t\t\t\t\tif (dirValid !== true) return dirValid;\n\t\t\t\t\treturn true;\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.template || options.yes ? null : 'select',\n\t\t\t\tname: 'template',\n\t\t\t\tmessage: 'Template:',\n\t\t\t\tchoices: templateChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'multiselect',\n\t\t\t\tname: 'services',\n\t\t\t\tmessage: 'Services (space to select, enter to confirm):',\n\t\t\t\tchoices: servicesChoices.map((c) => ({ ...c, selected: true })),\n\t\t\t\thint: '- Space to select. Return to submit',\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'select',\n\t\t\t\tname: 'eventsBackend',\n\t\t\t\tmessage: 'Event backend:',\n\t\t\t\tchoices: eventsBackendChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'select',\n\t\t\t\tname: 'packageManager',\n\t\t\t\tmessage: 'Package manager:',\n\t\t\t\tchoices: packageManagerChoices,\n\t\t\t\tinitial: packageManagerChoices.findIndex(\n\t\t\t\t\t(c) => c.value === detectedPkgManager,\n\t\t\t\t),\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'select',\n\t\t\t\tname: 'deployTarget',\n\t\t\t\tmessage: 'Deployment target:',\n\t\t\t\tchoices: deployTargetChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'confirm',\n\t\t\t\tname: 'telescope',\n\t\t\t\tmessage: 'Include Telescope (debugging dashboard)?',\n\t\t\t\tinitial: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'select',\n\t\t\t\tname: 'loggerType',\n\t\t\t\tmessage: 'Logger:',\n\t\t\t\tchoices: loggerTypeChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: options.yes ? null : 'select',\n\t\t\t\tname: 'routesStructure',\n\t\t\t\tmessage: 'Routes structure:',\n\t\t\t\tchoices: routesStructureChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\t// Only prompt for frontend framework on the fullstack template.\n\t\t\t\ttype: (_prev, values) =>\n\t\t\t\t\t!options.yes &&\n\t\t\t\t\t(options.template === 'fullstack' || values.template === 'fullstack')\n\t\t\t\t\t\t? 'select'\n\t\t\t\t\t\t: null,\n\t\t\t\tname: 'frontendFramework',\n\t\t\t\tmessage: 'Frontend framework:',\n\t\t\t\tchoices: frontendFrameworkChoices,\n\t\t\t\tinitial: 0,\n\t\t\t},\n\t\t],\n\t\t{ onCancel },\n\t);\n\n\t// Build final options\n\tconst name = projectName || options.name || answers.name;\n\tif (!name) {\n\t\tconsole.error('Project name is required');\n\t\tprocess.exit(1);\n\t}\n\n\t// Validate name if provided via argument\n\tif (projectName || options.name) {\n\t\tconst nameToValidate = projectName || options.name!;\n\t\tconst nameValid = validateProjectName(nameToValidate);\n\t\tif (nameValid !== true) {\n\t\t\tconsole.error(nameValid);\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tconst dirValid = checkDirectoryExists(nameToValidate, cwd);\n\t\tif (dirValid !== true) {\n\t\t\tconsole.error(dirValid);\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\tconst template: TemplateName = options.template || answers.template || 'api';\n\tconst isFullstack = isFullstackTemplate(template);\n\n\t// For fullstack, force monorepo mode\n\t// For api template, monorepo is optional (via --monorepo flag)\n\tconst monorepo = isFullstack || options.monorepo || false;\n\n\t// Parse services selection\n\tconst servicesArray: string[] = options.yes\n\t\t? ['db', 'cache', 'mail', 'storage']\n\t\t: answers.services || [];\n\n\t// Determine events backend (default to pgboss for all templates with --yes)\n\tconst eventsBackend: EventsBackend | undefined = options.yes\n\t\t? 'pgboss'\n\t\t: answers.eventsBackend;\n\n\tconst services: ServicesSelection = {\n\t\tdb: servicesArray.includes('db'),\n\t\tcache: servicesArray.includes('cache'),\n\t\tmail: servicesArray.includes('mail'),\n\t\tstorage: servicesArray.includes('storage'),\n\t\tevents: eventsBackend,\n\t};\n\n\t// pgboss requires postgres\n\tif (services.events === 'pgboss') {\n\t\tservices.db = true;\n\t}\n\n\tconst pkgManager: PackageManager = options.pm\n\t\t? options.pm\n\t\t: options.yes\n\t\t\t? 'pnpm'\n\t\t\t: (answers.packageManager ?? detectedPkgManager);\n\n\tconst deployTarget: DeployTarget = options.yes\n\t\t? 'dokploy'\n\t\t: (answers.deployTarget ?? 'dokploy');\n\n\tconst database = services.db;\n\tconst frontendFramework: FullstackFrontendFramework | undefined = isFullstack\n\t\t? options.yes\n\t\t\t? 'nextjs'\n\t\t\t: (answers.frontendFramework ?? 'nextjs')\n\t\t: undefined;\n\tconst templateOptions: TemplateOptions = {\n\t\tname,\n\t\ttemplate,\n\t\ttelescope: options.yes ? true : (answers.telescope ?? true),\n\t\tdatabase,\n\t\tstudio: database,\n\t\tloggerType: options.yes ? 'pino' : (answers.loggerType ?? 'pino'),\n\t\troutesStructure: options.yes\n\t\t\t? 'centralized-endpoints'\n\t\t\t: (answers.routesStructure ?? 'centralized-endpoints'),\n\t\tmonorepo,\n\t\tapiPath: monorepo ? (options.apiPath ?? 'apps/api') : '',\n\t\tpackageManager: pkgManager,\n\t\tdeployTarget,\n\t\tservices,\n\t\tfrontendFramework,\n\t};\n\n\tconst targetDir = join(cwd, name);\n\tconst baseTemplate = getTemplate(templateOptions.template);\n\n\tconst isMonorepo = templateOptions.monorepo;\n\tconst apiPath = templateOptions.apiPath;\n\n\tconsole.log('\\n🚀 Creating your project...\\n');\n\n\t// Create project directory\n\tawait mkdir(targetDir, { recursive: true });\n\n\t// For monorepo, app files go in the specified apiPath (e.g., apps/api)\n\tconst appDir = isMonorepo ? join(targetDir, apiPath) : targetDir;\n\tif (isMonorepo) {\n\t\tawait mkdir(appDir, { recursive: true });\n\t}\n\n\t// Generate per-app database configs for fullstack template\n\tconst dbApps: DatabaseAppConfig[] = [];\n\tif (isFullstack && services.db) {\n\t\tdbApps.push(\n\t\t\t{ name: 'api', password: generateDbPassword() },\n\t\t\t{ name: 'auth', password: generateDbPassword() },\n\t\t);\n\t}\n\n\t// Collect app files (backend/api)\n\t// Note: Docker files go to root for monorepo, so exclude them here\n\tconst appFiles = baseTemplate\n\t\t? [\n\t\t\t\t...generatePackageJson(templateOptions, baseTemplate),\n\t\t\t\t...generateConfigFiles(templateOptions, baseTemplate),\n\t\t\t\t...generateEnvFiles(templateOptions, baseTemplate),\n\t\t\t\t...generateSourceFiles(templateOptions, baseTemplate),\n\t\t\t\t...generateTestFiles(templateOptions, baseTemplate),\n\t\t\t\t...(isMonorepo\n\t\t\t\t\t? []\n\t\t\t\t\t: generateDockerFiles(templateOptions, baseTemplate, dbApps)),\n\t\t\t]\n\t\t: [];\n\n\t// For monorepo, docker files go at root level\n\tconst dockerFiles =\n\t\tisMonorepo && baseTemplate\n\t\t\t? generateDockerFiles(templateOptions, baseTemplate, dbApps)\n\t\t\t: [];\n\n\t// Collect root monorepo files (includes packages/models)\n\tconst rootFiles = baseTemplate\n\t\t? [\n\t\t\t\t...generateMonorepoFiles(templateOptions, baseTemplate),\n\t\t\t\t...generateModelsPackage(templateOptions),\n\t\t\t]\n\t\t: [];\n\n\t// Collect frontend app files for fullstack template, dispatched by framework\n\tconst webAppFiles = isFullstack\n\t\t? frontendFramework === 'tanstack-start'\n\t\t\t? generateTanStackWebFiles(templateOptions)\n\t\t\t: frontendFramework === 'expo'\n\t\t\t\t? generateExpoAppFiles(templateOptions)\n\t\t\t\t: generateWebAppFiles(templateOptions)\n\t\t: [];\n\n\t// Collect auth app files for fullstack template\n\tconst authAppFiles = isFullstack ? generateAuthAppFiles(templateOptions) : [];\n\n\t// Collect UI package files for fullstack template\n\tconst uiPackageFiles = isFullstack\n\t\t? generateUiPackageFiles(templateOptions)\n\t\t: [];\n\n\t// Write root files (for monorepo)\n\tfor (const { path, content } of rootFiles) {\n\t\tconst fullPath = join(targetDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Write docker files at root for monorepo\n\tfor (const { path, content } of dockerFiles) {\n\t\tconst fullPath = join(targetDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Write app files (backend)\n\tfor (const { path, content } of appFiles) {\n\t\tconst fullPath = join(appDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Write web app files (frontend)\n\tfor (const { path, content } of webAppFiles) {\n\t\tconst fullPath = join(targetDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Write auth app files (authentication service)\n\tfor (const { path, content } of authAppFiles) {\n\t\tconst fullPath = join(targetDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Write UI package files (shared components)\n\tfor (const { path, content } of uiPackageFiles) {\n\t\tconst fullPath = join(targetDir, path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, content);\n\t}\n\n\t// Initialize encrypted secrets for development stage\n\tconsole.log('🔐 Initializing encrypted secrets...\\n');\n\tconst secretServices: ComposeServiceName[] = [];\n\tif (services.db) secretServices.push('postgres');\n\tif (services.cache) secretServices.push('redis');\n\tif (services.storage) secretServices.push('minio');\n\tif (services.mail) secretServices.push('mailpit');\n\tif (services.events === 'sns') secretServices.push('localstack');\n\tif (services.events === 'rabbitmq') secretServices.push('rabbitmq');\n\n\tconst devSecrets = createStageSecrets('development', secretServices, {\n\t\tprojectName: name,\n\t\teventsBackend: services.events,\n\t});\n\n\t// Add common custom secrets\n\tconst customSecrets: Record<string, string> = {\n\t\tNODE_ENV: 'development',\n\t\tPORT: '3000',\n\t\tLOG_LEVEL: 'debug',\n\t\tJWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n\t};\n\n\t// Add per-app database URLs and passwords for fullstack template\n\tif (isFullstack && dbApps.length > 0) {\n\t\tfor (const app of dbApps) {\n\t\t\t// Database URL for the app to use (all apps use same database, different users/schemas)\n\t\t\tconst urlKey = `${app.name.toUpperCase()}_DATABASE_URL`;\n\t\t\tcustomSecrets[urlKey] = generateDbUrl(app.name, app.password, name);\n\n\t\t\t// Database password for docker-compose init script\n\t\t\tconst passwordKey = `${app.name.toUpperCase()}_DB_PASSWORD`;\n\t\t\tcustomSecrets[passwordKey] = app.password;\n\t\t}\n\n\t\t// Auth service secrets (better-auth)\n\t\tcustomSecrets.AUTH_PORT = '3002';\n\t\tcustomSecrets.AUTH_URL = 'http://localhost:3002'; // For API app to call auth service\n\t\tcustomSecrets.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n\t\tcustomSecrets.BETTER_AUTH_URL = 'http://localhost:3002';\n\t\tcustomSecrets.BETTER_AUTH_TRUSTED_ORIGINS =\n\t\t\t'http://localhost:3000,http://localhost:3001';\n\t}\n\n\tdevSecrets.custom = customSecrets;\n\n\tawait writeStageSecrets(devSecrets, targetDir);\n\tconst keyPath = getKeyPath('development', name);\n\tconsole.log(` Secrets: .gkm/secrets/development.json (encrypted)`);\n\tconsole.log(` Key: ${keyPath}\\n`);\n\n\t// Install dependencies\n\tif (!options.skipInstall) {\n\t\tconsole.log('\\n📦 Installing dependencies...\\n');\n\t\ttry {\n\t\t\texecSync(getInstallCommand(pkgManager), {\n\t\t\t\tcwd: targetDir,\n\t\t\t\tstdio: 'inherit',\n\t\t\t});\n\t\t} catch {\n\t\t\tconsole.error('Failed to install dependencies');\n\t\t}\n\n\t\t// Format generated files with biome\n\t\ttry {\n\t\t\texecSync('npx @biomejs/biome format --write --unsafe .', {\n\t\t\t\tcwd: targetDir,\n\t\t\t\tstdio: 'inherit',\n\t\t\t});\n\t\t} catch {\n\t\t\t// Silently ignore format errors\n\t\t}\n\t}\n\n\t// Initialize git repository\n\tconsole.log('\\n📦 Initializing git repository...\\n');\n\ttry {\n\t\texecSync('git init', { cwd: targetDir, stdio: 'pipe' });\n\t\texecSync('git branch -M main', { cwd: targetDir, stdio: 'pipe' });\n\t\texecSync('git add .', { cwd: targetDir, stdio: 'pipe' });\n\t\texecSync('git commit -m \"🎉 Project created with @geekmidas/toolbox\"', {\n\t\t\tcwd: targetDir,\n\t\t\tstdio: 'pipe',\n\t\t});\n\t\tconsole.log(' Initialized git repository on branch main');\n\t} catch {\n\t\tconsole.log(\n\t\t\t' Could not initialize git repository (git may not be installed)',\n\t\t);\n\t}\n\n\t// Print success message with next steps\n\tprintNextSteps(name, templateOptions, pkgManager);\n}\n\n/**\n * Print success message with next steps\n */\nfunction printNextSteps(\n\tprojectName: string,\n\toptions: TemplateOptions,\n\tpkgManager: PackageManager,\n): void {\n\tconst devCommand = getRunCommand(pkgManager, 'dev');\n\tconst cdCommand = `cd ${projectName}`;\n\n\tconsole.log(`\\n${'─'.repeat(50)}`);\n\tconsole.log('\\n✅ Project created successfully!\\n');\n\n\tconsole.log('Next steps:\\n');\n\tconsole.log(` ${cdCommand}`);\n\n\t// A project that declares its infrastructure starts nothing by hand:\n\t// `gkm dev` reconciles the containers its constructs imply first.\n\tif (options.services.db && options.monorepo) {\n\t\tconsole.log(` # Start PostgreSQL (if not running)`);\n\t\tconsole.log(` docker compose up -d postgres`);\n\t}\n\n\tconsole.log(` ${devCommand}`);\n\n\tif (!options.monorepo && options.services.db) {\n\t\tconsole.log('');\n\t\tconsole.log(' # Then, once the container is up:');\n\t\tconsole.log(` gkm exec -- pnpm kysely migrate:latest`);\n\t}\n\tconsole.log('');\n\n\tif (options.monorepo) {\n\t\tconsole.log('📁 Project structure:');\n\t\tconsole.log(` ${projectName}/`);\n\t\tconsole.log(` ├── apps/`);\n\t\tconsole.log(` │ ├── api/ # Backend API`);\n\t\tif (isFullstackTemplate(options.template)) {\n\t\t\tconsole.log(` │ ├── auth/ # Auth service (better-auth)`);\n\t\t\tswitch (options.frontendFramework) {\n\t\t\t\tcase 'tanstack-start':\n\t\t\t\t\tconsole.log(` │ └── web/ # TanStack Start frontend`);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'expo':\n\t\t\t\t\tconsole.log(` │ └── app/ # Expo (React Native) app`);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tconsole.log(` │ └── web/ # Next.js frontend`);\n\t\t\t}\n\t\t}\n\t\tconsole.log(` ├── packages/`);\n\t\tconsole.log(` │ ├── models/ # Shared Zod schemas`);\n\t\tif (isFullstackTemplate(options.template)) {\n\t\t\tconsole.log(` │ └── ui/ # Shared UI components`);\n\t\t}\n\t\tconsole.log(` ├── .gkm/secrets/ # Encrypted secrets`);\n\t\tconsole.log(` ├── gkm.config.ts # Workspace config`);\n\t\tconsole.log(` └── turbo.json # Turbo config`);\n\t\tconsole.log('');\n\t}\n\n\tconsole.log('🔐 Secrets management:');\n\tconsole.log(` gkm secrets:show --stage development # View secrets`);\n\tconsole.log(` gkm secrets:set KEY VALUE --stage development # Add secret`);\n\tconsole.log(\n\t\t` gkm secrets:init --stage production # Create production secrets`,\n\t);\n\tconsole.log('');\n\n\tif (options.deployTarget === 'dokploy') {\n\t\tconsole.log('🚀 Deployment:');\n\t\tconsole.log(` ${getRunCommand(pkgManager, 'deploy')}`);\n\t\tconsole.log('');\n\t}\n\n\tconsole.log('📚 Documentation: https://geekmidas.github.io/toolbox/');\n\tconsole.log('');\n}\n","import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { loadConfig, loadWorkspaceConfig } from '../config';\nimport { generateFullstackCustomSecrets } from '../setup/fullstack-secrets';\nimport type { ComposeServiceName, ComposeServicesConfig } from '../types';\nimport { createStageSecrets, rotateServicePassword } from './generator';\nimport {\n\tmaskPassword,\n\treadStageSecrets,\n\tsecretsExist,\n\tsetCustomSecret,\n\twriteStageSecrets,\n} from './storage';\n\nconst logger = console;\n\nexport interface SecretsInitOptions {\n\tstage: string;\n\tforce?: boolean;\n}\n\nexport interface SecretsSetOptions {\n\tstage: string;\n}\n\nexport interface SecretsShowOptions {\n\tstage: string;\n\treveal?: boolean;\n}\n\nexport interface SecretsRotateOptions {\n\tstage: string;\n\tservice?: ComposeServiceName;\n}\n\nexport interface SecretsImportOptions {\n\tstage: string;\n\t/** Merge with existing secrets (default: true) */\n\tmerge?: boolean;\n}\n\n/**\n * Extract service names from compose config.\n */\nexport function getServicesFromConfig(\n\tservices: ComposeServicesConfig | ComposeServiceName[] | undefined,\n): ComposeServiceName[] {\n\tif (!services) {\n\t\treturn [];\n\t}\n\n\tif (Array.isArray(services)) {\n\t\treturn services;\n\t}\n\n\t// Object format - get keys where value is truthy\n\treturn (Object.entries(services) as [ComposeServiceName, unknown][])\n\t\t.filter(([, config]) => config)\n\t\t.map(([name]) => name);\n}\n\n/**\n * Initialize secrets for a stage.\n * Generates secure random passwords for configured services.\n */\nexport async function secretsInitCommand(\n\toptions: SecretsInitOptions,\n): Promise<void> {\n\tconst { stage, force } = options;\n\n\t// Check if secrets already exist\n\tif (!force && secretsExist(stage)) {\n\t\tlogger.error(\n\t\t\t`Secrets already exist for stage \"${stage}\". Use --force to overwrite.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Load config to get services\n\tconst config = await loadConfig();\n\tconst services = getServicesFromConfig(config.docker?.compose?.services);\n\n\tif (services.length === 0) {\n\t\tlogger.warn(\n\t\t\t'No services configured in docker.compose.services. Creating secrets with empty services.',\n\t\t);\n\t}\n\n\t// Detect workspace mode for project name and fullstack secrets\n\tlet projectName: string | undefined;\n\tlet workspaceSecrets: Record<string, string> | undefined;\n\ttry {\n\t\tconst loaded = await loadWorkspaceConfig();\n\t\tprojectName = loaded.workspace.name;\n\t\tconst isMultiApp = Object.keys(loaded.workspace.apps).length > 1;\n\n\t\tif (isMultiApp) {\n\t\t\tworkspaceSecrets = generateFullstackCustomSecrets(loaded.workspace);\n\t\t\tlogger.log(' Detected workspace mode — generating per-app secrets');\n\t\t}\n\t} catch {\n\t\t// Not a workspace — single-app mode, skip custom secrets\n\t}\n\n\t// Generate secrets (with project name so DATABASE_URL matches app-specific URLs)\n\tconst secrets = createStageSecrets(stage, services, { projectName });\n\n\tif (workspaceSecrets) {\n\t\tsecrets.custom = workspaceSecrets;\n\t}\n\n\t// Write to file\n\tawait writeStageSecrets(secrets);\n\n\tlogger.log(`\\n✓ Secrets initialized for stage \"${stage}\"`);\n\tlogger.log(` Location: .gkm/secrets/${stage}.json`);\n\tlogger.log('\\n Generated credentials for:');\n\n\tfor (const service of services) {\n\t\tlogger.log(` - ${service}`);\n\t}\n\n\tif (secrets.urls.DATABASE_URL) {\n\t\tlogger.log(`\\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);\n\t}\n\tif (secrets.urls.REDIS_URL) {\n\t\tlogger.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);\n\t}\n\tif (secrets.urls.RABBITMQ_URL) {\n\t\tlogger.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);\n\t}\n\tif (secrets.urls.STORAGE_ENDPOINT) {\n\t\tlogger.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);\n\t}\n\n\tif (Object.keys(secrets.custom).length > 0) {\n\t\tlogger.log(`\\n Custom secrets: ${Object.keys(secrets.custom).length}`);\n\t}\n\n\tlogger.log(`\\n Use \"gkm secrets:show --stage ${stage}\" to view secrets`);\n\tlogger.log(\n\t\t' Use \"gkm secrets:set <KEY> <VALUE> --stage ' +\n\t\t\tstage +\n\t\t\t'\" to add custom secrets',\n\t);\n}\n\n/**\n * Read all data from stdin.\n */\nasync function readStdin(): Promise<string> {\n\tconst chunks: Buffer[] = [];\n\n\tfor await (const chunk of process.stdin) {\n\t\tchunks.push(chunk);\n\t}\n\n\treturn Buffer.concat(chunks).toString('utf-8').trim();\n}\n\n/**\n * Set a custom secret.\n * If value is not provided, reads from stdin.\n */\nexport async function secretsSetCommand(\n\tkey: string,\n\tvalue: string | undefined,\n\toptions: SecretsSetOptions,\n): Promise<void> {\n\tconst { stage } = options;\n\n\t// Read from stdin if value not provided\n\tlet secretValue = value;\n\tif (!secretValue) {\n\t\tif (process.stdin.isTTY) {\n\t\t\tlogger.error(\n\t\t\t\t'No value provided. Use: gkm secrets:set KEY VALUE --stage <stage>',\n\t\t\t);\n\t\t\tlogger.error(\n\t\t\t\t'Or pipe from stdin: echo \"value\" | gkm secrets:set KEY --stage <stage>',\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tsecretValue = await readStdin();\n\t\tif (!secretValue) {\n\t\t\tlogger.error('No value received from stdin');\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\ttry {\n\t\tawait setCustomSecret(stage, key, secretValue);\n\t\tlogger.log(`\\n✓ Secret \"${key}\" set for stage \"${stage}\"`);\n\t} catch (error) {\n\t\tlogger.error(\n\t\t\terror instanceof Error ? error.message : 'Failed to set secret',\n\t\t);\n\t\tprocess.exit(1);\n\t}\n}\n\n/**\n * Show secrets for a stage.\n */\nexport async function secretsShowCommand(\n\toptions: SecretsShowOptions,\n): Promise<void> {\n\tconst { stage, reveal } = options;\n\n\tconst secrets = await readStageSecrets(stage);\n\n\tif (!secrets) {\n\t\tlogger.error(\n\t\t\t`No secrets found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\tlogger.log(`\\nSecrets for stage \"${stage}\":`);\n\tlogger.log(` Created: ${secrets.createdAt}`);\n\tlogger.log(` Updated: ${secrets.updatedAt}`);\n\n\t// Show service credentials\n\tlogger.log('\\nService Credentials:');\n\tfor (const [service, creds] of Object.entries(secrets.services)) {\n\t\tif (creds) {\n\t\t\tlogger.log(`\\n ${service}:`);\n\t\t\tlogger.log(` host: ${creds.host}`);\n\t\t\tlogger.log(` port: ${creds.port}`);\n\t\t\tlogger.log(` username: ${creds.username}`);\n\t\t\tlogger.log(\n\t\t\t\t` password: ${reveal ? creds.password : maskPassword(creds.password)}`,\n\t\t\t);\n\t\t\tif (creds.database) {\n\t\t\t\tlogger.log(` database: ${creds.database}`);\n\t\t\t}\n\t\t\tif (creds.vhost) {\n\t\t\t\tlogger.log(` vhost: ${creds.vhost}`);\n\t\t\t}\n\t\t\tif (creds.bucket) {\n\t\t\t\tlogger.log(` bucket: ${creds.bucket}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Show URLs\n\tlogger.log('\\nConnection URLs:');\n\tif (secrets.urls.DATABASE_URL) {\n\t\tlogger.log(\n\t\t\t` DATABASE_URL: ${reveal ? secrets.urls.DATABASE_URL : maskUrl(secrets.urls.DATABASE_URL)}`,\n\t\t);\n\t}\n\tif (secrets.urls.REDIS_URL) {\n\t\tlogger.log(\n\t\t\t` REDIS_URL: ${reveal ? secrets.urls.REDIS_URL : maskUrl(secrets.urls.REDIS_URL)}`,\n\t\t);\n\t}\n\tif (secrets.urls.RABBITMQ_URL) {\n\t\tlogger.log(\n\t\t\t` RABBITMQ_URL: ${reveal ? secrets.urls.RABBITMQ_URL : maskUrl(secrets.urls.RABBITMQ_URL)}`,\n\t\t);\n\t}\n\tif (secrets.urls.STORAGE_ENDPOINT) {\n\t\tlogger.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);\n\t}\n\n\t// Show custom secrets\n\tconst customKeys = Object.keys(secrets.custom);\n\tif (customKeys.length > 0) {\n\t\tlogger.log('\\nCustom Secrets:');\n\t\tfor (const [key, value] of Object.entries(secrets.custom)) {\n\t\t\tlogger.log(` ${key}: ${reveal ? value : maskPassword(value)}`);\n\t\t}\n\t}\n\n\tif (!reveal) {\n\t\tlogger.log('\\nUse --reveal to show actual values');\n\t}\n}\n\n/**\n * Rotate passwords for services.\n */\nexport async function secretsRotateCommand(\n\toptions: SecretsRotateOptions,\n): Promise<void> {\n\tconst { stage, service } = options;\n\n\tconst secrets = await readStageSecrets(stage);\n\n\tif (!secrets) {\n\t\tlogger.error(\n\t\t\t`No secrets found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\tif (service) {\n\t\t// Rotate specific service\n\t\tif (!secrets.services[service]) {\n\t\t\tlogger.error(`Service \"${service}\" not configured in stage \"${stage}\"`);\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tconst updated = rotateServicePassword(secrets, service);\n\t\tawait writeStageSecrets(updated);\n\t\tlogger.log(`\\n✓ Password rotated for ${service} in stage \"${stage}\"`);\n\t} else {\n\t\t// Rotate all services\n\t\tlet updated = secrets;\n\t\tconst services = Object.keys(secrets.services) as ComposeServiceName[];\n\n\t\tfor (const svc of services) {\n\t\t\tupdated = rotateServicePassword(updated, svc);\n\t\t}\n\n\t\tawait writeStageSecrets(updated);\n\t\tlogger.log(\n\t\t\t`\\n✓ Passwords rotated for all services in stage \"${stage}\": ${services.join(', ')}`,\n\t\t);\n\t}\n\n\tlogger.log(`\\nUse \"gkm secrets:show --stage ${stage}\" to view new values`);\n}\n\n/**\n * Import secrets from a JSON file.\n */\nexport async function secretsImportCommand(\n\tfile: string,\n\toptions: SecretsImportOptions,\n): Promise<void> {\n\tconst { stage, merge = true } = options;\n\n\t// Check if file exists\n\tif (!existsSync(file)) {\n\t\tlogger.error(`File not found: ${file}`);\n\t\tprocess.exit(1);\n\t}\n\n\t// Read and parse JSON file\n\tlet importedSecrets: Record<string, string>;\n\ttry {\n\t\tconst content = await readFile(file, 'utf-8');\n\t\timportedSecrets = JSON.parse(content);\n\n\t\t// Validate it's a flat object with string values\n\t\tif (typeof importedSecrets !== 'object' || importedSecrets === null) {\n\t\t\tthrow new Error('JSON must be an object');\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(importedSecrets)) {\n\t\t\tif (typeof value !== 'string') {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Value for \"${key}\" must be a string, got ${typeof value}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tlogger.error(\n\t\t\t`Failed to parse JSON file: ${error instanceof Error ? error.message : 'Invalid JSON'}`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Check if secrets exist for stage\n\tconst secrets = await readStageSecrets(stage);\n\n\tif (!secrets) {\n\t\tlogger.error(\n\t\t\t`No secrets found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Merge or replace custom secrets\n\tconst updatedCustom = merge\n\t\t? { ...secrets.custom, ...importedSecrets }\n\t\t: importedSecrets;\n\n\tconst updated = {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tcustom: updatedCustom,\n\t};\n\n\tawait writeStageSecrets(updated);\n\n\tconst importedCount = Object.keys(importedSecrets).length;\n\tconst totalCount = Object.keys(updatedCustom).length;\n\n\tlogger.log(`\\n✓ Imported ${importedCount} secrets for stage \"${stage}\"`);\n\n\tif (merge && totalCount > importedCount) {\n\t\tlogger.log(` Total custom secrets: ${totalCount}`);\n\t}\n\n\tlogger.log('\\n Imported keys:');\n\tfor (const key of Object.keys(importedSecrets)) {\n\t\tlogger.log(` - ${key}`);\n\t}\n}\n\n/**\n * Mask password in a URL for display.\n */\nexport function maskUrl(url: string): string {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\tif (parsed.password) {\n\t\t\tparsed.password = maskPassword(parsed.password);\n\t\t}\n\t\treturn parsed.toString();\n\t} catch {\n\t\treturn url;\n\t}\n}\n","import { spawn } from 'node:child_process';\nimport { writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcreateCredentialsPreload,\n\tloadEnvFiles,\n\tprepareEntryCredentials,\n} from '../credentials';\nimport { sniffAppEnvironment } from '../deploy/sniffer';\n\nexport interface TestOptions {\n\t/** Stage to load secrets from (default: development) */\n\tstage?: string;\n\t/** Run tests once without watch mode */\n\trun?: boolean;\n\t/** Enable watch mode */\n\twatch?: boolean;\n\t/** Generate coverage report */\n\tcoverage?: boolean;\n\t/** Open Vitest UI */\n\tui?: boolean;\n\t/** Pattern to filter tests */\n\tpattern?: string;\n\t/**\n\t * Generate a fresh stage (secrets + encryption key) from the workspace config\n\t * when none exists, instead of requiring committed secrets / a shared key.\n\t * Intended for CI. Also enabled by the GKM_AUTO_SETUP env var.\n\t */\n\tautoSetup?: boolean;\n}\n\n/**\n * Run tests with secrets, dependency URLs, and .env files loaded.\n * Environment variables are sniffed to inject only what the app needs.\n */\nexport async function testCommand(options: TestOptions = {}): Promise<void> {\n\tconst stage = options.stage ?? 'development';\n\tconst cwd = process.cwd();\n\n\tconsole.log(`\\n🧪 Running tests with ${stage} environment...\\n`);\n\n\t// 0. Auto-setup (CI): regenerate a fresh stage from the workspace config when\n\t// none exists, so tests run without committed secrets or a shared key.\n\tconst autoSetup = options.autoSetup || Boolean(process.env.GKM_AUTO_SETUP);\n\tif (autoSetup) {\n\t\tconst { ensureStageSecrets } = await import('../setup/index.js');\n\t\tconst generated = await ensureStageSecrets(stage, cwd);\n\t\tif (generated) {\n\t\t\tconsole.log(` 🔐 Generated fresh ${stage} secrets (auto-setup)`);\n\t\t}\n\t}\n\n\t// 1. Load .env files\n\tconst defaultEnv = loadEnvFiles('.env');\n\tif (defaultEnv.loaded.length > 0) {\n\t\tconsole.log(` 📦 Loaded env: ${defaultEnv.loaded.join(', ')}`);\n\t}\n\n\t// 2. Prepare credentials: loads secrets, resolves Docker ports,\n\t// starts services, rewrites URLs, injects dependency URLs\n\tconst result = await prepareEntryCredentials({\n\t\tstages: [stage],\n\t\tstartDocker: true,\n\t\tsecretsFileName: 'test-secrets.json',\n\t\tresolveDockerPorts: 'full',\n\t\t// The same reconcile `gkm dev` runs, differing only in what the resources\n\t\t// are called: one container and one role pair serve both stages, so the\n\t\t// suffix is the whole of the isolation.\n\t\treconcileStage: 'test',\n\t});\n\n\tlet finalCredentials = { ...result.credentials };\n\n\t// 3. Sniff env vars to filter only what the app needs (workspace only)\n\tif (result.appInfo) {\n\t\tconst sniffed = await sniffAppEnvironment(\n\t\t\tresult.appInfo.app,\n\t\t\tresult.appInfo.appName,\n\t\t\tresult.appInfo.workspaceRoot,\n\t\t\t{ logWarnings: false },\n\t\t);\n\n\t\tif (sniffed.requiredEnvVars.length > 0) {\n\t\t\t// Sniffed *plus* declared. A construct reads its own key inside\n\t\t\t// `@geekmidas/constructs` rather than in application code, so a walk of\n\t\t\t// the app finds no `get('MAIL_URL')` and sniffing alone reports it as\n\t\t\t// unneeded — which is how a suite that resolved 25 declared URLs\n\t\t\t// started with 13 and failed on the first construct to look for one of\n\t\t\t// the other 12.\n\t\t\tconst needed = new Set([\n\t\t\t\t...sniffed.requiredEnvVars,\n\t\t\t\t...result.declaredKeys,\n\t\t\t]);\n\t\t\tconst filtered: Record<string, string> = {};\n\t\t\tfor (const [key, value] of Object.entries(finalCredentials)) {\n\t\t\t\tif (needed.has(key)) {\n\t\t\t\t\tfiltered[key] = value;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinalCredentials = filtered;\n\t\t\tconsole.log(\n\t\t\t\t` 🔍 Sniffed ${sniffed.requiredEnvVars.length} required env var(s)` +\n\t\t\t\t\t(result.declaredKeys.length > 0\n\t\t\t\t\t\t? `, kept ${result.declaredKeys.length} declared`\n\t\t\t\t\t\t: ''),\n\t\t\t);\n\t\t}\n\t}\n\n\t// 4. Rewrite DATABASE_URL for test isolation (append _test suffix)\n\tfinalCredentials = rewriteDatabaseUrlForTests(finalCredentials);\n\n\tconsole.log('');\n\n\t// 5. Write final credentials and create preload script\n\tawait writeFile(\n\t\tresult.secretsJsonPath,\n\t\tJSON.stringify(finalCredentials, null, 2),\n\t);\n\n\tconst gkmDir = join(cwd, '.gkm');\n\tconst preloadPath = join(gkmDir, 'test-credentials-preload.ts');\n\tawait createCredentialsPreload(preloadPath, result.secretsJsonPath);\n\n\t// Merge NODE_OPTIONS with existing value (if any)\n\tconst existingNodeOptions = process.env.NODE_OPTIONS ?? '';\n\tconst tsxImport = '--import=tsx';\n\tconst preloadImport = `--import=${preloadPath}`;\n\tconst nodeOptions = [existingNodeOptions, tsxImport, preloadImport]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\t// Build vitest args\n\tconst args: string[] = [];\n\n\tif (options.run) {\n\t\targs.push('run');\n\t} else if (options.watch) {\n\t\targs.push('--watch');\n\t}\n\n\tif (options.coverage) {\n\t\targs.push('--coverage');\n\t}\n\n\tif (options.ui) {\n\t\targs.push('--ui');\n\t}\n\n\tif (options.pattern) {\n\t\targs.push(options.pattern);\n\t}\n\n\t// Run vitest with combined environment and credentials preload\n\tconst vitestProcess = spawn('npx', ['vitest', ...args], {\n\t\tcwd,\n\t\tstdio: 'inherit',\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t...finalCredentials,\n\t\t\tNODE_ENV: 'test',\n\t\t\tNODE_OPTIONS: nodeOptions,\n\t\t},\n\t});\n\n\t// Wait for vitest to complete\n\treturn new Promise((resolve, reject) => {\n\t\tvitestProcess.on('close', (code) => {\n\t\t\tif (code === 0) {\n\t\t\t\tresolve();\n\t\t\t} else {\n\t\t\t\treject(new Error(`Tests failed with exit code ${code}`));\n\t\t\t}\n\t\t});\n\n\t\tvitestProcess.on('error', (error) => {\n\t\t\treject(error);\n\t\t});\n\t});\n}\n\nconst TEST_DB_SUFFIX = '_test';\n\n/**\n * Rewrite DATABASE_URL to point to a separate test database.\n * Appends `_test` to the database name (e.g., `app` -> `app_test`).\n * @internal Exported for testing\n */\nexport function rewriteDatabaseUrlForTests(\n\tenv: Record<string, string>,\n): Record<string, string> {\n\tconst result = { ...env };\n\n\tfor (const key of Object.keys(result)) {\n\t\tif (!key.includes('DATABASE_URL')) continue;\n\n\t\tconst value = result[key] as string;\n\t\ttry {\n\t\t\tconst url = new URL(value);\n\t\t\tconst dbName = url.pathname.slice(1);\n\t\t\tif (dbName && !dbName.endsWith(TEST_DB_SUFFIX)) {\n\t\t\t\turl.pathname = `/${dbName}${TEST_DB_SUFFIX}`;\n\t\t\t\tresult[key] = url.toString();\n\t\t\t\tconsole.log(\n\t\t\t\t\t` 🧪 ${key}: using test database \"${dbName}${TEST_DB_SUFFIX}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Not a valid URL, skip\n\t\t}\n\t}\n\n\treturn result;\n}\n","import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport {\n\tdetectPackageManager,\n\tfindWorkspacePackages,\n\ttype PackageManager,\n} from '../init/utils.js';\n\nconst logger = console;\n\nexport interface UpgradeOptions {\n\tdryRun?: boolean;\n}\n\ninterface FoundDependency {\n\tpackageName: string;\n\tcurrentVersion: string;\n\tdepType: 'dependencies' | 'devDependencies' | 'peerDependencies';\n\tpackageJsonPath: string;\n\tworkspaceName: string;\n}\n\ninterface UpgradeInfo extends FoundDependency {\n\tlatestVersion: string;\n\tisWorkspaceRef: boolean;\n\tneedsUpgrade: boolean;\n}\n\nexport async function upgradeCommand(\n\toptions: UpgradeOptions = {},\n): Promise<void> {\n\tconst cwd = process.cwd();\n\n\tlogger.log('\\n📦 Scanning workspace for @geekmidas packages...\\n');\n\n\tconst pm = detectPackageManager(cwd);\n\tlogger.log(` Package manager: ${pm}`);\n\n\tconst packageJsonPaths = findWorkspacePackages(cwd, pm);\n\tlogger.log(` Found ${packageJsonPaths.length} package(s) in workspace\\n`);\n\n\tconst dependencies = scanForGeekmidasDeps(packageJsonPaths);\n\n\tif (dependencies.length === 0) {\n\t\tlogger.log(' No @geekmidas packages found.\\n');\n\t\treturn;\n\t}\n\n\tconst uniquePackages = [...new Set(dependencies.map((d) => d.packageName))];\n\tlogger.log(\n\t\t` Checking ${uniquePackages.length} unique @geekmidas package(s) on npm...\\n`,\n\t);\n\n\tconst latestVersions = await fetchLatestVersions(uniquePackages);\n\n\tconst upgradeInfos = dependencies.map((dep) =>\n\t\tresolveUpgradeInfo(dep, latestVersions),\n\t);\n\n\tprintUpgradeTable(upgradeInfos);\n\n\tconst upgradable = upgradeInfos.filter(\n\t\t(info) => info.needsUpgrade && !info.isWorkspaceRef,\n\t);\n\n\tif (upgradable.length === 0) {\n\t\tlogger.log('\\n All @geekmidas packages are up to date!\\n');\n\t\treturn;\n\t}\n\n\tlogger.log(`\\n ${upgradable.length} package(s) can be upgraded.\\n`);\n\n\tif (options.dryRun) {\n\t\tlogger.log(' --dry-run: No changes made.\\n');\n\t\tprintUpgradeCommands(upgradable, pm);\n\t\treturn;\n\t}\n\n\texecuteUpgrade(upgradable, pm, cwd);\n\n\tlogger.log('\\n ✅ Upgrade complete! Run your tests to verify.\\n');\n}\n\nfunction scanForGeekmidasDeps(packageJsonPaths: string[]): FoundDependency[] {\n\tconst results: FoundDependency[] = [];\n\tconst depTypes = [\n\t\t'dependencies',\n\t\t'devDependencies',\n\t\t'peerDependencies',\n\t] as const;\n\n\tfor (const pkgJsonPath of packageJsonPaths) {\n\t\tconst pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));\n\t\tconst workspaceName = pkg.name ?? pkgJsonPath;\n\n\t\tfor (const depType of depTypes) {\n\t\t\tconst deps = pkg[depType];\n\t\t\tif (!deps) continue;\n\n\t\t\tfor (const [name, version] of Object.entries(deps)) {\n\t\t\t\tif (!name.startsWith('@geekmidas/')) continue;\n\n\t\t\t\tresults.push({\n\t\t\t\t\tpackageName: name,\n\t\t\t\t\tcurrentVersion: version as string,\n\t\t\t\t\tdepType,\n\t\t\t\t\tpackageJsonPath: pkgJsonPath,\n\t\t\t\t\tworkspaceName,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n\nasync function fetchLatestVersions(\n\tpackageNames: string[],\n): Promise<Map<string, string>> {\n\tconst versions = new Map<string, string>();\n\n\tconst results = await Promise.allSettled(\n\t\tpackageNames.map(async (name) => {\n\t\t\tconst res = await fetch(`https://registry.npmjs.org/${name}/latest`);\n\t\t\tif (!res.ok) throw new Error(`HTTP ${res.status}`);\n\t\t\tconst data = (await res.json()) as { version: string };\n\t\t\treturn { name, version: data.version };\n\t\t}),\n\t);\n\n\tfor (const result of results) {\n\t\tif (result.status === 'fulfilled') {\n\t\t\tversions.set(result.value.name, result.value.version);\n\t\t}\n\t}\n\n\treturn versions;\n}\n\nfunction resolveUpgradeInfo(\n\tdep: FoundDependency,\n\tlatestVersions: Map<string, string>,\n): UpgradeInfo {\n\tconst isWorkspaceRef = dep.currentVersion.startsWith('workspace:');\n\tconst latestVersion = latestVersions.get(dep.packageName) ?? 'unknown';\n\n\tconst currentBare = dep.currentVersion.replace(/^[\\^~>=<]*/g, '');\n\tconst needsUpgrade =\n\t\t!isWorkspaceRef &&\n\t\tlatestVersion !== 'unknown' &&\n\t\tcurrentBare !== latestVersion;\n\n\treturn {\n\t\t...dep,\n\t\tlatestVersion,\n\t\tisWorkspaceRef,\n\t\tneedsUpgrade,\n\t};\n}\n\nfunction printUpgradeTable(infos: UpgradeInfo[]): void {\n\tconst byPackage = new Map<string, UpgradeInfo>();\n\tfor (const info of infos) {\n\t\tif (!byPackage.has(info.packageName)) {\n\t\t\tbyPackage.set(info.packageName, info);\n\t\t}\n\t}\n\n\tconst nameWidth = 33;\n\tconst verWidth = 14;\n\tconst statusWidth = 14;\n\n\tconst hr = ` ${'─'.repeat(nameWidth + verWidth * 2 + statusWidth + 5)}`;\n\n\tlogger.log(hr);\n\tlogger.log(\n\t\t` ${'Package'.padEnd(nameWidth)} ${'Current'.padEnd(verWidth)} ${'Latest'.padEnd(verWidth)} ${'Status'.padEnd(statusWidth)}`,\n\t);\n\tlogger.log(hr);\n\n\tfor (const [, info] of byPackage) {\n\t\tconst name = info.packageName.padEnd(nameWidth);\n\t\tconst current = info.currentVersion.padEnd(verWidth);\n\t\tconst latest = info.latestVersion.padEnd(verWidth);\n\n\t\tlet status: string;\n\t\tif (info.isWorkspaceRef) {\n\t\t\tstatus = 'workspace';\n\t\t} else if (info.needsUpgrade) {\n\t\t\tstatus = '⬆ upgrade';\n\t\t} else {\n\t\t\tstatus = '✓ up-to-date';\n\t\t}\n\n\t\tlogger.log(` ${name} ${current} ${latest} ${status}`);\n\t}\n\n\tlogger.log(hr);\n}\n\nfunction printUpgradeCommands(\n\tupgradable: UpgradeInfo[],\n\tpm: PackageManager,\n): void {\n\tlogger.log(' Commands that would be run:\\n');\n\n\tconst uniquePackages = [...new Set(upgradable.map((i) => i.packageName))];\n\tconst cmd = getWorkspaceUpgradeCommand(pm, uniquePackages);\n\tlogger.log(` ${cmd}\\n`);\n}\n\nfunction getWorkspaceUpgradeCommand(\n\tpm: PackageManager,\n\tpackages: string[],\n): string {\n\tconst pkgList = packages.join(' ');\n\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn `pnpm update -r ${pkgList} --latest`;\n\t\tcase 'yarn':\n\t\t\treturn `yarn upgrade ${pkgList}`;\n\t\tcase 'bun':\n\t\t\treturn `bun update ${pkgList}`;\n\t\tcase 'npm':\n\t\t\treturn `npm update ${pkgList} --workspaces`;\n\t\tdefault:\n\t\t\treturn `npm update ${pkgList}`;\n\t}\n}\n\nfunction executeUpgrade(\n\tupgradable: UpgradeInfo[],\n\tpm: PackageManager,\n\tcwd: string,\n): void {\n\tconst uniquePackages = [...new Set(upgradable.map((i) => i.packageName))];\n\n\tconst cmd = getWorkspaceUpgradeCommand(pm, uniquePackages);\n\tlogger.log(` Running: ${cmd}\\n`);\n\n\ttry {\n\t\texecSync(cmd, {\n\t\t\tcwd,\n\t\t\tstdio: 'inherit',\n\t\t\ttimeout: 120_000,\n\t\t});\n\t} catch {\n\t\tthrow new Error(\n\t\t\t'Package upgrade failed. Check the output above for details.',\n\t\t);\n\t}\n}\n","#!/usr/bin/env -S npx tsx\n\nimport { Command } from 'commander';\nimport pkg from '../package.json';\nimport { loginCommand, logoutCommand, whoamiCommand } from './auth';\nimport { buildCommand } from './build/index';\nimport { enableDebug, formatError } from './debug';\nimport { type DeployProvider, deployCommand } from './deploy/index';\nimport { deployInitCommand, deployListCommand } from './deploy/init';\nimport {\n\tstateDiffCommand,\n\tstatePullCommand,\n\tstatePushCommand,\n\tstateShowCommand,\n} from './deploy/state-commands';\nimport { devCommand, execCommand } from './dev/index';\nimport { type DockerOptions, dockerCommand } from './docker/index';\nimport { type InitOptions, initCommand } from './init/index';\nimport { openapiCommand } from './openapi';\nimport { generateReactQueryCommand } from './openapi-react-query';\nimport {\n\tsecretsImportCommand,\n\tsecretsInitCommand,\n\tsecretsRotateCommand,\n\tsecretsSetCommand,\n\tsecretsShowCommand,\n} from './secrets';\nimport { type SetupOptions, setupCommand } from './setup/index';\nimport { type TestOptions, testCommand } from './test/index';\nimport { trustCommand } from './trust/index';\nimport type { ComposeServiceName, LegacyProvider, MainProvider } from './types';\nimport { type UpgradeOptions, upgradeCommand } from './upgrade/index';\n\nconst program = new Command();\n\nprogram\n\t.name('gkm')\n\t.description('GeekMidas backend framework CLI')\n\t.version(pkg.version)\n\t.option('--cwd <path>', 'Change working directory')\n\t.option('--debug', 'Enable debug mode (verbose errors with stack traces)')\n\t.hook('preAction', () => {\n\t\tconst opts = program.opts();\n\t\tif (opts.debug) enableDebug();\n\t});\n\nprogram\n\t.command('init')\n\t.description('Scaffold a new project')\n\t.argument('[name]', 'Project name')\n\t.option(\n\t\t'--template <template>',\n\t\t'Project template (minimal, api, serverless, worker)',\n\t)\n\t.option('--skip-install', 'Skip dependency installation', false)\n\t.option('-y, --yes', 'Skip prompts, use defaults', false)\n\t.option('--monorepo', 'Setup as monorepo with packages/models', false)\n\t.option('--api-path <path>', 'API app path in monorepo (default: apps/api)')\n\t.option('--pm <manager>', 'Package manager (pnpm, npm, yarn, bun)')\n\t.action(async (name: string | undefined, options: InitOptions) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait initCommand(name, options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('trust')\n\t.description(\n\t\t\"Trust the local edge's certificate authority, so a browser accepts https\",\n\t)\n\t.option('--dry-run', 'Print the commands instead of running them')\n\t.action(async (options: { dryRun?: boolean }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait trustCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('setup')\n\t.description(\n\t\t'Reconcile declared constructs — containers, databases, buckets, secrets',\n\t)\n\t.option('--stage <stage>', 'Stage name', 'development')\n\t.option('--force', 'Regenerate secrets even if they exist')\n\t.option('--skip-docker', 'Skip starting Docker services')\n\t.option('-y, --yes', 'Skip prompts')\n\t.action(async (options: SetupOptions) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait setupCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('build')\n\t.description('Build handlers and the manifest from declared constructs')\n\t.option(\n\t\t'--provider <provider>',\n\t\t'Target provider for generated handlers (aws, server)',\n\t)\n\t.option(\n\t\t'--providers <providers>',\n\t\t'[DEPRECATED] Use --provider instead. Target providers for generated handlers (comma-separated)',\n\t)\n\t.option(\n\t\t'--enable-openapi',\n\t\t'Enable OpenAPI documentation generation for server builds',\n\t)\n\t.option('--production', 'Build for production (no dev tools, bundled output)')\n\t.option('--skip-bundle', 'Skip bundling step in production build')\n\t.option('--stage <stage>', 'Inject encrypted secrets for deployment stage')\n\t.option(\n\t\t'--mark-optional',\n\t\t'Suffix optional env vars with ? in manifest envVars field (e.g. PORT?)',\n\t)\n\t.action(\n\t\tasync (options: {\n\t\t\tprovider?: string;\n\t\t\tproviders?: string;\n\t\t\tenableOpenapi?: boolean;\n\t\t\tproduction?: boolean;\n\t\t\tskipBundle?: boolean;\n\t\t\tstage?: string;\n\t\t\tmarkOptional?: boolean;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\t// Handle new single provider option\n\t\t\t\tif (options.provider) {\n\t\t\t\t\tif (!['aws', 'server'].includes(options.provider)) {\n\t\t\t\t\t\tprocess.exit(1);\n\t\t\t\t\t}\n\t\t\t\t\tawait buildCommand({\n\t\t\t\t\t\tprovider: options.provider as MainProvider,\n\t\t\t\t\t\tenableOpenApi: options.enableOpenapi || false,\n\t\t\t\t\t\tproduction: options.production || false,\n\t\t\t\t\t\tskipBundle: options.skipBundle || false,\n\t\t\t\t\t\tstage: options.stage,\n\t\t\t\t\t\tmarkOptional: options.markOptional || false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// Handle legacy providers option\n\t\t\t\telse if (options.providers) {\n\t\t\t\t\tconst providerList = [\n\t\t\t\t\t\t...new Set(options.providers.split(',').map((p) => p.trim())),\n\t\t\t\t\t] as LegacyProvider[];\n\t\t\t\t\tawait buildCommand({\n\t\t\t\t\t\tproviders: providerList,\n\t\t\t\t\t\tenableOpenApi: options.enableOpenapi || false,\n\t\t\t\t\t\tproduction: options.production || false,\n\t\t\t\t\t\tskipBundle: options.skipBundle || false,\n\t\t\t\t\t\tstage: options.stage,\n\t\t\t\t\t\tmarkOptional: options.markOptional || false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// Default to config-driven build\n\t\t\t\telse {\n\t\t\t\t\tawait buildCommand({\n\t\t\t\t\t\tenableOpenApi: options.enableOpenapi || false,\n\t\t\t\t\t\tproduction: options.production || false,\n\t\t\t\t\t\tskipBundle: options.skipBundle || false,\n\t\t\t\t\t\tstage: options.stage,\n\t\t\t\t\t\tmarkOptional: options.markOptional || false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command('dev')\n\t.description(\n\t\t'Reconcile constructs and start the dev server with automatic reload',\n\t)\n\t.option('-p, --port <port>', 'Port to run the development server on')\n\t.option('--entry <file>', 'Entry file to run (bypasses gkm config)')\n\t.option('--watch', 'Watch for file changes (default: true with --entry)')\n\t.option('--no-watch', 'Disable file watching')\n\t.option(\n\t\t'--enable-openapi',\n\t\t'Enable OpenAPI documentation for development server',\n\t\ttrue,\n\t)\n\t.action(\n\t\tasync (options: {\n\t\t\tport?: string;\n\t\t\tentry?: string;\n\t\t\twatch?: boolean;\n\t\t\tenableOpenapi?: boolean;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\tawait devCommand({\n\t\t\t\t\tport: options.port ? Number.parseInt(options.port, 10) : 3000,\n\t\t\t\t\tportExplicit: !!options.port,\n\t\t\t\t\tenableOpenApi: options.enableOpenapi ?? true,\n\t\t\t\t\tentry: options.entry,\n\t\t\t\t\twatch: options.watch,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command('exec')\n\t.description('Run a command with secrets injected into Credentials')\n\t.argument('<command...>', 'Command to run (use -- before command)')\n\t.action(async (commandArgs: string[]) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait execCommand(commandArgs);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('test')\n\t.description('Run tests with secrets loaded from environment')\n\t.option('--stage <stage>', 'Stage to load secrets from', 'development')\n\t.option('--run', 'Run tests once without watch mode')\n\t.option('--watch', 'Enable watch mode')\n\t.option('--coverage', 'Generate coverage report')\n\t.option('--ui', 'Open Vitest UI')\n\t.option(\n\t\t'--auto-setup',\n\t\t'Generate a fresh stage (secrets + key) from the workspace config when none exists (for CI; also via GKM_AUTO_SETUP)',\n\t)\n\t.argument('[pattern]', 'Pattern to filter tests')\n\t.action(async (pattern: string | undefined, options: TestOptions) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait testCommand({ ...options, pattern });\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('cron')\n\t.description('Manage cron jobs')\n\t.action(() => {\n\t\tconst globalOptions = program.opts();\n\t\tif (globalOptions.cwd) {\n\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t}\n\t\tprocess.stdout.write('Cron management - coming soon\\n');\n\t});\n\nprogram\n\t.command('function')\n\t.description('Manage serverless functions')\n\t.action(() => {\n\t\tconst globalOptions = program.opts();\n\t\tif (globalOptions.cwd) {\n\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t}\n\t\tprocess.stdout.write('Serverless function management - coming soon\\n');\n\t});\n\nprogram\n\t.command('api')\n\t.description('Manage REST API endpoints')\n\t.action(() => {\n\t\tconst globalOptions = program.opts();\n\t\tif (globalOptions.cwd) {\n\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t}\n\t\tprocess.stdout.write('REST API management - coming soon\\n');\n\t});\n\nprogram\n\t.command('openapi')\n\t.description('Generate OpenAPI specification from endpoints')\n\t.option(\n\t\t'--app <name>',\n\t\t'Workspace mode: generate for a single named backend app',\n\t)\n\t.action(async (options: { app?: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait openapiCommand({ app: options.app });\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('generate:react-query')\n\t.description('Generate React Query hooks from OpenAPI specification')\n\t.option('--input <path>', 'Input OpenAPI spec file path', 'openapi.json')\n\t.option(\n\t\t'--output <path>',\n\t\t'Output file path for generated hooks',\n\t\t'src/api/hooks.ts',\n\t)\n\t.option('--name <name>', 'API name prefix for generated code', 'API')\n\t.action(\n\t\tasync (options: { input?: string; output?: string; name?: string }) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\t\t\t\tawait generateReactQueryCommand(options);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command('docker')\n\t.description('Generate Docker deployment files')\n\t.option('--build', 'Build Docker image after generating files')\n\t.option('--push', 'Push image to registry after building')\n\t.option('--tag <tag>', 'Image tag', 'latest')\n\t.option('--registry <registry>', 'Container registry URL')\n\t.option('--slim', 'Use slim Dockerfile (assumes pre-built bundle exists)')\n\t.option('--turbo', 'Use turbo prune for monorepo optimization')\n\t.option('--turbo-package <name>', 'Package name for turbo prune')\n\t.action(async (options: DockerOptions) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait dockerCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('prepack')\n\t.description('Generate Docker files for production deployment')\n\t.option('--build', 'Build Docker image after generating files')\n\t.option('--push', 'Push image to registry after building')\n\t.option('--tag <tag>', 'Image tag', 'latest')\n\t.option('--registry <registry>', 'Container registry URL')\n\t.option('--slim', 'Build locally first, then use slim Dockerfile')\n\t.option('--skip-bundle', 'Skip bundling step (only with --slim)')\n\t.option('--turbo', 'Use turbo prune for monorepo optimization')\n\t.option('--turbo-package <name>', 'Package name for turbo prune')\n\t.action(\n\t\tasync (options: {\n\t\t\tbuild?: boolean;\n\t\t\tpush?: boolean;\n\t\t\ttag?: string;\n\t\t\tregistry?: string;\n\t\t\tslim?: boolean;\n\t\t\tskipBundle?: boolean;\n\t\t\tturbo?: boolean;\n\t\t\tturboPackage?: string;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\tif (options.slim) {\n\t\t\t\t\tawait buildCommand({\n\t\t\t\t\t\tprovider: 'server',\n\t\t\t\t\t\tproduction: true,\n\t\t\t\t\t\tskipBundle: options.skipBundle,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tawait dockerCommand({\n\t\t\t\t\tbuild: options.build,\n\t\t\t\t\tpush: options.push,\n\t\t\t\t\ttag: options.tag,\n\t\t\t\t\tregistry: options.registry,\n\t\t\t\t\tslim: options.slim,\n\t\t\t\t\tturbo: options.turbo,\n\t\t\t\t\tturboPackage: options.turboPackage,\n\t\t\t\t});\n\t\t\t\tif (options.slim) {\n\t\t\t\t} else {\n\t\t\t\t}\n\n\t\t\t\tif (options.build) {\n\t\t\t\t\tconst tag = options.tag ?? 'latest';\n\t\t\t\t\tconst registry = options.registry;\n\t\t\t\t\tconst _imageRef = registry ? `${registry}/api:${tag}` : `api:${tag}`;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n// Secrets management commands\nprogram\n\t.command('secrets:init')\n\t.description('Initialize secrets for a deployment stage')\n\t.requiredOption('--stage <stage>', 'Stage name (e.g., production, staging)')\n\t.option('--force', 'Overwrite existing secrets')\n\t.action(async (options: { stage: string; force?: boolean }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait secretsInitCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:set')\n\t.description('Set a custom secret for a stage')\n\t.argument('<key>', 'Secret key (e.g., API_KEY)')\n\t.argument('[value]', 'Secret value (reads from stdin if omitted)')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.action(\n\t\tasync (\n\t\t\tkey: string,\n\t\t\tvalue: string | undefined,\n\t\t\toptions: { stage: string },\n\t\t) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\t\t\t\tawait secretsSetCommand(key, value, options);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command('secrets:show')\n\t.description('Show secrets for a stage')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.option('--reveal', 'Show actual secret values (not masked)')\n\t.action(async (options: { stage: string; reveal?: boolean }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait secretsShowCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:rotate')\n\t.description('Rotate service passwords')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.option(\n\t\t'--service <service>',\n\t\t'Specific service to rotate (postgres, redis, rabbitmq)',\n\t)\n\t.action(async (options: { stage: string; service?: ComposeServiceName }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait secretsRotateCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:import')\n\t.description('Import secrets from a JSON file')\n\t.argument('<file>', 'JSON file path (e.g., secrets.json)')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.option('--no-merge', 'Replace all custom secrets instead of merging')\n\t.action(async (file: string, options: { stage: string; merge?: boolean }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait secretsImportCommand(file, options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:push')\n\t.description('Push secrets to remote provider (SSM)')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\n\t\t\tconst { loadWorkspaceConfig } = await import('./config');\n\t\t\tconst { pushSecrets } = await import('./secrets/sync');\n\t\t\tconst { reconcileMissingSecrets } = await import('./secrets/reconcile');\n\t\t\tconst { readStageSecrets, writeStageSecrets } = await import(\n\t\t\t\t'./secrets/storage'\n\t\t\t);\n\n\t\t\tconst { workspace } = await loadWorkspaceConfig();\n\n\t\t\tconst secrets = await readStageSecrets(options.stage, workspace.root);\n\t\t\tif (secrets) {\n\t\t\t\tconst { derivedContainers } = await import('./reconcile/workspace');\n\t\t\t\tconst result = reconcileMissingSecrets(\n\t\t\t\t\tsecrets,\n\t\t\t\t\tworkspace,\n\t\t\t\t\tawait derivedContainers(workspace, options.stage),\n\t\t\t\t);\n\t\t\t\tif (result) {\n\t\t\t\t\tawait writeStageSecrets(result.secrets, workspace.root);\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t` Reconciled ${result.addedKeys.length} missing secret(s):`,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const key of result.addedKeys) {\n\t\t\t\t\t\tconsole.log(` + ${key}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait pushSecrets(options.stage, workspace);\n\t\t\tconsole.log(`\\n✓ Secrets pushed for stage \"${options.stage}\"`);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:pull')\n\t.description('Pull secrets from remote provider (SSM)')\n\t.requiredOption('--stage <stage>', 'Stage name')\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\n\t\t\tconst { loadWorkspaceConfig } = await import('./config');\n\t\t\tconst { pullSecrets } = await import('./secrets/sync');\n\t\t\tconst { writeStageSecrets } = await import('./secrets/storage');\n\t\t\tconst { reconcileMissingSecrets } = await import('./secrets/reconcile');\n\n\t\t\tconst { workspace } = await loadWorkspaceConfig();\n\t\t\tlet secrets = await pullSecrets(options.stage, workspace);\n\n\t\t\tif (!secrets) {\n\t\t\t\tconsole.error(`No remote secrets found for stage \"${options.stage}\".`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\tconst { derivedContainers } = await import('./reconcile/workspace');\n\t\t\tconst result = reconcileMissingSecrets(\n\t\t\t\tsecrets,\n\t\t\t\tworkspace,\n\t\t\t\tawait derivedContainers(workspace, options.stage),\n\t\t\t);\n\t\t\tif (result) {\n\t\t\t\tsecrets = result.secrets;\n\t\t\t\tconsole.log(\n\t\t\t\t\t` Reconciled ${result.addedKeys.length} missing secret(s):`,\n\t\t\t\t);\n\t\t\t\tfor (const key of result.addedKeys) {\n\t\t\t\t\tconsole.log(` + ${key}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait writeStageSecrets(secrets, workspace.root);\n\t\t\tconsole.log(`\\n✓ Secrets pulled for stage \"${options.stage}\"`);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('secrets:reconcile')\n\t.description('Backfill missing custom secrets from workspace config')\n\t.option('--stage <stage>', 'Stage name', 'development')\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\n\t\t\tconst { loadWorkspaceConfig } = await import('./config');\n\t\t\tconst { reconcileMissingSecrets } = await import('./secrets/reconcile');\n\t\t\tconst { readStageSecrets, writeStageSecrets } = await import(\n\t\t\t\t'./secrets/storage'\n\t\t\t);\n\n\t\t\tconst { workspace } = await loadWorkspaceConfig();\n\t\t\tconst secrets = await readStageSecrets(options.stage, workspace.root);\n\n\t\t\tif (!secrets) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`No secrets found for stage \"${options.stage}\". Run \"gkm secrets:init --stage ${options.stage}\" first.`,\n\t\t\t\t);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\tconst { derivedContainers } = await import('./reconcile/workspace');\n\t\t\tconst result = reconcileMissingSecrets(\n\t\t\t\tsecrets,\n\t\t\t\tworkspace,\n\t\t\t\tawait derivedContainers(workspace, options.stage),\n\t\t\t);\n\n\t\t\tif (!result) {\n\t\t\t\tconsole.log(`\\n✓ Secrets for stage \"${options.stage}\" are up-to-date`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait writeStageSecrets(result.secrets, workspace.root);\n\t\t\tconsole.log(\n\t\t\t\t`\\n✓ Reconciled ${result.addedKeys.length} missing secret(s) for stage \"${options.stage}\":`,\n\t\t\t);\n\t\t\tfor (const key of result.addedKeys) {\n\t\t\t\tconsole.log(` + ${key}`);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\n// Deploy command\nprogram\n\t.command('deploy')\n\t.description('Deploy application to a provider')\n\t.requiredOption(\n\t\t'--provider <provider>',\n\t\t'Deploy provider (docker, dokploy, aws-lambda)',\n\t)\n\t.requiredOption(\n\t\t'--stage <stage>',\n\t\t'Deployment stage (e.g., production, staging)',\n\t)\n\t.option('--tag <tag>', 'Image tag (default: stage-timestamp)')\n\t.option('--skip-push', 'Skip pushing image to registry')\n\t.option('--skip-build', 'Skip build step (use existing build)')\n\t.action(\n\t\tasync (options: {\n\t\t\tprovider: string;\n\t\t\tstage: string;\n\t\t\ttag?: string;\n\t\t\tskipPush?: boolean;\n\t\t\tskipBuild?: boolean;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\tconst validProviders = ['docker', 'dokploy', 'aws-lambda'];\n\t\t\t\tif (!validProviders.includes(options.provider)) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`Invalid provider: ${options.provider}\\n` +\n\t\t\t\t\t\t\t`Valid providers: ${validProviders.join(', ')}`,\n\t\t\t\t\t);\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t}\n\n\t\t\t\tawait deployCommand({\n\t\t\t\t\tprovider: options.provider as DeployProvider,\n\t\t\t\t\tstage: options.stage,\n\t\t\t\t\ttag: options.tag,\n\t\t\t\t\tskipPush: options.skipPush,\n\t\t\t\t\tskipBuild: options.skipBuild,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n// Deploy init command - Initialize Dokploy project and application\nprogram\n\t.command('deploy:init')\n\t.description('Initialize Dokploy deployment (create project and application)')\n\t.option(\n\t\t'--endpoint <url>',\n\t\t'Dokploy server URL (uses stored credentials if logged in)',\n\t)\n\t.requiredOption('--project <name>', 'Project name (creates if not exists)')\n\t.requiredOption('--app <name>', 'Application name')\n\t.option('--project-id <id>', 'Use existing project ID instead of creating')\n\t.option('--registry-id <id>', 'Configure registry for the application')\n\t.action(\n\t\tasync (options: {\n\t\t\tendpoint?: string;\n\t\t\tproject: string;\n\t\t\tapp: string;\n\t\t\tprojectId?: string;\n\t\t\tregistryId?: string;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\tawait deployInitCommand({\n\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t\tprojectName: options.project,\n\t\t\t\t\tappName: options.app,\n\t\t\t\t\tprojectId: options.projectId,\n\t\t\t\t\tregistryId: options.registryId,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n// Deploy list command - List Dokploy resources\nprogram\n\t.command('deploy:list')\n\t.description('List Dokploy resources (projects, registries)')\n\t.option(\n\t\t'--endpoint <url>',\n\t\t'Dokploy server URL (uses stored credentials if logged in)',\n\t)\n\t.option('--projects', 'List projects')\n\t.option('--registries', 'List registries')\n\t.action(\n\t\tasync (options: {\n\t\t\tendpoint?: string;\n\t\t\tprojects?: boolean;\n\t\t\tregistries?: boolean;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\tif (options.projects) {\n\t\t\t\t\tawait deployListCommand({\n\t\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t\t\tresource: 'projects',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (options.registries) {\n\t\t\t\t\tawait deployListCommand({\n\t\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t\t\tresource: 'registries',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (!options.projects && !options.registries) {\n\t\t\t\t\t// Default to listing both\n\t\t\t\t\tawait deployListCommand({\n\t\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t\t\tresource: 'projects',\n\t\t\t\t\t});\n\t\t\t\t\tawait deployListCommand({\n\t\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t\t\tresource: 'registries',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n// Login command\nprogram\n\t.command('login')\n\t.description('Authenticate with a deployment or DNS provider')\n\t// `--provider`, the same word `gkm deploy` and the DNS config already use for\n\t// these same names. It was `--service`, which made `dokploy` a provider in\n\t// one command and a service in the next — and `service` is the most\n\t// overloaded word in this codebase already: the DI interface, the\n\t// `services:` backend block, and a Dokploy resource name.\n\t.option(\n\t\t'--provider <provider>',\n\t\t'Provider to log in to (dokploy, hostinger)',\n\t\t'dokploy',\n\t)\n\t// Kept working rather than removed: it is what shipped, and a flag rename is\n\t// not worth breaking somebody's CI over.\n\t.option('--service <provider>', '[DEPRECATED] Use --provider instead')\n\t.option('--token <token>', 'API token (will prompt if not provided)')\n\t.option('--endpoint <url>', 'Service endpoint URL')\n\t.action(\n\t\tasync (options: {\n\t\t\tprovider: string;\n\t\t\tservice?: string;\n\t\t\ttoken?: string;\n\t\t\tendpoint?: string;\n\t\t}) => {\n\t\t\ttry {\n\t\t\t\tconst globalOptions = program.opts();\n\t\t\t\tif (globalOptions.cwd) {\n\t\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t\t}\n\n\t\t\t\t// The alias wins when given, because a default is not a choice.\n\t\t\t\tconst provider = options.service ?? options.provider;\n\n\t\t\t\tif (!['dokploy', 'hostinger'].includes(provider)) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`Unknown provider: ${provider}. Supported: dokploy, hostinger`,\n\t\t\t\t\t);\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t}\n\n\t\t\t\tawait loginCommand({\n\t\t\t\t\tprovider: provider as 'dokploy' | 'hostinger',\n\t\t\t\t\ttoken: options.token,\n\t\t\t\t\tendpoint: options.endpoint,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n// Logout command\nprogram\n\t.command('logout')\n\t.description('Remove stored credentials')\n\t.option(\n\t\t'--provider <provider>',\n\t\t'Whose credentials to remove (dokploy, all)',\n\t\t'dokploy',\n\t)\n\t.option('--service <provider>', '[DEPRECATED] Use --provider instead')\n\t.action(async (options: { provider: string; service?: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\n\t\t\tawait logoutCommand({\n\t\t\t\tprovider: (options.service ?? options.provider) as 'dokploy' | 'all',\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\n// Whoami command\nprogram\n\t.command('whoami')\n\t.description('Show current authentication status')\n\t.action(async () => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\n\t\t\tawait whoamiCommand();\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\n// State management commands\nprogram\n\t.command('state:pull')\n\t.description('Pull deployment state from remote to local')\n\t.requiredOption(\n\t\t'--stage <stage>',\n\t\t'Deployment stage (e.g., production, staging)',\n\t)\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait statePullCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('state:push')\n\t.description('Push deployment state from local to remote')\n\t.requiredOption(\n\t\t'--stage <stage>',\n\t\t'Deployment stage (e.g., production, staging)',\n\t)\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait statePushCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('state:show')\n\t.description('Show deployment state for a stage')\n\t.requiredOption(\n\t\t'--stage <stage>',\n\t\t'Deployment stage (e.g., production, staging)',\n\t)\n\t.option('--json', 'Output as JSON')\n\t.action(async (options: { stage: string; json?: boolean }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait stateShowCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('state:diff')\n\t.description('Compare local and remote deployment state')\n\t.requiredOption(\n\t\t'--stage <stage>',\n\t\t'Deployment stage (e.g., production, staging)',\n\t)\n\t.action(async (options: { stage: string }) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait stateDiffCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram\n\t.command('upgrade')\n\t.description('Upgrade all @geekmidas packages to their latest versions')\n\t.option('--dry-run', 'Show what would be upgraded without making changes')\n\t.action(async (options: UpgradeOptions) => {\n\t\ttry {\n\t\t\tconst globalOptions = program.opts();\n\t\t\tif (globalOptions.cwd) {\n\t\t\t\tprocess.chdir(globalOptions.cwd);\n\t\t\t}\n\t\t\tawait upgradeCommand(options);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nprogram.parse();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WACS;cACG;kBACI;gBACJ;WACH;cACG;CACV,KAAK;EACJ,UAAU;GACT,SAAS;GACT,WAAW;EACX;EACD,WAAW;GACV,SAAS;GACT,WAAW;EACX;CACD;CACD,YAAY;EACX,UAAU;GACT,SAAS;GACT,WAAW;EACX;EACD,WAAW;GACV,SAAS;GACT,WAAW;EACX;CACD;CACD,eAAe;EACd,UAAU;GACT,SAAS;GACT,WAAW;EACX;EACD,WAAW;GACV,SAAS;GACT,WAAW;EACX;CACD;CACD,eAAe;EACd,UAAU;GACT,SAAS;GACT,WAAW;EACX;EACD,WAAW;GACV,SAAS;GACT,WAAW;EACX;CACD;CACD,aAAa;EACZ,UAAU;GACT,SAAS;GACT,WAAW;EACX;EACD,WAAW;GACV,SAAS;GACT,WAAW;EACX;CACD;CACD,yBAAyB;EACxB,UAAU;GACT,SAAS;GACT,WAAW;EACX;EACD,WAAW;GACV,SAAS;GACT,WAAW;EACX;CACD;AACD;YACQ,CACR,QACA,KACA;UACM,EACN,OAAO,gBACP;cACU;CACV,MAAM;CACN,iBAAiB;CACjB,YAAY;CACZ,QAAQ;CACR,aAAa;CACb,iBAAiB;AACjB;iBACa;CACb,QAAQ;CACR,OAAO;AACP;mBACe;CACf,+BAA+B;CAC/B,uBAAuB;CACvB,4BAA4B;CAC5B,sBAAsB;CACtB,uBAAuB;CACvB,iCAAiC;CACjC,oBAAoB;CACpB,yBAAyB;CACzB,iBAAiB;CACjB,qBAAqB;CACrB,qBAAqB;CACrB,qBAAqB;CACrB,uBAAuB;CACvB,qBAAqB;CACrB,YAAY;CACZ,aAAa;CACb,UAAU;CACV,WAAW;CACX,aAAa;CACb,QAAQ;CACR,oBAAoB;CACpB,sBAAsB;CACtB,MAAM;CACN,WAAW;CACX,OAAO;CACP,QAAQ;AACR;sBACkB;CAClB,sBAAsB;CACtB,2BAA2B;CAC3B,eAAe;CACf,aAAa;CACb,kBAAkB;CAClB,cAAc;CACd,UAAU;CACV,OAAO;AACP;uBACmB,EACnB,wBAAwB,cACxB;2BACuB,EACv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ACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;EACD;GACC,MAAM;GACN,SAAS;EACT;CACD;AACD;;;;;;;AC17CD,SAAgB,oBAAoBC,SAA2C;AAC9E,MAAK,QAAQ,YAAY,QAAQ,aAAa,YAC7C,QAAO,CAAE;CAGV,MAAM,eAAe,GAAG,QAAQ,KAAK;CACrC,MAAM,cAAc,GAAG,QAAQ,KAAK;CACpC,MAAM,iBAAiB,GAAG,QAAQ,KAAK;CACvC,MAAM,aAAa,GAAG,QAAQ,KAAK;CAGnC,MAAM,cAAc;EACnB,MAAM;EACN,SAAS;EACT,SAAS;EACT,MAAM;EACN,SAAS;GACR,KAAK;GACL,OAAO;GACP,OAAO;GACP,WAAW;EACX;EACD,cAAc;IACZ,aAAa;IACb,gBAAgB;IAChB,YAAY;GACb,qBAAqB,mBAAmB;GACxC,qBAAqB,mBAAmB;GACxC,yBAAyB;GACzB,eAAe;GACf,MAAM;GACN,OAAO;GACP,aAAa;EACb;EACD,iBAAiB;GAChB,kBAAkB,mBAAmB;GACrC,wBAAwB;GACxB,eAAe;GA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"}
|