@geekmidas/cli 10.0.0-alpha.11 → 10.0.0-alpha.13

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.
Files changed (242) hide show
  1. package/README.md +13 -40
  2. package/dist/{HostingerProvider-BcyFRbqV.mjs → HostingerProvider-BY26bWwB.mjs} +20 -21
  3. package/dist/HostingerProvider-BY26bWwB.mjs.map +1 -0
  4. package/dist/{HostingerProvider-BOvsgATO.cjs → HostingerProvider-BZFGYYVA.cjs} +21 -22
  5. package/dist/HostingerProvider-BZFGYYVA.cjs.map +1 -0
  6. package/dist/{LocalStateProvider-yjXZXZDM.cjs → LocalStateProvider-7HR6SS2t.cjs} +11 -6
  7. package/dist/LocalStateProvider-7HR6SS2t.cjs.map +1 -0
  8. package/dist/{LocalStateProvider-Bq-0C0sg.mjs → LocalStateProvider-D7QyJoLM.mjs} +10 -4
  9. package/dist/LocalStateProvider-D7QyJoLM.mjs.map +1 -0
  10. package/dist/{Route53Provider-C0xmer8U.mjs → Route53Provider-Cvj40A8I.mjs} +12 -12
  11. package/dist/Route53Provider-Cvj40A8I.mjs.map +1 -0
  12. package/dist/{Route53Provider-CH9XH7bz.cjs → Route53Provider-DVILBsvV.cjs} +20 -21
  13. package/dist/Route53Provider-DVILBsvV.cjs.map +1 -0
  14. package/dist/{SSMStateProvider-DOZifZfE.mjs → SSMStateProvider-B0Hu1MKZ.mjs} +13 -4
  15. package/dist/SSMStateProvider-B0Hu1MKZ.mjs.map +1 -0
  16. package/dist/{SSMStateProvider-CXtNgPfy.cjs → SSMStateProvider-VrQ-yeqd.cjs} +17 -9
  17. package/dist/SSMStateProvider-VrQ-yeqd.cjs.map +1 -0
  18. package/dist/{backup-provisioner-cE2PuCTb.cjs → backup-provisioner-BmhIO_Yr.cjs} +21 -19
  19. package/dist/backup-provisioner-BmhIO_Yr.cjs.map +1 -0
  20. package/dist/{backup-provisioner-DXXg6FIL.mjs → backup-provisioner-DIVCbfee.mjs} +10 -7
  21. package/dist/backup-provisioner-DIVCbfee.mjs.map +1 -0
  22. package/dist/{bundler-CaTGtiaO.cjs → bundler-BeNDZdHQ.cjs} +10 -28
  23. package/dist/bundler-BeNDZdHQ.cjs.map +1 -0
  24. package/dist/{bundler-S3ek7ea-.mjs → bundler-BodTL4kf.mjs} +9 -26
  25. package/dist/bundler-BodTL4kf.mjs.map +1 -0
  26. package/dist/{config-HhDpJmLO.cjs → config-Q1rvKHvw.cjs} +74 -69
  27. package/dist/config-Q1rvKHvw.cjs.map +1 -0
  28. package/dist/{config-B0L2tvvC.mjs → config-pqGD3hF9.mjs} +26 -14
  29. package/dist/config-pqGD3hF9.mjs.map +1 -0
  30. package/dist/config.cjs +4 -5
  31. package/dist/config.d.cts +14 -17
  32. package/dist/config.d.cts.map +1 -1
  33. package/dist/config.d.mts +14 -17
  34. package/dist/config.d.mts.map +1 -1
  35. package/dist/config.mjs +3 -5
  36. package/dist/{credentials-CZrys9C5.mjs → credentials-BekkKtY_.mjs} +1331 -1100
  37. package/dist/credentials-BekkKtY_.mjs.map +1 -0
  38. package/dist/{credentials-B2Pyh3bJ.cjs → credentials-Q6F0uqJq.cjs} +1468 -1232
  39. package/dist/credentials-Q6F0uqJq.cjs.map +1 -0
  40. package/dist/deploy/sniffer-envkit-patch.cjs +18 -10
  41. package/dist/deploy/sniffer-envkit-patch.cjs.map +1 -1
  42. package/dist/deploy/sniffer-envkit-patch.d.cts +0 -2
  43. package/dist/deploy/sniffer-envkit-patch.d.cts.map +1 -1
  44. package/dist/deploy/sniffer-envkit-patch.d.mts +0 -2
  45. package/dist/deploy/sniffer-envkit-patch.d.mts.map +1 -1
  46. package/dist/deploy/sniffer-envkit-patch.mjs +10 -2
  47. package/dist/deploy/sniffer-envkit-patch.mjs.map +1 -1
  48. package/dist/deploy/sniffer-hooks.cjs +13 -7
  49. package/dist/deploy/sniffer-hooks.cjs.map +1 -1
  50. package/dist/deploy/sniffer-hooks.d.cts +1 -2
  51. package/dist/deploy/sniffer-hooks.d.cts.map +1 -1
  52. package/dist/deploy/sniffer-hooks.d.mts +1 -2
  53. package/dist/deploy/sniffer-hooks.d.mts.map +1 -1
  54. package/dist/deploy/sniffer-hooks.mjs +9 -2
  55. package/dist/deploy/sniffer-hooks.mjs.map +1 -1
  56. package/dist/deploy/sniffer-loader.cjs +16 -7
  57. package/dist/deploy/sniffer-loader.cjs.map +1 -1
  58. package/dist/deploy/sniffer-loader.d.cts +1 -1
  59. package/dist/deploy/sniffer-loader.d.mts +1 -1
  60. package/dist/deploy/sniffer-loader.mjs +13 -2
  61. package/dist/deploy/sniffer-loader.mjs.map +1 -1
  62. package/dist/deploy/sniffer-routes-worker.cjs +11 -11
  63. package/dist/deploy/sniffer-routes-worker.cjs.map +1 -1
  64. package/dist/deploy/sniffer-routes-worker.d.cts +1 -1
  65. package/dist/deploy/sniffer-routes-worker.d.mts +1 -1
  66. package/dist/deploy/sniffer-routes-worker.mjs +2 -2
  67. package/dist/deploy/sniffer-routes-worker.mjs.map +1 -1
  68. package/dist/deploy/sniffer-worker.cjs +15 -6
  69. package/dist/deploy/sniffer-worker.cjs.map +1 -1
  70. package/dist/deploy/sniffer-worker.d.cts +1 -3
  71. package/dist/deploy/sniffer-worker.d.cts.map +1 -1
  72. package/dist/deploy/sniffer-worker.d.mts +1 -3
  73. package/dist/deploy/sniffer-worker.d.mts.map +1 -1
  74. package/dist/deploy/sniffer-worker.mjs +15 -4
  75. package/dist/deploy/sniffer-worker.mjs.map +1 -1
  76. package/dist/{discover-DTmFeloL.cjs → discover-BERGFg8v.cjs} +81 -64
  77. package/dist/discover-BERGFg8v.cjs.map +1 -0
  78. package/dist/{discover-CAlFgYec.mjs → discover-CK-n3w6b.mjs} +35 -20
  79. package/dist/discover-CK-n3w6b.mjs.map +1 -0
  80. package/dist/{emit-CUD1BWls.cjs → emit-BdoPFjZf.cjs} +53 -36
  81. package/dist/emit-BdoPFjZf.cjs.map +1 -0
  82. package/dist/{emit-CIlvnDvV.mjs → emit-Bzq7JsMP.mjs} +24 -6
  83. package/dist/emit-Bzq7JsMP.mjs.map +1 -0
  84. package/dist/{fullstack-secrets-METaRLS1.mjs → fullstack-secrets-BET8gqwK.mjs} +9 -15
  85. package/dist/fullstack-secrets-BET8gqwK.mjs.map +1 -0
  86. package/dist/{fullstack-secrets-D6MyHhQP.cjs → fullstack-secrets-DXsWO-5m.cjs} +19 -38
  87. package/dist/fullstack-secrets-DXsWO-5m.cjs.map +1 -0
  88. package/dist/{generator-VE2JrFlY.mjs → generator-LfZU7NlU.mjs} +8 -14
  89. package/dist/generator-LfZU7NlU.mjs.map +1 -0
  90. package/dist/{generator-CNzBw7g5.cjs → generator-Wdfp0jCS.cjs} +38 -45
  91. package/dist/generator-Wdfp0jCS.cjs.map +1 -0
  92. package/dist/{github-BrvaPkFf.cjs → github-BfflNPVe.cjs} +36 -30
  93. package/dist/{github-BrvaPkFf.cjs.map → github-BfflNPVe.cjs.map} +1 -1
  94. package/dist/{github-CrpsXSfV.mjs → github-CRwnxKmD.mjs} +27 -20
  95. package/dist/github-CRwnxKmD.mjs.map +1 -0
  96. package/dist/{index-CR5urhWX.d.cts → index-Cd-qeOam.d.mts} +3 -12
  97. package/dist/index-Cd-qeOam.d.mts.map +1 -0
  98. package/dist/{index-Ayxg_DQY.d.mts → index-D-iAVHx-.d.cts} +3 -12
  99. package/dist/index-D-iAVHx-.d.cts.map +1 -0
  100. package/dist/index.cjs +3320 -2961
  101. package/dist/index.cjs.map +1 -1
  102. package/dist/index.d.cts +1 -1
  103. package/dist/index.d.mts +1 -1
  104. package/dist/index.mjs +3200 -2858
  105. package/dist/index.mjs.map +1 -1
  106. package/dist/{keystore-TxeYM7an.mjs → keystore-B4PRlbgE.mjs} +5 -7
  107. package/dist/keystore-B4PRlbgE.mjs.map +1 -0
  108. package/dist/{keystore-C9rint2E.cjs → keystore-DgGRj2Yy.cjs} +23 -26
  109. package/dist/keystore-DgGRj2Yy.cjs.map +1 -0
  110. package/dist/{openapi-DaKlTKxz.cjs → openapi-Dcbme-Us.cjs} +134 -171
  111. package/dist/openapi-Dcbme-Us.cjs.map +1 -0
  112. package/dist/openapi-react-query.cjs +156 -2
  113. package/dist/openapi-react-query.cjs.map +1 -0
  114. package/dist/openapi-react-query.d.cts +1 -3
  115. package/dist/openapi-react-query.d.cts.map +1 -1
  116. package/dist/openapi-react-query.d.mts +1 -3
  117. package/dist/openapi-react-query.d.mts.map +1 -1
  118. package/dist/openapi-react-query.mjs +156 -2
  119. package/dist/openapi-react-query.mjs.map +1 -0
  120. package/dist/{openapi-y1d7Kalt.mjs → openapi-vy4JzhUv.mjs} +76 -106
  121. package/dist/openapi-vy4JzhUv.mjs.map +1 -0
  122. package/dist/openapi.cjs +3 -6
  123. package/dist/openapi.d.cts +8 -11
  124. package/dist/openapi.d.cts.map +1 -1
  125. package/dist/openapi.d.mts +8 -11
  126. package/dist/openapi.d.mts.map +1 -1
  127. package/dist/openapi.mjs +2 -6
  128. package/dist/reconcile/public.cjs +4 -4
  129. package/dist/reconcile/public.d.cts +8 -26
  130. package/dist/reconcile/public.d.cts.map +1 -1
  131. package/dist/reconcile/public.d.mts +8 -26
  132. package/dist/reconcile/public.d.mts.map +1 -1
  133. package/dist/reconcile/public.mjs +3 -4
  134. package/dist/{reconcile-CYMhFSYy.cjs → reconcile-Dd2iyILf.cjs} +4 -7
  135. package/dist/reconcile-Dd2iyILf.cjs.map +1 -0
  136. package/dist/{reconcile-C13ERt1U.mjs → reconcile-DhQ00DpU.mjs} +4 -7
  137. package/dist/reconcile-DhQ00DpU.mjs.map +1 -0
  138. package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
  139. package/dist/{chunk-CUT6urMc.cjs → rolldown-runtime-C0BPl7ul.cjs} +23 -10
  140. package/dist/{storage-CSNplaJn.cjs → storage-C-30BD1e.cjs} +59 -69
  141. package/dist/storage-C-30BD1e.cjs.map +1 -0
  142. package/dist/{storage-B020InPW.mjs → storage-P_b3w7uj.mjs} +22 -13
  143. package/dist/storage-P_b3w7uj.mjs.map +1 -0
  144. package/dist/{sync-CzHXiR9c.mjs → sync-B3xxXtJT.mjs} +18 -6
  145. package/dist/sync-B3xxXtJT.mjs.map +1 -0
  146. package/dist/{sync-0ILCb-1S.cjs → sync-D7uTIIEi.cjs} +43 -25
  147. package/dist/sync-D7uTIIEi.cjs.map +1 -0
  148. package/dist/types-BGo5TaNE.d.mts +6164 -0
  149. package/dist/types-BGo5TaNE.d.mts.map +1 -0
  150. package/dist/types-C5ZNpUad.d.cts +6165 -0
  151. package/dist/types-C5ZNpUad.d.cts.map +1 -0
  152. package/dist/workspace/index.cjs +3 -3
  153. package/dist/workspace/index.d.cts +3 -3
  154. package/dist/workspace/index.d.mts +3 -3
  155. package/dist/workspace/index.mjs +2 -3
  156. package/dist/{workspace-DaEX7FEg.mjs → workspace-CgwmXEtc.mjs} +725 -871
  157. package/dist/workspace-CgwmXEtc.mjs.map +1 -0
  158. package/dist/{workspace-C-a3G_U0.cjs → workspace-mFpI_ky_.cjs} +894 -1041
  159. package/dist/workspace-mFpI_ky_.cjs.map +1 -0
  160. package/package.json +14 -14
  161. package/dist/CachedStateProvider-BEBl2wQT.cjs +0 -60
  162. package/dist/CachedStateProvider-BEBl2wQT.cjs.map +0 -1
  163. package/dist/CachedStateProvider-C-8wtlto.mjs +0 -3
  164. package/dist/CachedStateProvider-Co4n5Wvs.mjs +0 -54
  165. package/dist/CachedStateProvider-Co4n5Wvs.mjs.map +0 -1
  166. package/dist/CachedStateProvider-DqhGpzsm.cjs +0 -3
  167. package/dist/HostingerProvider-BOvsgATO.cjs.map +0 -1
  168. package/dist/HostingerProvider-BcyFRbqV.mjs.map +0 -1
  169. package/dist/LocalStateProvider-Bq-0C0sg.mjs.map +0 -1
  170. package/dist/LocalStateProvider-yjXZXZDM.cjs.map +0 -1
  171. package/dist/Route53Provider-C0xmer8U.mjs.map +0 -1
  172. package/dist/Route53Provider-CH9XH7bz.cjs.map +0 -1
  173. package/dist/SSMStateProvider-CXtNgPfy.cjs.map +0 -1
  174. package/dist/SSMStateProvider-DOZifZfE.mjs.map +0 -1
  175. package/dist/backup-provisioner-DXXg6FIL.mjs.map +0 -1
  176. package/dist/backup-provisioner-cE2PuCTb.cjs.map +0 -1
  177. package/dist/bundler-CaTGtiaO.cjs.map +0 -1
  178. package/dist/bundler-S3ek7ea-.mjs.map +0 -1
  179. package/dist/chunk-Duj1WY3L.mjs +0 -7
  180. package/dist/config-B0L2tvvC.mjs.map +0 -1
  181. package/dist/config-HhDpJmLO.cjs.map +0 -1
  182. package/dist/credentials-B2Pyh3bJ.cjs.map +0 -1
  183. package/dist/credentials-B5zRHdAl.mjs +0 -141
  184. package/dist/credentials-B5zRHdAl.mjs.map +0 -1
  185. package/dist/credentials-BapzjyV5.cjs +0 -195
  186. package/dist/credentials-BapzjyV5.cjs.map +0 -1
  187. package/dist/credentials-CZrys9C5.mjs.map +0 -1
  188. package/dist/discover-CAlFgYec.mjs.map +0 -1
  189. package/dist/discover-DTmFeloL.cjs.map +0 -1
  190. package/dist/dokploy-api-5cK2InBm.cjs +0 -3
  191. package/dist/dokploy-api-D2Ny4Jrc.mjs +0 -3
  192. package/dist/dokploy-api-pdaBryoY.mjs +0 -706
  193. package/dist/dokploy-api-pdaBryoY.mjs.map +0 -1
  194. package/dist/dokploy-api-zqKe9lHH.cjs +0 -718
  195. package/dist/dokploy-api-zqKe9lHH.cjs.map +0 -1
  196. package/dist/emit-CIlvnDvV.mjs.map +0 -1
  197. package/dist/emit-CUD1BWls.cjs.map +0 -1
  198. package/dist/encryption-D2pL3tHH.cjs +0 -54
  199. package/dist/encryption-D2pL3tHH.cjs.map +0 -1
  200. package/dist/encryption-YDsXXi6w.mjs +0 -3
  201. package/dist/encryption-_4zwjOAR.mjs +0 -42
  202. package/dist/encryption-_4zwjOAR.mjs.map +0 -1
  203. package/dist/encryption-sQ2Oxx0d.cjs +0 -4
  204. package/dist/fullstack-secrets-D6MyHhQP.cjs.map +0 -1
  205. package/dist/fullstack-secrets-METaRLS1.mjs.map +0 -1
  206. package/dist/generator-CNzBw7g5.cjs.map +0 -1
  207. package/dist/generator-VE2JrFlY.mjs.map +0 -1
  208. package/dist/github-CrpsXSfV.mjs.map +0 -1
  209. package/dist/index-Ayxg_DQY.d.mts.map +0 -1
  210. package/dist/index-CR5urhWX.d.cts.map +0 -1
  211. package/dist/keystore-C9rint2E.cjs.map +0 -1
  212. package/dist/keystore-TxeYM7an.mjs.map +0 -1
  213. package/dist/openapi-DaKlTKxz.cjs.map +0 -1
  214. package/dist/openapi-react-query-BKH334Ug.cjs +0 -173
  215. package/dist/openapi-react-query-BKH334Ug.cjs.map +0 -1
  216. package/dist/openapi-react-query-DoR5CmzQ.mjs +0 -167
  217. package/dist/openapi-react-query-DoR5CmzQ.mjs.map +0 -1
  218. package/dist/openapi-y1d7Kalt.mjs.map +0 -1
  219. package/dist/reconcile-C13ERt1U.mjs.map +0 -1
  220. package/dist/reconcile-CYMhFSYy.cjs.map +0 -1
  221. package/dist/setup-BWgMs4gg.mjs +0 -12
  222. package/dist/setup-DOv92pIu.cjs +0 -473
  223. package/dist/setup-DOv92pIu.cjs.map +0 -1
  224. package/dist/setup-DQo_h_yK.mjs +0 -443
  225. package/dist/setup-DQo_h_yK.mjs.map +0 -1
  226. package/dist/setup-lCx_DcUo.cjs +0 -12
  227. package/dist/storage-B020InPW.mjs.map +0 -1
  228. package/dist/storage-CSNplaJn.cjs.map +0 -1
  229. package/dist/storage-DGBzMLZa.mjs +0 -4
  230. package/dist/storage-DlVol_oj.cjs +0 -8
  231. package/dist/sync-0ILCb-1S.cjs.map +0 -1
  232. package/dist/sync-CzHXiR9c.mjs.map +0 -1
  233. package/dist/sync-DYENfOt4.cjs +0 -4
  234. package/dist/sync-HAau6h_R.mjs +0 -3
  235. package/dist/types-CL7jPFM2.d.mts +0 -2220
  236. package/dist/types-CL7jPFM2.d.mts.map +0 -1
  237. package/dist/types-Y3fKQSAK.d.cts +0 -2220
  238. package/dist/types-Y3fKQSAK.d.cts.map +0 -1
  239. package/dist/workspace-C-a3G_U0.cjs.map +0 -1
  240. package/dist/workspace-DQxxs7Jk.cjs +0 -10
  241. package/dist/workspace-DaEX7FEg.mjs.map +0 -1
  242. package/dist/workspace-DbI580qS.mjs +0 -9
package/README.md CHANGED
@@ -444,7 +444,7 @@ gkm docker [options]
444
444
 
445
445
  **Generated Files:**
446
446
  - `.gkm/docker/Dockerfile` - Multi-stage or slim Dockerfile
447
- - `.gkm/docker/docker-compose.yml` - Docker Compose configuration
447
+ - `docker-compose.constructs.yml` - the containers the constructs imply, and the app (project root)
448
448
  - `.gkm/docker/docker-entrypoint.sh` - Entrypoint script
449
449
  - `.dockerignore` - Docker ignore file (project root)
450
450
 
@@ -505,38 +505,20 @@ export default defineConfig({
505
505
  baseImage: 'node:22-alpine',
506
506
  // Port to expose (default: 3000)
507
507
  port: 3000,
508
- // Docker Compose services
509
- compose: {
510
- services: {
511
- postgres: { image: 'postgis/postgis:16-3.4-alpine' }, // Custom image
512
- redis: true, // Default version
513
- rabbitmq: { version: '3.12-management-alpine' }, // Custom version
514
- },
515
- },
516
508
  },
517
509
  });
518
510
  ```
519
511
 
520
- **Docker Compose Services:**
521
-
522
- Services can be configured with custom versions, custom images, or use defaults:
523
-
524
- ```typescript
525
- // Object format (recommended)
526
- services: {
527
- postgres: { version: '15-alpine' }, // Custom version
528
- redis: true, // Default: redis:7-alpine
529
- rabbitmq: { version: '3.12-management-alpine' },
530
- }
531
-
532
- // Custom images (e.g., PostGIS, Redis Stack)
533
- services: {
534
- postgres: { image: 'postgis/postgis:16-3.4-alpine' }, // Full image reference
535
- redis: { image: 'redis/redis-stack:latest' },
536
- }
512
+ **Compose:** there is no `compose` option. `gkm docker` writes
513
+ `docker-compose.constructs.yml` at the project root from the declared
514
+ constructs; an image pin or an extra service goes in your own
515
+ `docker-compose.yml`, which is merged over it:
537
516
 
538
- // Legacy array format - uses default versions
539
- services: ['postgres', 'redis', 'rabbitmq']
517
+ ```yaml
518
+ # docker-compose.yml
519
+ services:
520
+ postgres:
521
+ image: postgis/postgis:16-3.4-alpine
540
522
  ```
541
523
 
542
524
  **Service Configuration Options:**
@@ -684,8 +666,8 @@ gkm secrets:init --stage production --force
684
666
  ```
685
667
 
686
668
  **Generated:**
687
- - Secure passwords for postgres, redis, rabbitmq (based on `docker.compose.services` config)
688
- - Connection URLs (`DATABASE_URL`, `REDIS_URL`, `RABBITMQ_URL`)
669
+ - Custom secrets for the stage; the containers' credentials are provisioned by
670
+ reconcile from the declared constructs, not stored here
689
671
  - Stored in `.gkm/secrets/<stage>.json` (gitignored)
690
672
 
691
673
  ### `gkm secrets:set`
@@ -1296,7 +1278,7 @@ gkm docker
1296
1278
  - `.gkm/docker/Dockerfile.api` - Routes-based backend (uses `gkm build`)
1297
1279
  - `.gkm/docker/Dockerfile.auth` - Entry-based backend (uses esbuild bundling)
1298
1280
  - `.gkm/docker/Dockerfile.web` - Next.js standalone output
1299
- - `.gkm/docker/docker-compose.yml` - Full stack with all apps and services
1281
+ - `docker-compose.constructs.yml` - every container the constructs imply, and each app behind the `apps` profile (project root)
1300
1282
  - `.dockerignore` - Optimized ignore patterns
1301
1283
 
1302
1284
  **Dockerfile types by app:**
@@ -2058,17 +2040,8 @@ interface DockerConfig {
2058
2040
  imageName?: string; // Image name (defaults to package.json name)
2059
2041
  baseImage?: string; // Base image (default: node:22-alpine)
2060
2042
  port?: number; // Port to expose (default: 3000)
2061
- compose?: {
2062
- services?: ComposeServicesConfig | ComposeServiceName[];
2063
- };
2064
2043
  }
2065
2044
 
2066
- // Service configuration for docker-compose
2067
- type ComposeServiceName = 'postgres' | 'redis' | 'rabbitmq';
2068
- type ComposeServicesConfig = {
2069
- [K in ComposeServiceName]?: boolean | { version?: string };
2070
- };
2071
-
2072
2045
  // Telescope configuration
2073
2046
  interface TelescopeConfig {
2074
2047
  enabled?: boolean;
@@ -1,5 +1,4 @@
1
- import { getHostingerToken } from "./credentials-B5zRHdAl.mjs";
2
-
1
+ import { n as getHostingerToken } from "./index.mjs";
3
2
  //#region src/deploy/dns/hostinger-api.ts
4
3
  /**
5
4
  * Hostinger DNS API client
@@ -12,6 +11,9 @@ const HOSTINGER_API_BASE = "https://developers.hostinger.com";
12
11
  * Hostinger API error
13
12
  */
14
13
  var HostingerApiError = class extends Error {
14
+ status;
15
+ statusText;
16
+ errors;
15
17
  constructor(message, status, statusText, errors) {
16
18
  super(message);
17
19
  this.status = status;
@@ -65,7 +67,7 @@ var HostingerApi = class {
65
67
  throw new HostingerApiError(errorMessage, response.status, response.statusText, errors);
66
68
  }
67
69
  const text = await response.text();
68
- if (!text || text.trim() === "") return void 0;
70
+ if (!text || text.trim() === "") return;
69
71
  return JSON.parse(text);
70
72
  }
71
73
  /**
@@ -122,8 +124,7 @@ var HostingerApi = class {
122
124
  * @param type - Record type (e.g., 'A')
123
125
  */
124
126
  async recordExists(domain, name, type = "A") {
125
- const records = await this.getRecords(domain);
126
- return records.some((r) => r.name === name && r.type === type);
127
+ return (await this.getRecords(domain)).some((r) => r.name === name && r.type === type);
127
128
  }
128
129
  /**
129
130
  * Create a single A record if it doesn't exist
@@ -135,8 +136,7 @@ var HostingerApi = class {
135
136
  * @returns true if created, false if already exists
136
137
  */
137
138
  async createARecordIfNotExists(domain, subdomain, ip, ttl = 300) {
138
- const exists = await this.recordExists(domain, subdomain, "A");
139
- if (exists) return false;
139
+ if (await this.recordExists(domain, subdomain, "A")) return false;
140
140
  await this.upsertRecords(domain, [{
141
141
  name: subdomain,
142
142
  type: "A",
@@ -146,10 +146,14 @@ var HostingerApi = class {
146
146
  return true;
147
147
  }
148
148
  };
149
-
150
149
  //#endregion
151
150
  //#region src/deploy/dns/HostingerProvider.ts
152
151
  /**
152
+ * Hostinger DNS Provider
153
+ *
154
+ * Implements DnsProvider interface using the Hostinger DNS API.
155
+ */
156
+ /**
153
157
  * Hostinger DNS provider implementation.
154
158
  */
155
159
  var HostingerProvider = class {
@@ -166,9 +170,7 @@ var HostingerProvider = class {
166
170
  return this.api;
167
171
  }
168
172
  async getRecords(domain) {
169
- const api = await this.getApi();
170
- const records = await api.getRecords(domain);
171
- return records.map((r) => ({
173
+ return (await (await this.getApi()).getRecords(domain)).map((r) => ({
172
174
  name: r.name,
173
175
  type: r.type,
174
176
  ttl: r.ttl,
@@ -227,18 +229,15 @@ var HostingerProvider = class {
227
229
  error: String(error)
228
230
  });
229
231
  }
230
- for (const record of records) {
231
- const existing = existingRecords.find((r) => r.name === record.name && r.type === record.type);
232
- if (!existing) results.push({
233
- record,
234
- deleted: false,
235
- notFound: true
236
- });
237
- }
232
+ for (const record of records) if (!existingRecords.find((r) => r.name === record.name && r.type === record.type)) results.push({
233
+ record,
234
+ deleted: false,
235
+ notFound: true
236
+ });
238
237
  return results;
239
238
  }
240
239
  };
241
-
242
240
  //#endregion
243
241
  export { HostingerProvider };
244
- //# sourceMappingURL=HostingerProvider-BcyFRbqV.mjs.map
242
+
243
+ //# sourceMappingURL=HostingerProvider-BY26bWwB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HostingerProvider-BY26bWwB.mjs","names":[],"sources":["../src/deploy/dns/hostinger-api.ts","../src/deploy/dns/HostingerProvider.ts"],"sourcesContent":["/**\n * Hostinger DNS API client\n *\n * API Documentation: https://developers.hostinger.com/\n * Authentication: Bearer token from hpanel.hostinger.com/profile/api\n */\n\nconst HOSTINGER_API_BASE = 'https://developers.hostinger.com';\n\n/**\n * DNS record types supported by Hostinger\n */\nexport type DnsRecordType =\n\t| 'A'\n\t| 'AAAA'\n\t| 'CNAME'\n\t| 'MX'\n\t| 'TXT'\n\t| 'NS'\n\t| 'SRV'\n\t| 'CAA';\n\n/**\n * A single DNS record\n */\nexport interface DnsRecord {\n\t/** Subdomain name (e.g., 'api.joemoer' for api.joemoer.traflabs.io) */\n\tname: string;\n\t/** Record type */\n\ttype: DnsRecordType;\n\t/** TTL in seconds */\n\tttl: number;\n\t/** Record values */\n\trecords: Array<{ content: string }>;\n}\n\n/**\n * Filter for deleting specific records\n */\nexport interface DnsRecordFilter {\n\tname: string;\n\ttype: DnsRecordType;\n}\n\n/**\n * API error response\n */\nexport interface HostingerErrorResponse {\n\tmessage?: string;\n\terrors?: Record<string, string[]>;\n}\n\n/**\n * Hostinger API error\n */\nexport class HostingerApiError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t\tpublic statusText: string,\n\t\tpublic errors?: Record<string, string[]>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'HostingerApiError';\n\t}\n}\n\n/**\n * Hostinger DNS API client\n *\n * @example\n * ```ts\n * const api = new HostingerApi(token);\n *\n * // Get all records for a domain\n * const records = await api.getRecords('traflabs.io');\n *\n * // Create/update records\n * await api.upsertRecords('traflabs.io', [\n * { name: 'api.joemoer', type: 'A', ttl: 300, records: ['1.2.3.4'] }\n * ]);\n * ```\n */\nexport class HostingerApi {\n\tprivate token: string;\n\n\tconstructor(token: string) {\n\t\tthis.token = token;\n\t}\n\n\t/**\n\t * Make a request to the Hostinger API\n\t */\n\tprivate async request<T>(\n\t\tmethod: 'GET' | 'POST' | 'PUT' | 'DELETE',\n\t\tendpoint: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst url = `${HOSTINGER_API_BASE}${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\tAuthorization: `Bearer ${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 = `Hostinger API error: ${response.status} ${response.statusText}`;\n\t\t\tlet errors: Record<string, string[]> | undefined;\n\n\t\t\ttry {\n\t\t\t\tconst errorBody = (await response.json()) as HostingerErrorResponse;\n\t\t\t\tif (errorBody.message) {\n\t\t\t\t\terrorMessage = `Hostinger API error: ${errorBody.message}`;\n\t\t\t\t}\n\t\t\t\terrors = errorBody.errors;\n\t\t\t} catch {\n\t\t\t\t// Ignore JSON parse errors\n\t\t\t}\n\n\t\t\tthrow new HostingerApiError(\n\t\t\t\terrorMessage,\n\t\t\t\tresponse.status,\n\t\t\t\tresponse.statusText,\n\t\t\t\terrors,\n\t\t\t);\n\t\t}\n\n\t\t// Handle empty responses\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 * Get all DNS records for a domain\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t */\n\tasync getRecords(domain: string): Promise<DnsRecord[]> {\n\t\t// The zone comes back as a **bare array**, not `{ data: [...] }`.\n\t\t//\n\t\t// This read `response.data || []`, so every call returned zero records\n\t\t// against the real API while every mocked test passed. Silently: no error,\n\t\t// no warning, just an empty zone. `upsertRecords` uses this to decide what\n\t\t// already exists, so everything looked new and every write was reported as\n\t\t// a creation — harmless only because the PUT merges rather than replaces.\n\t\t//\n\t\t// Both shapes are accepted because one of them is a guess: the wrapper was\n\t\t// written against documentation and the array is what the API actually\n\t\t// sends, so tolerating an envelope costs a line and survives it appearing.\n\t\tconst response = await this.request<DnsRecord[] | { data?: DnsRecord[] }>(\n\t\t\t'GET',\n\t\t\t`/api/dns/v1/zones/${domain}`,\n\t\t);\n\n\t\tif (Array.isArray(response)) return response;\n\n\t\treturn response?.data ?? [];\n\t}\n\n\t/**\n\t * Create or update DNS records\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param records - Records to create/update\n\t * @param overwrite - If true, replaces all existing records. If false, merges with existing.\n\t */\n\tasync upsertRecords(\n\t\tdomain: string,\n\t\trecords: DnsRecord[],\n\t\toverwrite = false,\n\t): Promise<void> {\n\t\tawait this.request('PUT', `/api/dns/v1/zones/${domain}`, {\n\t\t\toverwrite,\n\t\t\tzone: records,\n\t\t});\n\t}\n\n\t/**\n\t * Validate DNS records before applying\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param records - Records to validate\n\t * @returns true if valid, throws if invalid\n\t */\n\tasync validateRecords(\n\t\tdomain: string,\n\t\trecords: DnsRecord[],\n\t): Promise<boolean> {\n\t\tawait this.request('POST', `/api/dns/v1/zones/${domain}/validate`, {\n\t\t\toverwrite: false,\n\t\t\tzone: records,\n\t\t});\n\t\treturn true;\n\t}\n\n\t/**\n\t * Delete specific DNS records\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param filters - Filters to match records for deletion\n\t */\n\tasync deleteRecords(\n\t\tdomain: string,\n\t\tfilters: DnsRecordFilter[],\n\t): Promise<void> {\n\t\tawait this.request('DELETE', `/api/dns/v1/zones/${domain}`, {\n\t\t\tfilters,\n\t\t});\n\t}\n\n\t/**\n\t * Check if a specific record exists\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param name - Subdomain name (e.g., 'api.joemoer')\n\t * @param type - Record type (e.g., 'A')\n\t */\n\tasync recordExists(\n\t\tdomain: string,\n\t\tname: string,\n\t\ttype: DnsRecordType = 'A',\n\t): Promise<boolean> {\n\t\tconst records = await this.getRecords(domain);\n\t\treturn records.some((r) => r.name === name && r.type === type);\n\t}\n\n\t/**\n\t * Create a single A record if it doesn't exist\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param subdomain - Subdomain name (e.g., 'api.joemoer')\n\t * @param ip - IP address to point to\n\t * @param ttl - TTL in seconds (default: 300)\n\t * @returns true if created, false if already exists\n\t */\n\tasync createARecordIfNotExists(\n\t\tdomain: string,\n\t\tsubdomain: string,\n\t\tip: string,\n\t\tttl = 300,\n\t): Promise<boolean> {\n\t\tconst exists = await this.recordExists(domain, subdomain, 'A');\n\t\tif (exists) {\n\t\t\treturn false;\n\t\t}\n\n\t\tawait this.upsertRecords(domain, [\n\t\t\t{\n\t\t\t\tname: subdomain,\n\t\t\t\ttype: 'A',\n\t\t\t\tttl,\n\t\t\t\trecords: [{ content: ip }],\n\t\t\t},\n\t\t]);\n\n\t\treturn true;\n\t}\n}\n","/**\n * Hostinger DNS Provider\n *\n * Implements DnsProvider interface using the Hostinger DNS API.\n */\n\nimport { getHostingerToken } from '../../auth/credentials';\nimport type {\n\tDeleteDnsRecord,\n\tDeleteResult,\n\tDnsProvider,\n\tDnsRecord,\n\tUpsertDnsRecord,\n\tUpsertResult,\n} from './DnsProvider';\nimport { HostingerApi } from './hostinger-api';\n\n/**\n * Hostinger DNS provider implementation.\n */\nexport class HostingerProvider implements DnsProvider {\n\treadonly name = 'hostinger';\n\tprivate api: HostingerApi | null = null;\n\n\t/**\n\t * Get or create the Hostinger API client.\n\t */\n\tprivate async getApi(): Promise<HostingerApi> {\n\t\tif (this.api) {\n\t\t\treturn this.api;\n\t\t}\n\n\t\tconst token = await getHostingerToken();\n\t\tif (!token) {\n\t\t\tthrow new Error(\n\t\t\t\t'Hostinger API token not configured. Run `gkm login --service=hostinger` or get your token from https://hpanel.hostinger.com/profile/api',\n\t\t\t);\n\t\t}\n\n\t\tthis.api = new HostingerApi(token);\n\t\treturn this.api;\n\t}\n\n\tasync getRecords(domain: string): Promise<DnsRecord[]> {\n\t\tconst api = await this.getApi();\n\t\tconst records = await api.getRecords(domain);\n\n\t\treturn records.map((r) => ({\n\t\t\tname: r.name,\n\t\t\ttype: r.type,\n\t\t\tttl: r.ttl,\n\t\t\tvalues: r.records.map((rec) => rec.content),\n\t\t}));\n\t}\n\n\tasync upsertRecords(\n\t\tdomain: string,\n\t\trecords: UpsertDnsRecord[],\n\t): Promise<UpsertResult[]> {\n\t\tconst api = await this.getApi();\n\t\tconst results: UpsertResult[] = [];\n\n\t\t// Get existing records to check what already exists\n\t\tconst existingRecords = await api.getRecords(domain);\n\n\t\tfor (const record of records) {\n\t\t\tconst existing = existingRecords.find(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t);\n\n\t\t\tconst existingValue = existing?.records?.[0]?.content;\n\n\t\t\tif (existing && existingValue === record.value) {\n\t\t\t\t// Record exists with same value - unchanged\n\t\t\t\tresults.push({\n\t\t\t\t\trecord,\n\t\t\t\t\tcreated: false,\n\t\t\t\t\tunchanged: true,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Create or update the record\n\t\t\tawait api.upsertRecords(domain, [\n\t\t\t\t{\n\t\t\t\t\tname: record.name,\n\t\t\t\t\ttype: record.type,\n\t\t\t\t\tttl: record.ttl,\n\t\t\t\t\trecords: [{ content: record.value }],\n\t\t\t\t},\n\t\t\t]);\n\n\t\t\tresults.push({\n\t\t\t\trecord,\n\t\t\t\tcreated: !existing,\n\t\t\t\tunchanged: false,\n\t\t\t});\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tasync deleteRecords(\n\t\tdomain: string,\n\t\trecords: DeleteDnsRecord[],\n\t): Promise<DeleteResult[]> {\n\t\tconst api = await this.getApi();\n\t\tconst results: DeleteResult[] = [];\n\n\t\t// Get existing records to check what exists\n\t\tconst existingRecords = await api.getRecords(domain);\n\n\t\t// Filter to only records that exist\n\t\tconst recordsToDelete = records.filter((record) =>\n\t\t\texistingRecords.some(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t),\n\t\t);\n\n\t\t// Delete existing records\n\t\tif (recordsToDelete.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait api.deleteRecords(\n\t\t\t\t\tdomain,\n\t\t\t\t\trecordsToDelete.map((r) => ({\n\t\t\t\t\t\tname: r.name,\n\t\t\t\t\t\ttype: r.type as\n\t\t\t\t\t\t\t| 'A'\n\t\t\t\t\t\t\t| 'AAAA'\n\t\t\t\t\t\t\t| 'CNAME'\n\t\t\t\t\t\t\t| 'MX'\n\t\t\t\t\t\t\t| 'TXT'\n\t\t\t\t\t\t\t| 'SRV'\n\t\t\t\t\t\t\t| 'CAA',\n\t\t\t\t\t})),\n\t\t\t\t);\n\n\t\t\t\tfor (const record of recordsToDelete) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tdeleted: true,\n\t\t\t\t\t\tnotFound: false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// If batch delete fails, report error for all records\n\t\t\t\tfor (const record of recordsToDelete) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\tnotFound: false,\n\t\t\t\t\t\terror: String(error),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Mark non-existent records as not found\n\t\tfor (const record of records) {\n\t\t\tconst existing = existingRecords.find(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t);\n\t\t\tif (!existing) {\n\t\t\t\tresults.push({\n\t\t\t\t\trecord,\n\t\t\t\t\tdeleted: false,\n\t\t\t\t\tnotFound: true,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n}\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
- const require_credentials = require('./credentials-BapzjyV5.cjs');
2
-
1
+ const require_index = require("./index.cjs");
3
2
  //#region src/deploy/dns/hostinger-api.ts
4
3
  /**
5
4
  * Hostinger DNS API client
@@ -12,6 +11,9 @@ const HOSTINGER_API_BASE = "https://developers.hostinger.com";
12
11
  * Hostinger API error
13
12
  */
14
13
  var HostingerApiError = class extends Error {
14
+ status;
15
+ statusText;
16
+ errors;
15
17
  constructor(message, status, statusText, errors) {
16
18
  super(message);
17
19
  this.status = status;
@@ -65,7 +67,7 @@ var HostingerApi = class {
65
67
  throw new HostingerApiError(errorMessage, response.status, response.statusText, errors);
66
68
  }
67
69
  const text = await response.text();
68
- if (!text || text.trim() === "") return void 0;
70
+ if (!text || text.trim() === "") return;
69
71
  return JSON.parse(text);
70
72
  }
71
73
  /**
@@ -122,8 +124,7 @@ var HostingerApi = class {
122
124
  * @param type - Record type (e.g., 'A')
123
125
  */
124
126
  async recordExists(domain, name, type = "A") {
125
- const records = await this.getRecords(domain);
126
- return records.some((r) => r.name === name && r.type === type);
127
+ return (await this.getRecords(domain)).some((r) => r.name === name && r.type === type);
127
128
  }
128
129
  /**
129
130
  * Create a single A record if it doesn't exist
@@ -135,8 +136,7 @@ var HostingerApi = class {
135
136
  * @returns true if created, false if already exists
136
137
  */
137
138
  async createARecordIfNotExists(domain, subdomain, ip, ttl = 300) {
138
- const exists = await this.recordExists(domain, subdomain, "A");
139
- if (exists) return false;
139
+ if (await this.recordExists(domain, subdomain, "A")) return false;
140
140
  await this.upsertRecords(domain, [{
141
141
  name: subdomain,
142
142
  type: "A",
@@ -146,10 +146,14 @@ var HostingerApi = class {
146
146
  return true;
147
147
  }
148
148
  };
149
-
150
149
  //#endregion
151
150
  //#region src/deploy/dns/HostingerProvider.ts
152
151
  /**
152
+ * Hostinger DNS Provider
153
+ *
154
+ * Implements DnsProvider interface using the Hostinger DNS API.
155
+ */
156
+ /**
153
157
  * Hostinger DNS provider implementation.
154
158
  */
155
159
  var HostingerProvider = class {
@@ -160,15 +164,13 @@ var HostingerProvider = class {
160
164
  */
161
165
  async getApi() {
162
166
  if (this.api) return this.api;
163
- const token = await require_credentials.getHostingerToken();
167
+ const token = await require_index.getHostingerToken();
164
168
  if (!token) throw new Error("Hostinger API token not configured. Run `gkm login --service=hostinger` or get your token from https://hpanel.hostinger.com/profile/api");
165
169
  this.api = new HostingerApi(token);
166
170
  return this.api;
167
171
  }
168
172
  async getRecords(domain) {
169
- const api = await this.getApi();
170
- const records = await api.getRecords(domain);
171
- return records.map((r) => ({
173
+ return (await (await this.getApi()).getRecords(domain)).map((r) => ({
172
174
  name: r.name,
173
175
  type: r.type,
174
176
  ttl: r.ttl,
@@ -227,18 +229,15 @@ var HostingerProvider = class {
227
229
  error: String(error)
228
230
  });
229
231
  }
230
- for (const record of records) {
231
- const existing = existingRecords.find((r) => r.name === record.name && r.type === record.type);
232
- if (!existing) results.push({
233
- record,
234
- deleted: false,
235
- notFound: true
236
- });
237
- }
232
+ for (const record of records) if (!existingRecords.find((r) => r.name === record.name && r.type === record.type)) results.push({
233
+ record,
234
+ deleted: false,
235
+ notFound: true
236
+ });
238
237
  return results;
239
238
  }
240
239
  };
241
-
242
240
  //#endregion
243
241
  exports.HostingerProvider = HostingerProvider;
244
- //# sourceMappingURL=HostingerProvider-BOvsgATO.cjs.map
242
+
243
+ //# sourceMappingURL=HostingerProvider-BZFGYYVA.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HostingerProvider-BZFGYYVA.cjs","names":["getHostingerToken"],"sources":["../src/deploy/dns/hostinger-api.ts","../src/deploy/dns/HostingerProvider.ts"],"sourcesContent":["/**\n * Hostinger DNS API client\n *\n * API Documentation: https://developers.hostinger.com/\n * Authentication: Bearer token from hpanel.hostinger.com/profile/api\n */\n\nconst HOSTINGER_API_BASE = 'https://developers.hostinger.com';\n\n/**\n * DNS record types supported by Hostinger\n */\nexport type DnsRecordType =\n\t| 'A'\n\t| 'AAAA'\n\t| 'CNAME'\n\t| 'MX'\n\t| 'TXT'\n\t| 'NS'\n\t| 'SRV'\n\t| 'CAA';\n\n/**\n * A single DNS record\n */\nexport interface DnsRecord {\n\t/** Subdomain name (e.g., 'api.joemoer' for api.joemoer.traflabs.io) */\n\tname: string;\n\t/** Record type */\n\ttype: DnsRecordType;\n\t/** TTL in seconds */\n\tttl: number;\n\t/** Record values */\n\trecords: Array<{ content: string }>;\n}\n\n/**\n * Filter for deleting specific records\n */\nexport interface DnsRecordFilter {\n\tname: string;\n\ttype: DnsRecordType;\n}\n\n/**\n * API error response\n */\nexport interface HostingerErrorResponse {\n\tmessage?: string;\n\terrors?: Record<string, string[]>;\n}\n\n/**\n * Hostinger API error\n */\nexport class HostingerApiError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t\tpublic statusText: string,\n\t\tpublic errors?: Record<string, string[]>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'HostingerApiError';\n\t}\n}\n\n/**\n * Hostinger DNS API client\n *\n * @example\n * ```ts\n * const api = new HostingerApi(token);\n *\n * // Get all records for a domain\n * const records = await api.getRecords('traflabs.io');\n *\n * // Create/update records\n * await api.upsertRecords('traflabs.io', [\n * { name: 'api.joemoer', type: 'A', ttl: 300, records: ['1.2.3.4'] }\n * ]);\n * ```\n */\nexport class HostingerApi {\n\tprivate token: string;\n\n\tconstructor(token: string) {\n\t\tthis.token = token;\n\t}\n\n\t/**\n\t * Make a request to the Hostinger API\n\t */\n\tprivate async request<T>(\n\t\tmethod: 'GET' | 'POST' | 'PUT' | 'DELETE',\n\t\tendpoint: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst url = `${HOSTINGER_API_BASE}${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\tAuthorization: `Bearer ${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 = `Hostinger API error: ${response.status} ${response.statusText}`;\n\t\t\tlet errors: Record<string, string[]> | undefined;\n\n\t\t\ttry {\n\t\t\t\tconst errorBody = (await response.json()) as HostingerErrorResponse;\n\t\t\t\tif (errorBody.message) {\n\t\t\t\t\terrorMessage = `Hostinger API error: ${errorBody.message}`;\n\t\t\t\t}\n\t\t\t\terrors = errorBody.errors;\n\t\t\t} catch {\n\t\t\t\t// Ignore JSON parse errors\n\t\t\t}\n\n\t\t\tthrow new HostingerApiError(\n\t\t\t\terrorMessage,\n\t\t\t\tresponse.status,\n\t\t\t\tresponse.statusText,\n\t\t\t\terrors,\n\t\t\t);\n\t\t}\n\n\t\t// Handle empty responses\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 * Get all DNS records for a domain\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t */\n\tasync getRecords(domain: string): Promise<DnsRecord[]> {\n\t\t// The zone comes back as a **bare array**, not `{ data: [...] }`.\n\t\t//\n\t\t// This read `response.data || []`, so every call returned zero records\n\t\t// against the real API while every mocked test passed. Silently: no error,\n\t\t// no warning, just an empty zone. `upsertRecords` uses this to decide what\n\t\t// already exists, so everything looked new and every write was reported as\n\t\t// a creation — harmless only because the PUT merges rather than replaces.\n\t\t//\n\t\t// Both shapes are accepted because one of them is a guess: the wrapper was\n\t\t// written against documentation and the array is what the API actually\n\t\t// sends, so tolerating an envelope costs a line and survives it appearing.\n\t\tconst response = await this.request<DnsRecord[] | { data?: DnsRecord[] }>(\n\t\t\t'GET',\n\t\t\t`/api/dns/v1/zones/${domain}`,\n\t\t);\n\n\t\tif (Array.isArray(response)) return response;\n\n\t\treturn response?.data ?? [];\n\t}\n\n\t/**\n\t * Create or update DNS records\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param records - Records to create/update\n\t * @param overwrite - If true, replaces all existing records. If false, merges with existing.\n\t */\n\tasync upsertRecords(\n\t\tdomain: string,\n\t\trecords: DnsRecord[],\n\t\toverwrite = false,\n\t): Promise<void> {\n\t\tawait this.request('PUT', `/api/dns/v1/zones/${domain}`, {\n\t\t\toverwrite,\n\t\t\tzone: records,\n\t\t});\n\t}\n\n\t/**\n\t * Validate DNS records before applying\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param records - Records to validate\n\t * @returns true if valid, throws if invalid\n\t */\n\tasync validateRecords(\n\t\tdomain: string,\n\t\trecords: DnsRecord[],\n\t): Promise<boolean> {\n\t\tawait this.request('POST', `/api/dns/v1/zones/${domain}/validate`, {\n\t\t\toverwrite: false,\n\t\t\tzone: records,\n\t\t});\n\t\treturn true;\n\t}\n\n\t/**\n\t * Delete specific DNS records\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param filters - Filters to match records for deletion\n\t */\n\tasync deleteRecords(\n\t\tdomain: string,\n\t\tfilters: DnsRecordFilter[],\n\t): Promise<void> {\n\t\tawait this.request('DELETE', `/api/dns/v1/zones/${domain}`, {\n\t\t\tfilters,\n\t\t});\n\t}\n\n\t/**\n\t * Check if a specific record exists\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param name - Subdomain name (e.g., 'api.joemoer')\n\t * @param type - Record type (e.g., 'A')\n\t */\n\tasync recordExists(\n\t\tdomain: string,\n\t\tname: string,\n\t\ttype: DnsRecordType = 'A',\n\t): Promise<boolean> {\n\t\tconst records = await this.getRecords(domain);\n\t\treturn records.some((r) => r.name === name && r.type === type);\n\t}\n\n\t/**\n\t * Create a single A record if it doesn't exist\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param subdomain - Subdomain name (e.g., 'api.joemoer')\n\t * @param ip - IP address to point to\n\t * @param ttl - TTL in seconds (default: 300)\n\t * @returns true if created, false if already exists\n\t */\n\tasync createARecordIfNotExists(\n\t\tdomain: string,\n\t\tsubdomain: string,\n\t\tip: string,\n\t\tttl = 300,\n\t): Promise<boolean> {\n\t\tconst exists = await this.recordExists(domain, subdomain, 'A');\n\t\tif (exists) {\n\t\t\treturn false;\n\t\t}\n\n\t\tawait this.upsertRecords(domain, [\n\t\t\t{\n\t\t\t\tname: subdomain,\n\t\t\t\ttype: 'A',\n\t\t\t\tttl,\n\t\t\t\trecords: [{ content: ip }],\n\t\t\t},\n\t\t]);\n\n\t\treturn true;\n\t}\n}\n","/**\n * Hostinger DNS Provider\n *\n * Implements DnsProvider interface using the Hostinger DNS API.\n */\n\nimport { getHostingerToken } from '../../auth/credentials';\nimport type {\n\tDeleteDnsRecord,\n\tDeleteResult,\n\tDnsProvider,\n\tDnsRecord,\n\tUpsertDnsRecord,\n\tUpsertResult,\n} from './DnsProvider';\nimport { HostingerApi } from './hostinger-api';\n\n/**\n * Hostinger DNS provider implementation.\n */\nexport class HostingerProvider implements DnsProvider {\n\treadonly name = 'hostinger';\n\tprivate api: HostingerApi | null = null;\n\n\t/**\n\t * Get or create the Hostinger API client.\n\t */\n\tprivate async getApi(): Promise<HostingerApi> {\n\t\tif (this.api) {\n\t\t\treturn this.api;\n\t\t}\n\n\t\tconst token = await getHostingerToken();\n\t\tif (!token) {\n\t\t\tthrow new Error(\n\t\t\t\t'Hostinger API token not configured. Run `gkm login --service=hostinger` or get your token from https://hpanel.hostinger.com/profile/api',\n\t\t\t);\n\t\t}\n\n\t\tthis.api = new HostingerApi(token);\n\t\treturn this.api;\n\t}\n\n\tasync getRecords(domain: string): Promise<DnsRecord[]> {\n\t\tconst api = await this.getApi();\n\t\tconst records = await api.getRecords(domain);\n\n\t\treturn records.map((r) => ({\n\t\t\tname: r.name,\n\t\t\ttype: r.type,\n\t\t\tttl: r.ttl,\n\t\t\tvalues: r.records.map((rec) => rec.content),\n\t\t}));\n\t}\n\n\tasync upsertRecords(\n\t\tdomain: string,\n\t\trecords: UpsertDnsRecord[],\n\t): Promise<UpsertResult[]> {\n\t\tconst api = await this.getApi();\n\t\tconst results: UpsertResult[] = [];\n\n\t\t// Get existing records to check what already exists\n\t\tconst existingRecords = await api.getRecords(domain);\n\n\t\tfor (const record of records) {\n\t\t\tconst existing = existingRecords.find(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t);\n\n\t\t\tconst existingValue = existing?.records?.[0]?.content;\n\n\t\t\tif (existing && existingValue === record.value) {\n\t\t\t\t// Record exists with same value - unchanged\n\t\t\t\tresults.push({\n\t\t\t\t\trecord,\n\t\t\t\t\tcreated: false,\n\t\t\t\t\tunchanged: true,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Create or update the record\n\t\t\tawait api.upsertRecords(domain, [\n\t\t\t\t{\n\t\t\t\t\tname: record.name,\n\t\t\t\t\ttype: record.type,\n\t\t\t\t\tttl: record.ttl,\n\t\t\t\t\trecords: [{ content: record.value }],\n\t\t\t\t},\n\t\t\t]);\n\n\t\t\tresults.push({\n\t\t\t\trecord,\n\t\t\t\tcreated: !existing,\n\t\t\t\tunchanged: false,\n\t\t\t});\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tasync deleteRecords(\n\t\tdomain: string,\n\t\trecords: DeleteDnsRecord[],\n\t): Promise<DeleteResult[]> {\n\t\tconst api = await this.getApi();\n\t\tconst results: DeleteResult[] = [];\n\n\t\t// Get existing records to check what exists\n\t\tconst existingRecords = await api.getRecords(domain);\n\n\t\t// Filter to only records that exist\n\t\tconst recordsToDelete = records.filter((record) =>\n\t\t\texistingRecords.some(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t),\n\t\t);\n\n\t\t// Delete existing records\n\t\tif (recordsToDelete.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait api.deleteRecords(\n\t\t\t\t\tdomain,\n\t\t\t\t\trecordsToDelete.map((r) => ({\n\t\t\t\t\t\tname: r.name,\n\t\t\t\t\t\ttype: r.type as\n\t\t\t\t\t\t\t| 'A'\n\t\t\t\t\t\t\t| 'AAAA'\n\t\t\t\t\t\t\t| 'CNAME'\n\t\t\t\t\t\t\t| 'MX'\n\t\t\t\t\t\t\t| 'TXT'\n\t\t\t\t\t\t\t| 'SRV'\n\t\t\t\t\t\t\t| 'CAA',\n\t\t\t\t\t})),\n\t\t\t\t);\n\n\t\t\t\tfor (const record of recordsToDelete) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tdeleted: true,\n\t\t\t\t\t\tnotFound: false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// If batch delete fails, report error for all records\n\t\t\t\tfor (const record of recordsToDelete) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\tnotFound: false,\n\t\t\t\t\t\terror: String(error),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Mark non-existent records as not found\n\t\tfor (const record of records) {\n\t\t\tconst existing = existingRecords.find(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t);\n\t\t\tif (!existing) {\n\t\t\t\tresults.push({\n\t\t\t\t\trecord,\n\t\t\t\t\tdeleted: false,\n\t\t\t\t\tnotFound: true,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n}\n"],"mappings":"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"}
@@ -1,9 +1,13 @@
1
- const require_chunk = require('./chunk-CUT6urMc.cjs');
2
- const node_path = require_chunk.__toESM(require("node:path"));
3
- const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
4
-
1
+ let node_fs_promises = require("node:fs/promises");
2
+ let node_path = require("node:path");
5
3
  //#region src/deploy/LocalStateProvider.ts
6
4
  /**
5
+ * Local Filesystem State Provider
6
+ *
7
+ * Stores deployment state in .gkm/deploy-{stage}.json files.
8
+ * This is the default provider for local development.
9
+ */
10
+ /**
7
11
  * Get the state file path for a stage.
8
12
  */
9
13
  function getStateFilePath(workspaceRoot, stage) {
@@ -15,6 +19,7 @@ function getStateFilePath(workspaceRoot, stage) {
15
19
  * Stores state in .gkm/deploy-{stage}.json files in the workspace root.
16
20
  */
17
21
  var LocalStateProvider = class {
22
+ workspaceRoot;
18
23
  constructor(workspaceRoot) {
19
24
  this.workspaceRoot = workspaceRoot;
20
25
  }
@@ -37,7 +42,7 @@ var LocalStateProvider = class {
37
42
  await (0, node_fs_promises.writeFile)(filePath, JSON.stringify(state, null, 2));
38
43
  }
39
44
  };
40
-
41
45
  //#endregion
42
46
  exports.LocalStateProvider = LocalStateProvider;
43
- //# sourceMappingURL=LocalStateProvider-yjXZXZDM.cjs.map
47
+
48
+ //# sourceMappingURL=LocalStateProvider-7HR6SS2t.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalStateProvider-7HR6SS2t.cjs","names":["join","readFile","mkdir","writeFile"],"sources":["../src/deploy/LocalStateProvider.ts"],"sourcesContent":["/**\n * Local Filesystem State Provider\n *\n * Stores deployment state in .gkm/deploy-{stage}.json files.\n * This is the default provider for local development.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { StateProvider } from './StateProvider';\nimport type { DokployStageState } from './state';\n\n/**\n * Get the state file path for a stage.\n */\nfunction getStateFilePath(workspaceRoot: string, stage: string): string {\n\treturn join(workspaceRoot, '.gkm', `deploy-${stage}.json`);\n}\n\n/**\n * Local filesystem state provider.\n *\n * Stores state in .gkm/deploy-{stage}.json files in the workspace root.\n */\nexport class LocalStateProvider implements StateProvider {\n\tconstructor(private readonly workspaceRoot: string) {}\n\n\tasync read(stage: string): Promise<DokployStageState | null> {\n\t\tconst filePath = getStateFilePath(this.workspaceRoot, stage);\n\n\t\ttry {\n\t\t\tconst content = await readFile(filePath, 'utf-8');\n\t\t\treturn JSON.parse(content) as DokployStageState;\n\t\t} catch (error) {\n\t\t\t// File doesn't exist - return null\n\t\t\tif ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t// Log other errors but don't fail\n\t\t\tconsole.warn(`Warning: Could not read deploy state: ${error}`);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync write(stage: string, state: DokployStageState): Promise<void> {\n\t\tconst filePath = getStateFilePath(this.workspaceRoot, stage);\n\t\tconst dir = join(this.workspaceRoot, '.gkm');\n\n\t\t// Ensure .gkm directory exists\n\t\tawait mkdir(dir, { recursive: true });\n\n\t\t// Update last deployed timestamp\n\t\tstate.lastDeployedAt = new Date().toISOString();\n\n\t\tawait writeFile(filePath, JSON.stringify(state, null, 2));\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AAeA,SAAS,iBAAiB,eAAuB,OAAuB;CACvE,QAAA,GAAOA,UAAAA,KAAAA,CAAK,eAAe,QAAQ,UAAU,MAAM,MAAM;AAC1D;;;;;;AAOA,IAAa,qBAAb,MAAyD;CAC3B;CAA7B,YAAY,eAAwC;EAAvB,KAAA,gBAAA;CAAwB;CAErD,MAAM,KAAK,OAAkD;EAC5D,MAAM,WAAW,iBAAiB,KAAK,eAAe,KAAK;EAE3D,IAAI;GACH,MAAM,UAAU,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,UAAU,OAAO;GAChD,OAAO,KAAK,MAAM,OAAO;EAC1B,SAAS,OAAO;GAEf,IAAK,MAAgC,SAAS,UAC7C,OAAO;GAGR,QAAQ,KAAK,yCAAyC,OAAO;GAC7D,OAAO;EACR;CACD;CAEA,MAAM,MAAM,OAAe,OAAyC;EACnE,MAAM,WAAW,iBAAiB,KAAK,eAAe,KAAK;EAC3D,MAAM,OAAA,GAAMD,UAAAA,KAAAA,CAAK,KAAK,eAAe,MAAM;EAG3C,OAAA,GAAME,iBAAAA,MAAAA,CAAM,KAAK,EAAE,WAAW,KAAK,CAAC;EAGpC,MAAM,kCAAiB,IAAI,KAAK,EAAA,CAAE,YAAY;EAE9C,OAAA,GAAMC,iBAAAA,UAAAA,CAAU,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;CACzD;AACD"}
@@ -1,8 +1,13 @@
1
- import { join } from "node:path";
2
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
3
-
2
+ import { join } from "node:path";
4
3
  //#region src/deploy/LocalStateProvider.ts
5
4
  /**
5
+ * Local Filesystem State Provider
6
+ *
7
+ * Stores deployment state in .gkm/deploy-{stage}.json files.
8
+ * This is the default provider for local development.
9
+ */
10
+ /**
6
11
  * Get the state file path for a stage.
7
12
  */
8
13
  function getStateFilePath(workspaceRoot, stage) {
@@ -14,6 +19,7 @@ function getStateFilePath(workspaceRoot, stage) {
14
19
  * Stores state in .gkm/deploy-{stage}.json files in the workspace root.
15
20
  */
16
21
  var LocalStateProvider = class {
22
+ workspaceRoot;
17
23
  constructor(workspaceRoot) {
18
24
  this.workspaceRoot = workspaceRoot;
19
25
  }
@@ -36,7 +42,7 @@ var LocalStateProvider = class {
36
42
  await writeFile(filePath, JSON.stringify(state, null, 2));
37
43
  }
38
44
  };
39
-
40
45
  //#endregion
41
46
  export { LocalStateProvider };
42
- //# sourceMappingURL=LocalStateProvider-Bq-0C0sg.mjs.map
47
+
48
+ //# sourceMappingURL=LocalStateProvider-D7QyJoLM.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalStateProvider-D7QyJoLM.mjs","names":[],"sources":["../src/deploy/LocalStateProvider.ts"],"sourcesContent":["/**\n * Local Filesystem State Provider\n *\n * Stores deployment state in .gkm/deploy-{stage}.json files.\n * This is the default provider for local development.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { StateProvider } from './StateProvider';\nimport type { DokployStageState } from './state';\n\n/**\n * Get the state file path for a stage.\n */\nfunction getStateFilePath(workspaceRoot: string, stage: string): string {\n\treturn join(workspaceRoot, '.gkm', `deploy-${stage}.json`);\n}\n\n/**\n * Local filesystem state provider.\n *\n * Stores state in .gkm/deploy-{stage}.json files in the workspace root.\n */\nexport class LocalStateProvider implements StateProvider {\n\tconstructor(private readonly workspaceRoot: string) {}\n\n\tasync read(stage: string): Promise<DokployStageState | null> {\n\t\tconst filePath = getStateFilePath(this.workspaceRoot, stage);\n\n\t\ttry {\n\t\t\tconst content = await readFile(filePath, 'utf-8');\n\t\t\treturn JSON.parse(content) as DokployStageState;\n\t\t} catch (error) {\n\t\t\t// File doesn't exist - return null\n\t\t\tif ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t// Log other errors but don't fail\n\t\t\tconsole.warn(`Warning: Could not read deploy state: ${error}`);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync write(stage: string, state: DokployStageState): Promise<void> {\n\t\tconst filePath = getStateFilePath(this.workspaceRoot, stage);\n\t\tconst dir = join(this.workspaceRoot, '.gkm');\n\n\t\t// Ensure .gkm directory exists\n\t\tawait mkdir(dir, { recursive: true });\n\n\t\t// Update last deployed timestamp\n\t\tstate.lastDeployedAt = new Date().toISOString();\n\n\t\tawait writeFile(filePath, JSON.stringify(state, null, 2));\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AAeA,SAAS,iBAAiB,eAAuB,OAAuB;CACvE,OAAO,KAAK,eAAe,QAAQ,UAAU,MAAM,MAAM;AAC1D;;;;;;AAOA,IAAa,qBAAb,MAAyD;CAC3B;CAA7B,YAAY,eAAwC;EAAvB,KAAA,gBAAA;CAAwB;CAErD,MAAM,KAAK,OAAkD;EAC5D,MAAM,WAAW,iBAAiB,KAAK,eAAe,KAAK;EAE3D,IAAI;GACH,MAAM,UAAU,MAAM,SAAS,UAAU,OAAO;GAChD,OAAO,KAAK,MAAM,OAAO;EAC1B,SAAS,OAAO;GAEf,IAAK,MAAgC,SAAS,UAC7C,OAAO;GAGR,QAAQ,KAAK,yCAAyC,OAAO;GAC7D,OAAO;EACR;CACD;CAEA,MAAM,MAAM,OAAe,OAAyC;EACnE,MAAM,WAAW,iBAAiB,KAAK,eAAe,KAAK;EAC3D,MAAM,MAAM,KAAK,KAAK,eAAe,MAAM;EAG3C,MAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;EAGpC,MAAM,kCAAiB,IAAI,KAAK,EAAA,CAAE,YAAY;EAE9C,MAAM,UAAU,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;CACzD;AACD"}
@@ -1,8 +1,12 @@
1
1
  import { fromIni } from "@aws-sdk/credential-providers";
2
2
  import { ChangeResourceRecordSetsCommand, ListHostedZonesByNameCommand, ListResourceRecordSetsCommand, Route53Client } from "@aws-sdk/client-route-53";
3
-
4
3
  //#region src/deploy/dns/Route53Provider.ts
5
4
  /**
5
+ * Route53 DNS Provider
6
+ *
7
+ * Implements DnsProvider interface using AWS Route53.
8
+ */
9
+ /**
6
10
  * Route53 DNS provider implementation.
7
11
  *
8
12
  * Uses AWS default credential chain for authentication.
@@ -34,8 +38,7 @@ var Route53Provider = class {
34
38
  DNSName: domain,
35
39
  MaxItems: 1
36
40
  });
37
- const response = await this.client.send(command);
38
- const zones = response.HostedZones ?? [];
41
+ const zones = (await this.client.send(command)).HostedZones ?? [];
39
42
  const normalizedDomain = domain.endsWith(".") ? domain : `${domain}.`;
40
43
  const zone = zones.find((z) => z.Name === normalizedDomain);
41
44
  if (!zone?.Id) throw new Error(`No hosted zone found for domain: ${domain}. Create one in Route53 or provide hostedZoneId in config.`);
@@ -48,9 +51,8 @@ var Route53Provider = class {
48
51
  * Excludes NS and SOA which are auto-managed by Route53 for the zone.
49
52
  */
50
53
  toRecordType(type) {
51
- const managedTypes = ["NS", "SOA"];
52
- if (managedTypes.includes(type)) return null;
53
- const validTypes = [
54
+ if (["NS", "SOA"].includes(type)) return null;
55
+ return [
54
56
  "A",
55
57
  "AAAA",
56
58
  "CNAME",
@@ -58,8 +60,7 @@ var Route53Provider = class {
58
60
  "TXT",
59
61
  "SRV",
60
62
  "CAA"
61
- ];
62
- return validTypes.includes(type) ? type : null;
63
+ ].includes(type) ? type : null;
63
64
  }
64
65
  /**
65
66
  * Extract subdomain from full record name relative to domain.
@@ -68,8 +69,7 @@ var Route53Provider = class {
68
69
  const normalizedDomain = domain.endsWith(".") ? domain : `${domain}.`;
69
70
  const normalizedName = recordName.endsWith(".") ? recordName : `${recordName}.`;
70
71
  if (normalizedName === normalizedDomain) return "@";
71
- const subdomain = normalizedName.replace(`.${normalizedDomain}`, "");
72
- return subdomain.replace(/\.$/, "");
72
+ return normalizedName.replace(`.${normalizedDomain}`, "").replace(/\.$/, "");
73
73
  }
74
74
  async getRecords(domain) {
75
75
  const zoneId = await this.getHostedZoneId(domain);
@@ -203,7 +203,7 @@ var Route53Provider = class {
203
203
  return results;
204
204
  }
205
205
  };
206
-
207
206
  //#endregion
208
207
  export { Route53Provider };
209
- //# sourceMappingURL=Route53Provider-C0xmer8U.mjs.map
208
+
209
+ //# sourceMappingURL=Route53Provider-Cvj40A8I.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Route53Provider-Cvj40A8I.mjs","names":[],"sources":["../src/deploy/dns/Route53Provider.ts"],"sourcesContent":["/**\n * Route53 DNS Provider\n *\n * Implements DnsProvider interface using AWS Route53.\n */\n\nimport {\n\tChangeResourceRecordSetsCommand,\n\tListHostedZonesByNameCommand,\n\tListResourceRecordSetsCommand,\n\tRoute53Client,\n\ttype RRType,\n} from '@aws-sdk/client-route-53';\nimport { fromIni } from '@aws-sdk/credential-providers';\nimport type {\n\tDeleteDnsRecord,\n\tDeleteResult,\n\tDnsProvider,\n\tDnsRecord,\n\tDnsRecordType,\n\tUpsertDnsRecord,\n\tUpsertResult,\n} from './DnsProvider';\n\nexport interface Route53ProviderOptions {\n\t/** AWS region (optional - uses AWS_REGION env var if not provided) */\n\tregion?: string;\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile?: string;\n\t/** Hosted zone ID (optional - auto-detected from domain if not provided) */\n\thostedZoneId?: string;\n\t/** Custom endpoint for testing with localstack */\n\tendpoint?: string;\n}\n\n/**\n * Route53 DNS provider implementation.\n *\n * Uses AWS default credential chain for authentication.\n * Region can be specified or will use AWS_REGION/AWS_DEFAULT_REGION env vars.\n * Profile can be specified to use a named profile from ~/.aws/credentials.\n */\nexport class Route53Provider implements DnsProvider {\n\treadonly name = 'route53';\n\tprivate client: Route53Client;\n\tprivate hostedZoneId?: string;\n\tprivate hostedZoneCache: Map<string, string> = new Map();\n\n\tconstructor(options: Route53ProviderOptions = {}) {\n\t\t// Route53 is a global service, default to us-east-1 if no region specified\n\t\tconst region = options.region ?? process.env.AWS_REGION ?? 'us-east-1';\n\n\t\tthis.client = new Route53Client({\n\t\t\tregion,\n\t\t\t...(options.endpoint && { endpoint: options.endpoint }),\n\t\t\t...(options.profile && {\n\t\t\t\tcredentials: fromIni({ profile: options.profile }),\n\t\t\t}),\n\t\t});\n\t\tthis.hostedZoneId = options.hostedZoneId;\n\t}\n\n\t/**\n\t * Get the hosted zone ID for a domain.\n\t * Uses cache to avoid repeated API calls.\n\t */\n\tprivate async getHostedZoneId(domain: string): Promise<string> {\n\t\t// Use configured zone ID if provided\n\t\tif (this.hostedZoneId) {\n\t\t\treturn this.hostedZoneId;\n\t\t}\n\n\t\t// Check cache\n\t\tif (this.hostedZoneCache.has(domain)) {\n\t\t\treturn this.hostedZoneCache.get(domain)!;\n\t\t}\n\n\t\t// Auto-detect from domain\n\t\tconst command = new ListHostedZonesByNameCommand({\n\t\t\tDNSName: domain,\n\t\t\tMaxItems: 1,\n\t\t});\n\n\t\tconst response = await this.client.send(command);\n\t\tconst zones = response.HostedZones ?? [];\n\n\t\t// Find exact match (domain with trailing dot)\n\t\tconst normalizedDomain = domain.endsWith('.') ? domain : `${domain}.`;\n\t\tconst zone = zones.find((z) => z.Name === normalizedDomain);\n\n\t\tif (!zone?.Id) {\n\t\t\tthrow new Error(\n\t\t\t\t`No hosted zone found for domain: ${domain}. Create one in Route53 or provide hostedZoneId in config.`,\n\t\t\t);\n\t\t}\n\n\t\t// Zone ID comes as \"/hostedzone/Z1234567890\" - extract just the ID\n\t\tconst zoneId = zone.Id.replace('/hostedzone/', '');\n\t\tthis.hostedZoneCache.set(domain, zoneId);\n\t\treturn zoneId;\n\t}\n\n\t/**\n\t * Convert Route53 record type to our DnsRecordType.\n\t * Excludes NS and SOA which are auto-managed by Route53 for the zone.\n\t */\n\tprivate toRecordType(type: string): DnsRecordType | null {\n\t\t// Exclude NS and SOA which are auto-managed zone records\n\t\tconst managedTypes = ['NS', 'SOA'];\n\t\tif (managedTypes.includes(type)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst validTypes: DnsRecordType[] = [\n\t\t\t'A',\n\t\t\t'AAAA',\n\t\t\t'CNAME',\n\t\t\t'MX',\n\t\t\t'TXT',\n\t\t\t'SRV',\n\t\t\t'CAA',\n\t\t];\n\t\treturn validTypes.includes(type as DnsRecordType)\n\t\t\t? (type as DnsRecordType)\n\t\t\t: null;\n\t}\n\n\t/**\n\t * Extract subdomain from full record name relative to domain.\n\t */\n\tprivate extractSubdomain(recordName: string, domain: string): string {\n\t\tconst normalizedDomain = domain.endsWith('.') ? domain : `${domain}.`;\n\t\tconst normalizedName = recordName.endsWith('.')\n\t\t\t? recordName\n\t\t\t: `${recordName}.`;\n\n\t\tif (normalizedName === normalizedDomain) {\n\t\t\treturn '@';\n\t\t}\n\n\t\t// Remove the domain suffix\n\t\tconst subdomain = normalizedName.replace(`.${normalizedDomain}`, '');\n\t\treturn subdomain.replace(/\\.$/, ''); // Remove trailing dot if any\n\t}\n\n\tasync getRecords(domain: string): Promise<DnsRecord[]> {\n\t\tconst zoneId = await this.getHostedZoneId(domain);\n\t\tconst records: DnsRecord[] = [];\n\n\t\tlet nextRecordName: string | undefined;\n\t\tlet nextRecordType: RRType | undefined;\n\n\t\t// Paginate through all records\n\t\tdo {\n\t\t\tconst command = new ListResourceRecordSetsCommand({\n\t\t\t\tHostedZoneId: zoneId,\n\t\t\t\tStartRecordName: nextRecordName,\n\t\t\t\tStartRecordType: nextRecordType,\n\t\t\t\tMaxItems: 100,\n\t\t\t});\n\n\t\t\tconst response = await this.client.send(command);\n\n\t\t\tfor (const recordSet of response.ResourceRecordSets ?? []) {\n\t\t\t\tconst type = this.toRecordType(recordSet.Type ?? '');\n\t\t\t\tif (!type || !recordSet.Name) continue;\n\n\t\t\t\tconst values = (recordSet.ResourceRecords ?? [])\n\t\t\t\t\t.map((r) => r.Value)\n\t\t\t\t\t.filter((v): v is string => !!v);\n\n\t\t\t\trecords.push({\n\t\t\t\t\tname: this.extractSubdomain(recordSet.Name, domain),\n\t\t\t\t\ttype,\n\t\t\t\t\tttl: recordSet.TTL ?? 300,\n\t\t\t\t\tvalues,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (response.IsTruncated) {\n\t\t\t\tnextRecordName = response.NextRecordName;\n\t\t\t\tnextRecordType = response.NextRecordType;\n\t\t\t} else {\n\t\t\t\tnextRecordName = undefined;\n\t\t\t}\n\t\t} while (nextRecordName);\n\n\t\treturn records;\n\t}\n\n\tasync upsertRecords(\n\t\tdomain: string,\n\t\trecords: UpsertDnsRecord[],\n\t): Promise<UpsertResult[]> {\n\t\tconst zoneId = await this.getHostedZoneId(domain);\n\t\tconst results: UpsertResult[] = [];\n\n\t\t// Get existing records to determine if creating or updating\n\t\tconst existingRecords = await this.getRecords(domain);\n\n\t\t// Process records in batches (Route53 allows max 1000 changes per request)\n\t\tconst batchSize = 100;\n\t\tfor (let i = 0; i < records.length; i += batchSize) {\n\t\t\tconst batch = records.slice(i, i + batchSize);\n\t\t\tconst changes = [];\n\n\t\t\tfor (const record of batch) {\n\t\t\t\tconst existing = existingRecords.find(\n\t\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t\t);\n\n\t\t\t\tconst existingValue = existing?.values?.[0];\n\n\t\t\t\tif (existing && existingValue === record.value) {\n\t\t\t\t\t// Record exists with same value - unchanged\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tcreated: false,\n\t\t\t\t\t\tunchanged: true,\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Build full record name\n\t\t\t\tconst recordName =\n\t\t\t\t\trecord.name === '@' ? domain : `${record.name}.${domain}`;\n\n\t\t\t\tchanges.push({\n\t\t\t\t\tAction: 'UPSERT' as const,\n\t\t\t\t\tResourceRecordSet: {\n\t\t\t\t\t\tName: recordName,\n\t\t\t\t\t\tType: record.type,\n\t\t\t\t\t\tTTL: record.ttl,\n\t\t\t\t\t\tResourceRecords: [{ Value: record.value }],\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tresults.push({\n\t\t\t\t\trecord,\n\t\t\t\t\tcreated: !existing,\n\t\t\t\t\tunchanged: false,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Execute batch if there are changes\n\t\t\tif (changes.length > 0) {\n\t\t\t\tconst command = new ChangeResourceRecordSetsCommand({\n\t\t\t\t\tHostedZoneId: zoneId,\n\t\t\t\t\tChangeBatch: {\n\t\t\t\t\t\tComment: 'Upsert by gkm deploy',\n\t\t\t\t\t\tChanges: changes,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tawait this.client.send(command);\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tasync deleteRecords(\n\t\tdomain: string,\n\t\trecords: DeleteDnsRecord[],\n\t): Promise<DeleteResult[]> {\n\t\tconst zoneId = await this.getHostedZoneId(domain);\n\t\tconst results: DeleteResult[] = [];\n\n\t\t// Get existing records to find the ones to delete\n\t\tconst existingRecords = await this.getRecords(domain);\n\n\t\t// Process records in batches (Route53 allows max 1000 changes per request)\n\t\tconst batchSize = 100;\n\t\tfor (let i = 0; i < records.length; i += batchSize) {\n\t\t\tconst batch = records.slice(i, i + batchSize);\n\t\t\tconst changes = [];\n\n\t\t\tfor (const record of batch) {\n\t\t\t\tconst existing = existingRecords.find(\n\t\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t\t);\n\n\t\t\t\tif (!existing) {\n\t\t\t\t\t// Record doesn't exist - already deleted\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\tnotFound: true,\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Build full record name\n\t\t\t\tconst recordName =\n\t\t\t\t\trecord.name === '@' ? domain : `${record.name}.${domain}`;\n\n\t\t\t\tchanges.push({\n\t\t\t\t\tAction: 'DELETE' as const,\n\t\t\t\t\tResourceRecordSet: {\n\t\t\t\t\t\tName: recordName,\n\t\t\t\t\t\tType: record.type,\n\t\t\t\t\t\tTTL: existing.ttl,\n\t\t\t\t\t\tResourceRecords: existing.values.map((v) => ({ Value: v })),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tresults.push({\n\t\t\t\t\trecord,\n\t\t\t\t\tdeleted: true,\n\t\t\t\t\tnotFound: false,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Execute batch if there are changes\n\t\t\tif (changes.length > 0) {\n\t\t\t\ttry {\n\t\t\t\t\tconst command = new ChangeResourceRecordSetsCommand({\n\t\t\t\t\t\tHostedZoneId: zoneId,\n\t\t\t\t\t\tChangeBatch: {\n\t\t\t\t\t\t\tComment: 'Delete by gkm undeploy',\n\t\t\t\t\t\t\tChanges: changes,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tawait this.client.send(command);\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Mark all records in this batch as failed\n\t\t\t\t\tfor (const result of results.slice(-changes.length)) {\n\t\t\t\t\t\tif (result.deleted) {\n\t\t\t\t\t\t\tresult.deleted = false;\n\t\t\t\t\t\t\tresult.error = String(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n}\n"],"mappings":"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"}