@geekmidas/cli 9.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 -650
- 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/config-DujV8DbR.cjs
DELETED
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
2
|
-
const require_workspace = require('./workspace-CQTT7HJQ.cjs');
|
|
3
|
-
const node_fs = require_chunk.__toESM(require("node:fs"));
|
|
4
|
-
const node_path = require_chunk.__toESM(require("node:path"));
|
|
5
|
-
|
|
6
|
-
//#region src/config.ts
|
|
7
|
-
/**
|
|
8
|
-
* Define GKM configuration with full TypeScript support.
|
|
9
|
-
* This is an identity function that provides type safety and autocomplete.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* // gkm.config.ts
|
|
14
|
-
* import { defineConfig } from '@geekmidas/cli/config';
|
|
15
|
-
*
|
|
16
|
-
* export default defineConfig({
|
|
17
|
-
* routes: './src/endpoints/**\/*.ts',
|
|
18
|
-
* envParser: './src/config/env',
|
|
19
|
-
* logger: './src/config/logger',
|
|
20
|
-
* telescope: true,
|
|
21
|
-
* });
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
function defineConfig(config) {
|
|
25
|
-
return config;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Parse a module config string into path and import pattern.
|
|
29
|
-
*
|
|
30
|
-
* @param configString - Config string in format "./path/to/module" or "./path/to/module#exportName"
|
|
31
|
-
* @param defaultAlias - The default alias name to use if no export name specified
|
|
32
|
-
* @returns Object with path and import pattern
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* parseModuleConfig('./src/config/env', 'envParser')
|
|
36
|
-
* // { path: './src/config/env', importPattern: 'envParser' }
|
|
37
|
-
*
|
|
38
|
-
* parseModuleConfig('./src/config/env#envParser', 'envParser')
|
|
39
|
-
* // { path: './src/config/env', importPattern: '{ envParser }' }
|
|
40
|
-
*
|
|
41
|
-
* parseModuleConfig('./src/config/env#myEnv', 'envParser')
|
|
42
|
-
* // { path: './src/config/env', importPattern: '{ myEnv as envParser }' }
|
|
43
|
-
*/
|
|
44
|
-
function parseModuleConfig(configString, defaultAlias) {
|
|
45
|
-
const parts = configString.split("#");
|
|
46
|
-
const path = parts[0] ?? configString;
|
|
47
|
-
const exportName = parts[1];
|
|
48
|
-
const importPattern = !exportName ? defaultAlias : exportName === defaultAlias ? `{ ${defaultAlias} }` : `{ ${exportName} as ${defaultAlias} }`;
|
|
49
|
-
return {
|
|
50
|
-
path,
|
|
51
|
-
importPattern
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Find and return the path to the config file.
|
|
56
|
-
*
|
|
57
|
-
* Resolution order:
|
|
58
|
-
* 1. GKM_CONFIG_PATH env var (set by workspace dev command)
|
|
59
|
-
* 2. Walk up directory tree from cwd
|
|
60
|
-
*/
|
|
61
|
-
function findConfigPath(cwd) {
|
|
62
|
-
const files = [
|
|
63
|
-
"gkm.config.json",
|
|
64
|
-
"gkm.config.ts",
|
|
65
|
-
"gkm.config.js"
|
|
66
|
-
];
|
|
67
|
-
const envConfigPath = process.env.GKM_CONFIG_PATH;
|
|
68
|
-
if (envConfigPath && (0, node_fs.existsSync)(envConfigPath)) return {
|
|
69
|
-
configPath: envConfigPath,
|
|
70
|
-
workspaceRoot: (0, node_path.dirname)(envConfigPath)
|
|
71
|
-
};
|
|
72
|
-
let currentDir = cwd;
|
|
73
|
-
const { root } = (0, node_path.parse)(currentDir);
|
|
74
|
-
while (currentDir !== root) {
|
|
75
|
-
for (const file of files) {
|
|
76
|
-
const configPath = (0, node_path.join)(currentDir, file);
|
|
77
|
-
if ((0, node_fs.existsSync)(configPath)) return {
|
|
78
|
-
configPath,
|
|
79
|
-
workspaceRoot: currentDir
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
currentDir = (0, node_path.dirname)(currentDir);
|
|
83
|
-
}
|
|
84
|
-
throw new Error("Configuration file not found. Please create gkm.config.json, gkm.config.ts, or gkm.config.js in the project root.");
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Get app name from package.json in the given directory.
|
|
88
|
-
* Handles scoped packages by extracting the name after the scope.
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* getAppNameFromCwd('/path/to/apps/api')
|
|
92
|
-
* // package.json: { "name": "@myorg/api" }
|
|
93
|
-
* // Returns: 'api'
|
|
94
|
-
*/
|
|
95
|
-
function getAppNameFromCwd(cwd = process.cwd()) {
|
|
96
|
-
const packageJsonPath = (0, node_path.join)(cwd, "package.json");
|
|
97
|
-
if (!(0, node_fs.existsSync)(packageJsonPath)) return null;
|
|
98
|
-
try {
|
|
99
|
-
const packageJson = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8"));
|
|
100
|
-
const name = packageJson.name;
|
|
101
|
-
if (!name) return null;
|
|
102
|
-
if (name.startsWith("@") && name.includes("/")) return name.split("/")[1] ?? null;
|
|
103
|
-
return name;
|
|
104
|
-
} catch {
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Load raw configuration from file.
|
|
110
|
-
*/
|
|
111
|
-
async function loadRawConfig(cwd) {
|
|
112
|
-
const { configPath, workspaceRoot } = findConfigPath(cwd);
|
|
113
|
-
try {
|
|
114
|
-
const config = await import(configPath);
|
|
115
|
-
return {
|
|
116
|
-
config: config.default,
|
|
117
|
-
workspaceRoot
|
|
118
|
-
};
|
|
119
|
-
} catch (error) {
|
|
120
|
-
throw new Error(`Failed to load config: ${error.message}`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Load configuration file (single-app format).
|
|
125
|
-
* For backwards compatibility with existing code.
|
|
126
|
-
*
|
|
127
|
-
* @deprecated Use loadWorkspaceConfig for new code
|
|
128
|
-
*/
|
|
129
|
-
async function loadConfig(cwd = process.cwd()) {
|
|
130
|
-
const { config } = await loadRawConfig(cwd);
|
|
131
|
-
if (require_workspace.isWorkspaceConfig(config)) throw new Error("Workspace configuration detected. Use loadWorkspaceConfig() instead.");
|
|
132
|
-
return config;
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Load configuration file and process it as a workspace.
|
|
136
|
-
* Works with both single-app and workspace configurations.
|
|
137
|
-
*
|
|
138
|
-
* Single-app configs are automatically wrapped as a workspace with one app.
|
|
139
|
-
*
|
|
140
|
-
* @example
|
|
141
|
-
* ```ts
|
|
142
|
-
* const { type, workspace } = await loadWorkspaceConfig();
|
|
143
|
-
*
|
|
144
|
-
* if (type === 'workspace') {
|
|
145
|
-
* console.log('Multi-app workspace:', workspace.apps);
|
|
146
|
-
* } else {
|
|
147
|
-
* console.log('Single app wrapped as workspace');
|
|
148
|
-
* }
|
|
149
|
-
* ```
|
|
150
|
-
*/
|
|
151
|
-
async function loadWorkspaceConfig(cwd = process.cwd()) {
|
|
152
|
-
const { config, workspaceRoot } = await loadRawConfig(cwd);
|
|
153
|
-
return require_workspace.processConfig(config, workspaceRoot);
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Load app-specific configuration from workspace.
|
|
157
|
-
* Uses the app name from package.json to find the correct app config.
|
|
158
|
-
*
|
|
159
|
-
* @example
|
|
160
|
-
* ```ts
|
|
161
|
-
* // From apps/api directory with package.json: { "name": "@myorg/api" }
|
|
162
|
-
* const { app, workspace, workspaceRoot } = await loadAppConfig();
|
|
163
|
-
* console.log(app.routes); // './src/endpoints/**\/*.ts'
|
|
164
|
-
* ```
|
|
165
|
-
*/
|
|
166
|
-
async function loadAppConfig(cwd = process.cwd()) {
|
|
167
|
-
const appName = getAppNameFromCwd(cwd);
|
|
168
|
-
if (!appName) throw new Error("Could not determine app name. Ensure package.json exists with a \"name\" field.");
|
|
169
|
-
const { config, workspaceRoot } = await loadRawConfig(cwd);
|
|
170
|
-
const loadedConfig = require_workspace.processConfig(config, workspaceRoot);
|
|
171
|
-
const app = loadedConfig.workspace.apps[appName];
|
|
172
|
-
if (!app) {
|
|
173
|
-
const availableApps = Object.keys(loadedConfig.workspace.apps).join(", ");
|
|
174
|
-
throw new Error(`App "${appName}" not found in workspace config. Available apps: ${availableApps}. Ensure the package.json name matches the app key in gkm.config.ts.`);
|
|
175
|
-
}
|
|
176
|
-
const gkmConfig = require_workspace.getAppGkmConfig(loadedConfig.workspace, appName);
|
|
177
|
-
if (!gkmConfig) throw new Error(`App "${appName}" is not a backend app and cannot be run with gkm dev.`);
|
|
178
|
-
return {
|
|
179
|
-
appName,
|
|
180
|
-
app,
|
|
181
|
-
gkmConfig,
|
|
182
|
-
workspace: loadedConfig.workspace,
|
|
183
|
-
workspaceRoot,
|
|
184
|
-
appRoot: (0, node_path.join)(workspaceRoot, app.path)
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Load workspace info for any app (frontend or backend).
|
|
189
|
-
* Unlike loadAppConfig, this does NOT require the app to have a gkm config
|
|
190
|
-
* (routes, entry, etc.), making it suitable for gkm exec/test from frontend apps.
|
|
191
|
-
*/
|
|
192
|
-
async function loadWorkspaceAppInfo(cwd = process.cwd()) {
|
|
193
|
-
const appName = getAppNameFromCwd(cwd);
|
|
194
|
-
if (!appName) throw new Error("Could not determine app name. Ensure package.json exists with a \"name\" field.");
|
|
195
|
-
const { config, workspaceRoot } = await loadRawConfig(cwd);
|
|
196
|
-
const loadedConfig = require_workspace.processConfig(config, workspaceRoot);
|
|
197
|
-
const app = loadedConfig.workspace.apps[appName];
|
|
198
|
-
if (!app) {
|
|
199
|
-
const availableApps = Object.keys(loadedConfig.workspace.apps).join(", ");
|
|
200
|
-
throw new Error(`App "${appName}" not found in workspace config. Available apps: ${availableApps}. Ensure the package.json name matches the app key in gkm.config.ts.`);
|
|
201
|
-
}
|
|
202
|
-
return {
|
|
203
|
-
appName,
|
|
204
|
-
app,
|
|
205
|
-
workspace: loadedConfig.workspace,
|
|
206
|
-
workspaceRoot
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
//#endregion
|
|
211
|
-
Object.defineProperty(exports, 'defineConfig', {
|
|
212
|
-
enumerable: true,
|
|
213
|
-
get: function () {
|
|
214
|
-
return defineConfig;
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
Object.defineProperty(exports, 'getAppNameFromCwd', {
|
|
218
|
-
enumerable: true,
|
|
219
|
-
get: function () {
|
|
220
|
-
return getAppNameFromCwd;
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
Object.defineProperty(exports, 'loadAppConfig', {
|
|
224
|
-
enumerable: true,
|
|
225
|
-
get: function () {
|
|
226
|
-
return loadAppConfig;
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
Object.defineProperty(exports, 'loadConfig', {
|
|
230
|
-
enumerable: true,
|
|
231
|
-
get: function () {
|
|
232
|
-
return loadConfig;
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
Object.defineProperty(exports, 'loadWorkspaceAppInfo', {
|
|
236
|
-
enumerable: true,
|
|
237
|
-
get: function () {
|
|
238
|
-
return loadWorkspaceAppInfo;
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
Object.defineProperty(exports, 'loadWorkspaceConfig', {
|
|
242
|
-
enumerable: true,
|
|
243
|
-
get: function () {
|
|
244
|
-
return loadWorkspaceConfig;
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
Object.defineProperty(exports, 'parseModuleConfig', {
|
|
248
|
-
enumerable: true,
|
|
249
|
-
get: function () {
|
|
250
|
-
return parseModuleConfig;
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
//# sourceMappingURL=config-DujV8DbR.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-DujV8DbR.cjs","names":["config: GkmConfig","configString: string","defaultAlias: string","cwd: string"],"sources":["../src/config.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, parse } from 'node:path';\nimport type { GkmConfig } from './types.js';\nimport {\n\tgetAppGkmConfig,\n\tisWorkspaceConfig,\n\ttype LoadedConfig,\n\ttype NormalizedAppConfig,\n\ttype NormalizedWorkspace,\n\tprocessConfig,\n\ttype WorkspaceConfig,\n} from './workspace/index.js';\n\nexport type { GkmConfig } from './types.js';\nexport type { LoadedConfig, WorkspaceConfig } from './workspace/index.js';\nexport { defineWorkspace } from './workspace/index.js';\n/**\n * Define GKM configuration with full TypeScript support.\n * This is an identity function that provides type safety and autocomplete.\n *\n * @example\n * ```ts\n * // gkm.config.ts\n * import { defineConfig } from '@geekmidas/cli/config';\n *\n * export default defineConfig({\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/config/logger',\n * telescope: true,\n * });\n * ```\n */\nexport function defineConfig(config: GkmConfig): GkmConfig {\n\treturn config;\n}\n\nexport interface ParsedModuleConfig {\n\tpath: string;\n\timportPattern: string;\n}\n\n/**\n * Parse a module config string into path and import pattern.\n *\n * @param configString - Config string in format \"./path/to/module\" or \"./path/to/module#exportName\"\n * @param defaultAlias - The default alias name to use if no export name specified\n * @returns Object with path and import pattern\n *\n * @example\n * parseModuleConfig('./src/config/env', 'envParser')\n * // { path: './src/config/env', importPattern: 'envParser' }\n *\n * parseModuleConfig('./src/config/env#envParser', 'envParser')\n * // { path: './src/config/env', importPattern: '{ envParser }' }\n *\n * parseModuleConfig('./src/config/env#myEnv', 'envParser')\n * // { path: './src/config/env', importPattern: '{ myEnv as envParser }' }\n */\nexport function parseModuleConfig(\n\tconfigString: string,\n\tdefaultAlias: string,\n): ParsedModuleConfig {\n\tconst parts = configString.split('#');\n\tconst path = parts[0] ?? configString;\n\tconst exportName = parts[1];\n\tconst importPattern = !exportName\n\t\t? defaultAlias\n\t\t: exportName === defaultAlias\n\t\t\t? `{ ${defaultAlias} }`\n\t\t\t: `{ ${exportName} as ${defaultAlias} }`;\n\n\treturn { path, importPattern };\n}\n\nexport interface ConfigDiscoveryResult {\n\tconfigPath: string;\n\tworkspaceRoot: string;\n}\n\n/**\n * Find and return the path to the config file.\n *\n * Resolution order:\n * 1. GKM_CONFIG_PATH env var (set by workspace dev command)\n * 2. Walk up directory tree from cwd\n */\nfunction findConfigPath(cwd: string): ConfigDiscoveryResult {\n\tconst files = ['gkm.config.json', 'gkm.config.ts', 'gkm.config.js'];\n\n\t// Check GKM_CONFIG_PATH env var first (set by workspace dev command)\n\tconst envConfigPath = process.env.GKM_CONFIG_PATH;\n\tif (envConfigPath && existsSync(envConfigPath)) {\n\t\treturn {\n\t\t\tconfigPath: envConfigPath,\n\t\t\tworkspaceRoot: dirname(envConfigPath),\n\t\t};\n\t}\n\n\t// Walk up directory tree to find config\n\tlet currentDir = cwd;\n\tconst { root } = parse(currentDir);\n\n\twhile (currentDir !== root) {\n\t\tfor (const file of files) {\n\t\t\tconst configPath = join(currentDir, file);\n\t\t\tif (existsSync(configPath)) {\n\t\t\t\treturn {\n\t\t\t\t\tconfigPath,\n\t\t\t\t\tworkspaceRoot: currentDir,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tcurrentDir = dirname(currentDir);\n\t}\n\n\tthrow new Error(\n\t\t'Configuration file not found. Please create gkm.config.json, gkm.config.ts, or gkm.config.js in the project root.',\n\t);\n}\n\n/**\n * Get app name from package.json in the given directory.\n * Handles scoped packages by extracting the name after the scope.\n *\n * @example\n * getAppNameFromCwd('/path/to/apps/api')\n * // package.json: { \"name\": \"@myorg/api\" }\n * // Returns: 'api'\n */\nexport function getAppNameFromCwd(cwd: string = process.cwd()): string | null {\n\tconst packageJsonPath = join(cwd, 'package.json');\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n\t\tconst name = packageJson.name as string | undefined;\n\n\t\tif (!name) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Handle scoped packages: @scope/name -> name\n\t\tif (name.startsWith('@') && name.includes('/')) {\n\t\t\treturn name.split('/')[1] ?? null;\n\t\t}\n\n\t\treturn name;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\ninterface RawConfigResult {\n\tconfig: GkmConfig | WorkspaceConfig;\n\tworkspaceRoot: string;\n}\n\n/**\n * Load raw configuration from file.\n */\nasync function loadRawConfig(cwd: string): Promise<RawConfigResult> {\n\tconst { configPath, workspaceRoot } = findConfigPath(cwd);\n\n\ttry {\n\t\tconst config = await import(configPath);\n\t\treturn {\n\t\t\tconfig: config.default,\n\t\t\tworkspaceRoot,\n\t\t};\n\t} catch (error) {\n\t\tthrow new Error(`Failed to load config: ${(error as Error).message}`);\n\t}\n}\n\n/**\n * Load configuration file (single-app format).\n * For backwards compatibility with existing code.\n *\n * @deprecated Use loadWorkspaceConfig for new code\n */\nexport async function loadConfig(\n\tcwd: string = process.cwd(),\n): Promise<GkmConfig> {\n\tconst { config } = await loadRawConfig(cwd);\n\n\t// If it's a workspace config, throw an error\n\tif (isWorkspaceConfig(config)) {\n\t\tthrow new Error(\n\t\t\t'Workspace configuration detected. Use loadWorkspaceConfig() instead.',\n\t\t);\n\t}\n\n\treturn config;\n}\n\n/**\n * Load configuration file and process it as a workspace.\n * Works with both single-app and workspace configurations.\n *\n * Single-app configs are automatically wrapped as a workspace with one app.\n *\n * @example\n * ```ts\n * const { type, workspace } = await loadWorkspaceConfig();\n *\n * if (type === 'workspace') {\n * console.log('Multi-app workspace:', workspace.apps);\n * } else {\n * console.log('Single app wrapped as workspace');\n * }\n * ```\n */\nexport async function loadWorkspaceConfig(\n\tcwd: string = process.cwd(),\n): Promise<LoadedConfig> {\n\tconst { config, workspaceRoot } = await loadRawConfig(cwd);\n\treturn processConfig(config, workspaceRoot);\n}\n\nexport interface AppConfigResult {\n\tappName: string;\n\tapp: NormalizedAppConfig;\n\tgkmConfig: GkmConfig;\n\tworkspace: NormalizedWorkspace;\n\tworkspaceRoot: string;\n\tappRoot: string;\n}\n\n/**\n * Load app-specific configuration from workspace.\n * Uses the app name from package.json to find the correct app config.\n *\n * @example\n * ```ts\n * // From apps/api directory with package.json: { \"name\": \"@myorg/api\" }\n * const { app, workspace, workspaceRoot } = await loadAppConfig();\n * console.log(app.routes); // './src/endpoints/**\\/*.ts'\n * ```\n */\nexport async function loadAppConfig(\n\tcwd: string = process.cwd(),\n): Promise<AppConfigResult> {\n\tconst appName = getAppNameFromCwd(cwd);\n\n\tif (!appName) {\n\t\tthrow new Error(\n\t\t\t'Could not determine app name. Ensure package.json exists with a \"name\" field.',\n\t\t);\n\t}\n\n\tconst { config, workspaceRoot } = await loadRawConfig(cwd);\n\tconst loadedConfig = processConfig(config, workspaceRoot);\n\n\t// Find the app in workspace (apps is a Record<string, NormalizedAppConfig>)\n\tconst app = loadedConfig.workspace.apps[appName];\n\n\tif (!app) {\n\t\tconst availableApps = Object.keys(loadedConfig.workspace.apps).join(', ');\n\t\tthrow new Error(\n\t\t\t`App \"${appName}\" not found in workspace config. Available apps: ${availableApps}. ` +\n\t\t\t\t`Ensure the package.json name matches the app key in gkm.config.ts.`,\n\t\t);\n\t}\n\n\t// Get the app's GKM config using the helper\n\tconst gkmConfig = getAppGkmConfig(loadedConfig.workspace, appName);\n\n\tif (!gkmConfig) {\n\t\tthrow new Error(\n\t\t\t`App \"${appName}\" is not a backend app and cannot be run with gkm dev.`,\n\t\t);\n\t}\n\n\treturn {\n\t\tappName,\n\t\tapp,\n\t\tgkmConfig,\n\t\tworkspace: loadedConfig.workspace,\n\t\tworkspaceRoot,\n\t\tappRoot: join(workspaceRoot, app.path),\n\t};\n}\n\nexport interface WorkspaceAppInfo {\n\tappName: string;\n\tapp: NormalizedAppConfig;\n\tworkspace: NormalizedWorkspace;\n\tworkspaceRoot: string;\n}\n\n/**\n * Load workspace info for any app (frontend or backend).\n * Unlike loadAppConfig, this does NOT require the app to have a gkm config\n * (routes, entry, etc.), making it suitable for gkm exec/test from frontend apps.\n */\nexport async function loadWorkspaceAppInfo(\n\tcwd: string = process.cwd(),\n): Promise<WorkspaceAppInfo> {\n\tconst appName = getAppNameFromCwd(cwd);\n\n\tif (!appName) {\n\t\tthrow new Error(\n\t\t\t'Could not determine app name. Ensure package.json exists with a \"name\" field.',\n\t\t);\n\t}\n\n\tconst { config, workspaceRoot } = await loadRawConfig(cwd);\n\tconst loadedConfig = processConfig(config, workspaceRoot);\n\n\tconst app = loadedConfig.workspace.apps[appName];\n\n\tif (!app) {\n\t\tconst availableApps = Object.keys(loadedConfig.workspace.apps).join(', ');\n\t\tthrow new Error(\n\t\t\t`App \"${appName}\" not found in workspace config. Available apps: ${availableApps}. ` +\n\t\t\t\t`Ensure the package.json name matches the app key in gkm.config.ts.`,\n\t\t);\n\t}\n\n\treturn {\n\t\tappName,\n\t\tapp,\n\t\tworkspace: loadedConfig.workspace,\n\t\tworkspaceRoot,\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credentials-C8DWtnMY.cjs","names":["options?: CredentialOptions","credentials: StoredCredentials","token: string","endpoint: string","registryId: string"],"sources":["../src/auth/credentials.ts"],"sourcesContent":["import { existsSync, mkdirSync } from 'node:fs';\nimport { readFile, unlink, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Stored credentials for various services\n */\nexport interface StoredCredentials {\n\tdokploy?: {\n\t\t/** API token */\n\t\ttoken: string;\n\t\t/** Dokploy endpoint URL */\n\t\tendpoint: string;\n\t\t/** Registry ID in Dokploy (for Docker image pulls) */\n\t\tregistryId?: string;\n\t\t/** When the credentials were stored */\n\t\tstoredAt: string;\n\t};\n\thostinger?: {\n\t\t/** API token from hpanel.hostinger.com/profile/api */\n\t\ttoken: string;\n\t\t/** When the credentials were stored */\n\t\tstoredAt: string;\n\t};\n}\n\n/**\n * Options for credential operations\n */\nexport interface CredentialOptions {\n\t/** Root directory for credentials storage (default: user home directory) */\n\troot?: string;\n}\n\n/**\n * Get the path to the credentials directory\n */\nexport function getCredentialsDir(options?: CredentialOptions): string {\n\tconst root = options?.root ?? homedir();\n\treturn join(root, '.gkm');\n}\n\n/**\n * Get the path to the credentials file\n */\nexport function getCredentialsPath(options?: CredentialOptions): string {\n\treturn join(getCredentialsDir(options), 'credentials.json');\n}\n\n/**\n * Ensure the credentials directory exists\n */\nfunction ensureCredentialsDir(options?: CredentialOptions): void {\n\tconst dir = getCredentialsDir(options);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t}\n}\n\n/**\n * Read stored credentials from disk\n */\nexport async function readCredentials(\n\toptions?: CredentialOptions,\n): Promise<StoredCredentials> {\n\tconst path = getCredentialsPath(options);\n\n\tif (!existsSync(path)) {\n\t\treturn {};\n\t}\n\n\ttry {\n\t\tconst content = await readFile(path, 'utf-8');\n\t\treturn JSON.parse(content) as StoredCredentials;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/**\n * Write credentials to disk\n */\nexport async function writeCredentials(\n\tcredentials: StoredCredentials,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tensureCredentialsDir(options);\n\tconst path = getCredentialsPath(options);\n\n\tawait writeFile(path, JSON.stringify(credentials, null, 2), {\n\t\tmode: 0o600, // Owner read/write only\n\t});\n}\n\n/**\n * Store Dokploy credentials\n */\nexport async function storeDokployCredentials(\n\ttoken: string,\n\tendpoint: string,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst credentials = await readCredentials(options);\n\n\tcredentials.dokploy = {\n\t\ttoken,\n\t\tendpoint,\n\t\tstoredAt: new Date().toISOString(),\n\t};\n\n\tawait writeCredentials(credentials, options);\n}\n\n/**\n * Get stored Dokploy credentials\n */\nexport async function getDokployCredentials(\n\toptions?: CredentialOptions,\n): Promise<{\n\ttoken: string;\n\tendpoint: string;\n\tregistryId?: string;\n} | null> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.dokploy) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\ttoken: credentials.dokploy.token,\n\t\tendpoint: credentials.dokploy.endpoint,\n\t\tregistryId: credentials.dokploy.registryId,\n\t};\n}\n\n/**\n * Remove Dokploy credentials\n */\nexport async function removeDokployCredentials(\n\toptions?: CredentialOptions,\n): Promise<boolean> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.dokploy) {\n\t\treturn false;\n\t}\n\n\tdelete credentials.dokploy;\n\tawait writeCredentials(credentials, options);\n\treturn true;\n}\n\n/**\n * Remove all stored credentials\n */\nexport async function removeAllCredentials(\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst path = getCredentialsPath(options);\n\n\tif (existsSync(path)) {\n\t\tawait unlink(path);\n\t}\n}\n\n/**\n * Get Dokploy API token, checking stored credentials first, then environment\n */\nexport async function getDokployToken(\n\toptions?: CredentialOptions,\n): Promise<string | null> {\n\t// First check environment variable (takes precedence)\n\tconst envToken = process.env.DOKPLOY_API_TOKEN;\n\tif (envToken) {\n\t\treturn envToken;\n\t}\n\n\t// Then check stored credentials\n\tconst stored = await getDokployCredentials(options);\n\tif (stored) {\n\t\treturn stored.token;\n\t}\n\n\treturn null;\n}\n\n/**\n * Get Dokploy endpoint from stored credentials\n */\nexport async function getDokployEndpoint(\n\toptions?: CredentialOptions,\n): Promise<string | null> {\n\tconst stored = await getDokployCredentials(options);\n\treturn stored?.endpoint ?? null;\n}\n\n/**\n * Store Dokploy registry ID\n */\nexport async function storeDokployRegistryId(\n\tregistryId: string,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.dokploy) {\n\t\tthrow new Error(\n\t\t\t'Dokploy credentials not found. Run \"gkm login --service dokploy\" first.',\n\t\t);\n\t}\n\n\tcredentials.dokploy.registryId = registryId;\n\tawait writeCredentials(credentials, options);\n}\n\n/**\n * Get Dokploy registry ID from stored credentials\n */\nexport async function getDokployRegistryId(\n\toptions?: CredentialOptions,\n): Promise<string | undefined> {\n\tconst stored = await getDokployCredentials(options);\n\treturn stored?.registryId ?? undefined;\n}\n\n// ============================================\n// Hostinger credentials\n// ============================================\n\n/**\n * Store Hostinger API token\n *\n * @param token - API token from hpanel.hostinger.com/profile/api\n */\nexport async function storeHostingerToken(\n\ttoken: string,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst credentials = await readCredentials(options);\n\n\tcredentials.hostinger = {\n\t\ttoken,\n\t\tstoredAt: new Date().toISOString(),\n\t};\n\n\tawait writeCredentials(credentials, options);\n}\n\n/**\n * Get stored Hostinger API token\n *\n * Checks environment variable first (HOSTINGER_API_TOKEN),\n * then falls back to stored credentials.\n */\nexport async function getHostingerToken(\n\toptions?: CredentialOptions,\n): Promise<string | null> {\n\t// First check environment variable (takes precedence)\n\tconst envToken = process.env.HOSTINGER_API_TOKEN;\n\tif (envToken) {\n\t\treturn envToken;\n\t}\n\n\t// Then check stored credentials\n\tconst credentials = await readCredentials(options);\n\treturn credentials.hostinger?.token ?? null;\n}\n\n/**\n * Remove Hostinger credentials\n */\nexport async function removeHostingerCredentials(\n\toptions?: CredentialOptions,\n): Promise<boolean> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.hostinger) {\n\t\treturn false;\n\t}\n\n\tdelete credentials.hostinger;\n\tawait writeCredentials(credentials, options);\n\treturn true;\n}\n"],"mappings":";;;;;;;;;;AAsCA,SAAgB,kBAAkBA,SAAqC;CACtE,MAAM,OAAO,SAAS,QAAQ,sBAAS;AACvC,QAAO,oBAAK,MAAM,OAAO;AACzB;;;;AAKD,SAAgB,mBAAmBA,SAAqC;AACvE,QAAO,oBAAK,kBAAkB,QAAQ,EAAE,mBAAmB;AAC3D;;;;AAKD,SAAS,qBAAqBA,SAAmC;CAChE,MAAM,MAAM,kBAAkB,QAAQ;AACtC,MAAK,wBAAW,IAAI,CACnB,wBAAU,KAAK;EAAE,WAAW;EAAM,MAAM;CAAO,EAAC;AAEjD;;;;AAKD,eAAsB,gBACrBA,SAC6B;CAC7B,MAAM,OAAO,mBAAmB,QAAQ;AAExC,MAAK,wBAAW,KAAK,CACpB,QAAO,CAAE;AAGV,KAAI;EACH,MAAM,UAAU,MAAM,+BAAS,MAAM,QAAQ;AAC7C,SAAO,KAAK,MAAM,QAAQ;CAC1B,QAAO;AACP,SAAO,CAAE;CACT;AACD;;;;AAKD,eAAsB,iBACrBC,aACAD,SACgB;AAChB,sBAAqB,QAAQ;CAC7B,MAAM,OAAO,mBAAmB,QAAQ;AAExC,OAAM,gCAAU,MAAM,KAAK,UAAU,aAAa,MAAM,EAAE,EAAE,EAC3D,MAAM,IACN,EAAC;AACF;;;;AAKD,eAAsB,wBACrBE,OACAC,UACAH,SACgB;CAChB,MAAM,cAAc,MAAM,gBAAgB,QAAQ;AAElD,aAAY,UAAU;EACrB;EACA;EACA,UAAU,qBAAI,QAAO,aAAa;CAClC;AAED,OAAM,iBAAiB,aAAa,QAAQ;AAC5C;;;;AAKD,eAAsB,sBACrBA,SAKS;CACT,MAAM,cAAc,MAAM,gBAAgB,QAAQ;AAElD,MAAK,YAAY,QAChB,QAAO;AAGR,QAAO;EACN,OAAO,YAAY,QAAQ;EAC3B,UAAU,YAAY,QAAQ;EAC9B,YAAY,YAAY,QAAQ;CAChC;AACD;;;;AAKD,eAAsB,yBACrBA,SACmB;CACnB,MAAM,cAAc,MAAM,gBAAgB,QAAQ;AAElD,MAAK,YAAY,QAChB,QAAO;AAGR,QAAO,YAAY;AACnB,OAAM,iBAAiB,aAAa,QAAQ;AAC5C,QAAO;AACP;;;;AAkBD,eAAsB,gBACrBA,SACyB;CAEzB,MAAM,WAAW,QAAQ,IAAI;AAC7B,KAAI,SACH,QAAO;CAIR,MAAM,SAAS,MAAM,sBAAsB,QAAQ;AACnD,KAAI,OACH,QAAO,OAAO;AAGf,QAAO;AACP;;;;AAeD,eAAsB,uBACrBI,YACAJ,SACgB;CAChB,MAAM,cAAc,MAAM,gBAAgB,QAAQ;AAElD,MAAK,YAAY,QAChB,OAAM,IAAI,MACT;AAIF,aAAY,QAAQ,aAAa;AACjC,OAAM,iBAAiB,aAAa,QAAQ;AAC5C;;;;AAKD,eAAsB,qBACrBA,SAC8B;CAC9B,MAAM,SAAS,MAAM,sBAAsB,QAAQ;AACnD,QAAO,QAAQ;AACf;;;;;;;AA+BD,eAAsB,kBACrBA,SACyB;CAEzB,MAAM,WAAW,QAAQ,IAAI;AAC7B,KAAI,SACH,QAAO;CAIR,MAAM,cAAc,MAAM,gBAAgB,QAAQ;AAClD,QAAO,YAAY,WAAW,SAAS;AACvC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credentials-s1kLcIzK.mjs","names":["options?: CredentialOptions","credentials: StoredCredentials","token: string","endpoint: string","registryId: string"],"sources":["../src/auth/credentials.ts"],"sourcesContent":["import { existsSync, mkdirSync } from 'node:fs';\nimport { readFile, unlink, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Stored credentials for various services\n */\nexport interface StoredCredentials {\n\tdokploy?: {\n\t\t/** API token */\n\t\ttoken: string;\n\t\t/** Dokploy endpoint URL */\n\t\tendpoint: string;\n\t\t/** Registry ID in Dokploy (for Docker image pulls) */\n\t\tregistryId?: string;\n\t\t/** When the credentials were stored */\n\t\tstoredAt: string;\n\t};\n\thostinger?: {\n\t\t/** API token from hpanel.hostinger.com/profile/api */\n\t\ttoken: string;\n\t\t/** When the credentials were stored */\n\t\tstoredAt: string;\n\t};\n}\n\n/**\n * Options for credential operations\n */\nexport interface CredentialOptions {\n\t/** Root directory for credentials storage (default: user home directory) */\n\troot?: string;\n}\n\n/**\n * Get the path to the credentials directory\n */\nexport function getCredentialsDir(options?: CredentialOptions): string {\n\tconst root = options?.root ?? homedir();\n\treturn join(root, '.gkm');\n}\n\n/**\n * Get the path to the credentials file\n */\nexport function getCredentialsPath(options?: CredentialOptions): string {\n\treturn join(getCredentialsDir(options), 'credentials.json');\n}\n\n/**\n * Ensure the credentials directory exists\n */\nfunction ensureCredentialsDir(options?: CredentialOptions): void {\n\tconst dir = getCredentialsDir(options);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t}\n}\n\n/**\n * Read stored credentials from disk\n */\nexport async function readCredentials(\n\toptions?: CredentialOptions,\n): Promise<StoredCredentials> {\n\tconst path = getCredentialsPath(options);\n\n\tif (!existsSync(path)) {\n\t\treturn {};\n\t}\n\n\ttry {\n\t\tconst content = await readFile(path, 'utf-8');\n\t\treturn JSON.parse(content) as StoredCredentials;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/**\n * Write credentials to disk\n */\nexport async function writeCredentials(\n\tcredentials: StoredCredentials,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tensureCredentialsDir(options);\n\tconst path = getCredentialsPath(options);\n\n\tawait writeFile(path, JSON.stringify(credentials, null, 2), {\n\t\tmode: 0o600, // Owner read/write only\n\t});\n}\n\n/**\n * Store Dokploy credentials\n */\nexport async function storeDokployCredentials(\n\ttoken: string,\n\tendpoint: string,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst credentials = await readCredentials(options);\n\n\tcredentials.dokploy = {\n\t\ttoken,\n\t\tendpoint,\n\t\tstoredAt: new Date().toISOString(),\n\t};\n\n\tawait writeCredentials(credentials, options);\n}\n\n/**\n * Get stored Dokploy credentials\n */\nexport async function getDokployCredentials(\n\toptions?: CredentialOptions,\n): Promise<{\n\ttoken: string;\n\tendpoint: string;\n\tregistryId?: string;\n} | null> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.dokploy) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\ttoken: credentials.dokploy.token,\n\t\tendpoint: credentials.dokploy.endpoint,\n\t\tregistryId: credentials.dokploy.registryId,\n\t};\n}\n\n/**\n * Remove Dokploy credentials\n */\nexport async function removeDokployCredentials(\n\toptions?: CredentialOptions,\n): Promise<boolean> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.dokploy) {\n\t\treturn false;\n\t}\n\n\tdelete credentials.dokploy;\n\tawait writeCredentials(credentials, options);\n\treturn true;\n}\n\n/**\n * Remove all stored credentials\n */\nexport async function removeAllCredentials(\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst path = getCredentialsPath(options);\n\n\tif (existsSync(path)) {\n\t\tawait unlink(path);\n\t}\n}\n\n/**\n * Get Dokploy API token, checking stored credentials first, then environment\n */\nexport async function getDokployToken(\n\toptions?: CredentialOptions,\n): Promise<string | null> {\n\t// First check environment variable (takes precedence)\n\tconst envToken = process.env.DOKPLOY_API_TOKEN;\n\tif (envToken) {\n\t\treturn envToken;\n\t}\n\n\t// Then check stored credentials\n\tconst stored = await getDokployCredentials(options);\n\tif (stored) {\n\t\treturn stored.token;\n\t}\n\n\treturn null;\n}\n\n/**\n * Get Dokploy endpoint from stored credentials\n */\nexport async function getDokployEndpoint(\n\toptions?: CredentialOptions,\n): Promise<string | null> {\n\tconst stored = await getDokployCredentials(options);\n\treturn stored?.endpoint ?? null;\n}\n\n/**\n * Store Dokploy registry ID\n */\nexport async function storeDokployRegistryId(\n\tregistryId: string,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.dokploy) {\n\t\tthrow new Error(\n\t\t\t'Dokploy credentials not found. Run \"gkm login --service dokploy\" first.',\n\t\t);\n\t}\n\n\tcredentials.dokploy.registryId = registryId;\n\tawait writeCredentials(credentials, options);\n}\n\n/**\n * Get Dokploy registry ID from stored credentials\n */\nexport async function getDokployRegistryId(\n\toptions?: CredentialOptions,\n): Promise<string | undefined> {\n\tconst stored = await getDokployCredentials(options);\n\treturn stored?.registryId ?? undefined;\n}\n\n// ============================================\n// Hostinger credentials\n// ============================================\n\n/**\n * Store Hostinger API token\n *\n * @param token - API token from hpanel.hostinger.com/profile/api\n */\nexport async function storeHostingerToken(\n\ttoken: string,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst credentials = await readCredentials(options);\n\n\tcredentials.hostinger = {\n\t\ttoken,\n\t\tstoredAt: new Date().toISOString(),\n\t};\n\n\tawait writeCredentials(credentials, options);\n}\n\n/**\n * Get stored Hostinger API token\n *\n * Checks environment variable first (HOSTINGER_API_TOKEN),\n * then falls back to stored credentials.\n */\nexport async function getHostingerToken(\n\toptions?: CredentialOptions,\n): Promise<string | null> {\n\t// First check environment variable (takes precedence)\n\tconst envToken = process.env.HOSTINGER_API_TOKEN;\n\tif (envToken) {\n\t\treturn envToken;\n\t}\n\n\t// Then check stored credentials\n\tconst credentials = await readCredentials(options);\n\treturn credentials.hostinger?.token ?? null;\n}\n\n/**\n * Remove Hostinger credentials\n */\nexport async function removeHostingerCredentials(\n\toptions?: CredentialOptions,\n): Promise<boolean> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.hostinger) {\n\t\treturn false;\n\t}\n\n\tdelete credentials.hostinger;\n\tawait writeCredentials(credentials, options);\n\treturn true;\n}\n"],"mappings":";;;;;;;;;AAsCA,SAAgB,kBAAkBA,SAAqC;CACtE,MAAM,OAAO,SAAS,QAAQ,SAAS;AACvC,QAAO,KAAK,MAAM,OAAO;AACzB;;;;AAKD,SAAgB,mBAAmBA,SAAqC;AACvE,QAAO,KAAK,kBAAkB,QAAQ,EAAE,mBAAmB;AAC3D;;;;AAKD,SAAS,qBAAqBA,SAAmC;CAChE,MAAM,MAAM,kBAAkB,QAAQ;AACtC,MAAK,WAAW,IAAI,CACnB,WAAU,KAAK;EAAE,WAAW;EAAM,MAAM;CAAO,EAAC;AAEjD;;;;AAKD,eAAsB,gBACrBA,SAC6B;CAC7B,MAAM,OAAO,mBAAmB,QAAQ;AAExC,MAAK,WAAW,KAAK,CACpB,QAAO,CAAE;AAGV,KAAI;EACH,MAAM,UAAU,MAAM,SAAS,MAAM,QAAQ;AAC7C,SAAO,KAAK,MAAM,QAAQ;CAC1B,QAAO;AACP,SAAO,CAAE;CACT;AACD;;;;AAKD,eAAsB,iBACrBC,aACAD,SACgB;AAChB,sBAAqB,QAAQ;CAC7B,MAAM,OAAO,mBAAmB,QAAQ;AAExC,OAAM,UAAU,MAAM,KAAK,UAAU,aAAa,MAAM,EAAE,EAAE,EAC3D,MAAM,IACN,EAAC;AACF;;;;AAKD,eAAsB,wBACrBE,OACAC,UACAH,SACgB;CAChB,MAAM,cAAc,MAAM,gBAAgB,QAAQ;AAElD,aAAY,UAAU;EACrB;EACA;EACA,UAAU,qBAAI,QAAO,aAAa;CAClC;AAED,OAAM,iBAAiB,aAAa,QAAQ;AAC5C;;;;AAKD,eAAsB,sBACrBA,SAKS;CACT,MAAM,cAAc,MAAM,gBAAgB,QAAQ;AAElD,MAAK,YAAY,QAChB,QAAO;AAGR,QAAO;EACN,OAAO,YAAY,QAAQ;EAC3B,UAAU,YAAY,QAAQ;EAC9B,YAAY,YAAY,QAAQ;CAChC;AACD;;;;AAKD,eAAsB,yBACrBA,SACmB;CACnB,MAAM,cAAc,MAAM,gBAAgB,QAAQ;AAElD,MAAK,YAAY,QAChB,QAAO;AAGR,QAAO,YAAY;AACnB,OAAM,iBAAiB,aAAa,QAAQ;AAC5C,QAAO;AACP;;;;AAkBD,eAAsB,gBACrBA,SACyB;CAEzB,MAAM,WAAW,QAAQ,IAAI;AAC7B,KAAI,SACH,QAAO;CAIR,MAAM,SAAS,MAAM,sBAAsB,QAAQ;AACnD,KAAI,OACH,QAAO,OAAO;AAGf,QAAO;AACP;;;;AAeD,eAAsB,uBACrBI,YACAJ,SACgB;CAChB,MAAM,cAAc,MAAM,gBAAgB,QAAQ;AAElD,MAAK,YAAY,QAChB,OAAM,IAAI,MACT;AAIF,aAAY,QAAQ,aAAa;AACjC,OAAM,iBAAiB,aAAa,QAAQ;AAC5C;;;;AAKD,eAAsB,qBACrBA,SAC8B;CAC9B,MAAM,SAAS,MAAM,sBAAsB,QAAQ;AACnD,QAAO,QAAQ;AACf;;;;;;;AA+BD,eAAsB,kBACrBA,SACyB;CAEzB,MAAM,WAAW,QAAQ,IAAI;AAC7B,KAAI,SACH,QAAO;CAIR,MAAM,cAAc,MAAM,gBAAgB,QAAQ;AAClD,QAAO,YAAY,WAAW,SAAS;AACvC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dokploy-api-CqOIKHme.cjs","names":["message: string","status: number","statusText: string","issues?: Array<{ message: string }>","options: DokployApiOptions","endpoint: string","body?: Record<string, unknown>","method: 'GET' | 'POST' | 'PUT' | 'DELETE'","issues: Array<{ message: string }> | undefined","projectId: string","name: string","description?: string","environmentId: string","applicationId: string","updates: Partial<DokployApplicationUpdate>","env: string","dockerImage: string","options?: {\n\t\t\t/** Registry ID in Dokploy (for pre-configured registries) */\n\t\t\tregistryId?: string;\n\t\t\t/** Registry username (for direct auth) */\n\t\t\tusername?: string;\n\t\t\t/** Registry password (for direct auth) */\n\t\t\tpassword?: string;\n\t\t\t/** Registry URL (for direct auth, e.g., ghcr.io) */\n\t\t\tregistryUrl?: string;\n\t\t}","registryName: string","registryUrl: string","username: string","password: string","options?: {\n\t\t\timagePrefix?: string;\n\t\t}","registryId: string","updates: Partial<{\n\t\t\tregistryName: string;\n\t\t\tregistryUrl: string;\n\t\t\tusername: string;\n\t\t\tpassword: string;\n\t\t\timagePrefix: string;\n\t\t}>","options?: {\n\t\t\tappName?: string;\n\t\t\tdatabaseName?: string;\n\t\t\tdatabaseUser?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t}","options?: {\n\t\t\tdatabaseName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t}","postgresId: string","externalPort: number | null","updates: Partial<DokployPostgresUpdate>","options?: {\n\t\t\tappName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t}","options?: {\n\t\t\tdatabasePassword?: string;\n\t\t}","redisId: string","updates: Partial<DokployRedisUpdate>","options: DokployDomainCreate","domainId: string","updates: Partial<DokployDomainCreate>","domain: string","appName: string","serverId?: string","destinationId: string","options: DokployDestinationCreate","options: Omit<DokployDestinationCreate, 'name'>","updates: Partial<DokployDestinationCreate>","options: DokployBackupCreate","backupId: string","updates: Partial<DokployBackupUpdate>"],"sources":["../src/deploy/dokploy-api.ts"],"sourcesContent":["/**\n * Centralized Dokploy API client\n *\n * Handles authentication, error handling, and provides typed methods for all Dokploy API endpoints.\n */\n\nexport interface DokployApiOptions {\n\t/** Dokploy server URL (e.g., https://dokploy.example.com) */\n\tbaseUrl: string;\n\t/** API token for authentication */\n\ttoken: string;\n}\n\nexport interface DokployErrorResponse {\n\tmessage?: string;\n\tissues?: Array<{ message: string }>;\n}\n\nexport class DokployApiError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t\tpublic statusText: string,\n\t\tpublic issues?: Array<{ message: string }>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'DokployApiError';\n\t}\n}\n\n/**\n * Dokploy API client\n */\nexport class DokployApi {\n\tprivate baseUrl: string;\n\tprivate token: string;\n\n\tconstructor(options: DokployApiOptions) {\n\t\tthis.baseUrl = options.baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n\t\tthis.token = options.token;\n\t}\n\n\t/**\n\t * Make a GET request to the Dokploy API\n\t */\n\tasync get<T>(endpoint: string): Promise<T> {\n\t\treturn this.request<T>('GET', endpoint);\n\t}\n\n\t/**\n\t * Make a POST request to the Dokploy API\n\t */\n\tasync post<T>(endpoint: string, body?: Record<string, unknown>): Promise<T> {\n\t\treturn this.request<T>('POST', endpoint, body);\n\t}\n\n\t/**\n\t * Make a request to the Dokploy API\n\t */\n\tprivate async request<T>(\n\t\tmethod: 'GET' | 'POST' | 'PUT' | 'DELETE',\n\t\tendpoint: string,\n\t\tbody?: Record<string, unknown>,\n\t): Promise<T> {\n\t\tconst url = `${this.baseUrl}/api/${endpoint}`;\n\n\t\tconst response = await fetch(url, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t'x-api-key': this.token,\n\t\t\t},\n\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tlet errorMessage = `Dokploy API error: ${response.status} ${response.statusText}`;\n\t\t\tlet issues: Array<{ message: string }> | undefined;\n\n\t\t\ttry {\n\t\t\t\tconst errorBody = (await response.json()) as DokployErrorResponse;\n\t\t\t\tif (errorBody.message) {\n\t\t\t\t\terrorMessage = `Dokploy API error: ${errorBody.message}`;\n\t\t\t\t}\n\t\t\t\tif (errorBody.issues?.length) {\n\t\t\t\t\tissues = errorBody.issues;\n\t\t\t\t\terrorMessage += `\\n Issues: ${errorBody.issues.map((i) => i.message).join(', ')}`;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore JSON parse errors\n\t\t\t}\n\n\t\t\tthrow new DokployApiError(\n\t\t\t\terrorMessage,\n\t\t\t\tresponse.status,\n\t\t\t\tresponse.statusText,\n\t\t\t\tissues,\n\t\t\t);\n\t\t}\n\n\t\t// Handle empty responses (204 No Content or empty body)\n\t\tconst text = await response.text();\n\t\tif (!text || text.trim() === '') {\n\t\t\treturn undefined as T;\n\t\t}\n\t\treturn JSON.parse(text) as T;\n\t}\n\n\t/**\n\t * Validate the API token by making a test request\n\t */\n\tasync validateToken(): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.get('project.all');\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ============================================\n\t// Project endpoints\n\t// ============================================\n\n\t/**\n\t * List all projects\n\t */\n\tasync listProjects(): Promise<DokployProject[]> {\n\t\treturn this.get<DokployProject[]>('project.all');\n\t}\n\n\t/**\n\t * Get a single project by ID\n\t */\n\tasync getProject(projectId: string): Promise<DokployProjectDetails> {\n\t\treturn this.get<DokployProjectDetails>(\n\t\t\t`project.one?projectId=${projectId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new project\n\t */\n\tasync createProject(\n\t\tname: string,\n\t\tdescription?: string,\n\t): Promise<{ project: DokployProject; environment: DokployEnvironment }> {\n\t\treturn this.post<{\n\t\t\tproject: DokployProject;\n\t\t\tenvironment: DokployEnvironment;\n\t\t}>('project.create', {\n\t\t\tname,\n\t\t\tdescription: description ?? `Created by gkm CLI`,\n\t\t});\n\t}\n\n\t/**\n\t * Delete a project and all its resources\n\t */\n\tasync deleteProject(projectId: string): Promise<void> {\n\t\tawait this.post('project.remove', { projectId });\n\t}\n\n\t// ============================================\n\t// Environment endpoints\n\t// ============================================\n\n\t/**\n\t * Create an environment in a project\n\t */\n\tasync createEnvironment(\n\t\tprojectId: string,\n\t\tname: string,\n\t\tdescription?: string,\n\t): Promise<DokployEnvironment> {\n\t\treturn this.post<DokployEnvironment>('environment.create', {\n\t\t\tprojectId,\n\t\t\tname,\n\t\t\tdescription: description ?? `${name} environment`,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Application endpoints\n\t// ============================================\n\n\t/**\n\t * List all applications in a project\n\t */\n\tasync listApplications(projectId: string): Promise<DokployApplication[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployApplication[]>(\n\t\t\t\t`application.all?projectId=${projectId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find an application by name in a project\n\t */\n\tasync findApplicationByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployApplication | undefined> {\n\t\tconst applications = await this.listApplications(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn applications.find(\n\t\t\t(app) => app.name === name || app.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new application\n\t */\n\tasync createApplication(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t): Promise<DokployApplication> {\n\t\treturn this.post<DokployApplication>('application.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName: name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t});\n\t}\n\n\t/**\n\t * Find or create an application by name\n\t */\n\tasync findOrCreateApplication(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t): Promise<{ application: DokployApplication; created: boolean }> {\n\t\tconst existing = await this.findApplicationByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { application: existing, created: false };\n\t\t}\n\t\tconst application = await this.createApplication(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t);\n\t\treturn { application, created: true };\n\t}\n\n\t/**\n\t * Get an application by ID\n\t */\n\tasync getApplication(\n\t\tapplicationId: string,\n\t): Promise<DokployApplication | null> {\n\t\ttry {\n\t\t\treturn await this.get<DokployApplication>(\n\t\t\t\t`application.one?applicationId=${applicationId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Application not found\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Update an application\n\t */\n\tasync updateApplication(\n\t\tapplicationId: string,\n\t\tupdates: Partial<DokployApplicationUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('application.update', {\n\t\t\tapplicationId,\n\t\t\t...updates,\n\t\t});\n\t}\n\n\t/**\n\t * Save environment variables for an application\n\t */\n\tasync saveApplicationEnv(applicationId: string, env: string): Promise<void> {\n\t\tawait this.post('application.saveEnvironment', {\n\t\t\tapplicationId,\n\t\t\tenv,\n\t\t});\n\t}\n\n\t/**\n\t * Configure application to use Docker provider (pull from registry)\n\t *\n\t * For private registries, either:\n\t * - Use `registryId` if the registry is configured in Dokploy\n\t * - Or provide `username`, `password`, and `registryUrl` directly\n\t */\n\tasync saveDockerProvider(\n\t\tapplicationId: string,\n\t\tdockerImage: string,\n\t\toptions?: {\n\t\t\t/** Registry ID in Dokploy (for pre-configured registries) */\n\t\t\tregistryId?: string;\n\t\t\t/** Registry username (for direct auth) */\n\t\t\tusername?: string;\n\t\t\t/** Registry password (for direct auth) */\n\t\t\tpassword?: string;\n\t\t\t/** Registry URL (for direct auth, e.g., ghcr.io) */\n\t\t\tregistryUrl?: string;\n\t\t},\n\t): Promise<void> {\n\t\tawait this.post('application.saveDockerProvider', {\n\t\t\tapplicationId,\n\t\t\tdockerImage,\n\t\t\t...options,\n\t\t});\n\t}\n\n\t/**\n\t * Deploy an application\n\t */\n\tasync deployApplication(applicationId: string): Promise<void> {\n\t\tawait this.post('application.deploy', { applicationId });\n\t}\n\n\t/**\n\t * Delete an application\n\t */\n\tasync deleteApplication(applicationId: string): Promise<void> {\n\t\tawait this.post('application.remove', { applicationId });\n\t}\n\n\t// ============================================\n\t// Registry endpoints\n\t// ============================================\n\n\t/**\n\t * List all registries\n\t */\n\tasync listRegistries(): Promise<DokployRegistry[]> {\n\t\treturn this.get<DokployRegistry[]>('registry.all');\n\t}\n\n\t/**\n\t * Create a new registry\n\t */\n\tasync createRegistry(\n\t\tregistryName: string,\n\t\tregistryUrl: string,\n\t\tusername: string,\n\t\tpassword: string,\n\t\toptions?: {\n\t\t\timagePrefix?: string;\n\t\t},\n\t): Promise<DokployRegistry> {\n\t\treturn this.post<DokployRegistry>('registry.create', {\n\t\t\tregistryName,\n\t\t\tregistryUrl,\n\t\t\tusername,\n\t\t\tpassword,\n\t\t\timagePrefix: options?.imagePrefix,\n\t\t});\n\t}\n\n\t/**\n\t * Get a registry by ID\n\t */\n\tasync getRegistry(registryId: string): Promise<DokployRegistry> {\n\t\treturn this.get<DokployRegistry>(`registry.one?registryId=${registryId}`);\n\t}\n\n\t/**\n\t * Update a registry\n\t */\n\tasync updateRegistry(\n\t\tregistryId: string,\n\t\tupdates: Partial<{\n\t\t\tregistryName: string;\n\t\t\tregistryUrl: string;\n\t\t\tusername: string;\n\t\t\tpassword: string;\n\t\t\timagePrefix: string;\n\t\t}>,\n\t): Promise<void> {\n\t\tawait this.post('registry.update', { registryId, ...updates });\n\t}\n\n\t/**\n\t * Delete a registry\n\t */\n\tasync deleteRegistry(registryId: string): Promise<void> {\n\t\tawait this.post('registry.remove', { registryId });\n\t}\n\n\t// ============================================\n\t// Postgres endpoints\n\t// ============================================\n\n\t/**\n\t * List all Postgres databases in a project\n\t */\n\tasync listPostgres(projectId: string): Promise<DokployPostgres[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployPostgres[]>(\n\t\t\t\t`postgres.all?projectId=${projectId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find a Postgres database by name in a project\n\t */\n\tasync findPostgresByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployPostgres | undefined> {\n\t\tconst databases = await this.listPostgres(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn databases.find(\n\t\t\t(db) => db.name === name || db.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new Postgres database\n\t */\n\tasync createPostgres(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tappName?: string;\n\t\t\tdatabaseName?: string;\n\t\t\tdatabaseUser?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t},\n\t): Promise<DokployPostgres> {\n\t\treturn this.post<DokployPostgres>('postgres.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName:\n\t\t\t\toptions?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t\tdatabaseName: options?.databaseName,\n\t\t\tdatabaseUser: options?.databaseUser ?? 'postgres',\n\t\t\tdatabasePassword: options?.databasePassword,\n\t\t\tdockerImage: options?.dockerImage ?? 'postgres:18',\n\t\t\tdescription: options?.description ?? `Postgres database for ${name}`,\n\t\t});\n\t}\n\n\t/**\n\t * Find or create a Postgres database by name\n\t */\n\tasync findOrCreatePostgres(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tdatabaseName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t},\n\t): Promise<{ postgres: DokployPostgres; created: boolean }> {\n\t\tconst existing = await this.findPostgresByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { postgres: existing, created: false };\n\t\t}\n\t\tconst postgres = await this.createPostgres(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\toptions,\n\t\t);\n\t\treturn { postgres, created: true };\n\t}\n\n\t/**\n\t * Get a Postgres database by ID\n\t */\n\tasync getPostgres(postgresId: string): Promise<DokployPostgres> {\n\t\treturn this.get<DokployPostgres>(`postgres.one?postgresId=${postgresId}`);\n\t}\n\n\t/**\n\t * Deploy a Postgres database\n\t */\n\tasync deployPostgres(postgresId: string): Promise<void> {\n\t\tawait this.post('postgres.deploy', { postgresId });\n\t}\n\n\t/**\n\t * Save environment variables for Postgres\n\t */\n\tasync savePostgresEnv(postgresId: string, env: string): Promise<void> {\n\t\tawait this.post('postgres.saveEnvironment', { postgresId, env });\n\t}\n\n\t/**\n\t * Set external port for Postgres (for external access)\n\t */\n\tasync savePostgresExternalPort(\n\t\tpostgresId: string,\n\t\texternalPort: number | null,\n\t): Promise<void> {\n\t\tawait this.post('postgres.saveExternalPort', { postgresId, externalPort });\n\t}\n\n\t/**\n\t * Update Postgres configuration\n\t */\n\tasync updatePostgres(\n\t\tpostgresId: string,\n\t\tupdates: Partial<DokployPostgresUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('postgres.update', { postgresId, ...updates });\n\t}\n\n\t/**\n\t * Delete a Postgres database\n\t */\n\tasync deletePostgres(postgresId: string): Promise<void> {\n\t\tawait this.post('postgres.remove', { postgresId });\n\t}\n\n\t// ============================================\n\t// Redis endpoints\n\t// ============================================\n\n\t/**\n\t * List all Redis instances in a project\n\t */\n\tasync listRedis(projectId: string): Promise<DokployRedis[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployRedis[]>(`redis.all?projectId=${projectId}`);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find a Redis instance by name in a project\n\t */\n\tasync findRedisByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployRedis | undefined> {\n\t\tconst instances = await this.listRedis(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn instances.find(\n\t\t\t(redis) => redis.name === name || redis.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new Redis instance\n\t */\n\tasync createRedis(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tappName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t},\n\t): Promise<DokployRedis> {\n\t\treturn this.post<DokployRedis>('redis.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName:\n\t\t\t\toptions?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t\tdatabasePassword: options?.databasePassword,\n\t\t\tdockerImage: options?.dockerImage ?? 'redis:8',\n\t\t\tdescription: options?.description ?? `Redis instance for ${name}`,\n\t\t});\n\t}\n\n\t/**\n\t * Find or create a Redis instance by name\n\t */\n\tasync findOrCreateRedis(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tdatabasePassword?: string;\n\t\t},\n\t): Promise<{ redis: DokployRedis; created: boolean }> {\n\t\tconst existing = await this.findRedisByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { redis: existing, created: false };\n\t\t}\n\t\tconst redis = await this.createRedis(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\toptions,\n\t\t);\n\t\treturn { redis, created: true };\n\t}\n\n\t/**\n\t * Get a Redis instance by ID\n\t */\n\tasync getRedis(redisId: string): Promise<DokployRedis> {\n\t\treturn this.get<DokployRedis>(`redis.one?redisId=${redisId}`);\n\t}\n\n\t/**\n\t * Deploy a Redis instance\n\t */\n\tasync deployRedis(redisId: string): Promise<void> {\n\t\tawait this.post('redis.deploy', { redisId });\n\t}\n\n\t/**\n\t * Save environment variables for Redis\n\t */\n\tasync saveRedisEnv(redisId: string, env: string): Promise<void> {\n\t\tawait this.post('redis.saveEnvironment', { redisId, env });\n\t}\n\n\t/**\n\t * Set external port for Redis (for external access)\n\t */\n\tasync saveRedisExternalPort(\n\t\tredisId: string,\n\t\texternalPort: number | null,\n\t): Promise<void> {\n\t\tawait this.post('redis.saveExternalPort', { redisId, externalPort });\n\t}\n\n\t/**\n\t * Update Redis configuration\n\t */\n\tasync updateRedis(\n\t\tredisId: string,\n\t\tupdates: Partial<DokployRedisUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('redis.update', { redisId, ...updates });\n\t}\n\n\t/**\n\t * Delete a Redis instance\n\t */\n\tasync deleteRedis(redisId: string): Promise<void> {\n\t\tawait this.post('redis.remove', { redisId });\n\t}\n\n\t// ============================================\n\t// Domain endpoints\n\t// ============================================\n\n\t/**\n\t * Create a new domain for an application\n\t */\n\tasync createDomain(options: DokployDomainCreate): Promise<DokployDomain> {\n\t\treturn this.post<DokployDomain>(\n\t\t\t'domain.create',\n\t\t\toptions as unknown as Record<string, unknown>,\n\t\t);\n\t}\n\n\t/**\n\t * Update an existing domain\n\t */\n\tasync updateDomain(\n\t\tdomainId: string,\n\t\tupdates: Partial<DokployDomainCreate>,\n\t): Promise<void> {\n\t\tawait this.post('domain.update', { domainId, ...updates });\n\t}\n\n\t/**\n\t * Delete a domain\n\t */\n\tasync deleteDomain(domainId: string): Promise<void> {\n\t\tawait this.post('domain.delete', { domainId });\n\t}\n\n\t/**\n\t * Get a domain by ID\n\t */\n\tasync getDomain(domainId: string): Promise<DokployDomain> {\n\t\treturn this.get<DokployDomain>(`domain.one?domainId=${domainId}`);\n\t}\n\n\t/**\n\t * Get all domains for an application\n\t */\n\tasync getDomainsByApplicationId(\n\t\tapplicationId: string,\n\t): Promise<DokployDomain[]> {\n\t\treturn this.get<DokployDomain[]>(\n\t\t\t`domain.byApplicationId?applicationId=${applicationId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Validate a domain and trigger SSL certificate generation\n\t *\n\t * This should be called after DNS records are created and propagated.\n\t * It triggers Let's Encrypt certificate generation for HTTPS domains.\n\t *\n\t * @param domain - The domain hostname to validate (e.g., 'api.example.com')\n\t */\n\tasync validateDomain(\n\t\tdomain: string,\n\t): Promise<{ isValid: boolean; resolvedIp: string }> {\n\t\treturn this.post<{ isValid: boolean; resolvedIp: string }>(\n\t\t\t'domain.validateDomain',\n\t\t\t{ domain },\n\t\t);\n\t}\n\n\t/**\n\t * Auto-generate a domain name for an application\n\t */\n\tasync generateDomain(\n\t\tappName: string,\n\t\tserverId?: string,\n\t): Promise<{ domain: string }> {\n\t\treturn this.post<{ domain: string }>('domain.generateDomain', {\n\t\t\tappName,\n\t\t\tserverId,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Destination endpoints (backup storage)\n\t// ============================================\n\n\t/**\n\t * List all backup destinations\n\t */\n\tasync listDestinations(): Promise<DokployDestination[]> {\n\t\treturn this.get<DokployDestination[]>('destination.all');\n\t}\n\n\t/**\n\t * Get a destination by ID\n\t */\n\tasync getDestination(destinationId: string): Promise<DokployDestination> {\n\t\treturn this.get<DokployDestination>(\n\t\t\t`destination.one?destinationId=${destinationId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new S3 backup destination\n\t */\n\tasync createDestination(\n\t\toptions: DokployDestinationCreate,\n\t): Promise<DokployDestination> {\n\t\treturn this.post<DokployDestination>('destination.create', { ...options });\n\t}\n\n\t/**\n\t * Find a destination by name\n\t */\n\tasync findDestinationByName(\n\t\tname: string,\n\t): Promise<DokployDestination | undefined> {\n\t\tconst destinations = await this.listDestinations();\n\t\treturn destinations.find((d) => d.name === name);\n\t}\n\n\t/**\n\t * Find or create a destination by name\n\t */\n\tasync findOrCreateDestination(\n\t\tname: string,\n\t\toptions: Omit<DokployDestinationCreate, 'name'>,\n\t): Promise<{ destination: DokployDestination; created: boolean }> {\n\t\tconst existing = await this.findDestinationByName(name);\n\t\tif (existing) {\n\t\t\treturn { destination: existing, created: false };\n\t\t}\n\t\tconst destination = await this.createDestination({ name, ...options });\n\t\treturn { destination, created: true };\n\t}\n\n\t/**\n\t * Update a destination\n\t */\n\tasync updateDestination(\n\t\tdestinationId: string,\n\t\tupdates: Partial<DokployDestinationCreate>,\n\t): Promise<void> {\n\t\tawait this.post('destination.update', { destinationId, ...updates });\n\t}\n\n\t/**\n\t * Delete a destination\n\t */\n\tasync deleteDestination(destinationId: string): Promise<void> {\n\t\tawait this.post('destination.remove', { destinationId });\n\t}\n\n\t/**\n\t * Test connection to a destination\n\t */\n\tasync testDestinationConnection(\n\t\tdestinationId: string,\n\t): Promise<{ success: boolean }> {\n\t\treturn this.post<{ success: boolean }>('destination.testConnection', {\n\t\t\tdestinationId,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Backup endpoints (scheduled backups)\n\t// ============================================\n\n\t/**\n\t * Create a backup schedule for postgres\n\t */\n\tasync createPostgresBackup(\n\t\toptions: DokployBackupCreate,\n\t): Promise<DokployBackup> {\n\t\treturn this.post<DokployBackup>('backup.create', {\n\t\t\t...options,\n\t\t\tdatabaseType: 'postgres',\n\t\t});\n\t}\n\n\t/**\n\t * List backups for a postgres database\n\t */\n\tasync listPostgresBackups(postgresId: string): Promise<DokployBackup[]> {\n\t\treturn this.get<DokployBackup[]>(\n\t\t\t`backup.all?postgresId=${postgresId}&databaseType=postgres`,\n\t\t);\n\t}\n\n\t/**\n\t * Get a backup by ID\n\t */\n\tasync getBackup(backupId: string): Promise<DokployBackup> {\n\t\treturn this.get<DokployBackup>(`backup.one?backupId=${backupId}`);\n\t}\n\n\t/**\n\t * Update a backup schedule\n\t */\n\tasync updateBackup(\n\t\tbackupId: string,\n\t\tupdates: Partial<DokployBackupUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('backup.update', { backupId, ...updates });\n\t}\n\n\t/**\n\t * Delete a backup schedule\n\t */\n\tasync deleteBackup(backupId: string): Promise<void> {\n\t\tawait this.post('backup.remove', { backupId });\n\t}\n\n\t/**\n\t * Trigger a manual backup run\n\t */\n\tasync runBackupManually(backupId: string): Promise<void> {\n\t\tawait this.post('backup.manualBackup', { backupId });\n\t}\n}\n\n// ============================================\n// Type definitions for Dokploy API responses\n// ============================================\n\nexport interface DokployProject {\n\tprojectId: string;\n\tname: string;\n\tdescription: string | null;\n\tcreatedAt?: string;\n\tadminId?: string;\n}\n\nexport interface DokployEnvironment {\n\tenvironmentId: string;\n\tname: string;\n\tdescription: string | null;\n}\n\nexport interface DokployProjectDetails extends DokployProject {\n\tenvironments: DokployEnvironment[];\n}\n\nexport interface DokployApplication {\n\tapplicationId: string;\n\tname: string;\n\tappName: string;\n\tprojectId: string;\n\tenvironmentId?: string;\n}\n\nexport interface DokployApplicationUpdate {\n\tregistryId: string;\n\tdockerImage: string;\n\tsourceType: 'docker';\n}\n\nexport interface DokployRegistry {\n\tregistryId: string;\n\tregistryName: string;\n\tregistryUrl: string;\n\tusername: string;\n\timagePrefix: string | null;\n}\n\nexport interface DokployPostgres {\n\tpostgresId: string;\n\tname: string;\n\tappName: string;\n\tdatabaseName: string;\n\tdatabaseUser: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string | null;\n\tprojectId: string;\n\tenvironmentId: string;\n\tapplicationStatus: 'idle' | 'running' | 'done' | 'error';\n\texternalPort: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployPostgresUpdate {\n\tname: string;\n\tappName: string;\n\tdatabaseName: string;\n\tdatabaseUser: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string;\n}\n\nexport interface DokployRedis {\n\tredisId: string;\n\tname: string;\n\tappName: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string | null;\n\tprojectId: string;\n\tenvironmentId: string;\n\tapplicationStatus: 'idle' | 'running' | 'done' | 'error';\n\texternalPort: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployRedisUpdate {\n\tname: string;\n\tappName: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string;\n}\n\nexport type DokployCertificateType = 'letsencrypt' | 'none' | 'custom';\nexport type DokployDomainType = 'application' | 'compose' | 'preview';\n\nexport interface DokployDomainCreate {\n\t/** Domain hostname (e.g., 'api.example.com') */\n\thost: string;\n\t/** URL path (optional, e.g., '/api') */\n\tpath?: string | null;\n\t/** Container port to route to (1-65535) */\n\tport?: number | null;\n\t/** Enable HTTPS */\n\thttps?: boolean;\n\t/** Associated application ID */\n\tapplicationId?: string | null;\n\t/** Certificate type for HTTPS */\n\tcertificateType?: DokployCertificateType;\n\t/** Custom certificate resolver name */\n\tcustomCertResolver?: string | null;\n\t/** Docker Compose service ID */\n\tcomposeId?: string | null;\n\t/** Service name for compose */\n\tserviceName?: string | null;\n\t/** Domain type */\n\tdomainType?: DokployDomainType | null;\n\t/** Preview deployment ID */\n\tpreviewDeploymentId?: string | null;\n\t/** Internal routing path */\n\tinternalPath?: string | null;\n\t/** Strip path from forwarded requests */\n\tstripPath?: boolean;\n}\n\nexport interface DokployDomain extends DokployDomainCreate {\n\tdomainId: string;\n\tcreatedAt?: string;\n}\n\n// ============================================\n// Destination types (backup storage)\n// ============================================\n\nexport interface DokployDestination {\n\tdestinationId: string;\n\tname: string;\n\taccessKey: string;\n\tbucket: string;\n\tregion: string;\n\tendpoint: string | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployDestinationCreate {\n\t/** User-friendly name for the destination */\n\tname: string;\n\t/** S3 access key ID */\n\taccessKey: string;\n\t/** S3 secret access key */\n\tsecretAccessKey: string;\n\t/** S3 bucket name */\n\tbucket: string;\n\t/** AWS region (e.g., 'us-east-1') */\n\tregion: string;\n\t/** Optional endpoint for S3-compatible services */\n\tendpoint?: string;\n}\n\n// ============================================\n// Backup types (scheduled backups)\n// ============================================\n\nexport type DokployDatabaseType = 'postgres' | 'mysql' | 'mariadb' | 'mongo';\n\nexport interface DokployBackup {\n\tbackupId: string;\n\tschedule: string;\n\tprefix: string;\n\tenabled: boolean;\n\tdestinationId: string;\n\tpostgresId?: string;\n\tdatabaseType: DokployDatabaseType;\n\tkeepLatestCount: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployBackupCreate {\n\t/** Cron schedule (e.g., '0 2 * * *' for 2 AM daily) */\n\tschedule: string;\n\t/** Backup file prefix (e.g., 'production/postgres') */\n\tprefix: string;\n\t/** Destination ID for backup storage */\n\tdestinationId: string;\n\t/** Database name to backup */\n\tdatabase: string;\n\t/** Postgres instance ID */\n\tpostgresId: string;\n\t/** Enable/disable backup (default: true) */\n\tenabled?: boolean;\n\t/** Number of backups to keep (default: keep all) */\n\tkeepLatestCount?: number;\n}\n\nexport interface DokployBackupUpdate {\n\tschedule?: string;\n\tprefix?: string;\n\tenabled?: boolean;\n\tkeepLatestCount?: number;\n}\n\n/**\n * Create a Dokploy API client from stored credentials or environment\n */\nexport async function createDokployApi(\n\tendpoint?: string,\n): Promise<DokployApi | null> {\n\tconst { getDokployCredentials } = await import('../auth/credentials');\n\n\t// Try environment variable first\n\tconst envToken = process.env.DOKPLOY_API_TOKEN;\n\tconst envEndpoint = endpoint || process.env.DOKPLOY_ENDPOINT;\n\n\tif (envToken && envEndpoint) {\n\t\treturn new DokployApi({ baseUrl: envEndpoint, token: envToken });\n\t}\n\n\t// Fall back to stored credentials\n\tconst creds = await getDokployCredentials();\n\tif (creds) {\n\t\treturn new DokployApi({\n\t\t\tbaseUrl: endpoint || creds.endpoint,\n\t\t\ttoken: creds.token,\n\t\t});\n\t}\n\n\treturn null;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dokploy-api-sfZFlWuE.mjs","names":["message: string","status: number","statusText: string","issues?: Array<{ message: string }>","options: DokployApiOptions","endpoint: string","body?: Record<string, unknown>","method: 'GET' | 'POST' | 'PUT' | 'DELETE'","issues: Array<{ message: string }> | undefined","projectId: string","name: string","description?: string","environmentId: string","applicationId: string","updates: Partial<DokployApplicationUpdate>","env: string","dockerImage: string","options?: {\n\t\t\t/** Registry ID in Dokploy (for pre-configured registries) */\n\t\t\tregistryId?: string;\n\t\t\t/** Registry username (for direct auth) */\n\t\t\tusername?: string;\n\t\t\t/** Registry password (for direct auth) */\n\t\t\tpassword?: string;\n\t\t\t/** Registry URL (for direct auth, e.g., ghcr.io) */\n\t\t\tregistryUrl?: string;\n\t\t}","registryName: string","registryUrl: string","username: string","password: string","options?: {\n\t\t\timagePrefix?: string;\n\t\t}","registryId: string","updates: Partial<{\n\t\t\tregistryName: string;\n\t\t\tregistryUrl: string;\n\t\t\tusername: string;\n\t\t\tpassword: string;\n\t\t\timagePrefix: string;\n\t\t}>","options?: {\n\t\t\tappName?: string;\n\t\t\tdatabaseName?: string;\n\t\t\tdatabaseUser?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t}","options?: {\n\t\t\tdatabaseName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t}","postgresId: string","externalPort: number | null","updates: Partial<DokployPostgresUpdate>","options?: {\n\t\t\tappName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t}","options?: {\n\t\t\tdatabasePassword?: string;\n\t\t}","redisId: string","updates: Partial<DokployRedisUpdate>","options: DokployDomainCreate","domainId: string","updates: Partial<DokployDomainCreate>","domain: string","appName: string","serverId?: string","destinationId: string","options: DokployDestinationCreate","options: Omit<DokployDestinationCreate, 'name'>","updates: Partial<DokployDestinationCreate>","options: DokployBackupCreate","backupId: string","updates: Partial<DokployBackupUpdate>"],"sources":["../src/deploy/dokploy-api.ts"],"sourcesContent":["/**\n * Centralized Dokploy API client\n *\n * Handles authentication, error handling, and provides typed methods for all Dokploy API endpoints.\n */\n\nexport interface DokployApiOptions {\n\t/** Dokploy server URL (e.g., https://dokploy.example.com) */\n\tbaseUrl: string;\n\t/** API token for authentication */\n\ttoken: string;\n}\n\nexport interface DokployErrorResponse {\n\tmessage?: string;\n\tissues?: Array<{ message: string }>;\n}\n\nexport class DokployApiError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t\tpublic statusText: string,\n\t\tpublic issues?: Array<{ message: string }>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'DokployApiError';\n\t}\n}\n\n/**\n * Dokploy API client\n */\nexport class DokployApi {\n\tprivate baseUrl: string;\n\tprivate token: string;\n\n\tconstructor(options: DokployApiOptions) {\n\t\tthis.baseUrl = options.baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n\t\tthis.token = options.token;\n\t}\n\n\t/**\n\t * Make a GET request to the Dokploy API\n\t */\n\tasync get<T>(endpoint: string): Promise<T> {\n\t\treturn this.request<T>('GET', endpoint);\n\t}\n\n\t/**\n\t * Make a POST request to the Dokploy API\n\t */\n\tasync post<T>(endpoint: string, body?: Record<string, unknown>): Promise<T> {\n\t\treturn this.request<T>('POST', endpoint, body);\n\t}\n\n\t/**\n\t * Make a request to the Dokploy API\n\t */\n\tprivate async request<T>(\n\t\tmethod: 'GET' | 'POST' | 'PUT' | 'DELETE',\n\t\tendpoint: string,\n\t\tbody?: Record<string, unknown>,\n\t): Promise<T> {\n\t\tconst url = `${this.baseUrl}/api/${endpoint}`;\n\n\t\tconst response = await fetch(url, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t'x-api-key': this.token,\n\t\t\t},\n\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tlet errorMessage = `Dokploy API error: ${response.status} ${response.statusText}`;\n\t\t\tlet issues: Array<{ message: string }> | undefined;\n\n\t\t\ttry {\n\t\t\t\tconst errorBody = (await response.json()) as DokployErrorResponse;\n\t\t\t\tif (errorBody.message) {\n\t\t\t\t\terrorMessage = `Dokploy API error: ${errorBody.message}`;\n\t\t\t\t}\n\t\t\t\tif (errorBody.issues?.length) {\n\t\t\t\t\tissues = errorBody.issues;\n\t\t\t\t\terrorMessage += `\\n Issues: ${errorBody.issues.map((i) => i.message).join(', ')}`;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore JSON parse errors\n\t\t\t}\n\n\t\t\tthrow new DokployApiError(\n\t\t\t\terrorMessage,\n\t\t\t\tresponse.status,\n\t\t\t\tresponse.statusText,\n\t\t\t\tissues,\n\t\t\t);\n\t\t}\n\n\t\t// Handle empty responses (204 No Content or empty body)\n\t\tconst text = await response.text();\n\t\tif (!text || text.trim() === '') {\n\t\t\treturn undefined as T;\n\t\t}\n\t\treturn JSON.parse(text) as T;\n\t}\n\n\t/**\n\t * Validate the API token by making a test request\n\t */\n\tasync validateToken(): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.get('project.all');\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ============================================\n\t// Project endpoints\n\t// ============================================\n\n\t/**\n\t * List all projects\n\t */\n\tasync listProjects(): Promise<DokployProject[]> {\n\t\treturn this.get<DokployProject[]>('project.all');\n\t}\n\n\t/**\n\t * Get a single project by ID\n\t */\n\tasync getProject(projectId: string): Promise<DokployProjectDetails> {\n\t\treturn this.get<DokployProjectDetails>(\n\t\t\t`project.one?projectId=${projectId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new project\n\t */\n\tasync createProject(\n\t\tname: string,\n\t\tdescription?: string,\n\t): Promise<{ project: DokployProject; environment: DokployEnvironment }> {\n\t\treturn this.post<{\n\t\t\tproject: DokployProject;\n\t\t\tenvironment: DokployEnvironment;\n\t\t}>('project.create', {\n\t\t\tname,\n\t\t\tdescription: description ?? `Created by gkm CLI`,\n\t\t});\n\t}\n\n\t/**\n\t * Delete a project and all its resources\n\t */\n\tasync deleteProject(projectId: string): Promise<void> {\n\t\tawait this.post('project.remove', { projectId });\n\t}\n\n\t// ============================================\n\t// Environment endpoints\n\t// ============================================\n\n\t/**\n\t * Create an environment in a project\n\t */\n\tasync createEnvironment(\n\t\tprojectId: string,\n\t\tname: string,\n\t\tdescription?: string,\n\t): Promise<DokployEnvironment> {\n\t\treturn this.post<DokployEnvironment>('environment.create', {\n\t\t\tprojectId,\n\t\t\tname,\n\t\t\tdescription: description ?? `${name} environment`,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Application endpoints\n\t// ============================================\n\n\t/**\n\t * List all applications in a project\n\t */\n\tasync listApplications(projectId: string): Promise<DokployApplication[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployApplication[]>(\n\t\t\t\t`application.all?projectId=${projectId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find an application by name in a project\n\t */\n\tasync findApplicationByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployApplication | undefined> {\n\t\tconst applications = await this.listApplications(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn applications.find(\n\t\t\t(app) => app.name === name || app.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new application\n\t */\n\tasync createApplication(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t): Promise<DokployApplication> {\n\t\treturn this.post<DokployApplication>('application.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName: name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t});\n\t}\n\n\t/**\n\t * Find or create an application by name\n\t */\n\tasync findOrCreateApplication(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t): Promise<{ application: DokployApplication; created: boolean }> {\n\t\tconst existing = await this.findApplicationByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { application: existing, created: false };\n\t\t}\n\t\tconst application = await this.createApplication(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t);\n\t\treturn { application, created: true };\n\t}\n\n\t/**\n\t * Get an application by ID\n\t */\n\tasync getApplication(\n\t\tapplicationId: string,\n\t): Promise<DokployApplication | null> {\n\t\ttry {\n\t\t\treturn await this.get<DokployApplication>(\n\t\t\t\t`application.one?applicationId=${applicationId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Application not found\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Update an application\n\t */\n\tasync updateApplication(\n\t\tapplicationId: string,\n\t\tupdates: Partial<DokployApplicationUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('application.update', {\n\t\t\tapplicationId,\n\t\t\t...updates,\n\t\t});\n\t}\n\n\t/**\n\t * Save environment variables for an application\n\t */\n\tasync saveApplicationEnv(applicationId: string, env: string): Promise<void> {\n\t\tawait this.post('application.saveEnvironment', {\n\t\t\tapplicationId,\n\t\t\tenv,\n\t\t});\n\t}\n\n\t/**\n\t * Configure application to use Docker provider (pull from registry)\n\t *\n\t * For private registries, either:\n\t * - Use `registryId` if the registry is configured in Dokploy\n\t * - Or provide `username`, `password`, and `registryUrl` directly\n\t */\n\tasync saveDockerProvider(\n\t\tapplicationId: string,\n\t\tdockerImage: string,\n\t\toptions?: {\n\t\t\t/** Registry ID in Dokploy (for pre-configured registries) */\n\t\t\tregistryId?: string;\n\t\t\t/** Registry username (for direct auth) */\n\t\t\tusername?: string;\n\t\t\t/** Registry password (for direct auth) */\n\t\t\tpassword?: string;\n\t\t\t/** Registry URL (for direct auth, e.g., ghcr.io) */\n\t\t\tregistryUrl?: string;\n\t\t},\n\t): Promise<void> {\n\t\tawait this.post('application.saveDockerProvider', {\n\t\t\tapplicationId,\n\t\t\tdockerImage,\n\t\t\t...options,\n\t\t});\n\t}\n\n\t/**\n\t * Deploy an application\n\t */\n\tasync deployApplication(applicationId: string): Promise<void> {\n\t\tawait this.post('application.deploy', { applicationId });\n\t}\n\n\t/**\n\t * Delete an application\n\t */\n\tasync deleteApplication(applicationId: string): Promise<void> {\n\t\tawait this.post('application.remove', { applicationId });\n\t}\n\n\t// ============================================\n\t// Registry endpoints\n\t// ============================================\n\n\t/**\n\t * List all registries\n\t */\n\tasync listRegistries(): Promise<DokployRegistry[]> {\n\t\treturn this.get<DokployRegistry[]>('registry.all');\n\t}\n\n\t/**\n\t * Create a new registry\n\t */\n\tasync createRegistry(\n\t\tregistryName: string,\n\t\tregistryUrl: string,\n\t\tusername: string,\n\t\tpassword: string,\n\t\toptions?: {\n\t\t\timagePrefix?: string;\n\t\t},\n\t): Promise<DokployRegistry> {\n\t\treturn this.post<DokployRegistry>('registry.create', {\n\t\t\tregistryName,\n\t\t\tregistryUrl,\n\t\t\tusername,\n\t\t\tpassword,\n\t\t\timagePrefix: options?.imagePrefix,\n\t\t});\n\t}\n\n\t/**\n\t * Get a registry by ID\n\t */\n\tasync getRegistry(registryId: string): Promise<DokployRegistry> {\n\t\treturn this.get<DokployRegistry>(`registry.one?registryId=${registryId}`);\n\t}\n\n\t/**\n\t * Update a registry\n\t */\n\tasync updateRegistry(\n\t\tregistryId: string,\n\t\tupdates: Partial<{\n\t\t\tregistryName: string;\n\t\t\tregistryUrl: string;\n\t\t\tusername: string;\n\t\t\tpassword: string;\n\t\t\timagePrefix: string;\n\t\t}>,\n\t): Promise<void> {\n\t\tawait this.post('registry.update', { registryId, ...updates });\n\t}\n\n\t/**\n\t * Delete a registry\n\t */\n\tasync deleteRegistry(registryId: string): Promise<void> {\n\t\tawait this.post('registry.remove', { registryId });\n\t}\n\n\t// ============================================\n\t// Postgres endpoints\n\t// ============================================\n\n\t/**\n\t * List all Postgres databases in a project\n\t */\n\tasync listPostgres(projectId: string): Promise<DokployPostgres[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployPostgres[]>(\n\t\t\t\t`postgres.all?projectId=${projectId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find a Postgres database by name in a project\n\t */\n\tasync findPostgresByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployPostgres | undefined> {\n\t\tconst databases = await this.listPostgres(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn databases.find(\n\t\t\t(db) => db.name === name || db.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new Postgres database\n\t */\n\tasync createPostgres(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tappName?: string;\n\t\t\tdatabaseName?: string;\n\t\t\tdatabaseUser?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t},\n\t): Promise<DokployPostgres> {\n\t\treturn this.post<DokployPostgres>('postgres.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName:\n\t\t\t\toptions?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t\tdatabaseName: options?.databaseName,\n\t\t\tdatabaseUser: options?.databaseUser ?? 'postgres',\n\t\t\tdatabasePassword: options?.databasePassword,\n\t\t\tdockerImage: options?.dockerImage ?? 'postgres:18',\n\t\t\tdescription: options?.description ?? `Postgres database for ${name}`,\n\t\t});\n\t}\n\n\t/**\n\t * Find or create a Postgres database by name\n\t */\n\tasync findOrCreatePostgres(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tdatabaseName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t},\n\t): Promise<{ postgres: DokployPostgres; created: boolean }> {\n\t\tconst existing = await this.findPostgresByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { postgres: existing, created: false };\n\t\t}\n\t\tconst postgres = await this.createPostgres(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\toptions,\n\t\t);\n\t\treturn { postgres, created: true };\n\t}\n\n\t/**\n\t * Get a Postgres database by ID\n\t */\n\tasync getPostgres(postgresId: string): Promise<DokployPostgres> {\n\t\treturn this.get<DokployPostgres>(`postgres.one?postgresId=${postgresId}`);\n\t}\n\n\t/**\n\t * Deploy a Postgres database\n\t */\n\tasync deployPostgres(postgresId: string): Promise<void> {\n\t\tawait this.post('postgres.deploy', { postgresId });\n\t}\n\n\t/**\n\t * Save environment variables for Postgres\n\t */\n\tasync savePostgresEnv(postgresId: string, env: string): Promise<void> {\n\t\tawait this.post('postgres.saveEnvironment', { postgresId, env });\n\t}\n\n\t/**\n\t * Set external port for Postgres (for external access)\n\t */\n\tasync savePostgresExternalPort(\n\t\tpostgresId: string,\n\t\texternalPort: number | null,\n\t): Promise<void> {\n\t\tawait this.post('postgres.saveExternalPort', { postgresId, externalPort });\n\t}\n\n\t/**\n\t * Update Postgres configuration\n\t */\n\tasync updatePostgres(\n\t\tpostgresId: string,\n\t\tupdates: Partial<DokployPostgresUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('postgres.update', { postgresId, ...updates });\n\t}\n\n\t/**\n\t * Delete a Postgres database\n\t */\n\tasync deletePostgres(postgresId: string): Promise<void> {\n\t\tawait this.post('postgres.remove', { postgresId });\n\t}\n\n\t// ============================================\n\t// Redis endpoints\n\t// ============================================\n\n\t/**\n\t * List all Redis instances in a project\n\t */\n\tasync listRedis(projectId: string): Promise<DokployRedis[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployRedis[]>(`redis.all?projectId=${projectId}`);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find a Redis instance by name in a project\n\t */\n\tasync findRedisByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployRedis | undefined> {\n\t\tconst instances = await this.listRedis(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn instances.find(\n\t\t\t(redis) => redis.name === name || redis.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new Redis instance\n\t */\n\tasync createRedis(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tappName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t},\n\t): Promise<DokployRedis> {\n\t\treturn this.post<DokployRedis>('redis.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName:\n\t\t\t\toptions?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t\tdatabasePassword: options?.databasePassword,\n\t\t\tdockerImage: options?.dockerImage ?? 'redis:8',\n\t\t\tdescription: options?.description ?? `Redis instance for ${name}`,\n\t\t});\n\t}\n\n\t/**\n\t * Find or create a Redis instance by name\n\t */\n\tasync findOrCreateRedis(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tdatabasePassword?: string;\n\t\t},\n\t): Promise<{ redis: DokployRedis; created: boolean }> {\n\t\tconst existing = await this.findRedisByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { redis: existing, created: false };\n\t\t}\n\t\tconst redis = await this.createRedis(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\toptions,\n\t\t);\n\t\treturn { redis, created: true };\n\t}\n\n\t/**\n\t * Get a Redis instance by ID\n\t */\n\tasync getRedis(redisId: string): Promise<DokployRedis> {\n\t\treturn this.get<DokployRedis>(`redis.one?redisId=${redisId}`);\n\t}\n\n\t/**\n\t * Deploy a Redis instance\n\t */\n\tasync deployRedis(redisId: string): Promise<void> {\n\t\tawait this.post('redis.deploy', { redisId });\n\t}\n\n\t/**\n\t * Save environment variables for Redis\n\t */\n\tasync saveRedisEnv(redisId: string, env: string): Promise<void> {\n\t\tawait this.post('redis.saveEnvironment', { redisId, env });\n\t}\n\n\t/**\n\t * Set external port for Redis (for external access)\n\t */\n\tasync saveRedisExternalPort(\n\t\tredisId: string,\n\t\texternalPort: number | null,\n\t): Promise<void> {\n\t\tawait this.post('redis.saveExternalPort', { redisId, externalPort });\n\t}\n\n\t/**\n\t * Update Redis configuration\n\t */\n\tasync updateRedis(\n\t\tredisId: string,\n\t\tupdates: Partial<DokployRedisUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('redis.update', { redisId, ...updates });\n\t}\n\n\t/**\n\t * Delete a Redis instance\n\t */\n\tasync deleteRedis(redisId: string): Promise<void> {\n\t\tawait this.post('redis.remove', { redisId });\n\t}\n\n\t// ============================================\n\t// Domain endpoints\n\t// ============================================\n\n\t/**\n\t * Create a new domain for an application\n\t */\n\tasync createDomain(options: DokployDomainCreate): Promise<DokployDomain> {\n\t\treturn this.post<DokployDomain>(\n\t\t\t'domain.create',\n\t\t\toptions as unknown as Record<string, unknown>,\n\t\t);\n\t}\n\n\t/**\n\t * Update an existing domain\n\t */\n\tasync updateDomain(\n\t\tdomainId: string,\n\t\tupdates: Partial<DokployDomainCreate>,\n\t): Promise<void> {\n\t\tawait this.post('domain.update', { domainId, ...updates });\n\t}\n\n\t/**\n\t * Delete a domain\n\t */\n\tasync deleteDomain(domainId: string): Promise<void> {\n\t\tawait this.post('domain.delete', { domainId });\n\t}\n\n\t/**\n\t * Get a domain by ID\n\t */\n\tasync getDomain(domainId: string): Promise<DokployDomain> {\n\t\treturn this.get<DokployDomain>(`domain.one?domainId=${domainId}`);\n\t}\n\n\t/**\n\t * Get all domains for an application\n\t */\n\tasync getDomainsByApplicationId(\n\t\tapplicationId: string,\n\t): Promise<DokployDomain[]> {\n\t\treturn this.get<DokployDomain[]>(\n\t\t\t`domain.byApplicationId?applicationId=${applicationId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Validate a domain and trigger SSL certificate generation\n\t *\n\t * This should be called after DNS records are created and propagated.\n\t * It triggers Let's Encrypt certificate generation for HTTPS domains.\n\t *\n\t * @param domain - The domain hostname to validate (e.g., 'api.example.com')\n\t */\n\tasync validateDomain(\n\t\tdomain: string,\n\t): Promise<{ isValid: boolean; resolvedIp: string }> {\n\t\treturn this.post<{ isValid: boolean; resolvedIp: string }>(\n\t\t\t'domain.validateDomain',\n\t\t\t{ domain },\n\t\t);\n\t}\n\n\t/**\n\t * Auto-generate a domain name for an application\n\t */\n\tasync generateDomain(\n\t\tappName: string,\n\t\tserverId?: string,\n\t): Promise<{ domain: string }> {\n\t\treturn this.post<{ domain: string }>('domain.generateDomain', {\n\t\t\tappName,\n\t\t\tserverId,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Destination endpoints (backup storage)\n\t// ============================================\n\n\t/**\n\t * List all backup destinations\n\t */\n\tasync listDestinations(): Promise<DokployDestination[]> {\n\t\treturn this.get<DokployDestination[]>('destination.all');\n\t}\n\n\t/**\n\t * Get a destination by ID\n\t */\n\tasync getDestination(destinationId: string): Promise<DokployDestination> {\n\t\treturn this.get<DokployDestination>(\n\t\t\t`destination.one?destinationId=${destinationId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new S3 backup destination\n\t */\n\tasync createDestination(\n\t\toptions: DokployDestinationCreate,\n\t): Promise<DokployDestination> {\n\t\treturn this.post<DokployDestination>('destination.create', { ...options });\n\t}\n\n\t/**\n\t * Find a destination by name\n\t */\n\tasync findDestinationByName(\n\t\tname: string,\n\t): Promise<DokployDestination | undefined> {\n\t\tconst destinations = await this.listDestinations();\n\t\treturn destinations.find((d) => d.name === name);\n\t}\n\n\t/**\n\t * Find or create a destination by name\n\t */\n\tasync findOrCreateDestination(\n\t\tname: string,\n\t\toptions: Omit<DokployDestinationCreate, 'name'>,\n\t): Promise<{ destination: DokployDestination; created: boolean }> {\n\t\tconst existing = await this.findDestinationByName(name);\n\t\tif (existing) {\n\t\t\treturn { destination: existing, created: false };\n\t\t}\n\t\tconst destination = await this.createDestination({ name, ...options });\n\t\treturn { destination, created: true };\n\t}\n\n\t/**\n\t * Update a destination\n\t */\n\tasync updateDestination(\n\t\tdestinationId: string,\n\t\tupdates: Partial<DokployDestinationCreate>,\n\t): Promise<void> {\n\t\tawait this.post('destination.update', { destinationId, ...updates });\n\t}\n\n\t/**\n\t * Delete a destination\n\t */\n\tasync deleteDestination(destinationId: string): Promise<void> {\n\t\tawait this.post('destination.remove', { destinationId });\n\t}\n\n\t/**\n\t * Test connection to a destination\n\t */\n\tasync testDestinationConnection(\n\t\tdestinationId: string,\n\t): Promise<{ success: boolean }> {\n\t\treturn this.post<{ success: boolean }>('destination.testConnection', {\n\t\t\tdestinationId,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Backup endpoints (scheduled backups)\n\t// ============================================\n\n\t/**\n\t * Create a backup schedule for postgres\n\t */\n\tasync createPostgresBackup(\n\t\toptions: DokployBackupCreate,\n\t): Promise<DokployBackup> {\n\t\treturn this.post<DokployBackup>('backup.create', {\n\t\t\t...options,\n\t\t\tdatabaseType: 'postgres',\n\t\t});\n\t}\n\n\t/**\n\t * List backups for a postgres database\n\t */\n\tasync listPostgresBackups(postgresId: string): Promise<DokployBackup[]> {\n\t\treturn this.get<DokployBackup[]>(\n\t\t\t`backup.all?postgresId=${postgresId}&databaseType=postgres`,\n\t\t);\n\t}\n\n\t/**\n\t * Get a backup by ID\n\t */\n\tasync getBackup(backupId: string): Promise<DokployBackup> {\n\t\treturn this.get<DokployBackup>(`backup.one?backupId=${backupId}`);\n\t}\n\n\t/**\n\t * Update a backup schedule\n\t */\n\tasync updateBackup(\n\t\tbackupId: string,\n\t\tupdates: Partial<DokployBackupUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('backup.update', { backupId, ...updates });\n\t}\n\n\t/**\n\t * Delete a backup schedule\n\t */\n\tasync deleteBackup(backupId: string): Promise<void> {\n\t\tawait this.post('backup.remove', { backupId });\n\t}\n\n\t/**\n\t * Trigger a manual backup run\n\t */\n\tasync runBackupManually(backupId: string): Promise<void> {\n\t\tawait this.post('backup.manualBackup', { backupId });\n\t}\n}\n\n// ============================================\n// Type definitions for Dokploy API responses\n// ============================================\n\nexport interface DokployProject {\n\tprojectId: string;\n\tname: string;\n\tdescription: string | null;\n\tcreatedAt?: string;\n\tadminId?: string;\n}\n\nexport interface DokployEnvironment {\n\tenvironmentId: string;\n\tname: string;\n\tdescription: string | null;\n}\n\nexport interface DokployProjectDetails extends DokployProject {\n\tenvironments: DokployEnvironment[];\n}\n\nexport interface DokployApplication {\n\tapplicationId: string;\n\tname: string;\n\tappName: string;\n\tprojectId: string;\n\tenvironmentId?: string;\n}\n\nexport interface DokployApplicationUpdate {\n\tregistryId: string;\n\tdockerImage: string;\n\tsourceType: 'docker';\n}\n\nexport interface DokployRegistry {\n\tregistryId: string;\n\tregistryName: string;\n\tregistryUrl: string;\n\tusername: string;\n\timagePrefix: string | null;\n}\n\nexport interface DokployPostgres {\n\tpostgresId: string;\n\tname: string;\n\tappName: string;\n\tdatabaseName: string;\n\tdatabaseUser: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string | null;\n\tprojectId: string;\n\tenvironmentId: string;\n\tapplicationStatus: 'idle' | 'running' | 'done' | 'error';\n\texternalPort: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployPostgresUpdate {\n\tname: string;\n\tappName: string;\n\tdatabaseName: string;\n\tdatabaseUser: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string;\n}\n\nexport interface DokployRedis {\n\tredisId: string;\n\tname: string;\n\tappName: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string | null;\n\tprojectId: string;\n\tenvironmentId: string;\n\tapplicationStatus: 'idle' | 'running' | 'done' | 'error';\n\texternalPort: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployRedisUpdate {\n\tname: string;\n\tappName: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string;\n}\n\nexport type DokployCertificateType = 'letsencrypt' | 'none' | 'custom';\nexport type DokployDomainType = 'application' | 'compose' | 'preview';\n\nexport interface DokployDomainCreate {\n\t/** Domain hostname (e.g., 'api.example.com') */\n\thost: string;\n\t/** URL path (optional, e.g., '/api') */\n\tpath?: string | null;\n\t/** Container port to route to (1-65535) */\n\tport?: number | null;\n\t/** Enable HTTPS */\n\thttps?: boolean;\n\t/** Associated application ID */\n\tapplicationId?: string | null;\n\t/** Certificate type for HTTPS */\n\tcertificateType?: DokployCertificateType;\n\t/** Custom certificate resolver name */\n\tcustomCertResolver?: string | null;\n\t/** Docker Compose service ID */\n\tcomposeId?: string | null;\n\t/** Service name for compose */\n\tserviceName?: string | null;\n\t/** Domain type */\n\tdomainType?: DokployDomainType | null;\n\t/** Preview deployment ID */\n\tpreviewDeploymentId?: string | null;\n\t/** Internal routing path */\n\tinternalPath?: string | null;\n\t/** Strip path from forwarded requests */\n\tstripPath?: boolean;\n}\n\nexport interface DokployDomain extends DokployDomainCreate {\n\tdomainId: string;\n\tcreatedAt?: string;\n}\n\n// ============================================\n// Destination types (backup storage)\n// ============================================\n\nexport interface DokployDestination {\n\tdestinationId: string;\n\tname: string;\n\taccessKey: string;\n\tbucket: string;\n\tregion: string;\n\tendpoint: string | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployDestinationCreate {\n\t/** User-friendly name for the destination */\n\tname: string;\n\t/** S3 access key ID */\n\taccessKey: string;\n\t/** S3 secret access key */\n\tsecretAccessKey: string;\n\t/** S3 bucket name */\n\tbucket: string;\n\t/** AWS region (e.g., 'us-east-1') */\n\tregion: string;\n\t/** Optional endpoint for S3-compatible services */\n\tendpoint?: string;\n}\n\n// ============================================\n// Backup types (scheduled backups)\n// ============================================\n\nexport type DokployDatabaseType = 'postgres' | 'mysql' | 'mariadb' | 'mongo';\n\nexport interface DokployBackup {\n\tbackupId: string;\n\tschedule: string;\n\tprefix: string;\n\tenabled: boolean;\n\tdestinationId: string;\n\tpostgresId?: string;\n\tdatabaseType: DokployDatabaseType;\n\tkeepLatestCount: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployBackupCreate {\n\t/** Cron schedule (e.g., '0 2 * * *' for 2 AM daily) */\n\tschedule: string;\n\t/** Backup file prefix (e.g., 'production/postgres') */\n\tprefix: string;\n\t/** Destination ID for backup storage */\n\tdestinationId: string;\n\t/** Database name to backup */\n\tdatabase: string;\n\t/** Postgres instance ID */\n\tpostgresId: string;\n\t/** Enable/disable backup (default: true) */\n\tenabled?: boolean;\n\t/** Number of backups to keep (default: keep all) */\n\tkeepLatestCount?: number;\n}\n\nexport interface DokployBackupUpdate {\n\tschedule?: string;\n\tprefix?: string;\n\tenabled?: boolean;\n\tkeepLatestCount?: number;\n}\n\n/**\n * Create a Dokploy API client from stored credentials or environment\n */\nexport async function createDokployApi(\n\tendpoint?: string,\n): Promise<DokployApi | null> {\n\tconst { getDokployCredentials } = await import('../auth/credentials');\n\n\t// Try environment variable first\n\tconst envToken = process.env.DOKPLOY_API_TOKEN;\n\tconst envEndpoint = endpoint || process.env.DOKPLOY_ENDPOINT;\n\n\tif (envToken && envEndpoint) {\n\t\treturn new DokployApi({ baseUrl: envEndpoint, token: envToken });\n\t}\n\n\t// Fall back to stored credentials\n\tconst creds = await getDokployCredentials();\n\tif (creds) {\n\t\treturn new DokployApi({\n\t\t\tbaseUrl: endpoint || creds.endpoint,\n\t\t\ttoken: creds.token,\n\t\t});\n\t}\n\n\treturn null;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fullstack-secrets-BYzi6Hwl.cjs","names":["SERVICE_DEFAULTS: Record<\n\tComposeServiceName,\n\tOmit<ServiceCredentials, 'password'>\n>","PGBOSS_DEFAULTS: Omit<ServiceCredentials, 'password'>","service: ComposeServiceName","services: ComposeServiceName[]","result: StageSecrets['services']","creds: ServiceCredentials","eventsBackend: EventsBackend","services: StageSecrets['services']","eventsBackend?: EventsBackend","urls: StageSecrets['urls']","stage: string","options?: { projectName?: string; eventsBackend?: EventsBackend }","secrets: StageSecrets","newCreds: ServiceCredentials","appName: string","password: string","projectName: string","workspace: NormalizedWorkspace","customs: Record<string, string>","frontendPorts: number[]","upperName","secrets: StageSecrets","workspaceRoot: string"],"sources":["../src/secrets/generator.ts","../src/setup/fullstack-secrets.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport type { ComposeServiceName, EventsBackend } from '../types';\nimport type { ServiceCredentials, StageSecrets } from './types';\n\n/**\n * Generate a secure random password using URL-safe base64 characters.\n * @param length Password length (default: 32)\n */\nexport function generateSecurePassword(length = 32): string {\n\treturn randomBytes(Math.ceil((length * 3) / 4))\n\t\t.toString('base64url')\n\t\t.slice(0, length);\n}\n\n/** Default service configurations (localhost for local dev via Docker port mapping) */\nconst SERVICE_DEFAULTS: Record<\n\tComposeServiceName,\n\tOmit<ServiceCredentials, 'password'>\n> = {\n\tpostgres: {\n\t\thost: 'localhost',\n\t\tport: 5432,\n\t\tusername: 'app',\n\t\tdatabase: 'app',\n\t},\n\tredis: {\n\t\thost: 'localhost',\n\t\tport: 6379,\n\t\tusername: 'default',\n\t},\n\trabbitmq: {\n\t\thost: 'localhost',\n\t\tport: 5672,\n\t\tusername: 'app',\n\t\tvhost: '/',\n\t},\n\tminio: {\n\t\thost: 'localhost',\n\t\tport: 9000,\n\t\tusername: 'app',\n\t\tbucket: 'app',\n\t},\n\tmailpit: {\n\t\thost: 'localhost',\n\t\tport: 1025,\n\t\tusername: 'app',\n\t},\n\tlocalstack: {\n\t\thost: 'localhost',\n\t\tport: 4566,\n\t\tusername: 'localstack',\n\t\tregion: 'us-east-1',\n\t},\n};\n\n/** Default credentials for pgboss (not a Docker service, reuses postgres) */\nconst PGBOSS_DEFAULTS: Omit<ServiceCredentials, 'password'> = {\n\thost: 'localhost',\n\tport: 5432,\n\tusername: 'pgboss',\n\tdatabase: 'app',\n};\n\n/**\n * Generate credentials for a specific service.\n */\nexport function generateServiceCredentials(\n\tservice: ComposeServiceName,\n): ServiceCredentials {\n\tconst defaults = SERVICE_DEFAULTS[service];\n\treturn {\n\t\t...defaults,\n\t\tpassword: generateSecurePassword(),\n\t};\n}\n\n/**\n * Generate credentials for multiple services.\n */\nexport function generateServicesCredentials(\n\tservices: ComposeServiceName[],\n): StageSecrets['services'] {\n\tconst result: StageSecrets['services'] = {};\n\n\tfor (const service of services) {\n\t\tresult[service] = generateServiceCredentials(service);\n\t}\n\n\treturn result;\n}\n\n/**\n * Generate connection URL for PostgreSQL.\n */\nexport function generatePostgresUrl(creds: ServiceCredentials): string {\n\tconst { username, password, host, port, database } = creds;\n\treturn `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;\n}\n\n/**\n * Generate connection URL for Redis.\n */\nexport function generateRedisUrl(creds: ServiceCredentials): string {\n\tconst { password, host, port } = creds;\n\treturn `redis://:${encodeURIComponent(password)}@${host}:${port}`;\n}\n\n/**\n * Generate connection URL for RabbitMQ.\n */\nexport function generateRabbitmqUrl(creds: ServiceCredentials): string {\n\tconst { username, password, host, port, vhost } = creds;\n\tconst encodedVhost = encodeURIComponent(vhost ?? '/');\n\treturn `amqp://${username}:${encodeURIComponent(password)}@${host}:${port}/${encodedVhost}`;\n}\n\n/**\n * Generate endpoint URL for MinIO (S3-compatible).\n */\nexport function generateMinioEndpoint(creds: ServiceCredentials): string {\n\tconst { host, port } = creds;\n\treturn `http://${host}:${port}`;\n}\n\n/**\n * Generate a LocalStack-compatible access key ID.\n * Must start with 'LSIA' prefix and be at least 20 characters.\n * @see https://docs.localstack.cloud/aws/capabilities/config/credentials/\n */\nexport function generateLocalStackAccessKeyId(): string {\n\tconst suffix = randomBytes(12).toString('base64url').slice(0, 16);\n\treturn `LSIA${suffix}`;\n}\n\n/**\n * Generate connection URL for pg-boss (uses PostgreSQL protocol).\n * Format: pgboss://user:pass@host:port/db?schema=pgboss\n */\nexport function generatePgBossUrl(creds: ServiceCredentials): string {\n\tconst { username, password, host, port, database } = creds;\n\treturn `pgboss://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}?schema=pgboss`;\n}\n\n/**\n * Generate event connection strings based on the events backend.\n */\nexport function generateEventConnectionStrings(\n\teventsBackend: EventsBackend,\n\tservices: StageSecrets['services'],\n): { publisher: string; subscriber: string } {\n\tswitch (eventsBackend) {\n\t\tcase 'pgboss': {\n\t\t\tconst creds = services.pgboss;\n\t\t\tif (!creds) {\n\t\t\t\tthrow new Error('pgboss credentials required for pgboss events');\n\t\t\t}\n\t\t\tconst url = generatePgBossUrl(creds);\n\t\t\treturn { publisher: url, subscriber: url };\n\t\t}\n\t\tcase 'sns': {\n\t\t\tconst creds = services.localstack;\n\t\t\tif (!creds) {\n\t\t\t\tthrow new Error('localstack credentials required for sns events');\n\t\t\t}\n\t\t\tconst endpoint = `http://${creds.host}:${creds.port}`;\n\t\t\tconst region = creds.region ?? 'us-east-1';\n\t\t\tconst accessKeyId = creds.accessKeyId ?? creds.username;\n\t\t\tconst secretKey = encodeURIComponent(creds.password);\n\t\t\treturn {\n\t\t\t\tpublisher: `sns://${accessKeyId}:${secretKey}@${creds.host}:${creds.port}?region=${region}&endpoint=${encodeURIComponent(endpoint)}`,\n\t\t\t\tsubscriber: `sqs://${accessKeyId}:${secretKey}@${creds.host}:${creds.port}?region=${region}&endpoint=${encodeURIComponent(endpoint)}`,\n\t\t\t};\n\t\t}\n\t\tcase 'rabbitmq': {\n\t\t\tconst creds = services.rabbitmq;\n\t\t\tif (!creds) {\n\t\t\t\tthrow new Error('rabbitmq credentials required for rabbitmq events');\n\t\t\t}\n\t\t\tconst url = generateRabbitmqUrl(creds);\n\t\t\treturn { publisher: url, subscriber: url };\n\t\t}\n\t}\n}\n\n/**\n * Generate connection URLs from service credentials.\n */\nexport function generateConnectionUrls(\n\tservices: StageSecrets['services'],\n\teventsBackend?: EventsBackend,\n): StageSecrets['urls'] {\n\tconst urls: StageSecrets['urls'] = {};\n\n\tif (services.postgres) {\n\t\turls.DATABASE_URL = generatePostgresUrl(services.postgres);\n\t}\n\n\tif (services.redis) {\n\t\turls.REDIS_URL = generateRedisUrl(services.redis);\n\t}\n\n\tif (services.rabbitmq) {\n\t\turls.RABBITMQ_URL = generateRabbitmqUrl(services.rabbitmq);\n\t}\n\n\tif (services.minio) {\n\t\turls.STORAGE_ENDPOINT = generateMinioEndpoint(services.minio);\n\t}\n\n\tif (eventsBackend) {\n\t\tconst eventUrls = generateEventConnectionStrings(eventsBackend, services);\n\t\turls.EVENT_PUBLISHER_CONNECTION_STRING = eventUrls.publisher;\n\t\turls.EVENT_SUBSCRIBER_CONNECTION_STRING = eventUrls.subscriber;\n\t} else if (services.pgboss) {\n\t\t// Default to pgboss when credentials exist but no explicit events backend\n\t\tconst pgbossUrl = generatePgBossUrl(services.pgboss);\n\t\turls.EVENT_PUBLISHER_CONNECTION_STRING = pgbossUrl;\n\t\turls.EVENT_SUBSCRIBER_CONNECTION_STRING = pgbossUrl;\n\t}\n\n\tif (services.mailpit) {\n\t\turls.SMTP_HOST = services.mailpit.host;\n\t\turls.SMTP_PORT = String(services.mailpit.port);\n\t}\n\n\treturn urls;\n}\n\n/**\n * Generate LocalStack service credentials with LSIA-prefixed access key.\n */\nexport function generateLocalStackCredentials(): ServiceCredentials {\n\tconst defaults = SERVICE_DEFAULTS.localstack;\n\treturn {\n\t\t...defaults,\n\t\tpassword: generateSecurePassword(),\n\t\taccessKeyId: generateLocalStackAccessKeyId(),\n\t};\n}\n\n/**\n * Create a new StageSecrets object with generated credentials.\n * @param stage - The deployment stage (e.g., 'development', 'production')\n * @param services - List of services to generate credentials for\n * @param options - Optional configuration\n * @param options.projectName - Project name used to derive the database name (e.g., 'myapp' → 'myapp_dev')\n * @param options.eventsBackend - Event backend type (pgboss, sns, rabbitmq)\n */\nexport function createStageSecrets(\n\tstage: string,\n\tservices: ComposeServiceName[],\n\toptions?: { projectName?: string; eventsBackend?: EventsBackend },\n): StageSecrets {\n\tconst now = new Date().toISOString();\n\tconst serviceCredentials = generateServicesCredentials(services);\n\n\t// Override service defaults with project-derived names if provided\n\tif (options?.projectName) {\n\t\tif (serviceCredentials.postgres) {\n\t\t\tserviceCredentials.postgres.database = `${options.projectName.replace(/-/g, '_')}_dev`;\n\t\t}\n\t\tif (serviceCredentials.minio) {\n\t\t\tserviceCredentials.minio.bucket = options.projectName;\n\t\t\tserviceCredentials.minio.username = options.projectName;\n\t\t}\n\t}\n\n\t// Always create pgboss credentials when postgres is available.\n\t// pgboss reuses postgres host/port/database but with a dedicated user,\n\t// so subscribers work out of the box without explicit events config.\n\tif (serviceCredentials.postgres && !serviceCredentials.pgboss) {\n\t\tserviceCredentials.pgboss = {\n\t\t\t...PGBOSS_DEFAULTS,\n\t\t\tpassword: generateSecurePassword(),\n\t\t\thost: serviceCredentials.postgres.host,\n\t\t\tport: serviceCredentials.postgres.port,\n\t\t\tdatabase: serviceCredentials.postgres.database,\n\t\t};\n\t}\n\n\t// Generate event-specific credentials\n\tconst eventsBackend = options?.eventsBackend;\n\tif (eventsBackend === 'sns') {\n\t\t// LocalStack credentials with LSIA-prefixed access key\n\t\tserviceCredentials.localstack = generateLocalStackCredentials();\n\t}\n\n\tconst urls = generateConnectionUrls(serviceCredentials, eventsBackend);\n\n\treturn {\n\t\tstage,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\teventsBackend,\n\t\tservices: serviceCredentials,\n\t\turls,\n\t\tcustom: {},\n\t};\n}\n\n/**\n * Rotate password for a specific service.\n */\nexport function rotateServicePassword(\n\tsecrets: StageSecrets,\n\tservice: ComposeServiceName,\n): StageSecrets {\n\tconst currentCreds = secrets.services[service];\n\tif (!currentCreds) {\n\t\tthrow new Error(`Service \"${service}\" not configured in secrets`);\n\t}\n\n\tconst newCreds: ServiceCredentials = {\n\t\t...currentCreds,\n\t\tpassword: generateSecurePassword(),\n\t};\n\n\tconst newServices = {\n\t\t...secrets.services,\n\t\t[service]: newCreds,\n\t};\n\n\treturn {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tservices: newServices,\n\t\turls: generateConnectionUrls(newServices, secrets.eventsBackend),\n\t};\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { generateSecurePassword } from '../secrets/generator.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\n/**\n * Generate a secure random password for database users.\n * Uses a combination of timestamp and random bytes for uniqueness.\n */\nexport function generateDbPassword(): string {\n\treturn `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Generate database URL for an app.\n * All apps connect to the same database, but use different users/schemas.\n */\nexport function generateDbUrl(\n\tappName: string,\n\tpassword: string,\n\tprojectName: string,\n\thost = 'localhost',\n\tport = 5432,\n): string {\n\tconst userName = appName.replace(/-/g, '_');\n\tconst dbName = `${projectName.replace(/-/g, '_')}_dev`;\n\treturn `postgresql://${userName}:${password}@${host}:${port}/${dbName}`;\n}\n\n/**\n * Generate fullstack-aware custom secrets for a workspace.\n *\n * Generates:\n * - Common secrets: NODE_ENV, PORT, LOG_LEVEL, JWT_SECRET\n * - Per-app database passwords and URLs for backend apps with db service\n * - Better-auth secrets for apps using the better-auth framework\n */\nexport function generateFullstackCustomSecrets(\n\tworkspace: NormalizedWorkspace,\n): Record<string, string> {\n\tconst hasDb = !!workspace.services.db;\n\tconst customs: 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\tif (!hasDb) {\n\t\treturn customs;\n\t}\n\n\t// Collect all frontend ports for trusted origins\n\tconst frontendPorts: number[] = [];\n\n\tfor (const [appName, appConfig] of Object.entries(workspace.apps)) {\n\t\tif (appConfig.type === 'web') {\n\t\t\tfrontendPorts.push(appConfig.port);\n\t\t\tconst upperName = appName.toUpperCase();\n\t\t\tcustoms[`${upperName}_URL`] = `http://localhost:${appConfig.port}`;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Mobile apps (Expo, etc.) don't run on a host port the backend\n\t\t// cares about for CORS, and don't need DB credentials.\n\t\tif (appConfig.type === 'mobile') {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Backend apps with database: generate per-app DB passwords and URLs\n\t\tconst password = generateDbPassword();\n\t\tconst upperName = appName.toUpperCase();\n\n\t\tcustoms[`${upperName}_DATABASE_URL`] = generateDbUrl(\n\t\t\tappName,\n\t\t\tpassword,\n\t\t\tworkspace.name,\n\t\t);\n\t\tcustoms[`${upperName}_DB_PASSWORD`] = password;\n\n\t\t// Better-auth framework secrets\n\t\tif (appConfig.framework === 'better-auth') {\n\t\t\tcustoms.AUTH_PORT = String(appConfig.port);\n\t\t\tcustoms.AUTH_URL = `http://localhost:${appConfig.port}`;\n\t\t\tcustoms.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${generateSecurePassword(16)}`;\n\t\t\tcustoms.BETTER_AUTH_URL = `http://localhost:${appConfig.port}`;\n\t\t}\n\t}\n\n\t// Generate trusted origins for better-auth (all app ports)\n\tif (customs.BETTER_AUTH_SECRET) {\n\t\tconst allPorts = Object.values(workspace.apps).map((a) => a.port);\n\t\tcustoms.BETTER_AUTH_TRUSTED_ORIGINS = allPorts\n\t\t\t.map((p) => `http://localhost:${p}`)\n\t\t\t.join(',');\n\t}\n\n\treturn customs;\n}\n\n/**\n * Extract *_DB_PASSWORD keys from secrets and write docker/.env.\n *\n * The docker/.env file contains database passwords that the PostgreSQL\n * init script reads to create per-app database users.\n */\nexport async function writeDockerEnvFromSecrets(\n\tsecrets: StageSecrets,\n\tworkspaceRoot: string,\n): Promise<void> {\n\tconst dbPasswordEntries = Object.entries(secrets.custom).filter(([key]) =>\n\t\tkey.endsWith('_DB_PASSWORD'),\n\t);\n\n\t// Always include pgboss password when pgboss credentials exist\n\tif (\n\t\tsecrets.services.pgboss &&\n\t\t!dbPasswordEntries.some(([key]) => key === 'PGBOSS_DB_PASSWORD')\n\t) {\n\t\tdbPasswordEntries.push([\n\t\t\t'PGBOSS_DB_PASSWORD',\n\t\t\tsecrets.services.pgboss.password,\n\t\t]);\n\t}\n\n\tif (dbPasswordEntries.length === 0) {\n\t\treturn;\n\t}\n\n\tconst envContent = `# 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${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join('\\n')}\n`;\n\n\tconst envPath = join(workspaceRoot, 'docker', '.env');\n\tawait mkdir(dirname(envPath), { recursive: true });\n\tawait writeFile(envPath, envContent);\n}\n"],"mappings":"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"}
|