@geekmidas/cli 10.0.0-alpha.64 → 10.0.0-alpha.65

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 (175) hide show
  1. package/README.md +1 -1
  2. package/dist/{HostingerProvider-Bx2lHHvu.cjs → HostingerProvider-Dlmbfass.cjs} +3 -3
  3. package/dist/{HostingerProvider-Bx2lHHvu.cjs.map → HostingerProvider-Dlmbfass.cjs.map} +1 -1
  4. package/dist/{HostingerProvider-Cd4aS5Kj.mjs → HostingerProvider-x9gQTAGZ.mjs} +2 -2
  5. package/dist/{HostingerProvider-Cd4aS5Kj.mjs.map → HostingerProvider-x9gQTAGZ.mjs.map} +1 -1
  6. package/dist/{LegacyStateStore-C2abV49N.cjs → LegacyStateStore-BGoo__sO.cjs} +2 -2
  7. package/dist/{LegacyStateStore-C2abV49N.cjs.map → LegacyStateStore-BGoo__sO.cjs.map} +1 -1
  8. package/dist/{LegacyStateStore-DIgkGaUP.mjs → LegacyStateStore-BUu1nQ5u.mjs} +2 -2
  9. package/dist/{LegacyStateStore-DIgkGaUP.mjs.map → LegacyStateStore-BUu1nQ5u.mjs.map} +1 -1
  10. package/dist/{LocalStateStore-DiJqKyPy.cjs → LocalStateStore-93urXyQe.cjs} +2 -2
  11. package/dist/{LocalStateStore-DiJqKyPy.cjs.map → LocalStateStore-93urXyQe.cjs.map} +1 -1
  12. package/dist/{LocalStateStore-Cy1ZBW7U.mjs → LocalStateStore-BRhMQRuy.mjs} +2 -2
  13. package/dist/{LocalStateStore-Cy1ZBW7U.mjs.map → LocalStateStore-BRhMQRuy.mjs.map} +1 -1
  14. package/dist/{S3StateStore-Ds0V1Lgm.cjs → S3StateStore-F4dxw6kS.cjs} +2 -2
  15. package/dist/{S3StateStore-Ds0V1Lgm.cjs.map → S3StateStore-F4dxw6kS.cjs.map} +1 -1
  16. package/dist/{S3StateStore-D3x6ivBL.mjs → S3StateStore-pw1GhWwt.mjs} +2 -2
  17. package/dist/{S3StateStore-D3x6ivBL.mjs.map → S3StateStore-pw1GhWwt.mjs.map} +1 -1
  18. package/dist/{SSMStateStore-CLipvx47.cjs → SSMStateStore-Bt9acuAF.cjs} +2 -2
  19. package/dist/{SSMStateStore-CLipvx47.cjs.map → SSMStateStore-Bt9acuAF.cjs.map} +1 -1
  20. package/dist/{SSMStateStore-DfF174xQ.mjs → SSMStateStore-Dmj7zzFO.mjs} +2 -2
  21. package/dist/{SSMStateStore-DfF174xQ.mjs.map → SSMStateStore-Dmj7zzFO.mjs.map} +1 -1
  22. package/dist/{bundler-C8uJf324.cjs → bundler-D6MlIfBT.cjs} +3 -3
  23. package/dist/{bundler-C8uJf324.cjs.map → bundler-D6MlIfBT.cjs.map} +1 -1
  24. package/dist/{bundler-DYgm9xsd.mjs → bundler-D_14JLZF.mjs} +3 -3
  25. package/dist/{bundler-DYgm9xsd.mjs.map → bundler-D_14JLZF.mjs.map} +1 -1
  26. package/dist/{cli-D3jofJ6j.cjs → cli-BfH1deAT.cjs} +6 -6
  27. package/dist/{cli-D3jofJ6j.cjs.map → cli-BfH1deAT.cjs.map} +1 -1
  28. package/dist/{cli-Bogz-zEj.mjs → cli-P56vvrDQ.mjs} +5 -5
  29. package/dist/{cli-Bogz-zEj.mjs.map → cli-P56vvrDQ.mjs.map} +1 -1
  30. package/dist/compose-391oEKj1.cjs +1982 -0
  31. package/dist/compose-391oEKj1.cjs.map +1 -0
  32. package/dist/compose-DprE3ASm.mjs +1862 -0
  33. package/dist/compose-DprE3ASm.mjs.map +1 -0
  34. package/dist/{config-DqNRiQOp.d.mts → config-BonvTW8p.d.mts} +3 -3
  35. package/dist/{config-Be-4_I6H.d.cts.map → config-BonvTW8p.d.mts.map} +1 -1
  36. package/dist/{config-Be-4_I6H.d.cts → config-V_X_BMpm.d.cts} +3 -3
  37. package/dist/{config-DqNRiQOp.d.mts.map → config-V_X_BMpm.d.cts.map} +1 -1
  38. package/dist/config.cjs +1 -1
  39. package/dist/config.d.cts +3 -3
  40. package/dist/config.d.mts +3 -3
  41. package/dist/config.mjs +1 -1
  42. package/dist/{credentials-B70vmLcm.cjs → credentials-Bzbg23n8.cjs} +176 -3
  43. package/dist/credentials-Bzbg23n8.cjs.map +1 -0
  44. package/dist/{credentials-s0wMeZBU.mjs → credentials-Dq1MnWQf.mjs} +128 -3
  45. package/dist/credentials-Dq1MnWQf.mjs.map +1 -0
  46. package/dist/{define-CYs0PEfZ.mjs → define-DR8xGYfL.mjs} +5 -1
  47. package/dist/define-DR8xGYfL.mjs.map +1 -0
  48. package/dist/{define-zUsP72ym.cjs → define-DzRz8FgH.cjs} +5 -1
  49. package/dist/define-DzRz8FgH.cjs.map +1 -0
  50. package/dist/deploy/public.cjs +18 -18
  51. package/dist/deploy/public.cjs.map +1 -1
  52. package/dist/deploy/public.d.cts +7 -4
  53. package/dist/deploy/public.d.cts.map +1 -1
  54. package/dist/deploy/public.d.mts +7 -4
  55. package/dist/deploy/public.d.mts.map +1 -1
  56. package/dist/deploy/public.mjs +11 -11
  57. package/dist/{deploy-Dc2LBJaK.mjs → deploy-9MBG_QLd.mjs} +8 -7
  58. package/dist/deploy-9MBG_QLd.mjs.map +1 -0
  59. package/dist/{deploy-yUcR2OlW.cjs → deploy-B80idPUZ.cjs} +11 -10
  60. package/dist/deploy-B80idPUZ.cjs.map +1 -0
  61. package/dist/{deploy-ZRXIPp1c.mjs → deploy-CZ5nlpsu.mjs} +13 -119
  62. package/dist/deploy-CZ5nlpsu.mjs.map +1 -0
  63. package/dist/{deploy-55rJkdKz.cjs → deploy-Cy8igpQF.cjs} +39 -145
  64. package/dist/deploy-Cy8igpQF.cjs.map +1 -0
  65. package/dist/{dokploy-CepdXuMl.mjs → dokploy-BdtBePjp.mjs} +3 -3
  66. package/dist/{dokploy-CepdXuMl.mjs.map → dokploy-BdtBePjp.mjs.map} +1 -1
  67. package/dist/{dokploy-D1d37rvM.cjs → dokploy-iFY8MY-L.cjs} +3 -3
  68. package/dist/{dokploy-D1d37rvM.cjs.map → dokploy-iFY8MY-L.cjs.map} +1 -1
  69. package/dist/{encryption-C9X98u2F.cjs → encryption-B1wJwgae.cjs} +5 -5
  70. package/dist/{encryption-C9X98u2F.cjs.map → encryption-B1wJwgae.cjs.map} +1 -1
  71. package/dist/{encryption-CKHjMpCr.mjs → encryption-DF_hgIpl.mjs} +4 -4
  72. package/dist/{encryption-CKHjMpCr.mjs.map → encryption-DF_hgIpl.mjs.map} +1 -1
  73. package/dist/{github-D-byNI7T.mjs → github-B0YYE98x.mjs} +3 -3
  74. package/dist/{github-D-byNI7T.mjs.map → github-B0YYE98x.mjs.map} +1 -1
  75. package/dist/{github-Dd3tMa1u.cjs → github-CYcmTdFs.cjs} +3 -3
  76. package/dist/{github-Dd3tMa1u.cjs.map → github-CYcmTdFs.cjs.map} +1 -1
  77. package/dist/{index-CfE9Y885.d.cts → index-DPUAGJaK.d.cts} +16 -3
  78. package/dist/index-DPUAGJaK.d.cts.map +1 -0
  79. package/dist/{index-CJ2qsSpp.d.mts → index-DwKD_8lt.d.mts} +16 -3
  80. package/dist/index-DwKD_8lt.d.mts.map +1 -0
  81. package/dist/index.cjs +233 -1667
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.mjs +196 -1614
  84. package/dist/index.mjs.map +1 -1
  85. package/dist/{local-C7wnrcYK.cjs → local-BdH4XN0E.cjs} +1 -178
  86. package/dist/local-BdH4XN0E.cjs.map +1 -0
  87. package/dist/{local-Cd8ZYQG3.mjs → local-CWR4Y_kW.mjs} +3 -126
  88. package/dist/local-CWR4Y_kW.mjs.map +1 -0
  89. package/dist/{migrate-B4gngTLe.mjs → migrate-B2qL5wj4.mjs} +6 -5
  90. package/dist/{migrate-B4gngTLe.mjs.map → migrate-B2qL5wj4.mjs.map} +1 -1
  91. package/dist/{migrate-tx9-P9uz.cjs → migrate-q4pPAXtO.cjs} +19 -18
  92. package/dist/{migrate-tx9-P9uz.cjs.map → migrate-q4pPAXtO.cjs.map} +1 -1
  93. package/dist/openapi.d.cts +1 -1
  94. package/dist/openapi.d.mts +1 -1
  95. package/dist/provider-C9_-45d5.cjs +56 -0
  96. package/dist/provider-C9_-45d5.cjs.map +1 -0
  97. package/dist/provider-q4lsYzYG.mjs +45 -0
  98. package/dist/provider-q4lsYzYG.mjs.map +1 -0
  99. package/dist/reconcile/public.d.cts +1 -1
  100. package/dist/reconcile/public.d.mts +1 -1
  101. package/dist/resolve-DLkIXTct.mjs +262 -0
  102. package/dist/resolve-DLkIXTct.mjs.map +1 -0
  103. package/dist/resolve-N_i4XNd-.cjs +315 -0
  104. package/dist/resolve-N_i4XNd-.cjs.map +1 -0
  105. package/dist/{runtime-CJEYK4Gz.d.mts → runtime-BnD7Nn96.d.cts} +2 -2
  106. package/dist/{runtime-CJEYK4Gz.d.mts.map → runtime-BnD7Nn96.d.cts.map} +1 -1
  107. package/dist/{runtime-Cjq1jlVX.d.cts → runtime-j-tmFL65.d.mts} +2 -2
  108. package/dist/{runtime-Cjq1jlVX.d.cts.map → runtime-j-tmFL65.d.mts.map} +1 -1
  109. package/dist/sniffer-BSwaL-dX.cjs +2236 -0
  110. package/dist/sniffer-BSwaL-dX.cjs.map +1 -0
  111. package/dist/sniffer-BZ-OJO4Y.mjs +2068 -0
  112. package/dist/sniffer-BZ-OJO4Y.mjs.map +1 -0
  113. package/dist/{sst-Cper040I.mjs → sst-B2HbrDR3.mjs} +4 -4
  114. package/dist/{sst-Cper040I.mjs.map → sst-B2HbrDR3.mjs.map} +1 -1
  115. package/dist/{sst-BdALKEas.cjs → sst-DcfsVH6x.cjs} +4 -4
  116. package/dist/{sst-BdALKEas.cjs.map → sst-DcfsVH6x.cjs.map} +1 -1
  117. package/dist/{sniffer-49KPbDgq.mjs → state-B8YwmUZU.mjs} +878 -2465
  118. package/dist/state-B8YwmUZU.mjs.map +1 -0
  119. package/dist/{sniffer-ByhbEW5A.cjs → state-DF26e_lo.cjs} +811 -2525
  120. package/dist/state-DF26e_lo.cjs.map +1 -0
  121. package/dist/{store-D_2ByRtW.cjs → store-CJF1Tqkq.cjs} +2 -2
  122. package/dist/{store-D_2ByRtW.cjs.map → store-CJF1Tqkq.cjs.map} +1 -1
  123. package/dist/{store-BaaonhkJ.mjs → store-CLiHnqOY.mjs} +2 -2
  124. package/dist/{store-BaaonhkJ.mjs.map → store-CLiHnqOY.mjs.map} +1 -1
  125. package/dist/target/public.cjs +6 -6
  126. package/dist/target/public.d.cts +2 -2
  127. package/dist/target/public.d.mts +2 -2
  128. package/dist/target/public.mjs +3 -3
  129. package/dist/terminal-Bs5E1oxm.cjs +88 -0
  130. package/dist/terminal-Bs5E1oxm.cjs.map +1 -0
  131. package/dist/terminal-CfbiYUbZ.mjs +84 -0
  132. package/dist/terminal-CfbiYUbZ.mjs.map +1 -0
  133. package/dist/{types-B_bEQvb9.d.mts → types-BCzMWBb6.d.mts} +43 -11
  134. package/dist/types-BCzMWBb6.d.mts.map +1 -0
  135. package/dist/{types-DkrDOMxw.d.cts → types-ByMWkXKP.d.cts} +43 -11
  136. package/dist/types-ByMWkXKP.d.cts.map +1 -0
  137. package/dist/workspace/index.cjs +3 -1
  138. package/dist/workspace/index.d.cts +3 -3
  139. package/dist/workspace/index.d.mts +3 -3
  140. package/dist/workspace/index.mjs +2 -2
  141. package/dist/{workspace-BwgWJK7h.cjs → workspace-BewAHZIo.cjs} +38 -3
  142. package/dist/workspace-BewAHZIo.cjs.map +1 -0
  143. package/dist/{workspace-Bwit4q9O.mjs → workspace-C06_pW4g.mjs} +27 -4
  144. package/dist/workspace-C06_pW4g.mjs.map +1 -0
  145. package/package.json +13 -13
  146. package/dist/credentials-B70vmLcm.cjs.map +0 -1
  147. package/dist/credentials-s0wMeZBU.mjs.map +0 -1
  148. package/dist/define-CYs0PEfZ.mjs.map +0 -1
  149. package/dist/define-zUsP72ym.cjs.map +0 -1
  150. package/dist/deploy-55rJkdKz.cjs.map +0 -1
  151. package/dist/deploy-Dc2LBJaK.mjs.map +0 -1
  152. package/dist/deploy-ZRXIPp1c.mjs.map +0 -1
  153. package/dist/deploy-yUcR2OlW.cjs.map +0 -1
  154. package/dist/index-CJ2qsSpp.d.mts.map +0 -1
  155. package/dist/index-CfE9Y885.d.cts.map +0 -1
  156. package/dist/local-C7wnrcYK.cjs.map +0 -1
  157. package/dist/local-Cd8ZYQG3.mjs.map +0 -1
  158. package/dist/resolve-BXsNQjMR.cjs +0 -220
  159. package/dist/resolve-BXsNQjMR.cjs.map +0 -1
  160. package/dist/resolve-DZ2IJ0qY.mjs +0 -173
  161. package/dist/resolve-DZ2IJ0qY.mjs.map +0 -1
  162. package/dist/runtime-BGNDrHzh.cjs +0 -163
  163. package/dist/runtime-BGNDrHzh.cjs.map +0 -1
  164. package/dist/runtime-Cmn-FeYO.mjs +0 -134
  165. package/dist/runtime-Cmn-FeYO.mjs.map +0 -1
  166. package/dist/sniffer-49KPbDgq.mjs.map +0 -1
  167. package/dist/sniffer-ByhbEW5A.cjs.map +0 -1
  168. package/dist/terminal-Bdv--jhY.cjs +0 -481
  169. package/dist/terminal-Bdv--jhY.cjs.map +0 -1
  170. package/dist/terminal-D7Ntz6WE.mjs +0 -440
  171. package/dist/terminal-D7Ntz6WE.mjs.map +0 -1
  172. package/dist/types-B_bEQvb9.d.mts.map +0 -1
  173. package/dist/types-DkrDOMxw.d.cts.map +0 -1
  174. package/dist/workspace-BwgWJK7h.cjs.map +0 -1
  175. package/dist/workspace-Bwit4q9O.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,37 +1,39 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  import { n as __require, t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
3
- import { $ as pgClient, B as FAKE_ENV, Ct as loginCommand, Et as whoamiCommand, H as constructGlobs, I as withGeneratedSecrets, J as applyPostgres, K as LOCAL_CA_PATH, P as createStateStore, Q as composeFor, St as prepareEntryCredentials, U as derivedContainers, V as backendsOf$1, W as reconcileWorkspace, X as postgresStatements, Y as postgresDatabaseNames, Z as quoteIdentifier, _t as createEntryWrapper, bt as loadEnvFiles, c as dockerCommand, d as generateDockerignore, dt as DEFAULT_IMAGES, et as appEnvKeys, f as generateSlimDockerfile, ft as describeServices, g as createEmptyState, gt as createCredentialsPreload, h as resolveHost, ht as primaryPortKey, it as planFor, l as siteDockerfile, lt as providerOf, m as isMainFrontendApp, mt as portKeys, n as sniffAppEnvironment, nt as envFor, ot as hostFor, p as validateImageRef, pt as fakeKey, q as forgetState, r as DokployApi, rt as localRolePassword, s as appPackageName$1, st as cacheBackendFor, tt as networkEnv, u as detectPackageManager$1, vt as findAvailablePort, wt as logoutCommand, xt as loadSecretsForApp, yt as isPortAvailable, z as keyFingerprint } from "./sniffer-49KPbDgq.mjs";
4
- import { c as runOutput, d as getDokployRegistryId, f as getDokployToken, i as CommandFailed, o as DEFAULT_TIMEOUT_MS, s as run$1, t as LocalSandbox, u as getDokployCredentials } from "./local-Cd8ZYQG3.mjs";
3
+ import { C as loginCommand, E as whoamiCommand, S as keyFingerprint, g as createStateStore, n as sniffAppEnvironment, r as DokployApi, w as logoutCommand } from "./sniffer-BZ-OJO4Y.mjs";
4
+ import { i as CommandFailed, o as DEFAULT_TIMEOUT_MS, t as LocalSandbox } from "./local-CWR4Y_kW.mjs";
5
+ import { a as getDokployCredentials, o as getDokployRegistryId, s as getDokployToken } from "./credentials-Dq1MnWQf.mjs";
5
6
  import { l as withOwningTsconfigJsx, r as discover, u as ConstructGenerator } from "./discover-LMrqsWSU.mjs";
6
7
  import { t as appKey } from "./derive-B4vDpzgP.mjs";
7
- import { D as assertDeployedStage, E as TEST_STAGE, O as deployedProblems, T as InvalidStages, a as getAppBuildOrder, k as stageProblems, o as getAppGkmConfig, t as allConstructGlobs } from "./workspace-Bwit4q9O.mjs";
8
+ import { A as deployedProblems, D as InvalidStages, O as TEST_STAGE, c as getAppGkmConfig, j as stageProblems, n as allConstructGlobs, s as getAppBuildOrder } from "./workspace-C06_pW4g.mjs";
8
9
  import { ConfigNotFound, NotABackendApp, NotInAnApp, loadAppConfig, loadConfig, loadWorkspaceConfig, loadWorkspaceSettings, parseModuleConfig } from "./config.mjs";
9
- import { a as withCustomSecret, i as toEmbeddableSecrets, n as maskPassword, t as initStageSecrets } from "./storage-BzaUxmqe.mjs";
10
- import { a as getKeyPath, f as imageRef, i as FileSecretsStore, n as secretsStoreFor, s as projectKey, u as deployIdentity } from "./store-BaaonhkJ.mjs";
10
+ import { a as withCustomSecret, i as toEmbeddableSecrets, n as maskPassword } from "./storage-BzaUxmqe.mjs";
11
+ import { a as getKeyPath, i as FileSecretsStore, n as secretsStoreFor, s as projectKey, u as deployIdentity } from "./store-CLiHnqOY.mjs";
12
+ import { D as constructGlobs, E as backendsOf$1, I as quoteIdentifier, J as providerOf, K as cacheBackendFor, M as forgetState, O as derivedContainers, P as postgresDatabaseNames, Q as fakeKey, R as pgClient, T as FAKE_ENV, Z as describeServices, _ as dockerCommand, at as loadEnvFiles, et as primaryPortKey, it as isPortAvailable, j as LOCAL_CA_PATH, k as reconcileWorkspace, nt as createEntryWrapper, ot as loadSecretsForApp, rt as findAvailablePort, st as prepareEntryCredentials, tt as createCredentialsPreload } from "./state-B8YwmUZU.mjs";
11
13
  import { i as telemetryFor, n as EndpointGenerator, r as runtimeFor } from "./OpenApiTsGenerator-C6v-IeHt.mjs";
12
14
  import { generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi.mjs";
13
- import { n as DEFAULT_CACHE, r as DEFAULT_EVENTS } from "./fakes-C9RiT6XG.mjs";
14
15
  import { a as describeHolder, i as assignAppPorts, o as holderOf, r as appTag, s as withAppPorts, t as APP_TAG_ENV } from "./appPorts-C9z9Pzn9.mjs";
15
16
  import { a as writeTestHarness, c as driversFor, i as TEST_MANIFEST_ENV, n as TEST_READY_FILE, o as SubscriberGenerator, s as cacheBackendsIn, t as TEST_READY_ENV } from "./ready-bQfu9GES.mjs";
16
17
  import { n as withCompute, r as withRoutes, t as manifestModule } from "./emit-C1e8qD08.mjs";
17
- import { t as LocalStateStore } from "./LocalStateStore-Cy1ZBW7U.mjs";
18
- import { a as getExecCommand, c as validateProjectName, i as findWorkspaceRoot, n as detectPackageManager$2, o as getInstallCommand, r as findWorkspacePackages, s as getRunCommand, t as checkDirectoryExists } from "./utils-D_fxUqvU.mjs";
18
+ import { t as deploy$1 } from "./deploy-9MBG_QLd.mjs";
19
+ import { b as dockerCompose, t as composeTarget, v as composeProject, y as stackDir } from "./compose-DprE3ASm.mjs";
20
+ import { t as LocalStateStore } from "./LocalStateStore-BRhMQRuy.mjs";
21
+ import { a as getExecCommand, c as validateProjectName, i as findWorkspaceRoot, n as detectPackageManager$1, o as getInstallCommand, r as findWorkspacePackages, s as getRunCommand, t as checkDirectoryExists } from "./utils-D_fxUqvU.mjs";
19
22
  import { createRequire } from "node:module";
20
- import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
23
+ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
21
24
  import { Command } from "commander";
22
25
  import { existsSync, linkSync, mkdirSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
23
- import { appendFile, chmod, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
26
+ import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
24
27
  import { homedir } from "node:os";
25
28
  import { promisify } from "node:util";
26
- import { DATABASE_FOLDERS, MIGRATIONS_ROOT, canonicalId, kebabCase, migrationFolder, migrationTargets, provideKey, provisionOrder, publicEnvFor, schemeBase, serviceKey } from "@geekmidas/manifest";
29
+ import { canonicalId, kebabCase, migrationFolder, provideKey, schemeBase, serviceKey } from "@geekmidas/manifest";
27
30
  import fg from "fast-glob";
28
31
  import { pathToFileURL } from "node:url";
29
32
  import { execFile, execSync, spawn } from "node:child_process";
30
33
  import chokidar from "chokidar";
31
34
  import { connect } from "node:net";
32
35
  import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
33
- import pg from "pg";
34
- import { parseDocument, stringify } from "yaml";
36
+ import { parseDocument } from "yaml";
35
37
  import prompts from "prompts";
36
38
  import { createServer as createServer$1 } from "node:http";
37
39
  import { Readable } from "node:stream";
@@ -44,7 +46,7 @@ import { Function } from "@geekmidas/constructs/functions";
44
46
  import { Queue } from "@geekmidas/constructs/queue";
45
47
  import { Topic } from "@geekmidas/constructs/topic";
46
48
  //#region package.json
47
- var version = "10.0.0-alpha.64";
49
+ var version = "10.0.0-alpha.65";
48
50
  //#endregion
49
51
  //#region src/trust/index.ts
50
52
  /**
@@ -61,7 +63,7 @@ var version = "10.0.0-alpha.64";
61
63
  * worse trade than one that tells you what to type. Caddy's own `caddy trust`
62
64
  * draws the line in the same place.
63
65
  */
64
- const logger$8 = console;
66
+ const logger$7 = console;
65
67
  const run = promisify(execFile);
66
68
  /**
67
69
  * How each platform is told about a new authority.
@@ -113,15 +115,15 @@ async function trustCommand(options = {}) {
113
115
  const certificate = join(workspace.root, LOCAL_CA_PATH);
114
116
  if (!existsSync(certificate)) throw new NoLocalAuthority(certificate);
115
117
  const { describe, asks, steps } = installation(process.platform, certificate);
116
- logger$8.log(`\n🔐 Trusting the local authority in ${describe}`);
117
- logger$8.log(` ${certificate}\n`);
118
+ logger$7.log(`\n🔐 Trusting the local authority in ${describe}`);
119
+ logger$7.log(` ${certificate}\n`);
118
120
  if (options.dryRun) {
119
- for (const [command, args] of steps) logger$8.log(` ${command} ${args.join(" ")}`);
121
+ for (const [command, args] of steps) logger$7.log(` ${command} ${args.join(" ")}`);
120
122
  return;
121
123
  }
122
- logger$8.log(` ${asks}\n`);
124
+ logger$7.log(` ${asks}\n`);
123
125
  for (const [command, args] of steps) await run(command, args, { stdio: "inherit" });
124
- logger$8.log("\n✅ Local https addresses are trusted. Restart your browser.\n");
126
+ logger$7.log("\n✅ Local https addresses are trusted. Restart your browser.\n");
125
127
  }
126
128
  /**
127
129
  * Trust the authority if this machine lacks it and its owner agrees.
@@ -138,7 +140,7 @@ async function ensureTrusted(root, url, options = {}) {
138
140
  if (await isTrusted(url)) return;
139
141
  if (!options.assumeYes) {
140
142
  if (!process.stdin.isTTY) {
141
- logger$8.log(" Local https addresses are not trusted here. Run \"gkm trust\", or \"gkm setup --yes\".");
143
+ logger$7.log(" Local https addresses are not trusted here. Run \"gkm trust\", or \"gkm setup --yes\".");
142
144
  return;
143
145
  }
144
146
  if (await declined(root)) return;
@@ -150,7 +152,7 @@ async function ensureTrusted(root, url, options = {}) {
150
152
  });
151
153
  if (!install) {
152
154
  await recordDeclined(root);
153
- logger$8.log(" Skipped. Run \"gkm trust\" later.");
155
+ logger$7.log(" Skipped. Run \"gkm trust\" later.");
154
156
  return;
155
157
  }
156
158
  }
@@ -1315,7 +1317,7 @@ function closeFakes(served) {
1315
1317
  }
1316
1318
  //#endregion
1317
1319
  //#region src/exec/index.ts
1318
- const logger$7 = console;
1320
+ const logger$6 = console;
1319
1321
  /** `gkm exec` was given nothing to run. */
1320
1322
  var NoCommandSpecified = class extends Error {
1321
1323
  constructor() {
@@ -1338,17 +1340,17 @@ async function execCommand(commandArgs, options = {}) {
1338
1340
  const cwd = options.cwd ?? process.cwd();
1339
1341
  if (!commandArgs[0]) throw new NoCommandSpecified();
1340
1342
  const defaultEnv = loadEnvFiles(".env");
1341
- if (defaultEnv.loaded.length > 0) logger$7.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1343
+ if (defaultEnv.loaded.length > 0) logger$6.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1342
1344
  const { credentials, secretsJsonPath, appName } = await prepareEntryCredentials({ cwd });
1343
- if (appName) logger$7.log(`📦 App: ${appName}`);
1345
+ if (appName) logger$6.log(`📦 App: ${appName}`);
1344
1346
  const secretCount = Object.keys(credentials).filter((k) => k !== "PORT").length;
1345
- if (secretCount > 0) logger$7.log(`🔐 Loaded ${secretCount} secret(s)`);
1347
+ if (secretCount > 0) logger$6.log(`🔐 Loaded ${secretCount} secret(s)`);
1346
1348
  const preloadDir = join(cwd, ".gkm");
1347
1349
  await mkdir(preloadDir, { recursive: true });
1348
1350
  const preloadPath = join(preloadDir, "credentials-preload.mjs");
1349
1351
  await createCredentialsPreload(preloadPath, secretsJsonPath);
1350
1352
  const [cmd, ...args] = commandArgs;
1351
- logger$7.log(`🚀 Running: ${[cmd, ...args].join(" ")}`);
1353
+ logger$6.log(`🚀 Running: ${[cmd, ...args].join(" ")}`);
1352
1354
  const nodeOptions = [(process.env.NODE_OPTIONS ?? "").replace(/--import[= ]\S+/g, "").trim(), `--import=${preloadPath}`].filter(Boolean).join(" ");
1353
1355
  const child = spawn(cmd, args, {
1354
1356
  cwd,
@@ -1362,7 +1364,7 @@ async function execCommand(commandArgs, options = {}) {
1362
1364
  const exitCode = await new Promise((resolve) => {
1363
1365
  child.on("close", (code) => resolve(code ?? 0));
1364
1366
  child.on("error", (error) => {
1365
- logger$7.error(`Failed to run command: ${error.message}`);
1367
+ logger$6.error(`Failed to run command: ${error.message}`);
1366
1368
  resolve(1);
1367
1369
  });
1368
1370
  });
@@ -1370,7 +1372,7 @@ async function execCommand(commandArgs, options = {}) {
1370
1372
  }
1371
1373
  //#endregion
1372
1374
  //#region src/dev/index.ts
1373
- const logger$6 = console;
1375
+ const logger$5 = console;
1374
1376
  /**
1375
1377
  * Normalize telescope configuration
1376
1378
  * @internal Exported for testing
@@ -1440,13 +1442,13 @@ async function devCommand(options) {
1440
1442
  if (options.subscribers === false) process.env[SUBSCRIBERS_ENV] = "off";
1441
1443
  if (options.entry) return entryDevCommand(options);
1442
1444
  const defaultEnv = loadEnvFiles(".env");
1443
- if (defaultEnv.loaded.length > 0) logger$6.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1445
+ if (defaultEnv.loaded.length > 0) logger$5.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1444
1446
  const appConfig = await loadAppConfig().catch((error) => {
1445
1447
  if (error instanceof NotInAnApp || error instanceof NotABackendApp) return;
1446
1448
  throw error;
1447
1449
  });
1448
1450
  if (!appConfig) {
1449
- logger$6.log("🔎 Reading gkm.config.ts and the constructs");
1451
+ logger$5.log("🔎 Reading gkm.config.ts and the constructs");
1450
1452
  const { workspace: everything } = await loadWorkspaceConfig();
1451
1453
  return workspaceDevCommand(everything, options);
1452
1454
  }
@@ -1459,12 +1461,12 @@ async function devCommand(options) {
1459
1461
  holder: holderOf
1460
1462
  });
1461
1463
  if (assigned.running.length > 0) throw new WorkspacePortsInUse(assigned.running);
1462
- for (const { app, from, to, holder } of assigned.moved) logger$6.log(`↪️ ${app}: ${from} is held by ${describeHolder(holder)}, which is not this workspace's — using ${to}`);
1464
+ for (const { app, from, to, holder } of assigned.moved) logger$5.log(`↪️ ${app}: ${from} is held by ${describeHolder(holder)}, which is not this workspace's — using ${to}`);
1463
1465
  const workspace = withAppPorts(appConfig.workspace, assigned.ports);
1464
1466
  const workspaceAppPort = assigned.ports[workspaceAppName] ?? appConfig.app.port;
1465
1467
  process.env[APP_TAG_ENV] = appTag(appConfig.workspaceRoot, workspaceAppName);
1466
1468
  if (appConfig.app.entry) {
1467
- logger$6.log(`📄 Using entry point: ${appConfig.app.entry}`);
1469
+ logger$5.log(`📄 Using entry point: ${appConfig.app.entry}`);
1468
1470
  return entryDevCommand({
1469
1471
  ...options,
1470
1472
  entry: appConfig.app.entry,
@@ -1474,8 +1476,8 @@ async function devCommand(options) {
1474
1476
  }
1475
1477
  if (config.env) {
1476
1478
  const { loaded, missing } = loadEnvFiles(config.env, appRoot);
1477
- if (loaded.length > 0) logger$6.log(`📦 Loaded env: ${loaded.join(", ")}`);
1478
- if (missing.length > 0) logger$6.warn(`⚠️ Missing env files: ${missing.join(", ")}`);
1479
+ if (loaded.length > 0) logger$5.log(`📦 Loaded env: ${loaded.join(", ")}`);
1480
+ if (missing.length > 0) logger$5.warn(`⚠️ Missing env files: ${missing.join(", ")}`);
1479
1481
  }
1480
1482
  const telescope = normalizeTelescopeConfig(config.telescope);
1481
1483
  const hooks = normalizeHooksConfig(config.hooks, appRoot);
@@ -1494,7 +1496,7 @@ async function devCommand(options) {
1494
1496
  bustCache,
1495
1497
  serveEmpty: true
1496
1498
  }));
1497
- logger$6.log(`🔨 Building ${workspaceAppName ?? "the app"}`);
1499
+ logger$5.log(`🔨 Building ${workspaceAppName ?? "the app"}`);
1498
1500
  const initial = await build();
1499
1501
  const runtime = config.runtime ?? "node";
1500
1502
  let secretsJsonPath;
@@ -1505,11 +1507,11 @@ async function devCommand(options) {
1505
1507
  const reconciled = await reconcileWorkspace(workspace, {
1506
1508
  stage: workspace.stages.local,
1507
1509
  ...options.fake ? { fake: true } : {},
1508
- progress: (message) => logger$6.log(message)
1510
+ progress: (message) => logger$5.log(message)
1509
1511
  });
1510
1512
  if (reconciled.services.length > 0) {
1511
- logger$6.log("🐳 Services");
1512
- for (const line of describeServices(reconciled.services)) logger$6.log(line);
1513
+ logger$5.log("🐳 Services");
1514
+ for (const line of describeServices(reconciled.services)) logger$5.log(line);
1513
1515
  }
1514
1516
  Object.assign(appSecrets, reconciled.env);
1515
1517
  const own = reconciled.plan.resources.find((r) => (r.kind === "rest-api" || r.kind === "site") && appKey(r.id) === workspaceAppName);
@@ -1576,11 +1578,11 @@ async function devCommand(options) {
1576
1578
  cwd: appRoot
1577
1579
  });
1578
1580
  watcher.on("error", (error) => {
1579
- logger$6.error("❌ Watcher error:", error);
1581
+ logger$5.error("❌ Watcher error:", error);
1580
1582
  });
1581
1583
  let rebuildTimeout = null;
1582
1584
  watcher.on("change", async (path) => {
1583
- logger$6.log(`🔄 ${path} changed — rebuilding`);
1585
+ logger$5.log(`🔄 ${path} changed — rebuilding`);
1584
1586
  if (rebuildTimeout) clearTimeout(rebuildTimeout);
1585
1587
  rebuildTimeout = setTimeout(async () => {
1586
1588
  try {
@@ -1589,8 +1591,8 @@ async function devCommand(options) {
1589
1591
  await devServer.restart();
1590
1592
  } catch (error) {
1591
1593
  const err = error;
1592
- logger$6.error(`❌ Rebuild failed: ${err.message}`);
1593
- if (err.stack) logger$6.error(err.stack);
1594
+ logger$5.error(`❌ Rebuild failed: ${err.message}`);
1595
+ if (err.stack) logger$5.error(err.stack);
1594
1596
  }
1595
1597
  }, 300);
1596
1598
  });
@@ -1598,13 +1600,13 @@ async function devCommand(options) {
1598
1600
  const shutdown = () => {
1599
1601
  if (isShuttingDown) return;
1600
1602
  isShuttingDown = true;
1601
- logger$6.log("\n🛑 Shutting down...");
1603
+ logger$5.log("\n🛑 Shutting down...");
1602
1604
  Promise.all([
1603
1605
  watcher.close(),
1604
1606
  devServer.stop(),
1605
1607
  discovery.leave()
1606
1608
  ]).catch((err) => {
1607
- logger$6.error("Error during shutdown:", err);
1609
+ logger$5.error("Error during shutdown:", err);
1608
1610
  }).finally(() => {
1609
1611
  process.exit(0);
1610
1612
  });
@@ -1808,13 +1810,13 @@ async function workspaceDevCommand(configured, options) {
1808
1810
  holder: holderOf
1809
1811
  });
1810
1812
  if (assigned.running.length > 0) throw new WorkspacePortsInUse(assigned.running);
1811
- for (const { app, from, to, holder } of assigned.moved) logger$6.log(`↪️ ${app}: ${from} is held by ${describeHolder(holder)}, which is not this workspace's — using ${to}`);
1813
+ for (const { app, from, to, holder } of assigned.moved) logger$5.log(`↪️ ${app}: ${from} is held by ${describeHolder(holder)}, which is not this workspace's — using ${to}`);
1812
1814
  const workspace = withAppPorts(configured, assigned.ports);
1813
1815
  const appCount = Object.keys(workspace.apps).length;
1814
1816
  const frontendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "web");
1815
1817
  const conflicts = checkPortConflicts(workspace);
1816
1818
  if (conflicts.length > 0) {
1817
- for (const conflict of conflicts) logger$6.error(`❌ Port conflict: Apps "${conflict.app1}" and "${conflict.app2}" both use port ${conflict.port}`);
1819
+ for (const conflict of conflicts) logger$5.error(`❌ Port conflict: Apps "${conflict.app1}" and "${conflict.app2}" both use port ${conflict.port}`);
1818
1820
  throw new Error("Port conflicts detected. Please assign unique ports to each app.");
1819
1821
  }
1820
1822
  if (frontendApps.length > 0) {
@@ -1823,10 +1825,10 @@ async function workspaceDevCommand(configured, options) {
1823
1825
  for (const result of validationResults) {
1824
1826
  if (!result.valid) {
1825
1827
  hasErrors = true;
1826
- logger$6.error(`\n❌ Frontend app "${result.appName}" validation failed:`);
1827
- for (const error of result.errors) logger$6.error(` • ${error}`);
1828
+ logger$5.error(`\n❌ Frontend app "${result.appName}" validation failed:`);
1829
+ for (const error of result.errors) logger$5.error(` • ${error}`);
1828
1830
  }
1829
- for (const warning of result.warnings) logger$6.warn(` ⚠️ ${result.appName}: ${warning}`);
1831
+ for (const warning of result.warnings) logger$5.warn(` ⚠️ ${result.appName}: ${warning}`);
1830
1832
  }
1831
1833
  if (hasErrors) throw new Error("Frontend app validation failed. Fix the issues above and try again.");
1832
1834
  }
@@ -1834,24 +1836,24 @@ async function workspaceDevCommand(configured, options) {
1834
1836
  const reconciled = await reconcileWorkspace(workspace, {
1835
1837
  stage: workspace.stages.local,
1836
1838
  ...options.fake ? { fake: true } : {},
1837
- progress: (message) => logger$6.log(message)
1839
+ progress: (message) => logger$5.log(message)
1838
1840
  });
1839
1841
  if (reconciled.services.length > 0) {
1840
- logger$6.log("🐳 Services");
1841
- for (const line of describeServices(reconciled.services)) logger$6.log(line);
1842
+ logger$5.log("🐳 Services");
1843
+ for (const line of describeServices(reconciled.services)) logger$5.log(line);
1842
1844
  }
1843
1845
  const secretsEnv = {
1844
1846
  ...rawSecrets,
1845
1847
  ...reconciled.env
1846
1848
  };
1847
1849
  const fakes = options.fake ? await serveFakes(reconciled.fakes, reconciled.ports) : [];
1848
- for (const { id, port } of fakes) logger$6.log(`🎭 ${id} fake: http://localhost:${port}`);
1849
- logger$6.log(options.migrate || options.seed ? "🗄️ Migrating the databases" : "🗄️ Checking for pending migrations");
1850
+ for (const { id, port } of fakes) logger$5.log(`🎭 ${id} fake: http://localhost:${port}`);
1851
+ logger$5.log(options.migrate || options.seed ? "🗄️ Migrating the databases" : "🗄️ Checking for pending migrations");
1850
1852
  if (options.migrate || options.seed) {
1851
- const { prepareDevDatabases } = await import("./migrate-B4gngTLe.mjs");
1853
+ const { prepareDevDatabases } = await import("./migrate-B2qL5wj4.mjs");
1852
1854
  await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
1853
- } else await import("./migrate-B4gngTLe.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
1854
- logger$6.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
1855
+ } else await import("./migrate-B2qL5wj4.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
1856
+ logger$5.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
1855
1857
  });
1856
1858
  const appUrls = {};
1857
1859
  for (const resource of reconciled.plan.resources) {
@@ -1872,11 +1874,11 @@ async function workspaceDevCommand(configured, options) {
1872
1874
  else {
1873
1875
  const { filters, unpackaged } = turboFilters(workspace);
1874
1876
  turboFilter = filters.flatMap((name) => ["--filter", name]);
1875
- if (unpackaged.length > 0) logger$6.warn(`⚠️ No package.json with a name for: ${unpackaged.join(", ")} — turbo cannot run ${unpackaged.length === 1 ? "it" : "them"}.`);
1877
+ if (unpackaged.length > 0) logger$5.warn(`⚠️ No package.json with a name for: ${unpackaged.join(", ")} — turbo cannot run ${unpackaged.length === 1 ? "it" : "them"}.`);
1876
1878
  }
1877
1879
  const buildOrder = getAppBuildOrder(workspace);
1878
1880
  const width = Math.max(...buildOrder.map((name) => name.length));
1879
- logger$6.log(`\n${workspace.name}: ${appCount} app(s)`);
1881
+ logger$5.log(`\n${workspace.name}: ${appCount} app(s)`);
1880
1882
  for (const appName of buildOrder) {
1881
1883
  const app = workspace.apps[appName];
1882
1884
  if (!app) continue;
@@ -1884,14 +1886,14 @@ async function workspaceDevCommand(configured, options) {
1884
1886
  const own = reconciled.plan.resources.find((r) => r.kind === "mobile-app" && appKey(r.id) === appName);
1885
1887
  const scheme = own ? reconciled.env[own.envKey] : void 0;
1886
1888
  const device = Object.entries(reconciled.env).find(([key]) => key.endsWith("_DEVICE_URL"))?.[1];
1887
- logger$6.log(` ${appName.padEnd(width)} ${scheme ? `${scheme}://` : "mobile"}${device ? ` — devices reach ${new URL(device).hostname}` : ""}`);
1889
+ logger$5.log(` ${appName.padEnd(width)} ${scheme ? `${scheme}://` : "mobile"}${device ? ` — devices reach ${new URL(device).hostname}` : ""}`);
1888
1890
  continue;
1889
1891
  }
1890
1892
  const local = `http://localhost:${app.port}`;
1891
1893
  const url = Object.entries(appUrls).find(([id]) => appKey(id) === appName)?.[1];
1892
- logger$6.log(` ${appName.padEnd(width)} ${url ? `${url} -> ${local}` : local}`);
1894
+ logger$5.log(` ${appName.padEnd(width)} ${url ? `${url} -> ${local}` : local}`);
1893
1895
  }
1894
- logger$6.log("");
1896
+ logger$5.log("");
1895
1897
  const configFiles = [
1896
1898
  "gkm.config.ts",
1897
1899
  "gkm.config.js",
@@ -1939,7 +1941,7 @@ async function workspaceDevCommand(configured, options) {
1939
1941
  ...options.fake ? { [FAKE_ENV]: "1" } : {},
1940
1942
  [DISCOVERY_QUIET_ENV]: "1"
1941
1943
  };
1942
- logger$6.log(`🚀 Starting ${appCount} app(s) — each builds before it answers, and says so when it is ready`);
1944
+ logger$5.log(`🚀 Starting ${appCount} app(s) — each builds before it answers, and says so when it is ready`);
1943
1945
  const turboProcess = spawn("pnpm", [
1944
1946
  "turbo",
1945
1947
  "run",
@@ -1955,7 +1957,7 @@ async function workspaceDevCommand(configured, options) {
1955
1957
  const shutdown = () => {
1956
1958
  if (isShuttingDown) return;
1957
1959
  isShuttingDown = true;
1958
- logger$6.log("\n🛑 Shutting down workspace...");
1960
+ logger$5.log("\n🛑 Shutting down workspace...");
1959
1961
  closeFakes(fakes);
1960
1962
  discovery.leave();
1961
1963
  const pid = turboProcess.pid;
@@ -1981,7 +1983,7 @@ async function workspaceDevCommand(configured, options) {
1981
1983
  process.on("SIGTERM", shutdown);
1982
1984
  return new Promise((resolve, reject) => {
1983
1985
  turboProcess.on("error", (error) => {
1984
- logger$6.error("❌ Turbo error:", error);
1986
+ logger$5.error("❌ Turbo error:", error);
1985
1987
  reject(error);
1986
1988
  });
1987
1989
  turboProcess.on("exit", (code) => {
@@ -2000,11 +2002,11 @@ async function entryDevCommand(options) {
2000
2002
  const entryPath = resolve(process.cwd(), entry);
2001
2003
  if (!existsSync(entryPath)) throw new Error(`Entry file not found: ${entryPath}`);
2002
2004
  const defaultEnv = loadEnvFiles(".env");
2003
- if (defaultEnv.loaded.length > 0) logger$6.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
2005
+ if (defaultEnv.loaded.length > 0) logger$5.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
2004
2006
  const { credentials, resolvedPort, secretsJsonPath, appName } = await prepareEntryCredentials({ explicitPort: options.portExplicit ? options.port : void 0 });
2005
- if (appName) logger$6.log(`📦 App: ${appName} (port ${resolvedPort})`);
2006
- logger$6.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);
2007
- if (Object.keys(credentials).length > 1) logger$6.log(`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`);
2007
+ if (appName) logger$5.log(`📦 App: ${appName} (port ${resolvedPort})`);
2008
+ logger$5.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);
2009
+ if (Object.keys(credentials).length > 1) logger$5.log(`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`);
2008
2010
  const wrapperDir = join(process.cwd(), ".gkm");
2009
2011
  await mkdir(wrapperDir, { recursive: true });
2010
2012
  const wrapperPath = join(wrapperDir, "entry-wrapper.ts");
@@ -2015,7 +2017,7 @@ async function entryDevCommand(options) {
2015
2017
  const shutdown = () => {
2016
2018
  if (isShuttingDown) return;
2017
2019
  isShuttingDown = true;
2018
- logger$6.log("\n🛑 Shutting down...");
2020
+ logger$5.log("\n🛑 Shutting down...");
2019
2021
  runner.stop();
2020
2022
  process.exit(0);
2021
2023
  };
@@ -2051,14 +2053,14 @@ var EntryRunner = class {
2051
2053
  });
2052
2054
  let restartTimeout = null;
2053
2055
  this.watcher.on("change", (path) => {
2054
- logger$6.log(`📝 File changed: ${path}`);
2056
+ logger$5.log(`📝 File changed: ${path}`);
2055
2057
  if (restartTimeout) clearTimeout(restartTimeout);
2056
2058
  restartTimeout = setTimeout(async () => {
2057
- logger$6.log("🔄 Restarting...");
2059
+ logger$5.log("🔄 Restarting...");
2058
2060
  await this.restart();
2059
2061
  }, 300);
2060
2062
  });
2061
- logger$6.log(`👀 Watching for changes in: ${watchDir}`);
2063
+ logger$5.log(`👀 Watching for changes in: ${watchDir}`);
2062
2064
  }
2063
2065
  }
2064
2066
  async runProcess() {
@@ -2074,17 +2076,17 @@ var EntryRunner = class {
2074
2076
  });
2075
2077
  this.isRunning = true;
2076
2078
  this.childProcess.on("error", (error) => {
2077
- logger$6.error("❌ Process error:", error);
2079
+ logger$5.error("❌ Process error:", error);
2078
2080
  });
2079
2081
  this.childProcess.on("exit", (code) => {
2080
- if (code !== null && code !== 0 && code !== 143) logger$6.error(`❌ Process exited with code ${code}`);
2082
+ if (code !== null && code !== 0 && code !== 143) logger$5.error(`❌ Process exited with code ${code}`);
2081
2083
  this.isRunning = false;
2082
2084
  });
2083
2085
  await new Promise((resolve) => setTimeout(resolve, 500));
2084
2086
  if (this.isRunning) {
2085
- logger$6.log("");
2086
- logger$6.log(` \x1b[32m✓ Ready\x1b[0m at \x1b[36mhttp://localhost:${this.port}\x1b[0m`);
2087
- logger$6.log("");
2087
+ logger$5.log("");
2088
+ logger$5.log(` \x1b[32m✓ Ready\x1b[0m at \x1b[36mhttp://localhost:${this.port}\x1b[0m`);
2089
+ logger$5.log("");
2088
2090
  }
2089
2091
  }
2090
2092
  async restart() {
@@ -2203,7 +2205,7 @@ start({
2203
2205
  * lets a console read what this machine is running.
2204
2206
  */
2205
2207
  function announce(discovery) {
2206
- logger$6.log(`🧭 Discovery: ${discovery.connectUrl}`);
2208
+ logger$5.log(`🧭 Discovery: ${discovery.connectUrl}`);
2207
2209
  }
2208
2210
  /** Run `fn` with `console.log` muted — warnings and errors still print. */
2209
2211
  async function quietly(fn) {
@@ -2321,7 +2323,7 @@ var DevServer = class {
2321
2323
  this.actualPort = this.requestedPort;
2322
2324
  } else {
2323
2325
  this.actualPort = await findAvailablePort(this.requestedPort);
2324
- if (this.actualPort !== this.requestedPort) logger$6.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
2326
+ if (this.actualPort !== this.requestedPort) logger$5.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
2325
2327
  }
2326
2328
  const serverEntryPath = join(this.appRoot, ".gkm", "server", "server.ts");
2327
2329
  await this.createServerEntry();
@@ -2341,11 +2343,11 @@ var DevServer = class {
2341
2343
  });
2342
2344
  this.isRunning = true;
2343
2345
  this.serverProcess.on("error", (error) => {
2344
- logger$6.error("❌ Server error:", error);
2346
+ logger$5.error("❌ Server error:", error);
2345
2347
  });
2346
2348
  const child = this.serverProcess;
2347
2349
  child.on("exit", (code, signal) => {
2348
- if (code !== null && code !== 0 && signal !== "SIGTERM") logger$6.error(`❌ Server exited with code ${code}`);
2350
+ if (code !== null && code !== 0 && signal !== "SIGTERM") logger$5.error(`❌ Server exited with code ${code}`);
2349
2351
  this.isRunning = false;
2350
2352
  if (this.serverProcess === child) this.onStatus?.("stopped");
2351
2353
  });
@@ -2354,13 +2356,13 @@ var DevServer = class {
2354
2356
  const local = `http://localhost:${this.actualPort}`;
2355
2357
  const address = this.publicUrl ? `${this.publicUrl} -> ${local}` : local;
2356
2358
  const seconds = ((Date.now() - this.startTime) / 1e3).toFixed(1);
2357
- logger$6.log(`\x1b[32m✓\x1b[0m ${this.label} ready in ${seconds}s ${address}`);
2359
+ logger$5.log(`\x1b[32m✓\x1b[0m ${this.label} ready in ${seconds}s ${address}`);
2358
2360
  const tools = this.selfServing ? [] : [
2359
2361
  ...this.enableOpenApi ? ["docs /__docs"] : [],
2360
2362
  ...this.telescope ? [`telescope ${this.telescope.path}`] : [],
2361
2363
  ...this.databaseApi ? [`db ${this.databaseApi}`] : []
2362
2364
  ];
2363
- if (tools.length > 0) logger$6.log(` ${tools.join(" · ")}`);
2365
+ if (tools.length > 0) logger$5.log(` ${tools.join(" · ")}`);
2364
2366
  this.onStatus?.("ready");
2365
2367
  }
2366
2368
  }
@@ -3056,7 +3058,7 @@ ${shutdown}`);
3056
3058
  }
3057
3059
  //#endregion
3058
3060
  //#region src/build/index.ts
3059
- const logger$5 = console;
3061
+ const logger$4 = console;
3060
3062
  /**
3061
3063
  * Where `gkm build --stage` leaves the key its bundle's secrets were encrypted
3062
3064
  * with, beside the bundle in `.gkm/server/` and readable by its owner alone.
@@ -3104,7 +3106,7 @@ async function buildCommand(options) {
3104
3106
  const isAtWorkspaceRoot = cwd === workspaceRoot;
3105
3107
  const someAppIsElsewhere = Object.values(loadedConfig.workspace.apps).some((app) => resolve(workspaceRoot, app.path) !== workspaceRoot);
3106
3108
  if (isAtWorkspaceRoot && someAppIsElsewhere) {
3107
- logger$5.log("📦 Detected workspace configuration");
3109
+ logger$4.log("📦 Detected workspace configuration");
3108
3110
  return workspaceBuildCommand(loadedConfig.workspace, options);
3109
3111
  }
3110
3112
  }
@@ -3150,13 +3152,13 @@ async function buildOneApp(input) {
3150
3152
  const { config, options, workspace, appRoot, target } = input;
3151
3153
  const cacheBackend = cacheBackendFor(providerOf(workspace));
3152
3154
  const production = normalizeProductionConfig(options.production ?? false);
3153
- if (production) logger$5.log(`🏭 Building for PRODUCTION`);
3154
- logger$5.log(`Building for ${target}`);
3155
- logger$5.log(`Loading constructs from: ${formatRoutes(config.constructs)}`);
3155
+ if (production) logger$4.log(`🏭 Building for PRODUCTION`);
3156
+ logger$4.log(`Building for ${target}`);
3157
+ logger$4.log(`Loading constructs from: ${formatRoutes(config.constructs)}`);
3156
3158
  const telescope = production ? void 0 : normalizeTelescopeConfig(config.telescope);
3157
- if (telescope) logger$5.log(`🔭 Telescope enabled at ${telescope.path}`);
3159
+ if (telescope) logger$4.log(`🔭 Telescope enabled at ${telescope.path}`);
3158
3160
  const hooks = normalizeHooksConfig(config.hooks, appRoot);
3159
- if (hooks) logger$5.log(`🪝 Server hooks enabled`);
3161
+ if (hooks) logger$4.log(`🪝 Server hooks enabled`);
3160
3162
  return buildApp({
3161
3163
  config,
3162
3164
  workspaceRoot: workspace.root,
@@ -3198,7 +3200,7 @@ async function writeManifest(input) {
3198
3200
  });
3199
3201
  const path = join(manifestDir, `${target}.ts`);
3200
3202
  await writeFile(path, manifestModule(folded, backends));
3201
- logger$5.log(`Manifest: ${relative(process.cwd(), path)}`);
3203
+ logger$4.log(`Manifest: ${relative(process.cwd(), path)}`);
3202
3204
  }
3203
3205
  /**
3204
3206
  * Turn one app's constructs into its generated entry.
@@ -3226,11 +3228,11 @@ async function buildApp(input) {
3226
3228
  });
3227
3229
  if (selfServing) {
3228
3230
  await writeSurfaceEntry(join(appRoot, ".gkm", "server"), selfServing.source);
3229
- logger$5.log(`Generated a server for ${selfServing.id} from its own declaration`);
3231
+ logger$4.log(`Generated a server for ${selfServing.id} from its own declaration`);
3230
3232
  if (production?.bundle && !input.skipBundle) {
3231
3233
  const outputDir = join(appRoot, ".gkm", "server");
3232
3234
  const entryPoint = await writeSurfaceServer(outputDir, production);
3233
- const { bundleServer } = await import("./bundler-DYgm9xsd.mjs");
3235
+ const { bundleServer } = await import("./bundler-D_14JLZF.mjs");
3234
3236
  await bundleServer({
3235
3237
  entryPoint,
3236
3238
  outputDir: join(outputDir, "dist"),
@@ -3239,7 +3241,7 @@ async function buildApp(input) {
3239
3241
  external: production.external,
3240
3242
  ...input.stage ? { stage: input.stage } : {}
3241
3243
  });
3242
- logger$5.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
3244
+ logger$4.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
3243
3245
  }
3244
3246
  return { selfServing: selfServing.id };
3245
3247
  }
@@ -3285,14 +3287,14 @@ async function buildApp(input) {
3285
3287
  topicGenerator.load(code, appRoot, bustCache)
3286
3288
  ]);
3287
3289
  const allEndpoints = servedBy(loadedEndpoints, derived.surface);
3288
- logger$5.log(`Found ${allEndpoints.length} endpoints`);
3289
- logger$5.log(`Found ${allFunctions.length} functions`);
3290
- logger$5.log(`Found ${allCrons.length} crons`);
3291
- logger$5.log(`Found ${allSubscribers.length} subscribers`);
3292
- logger$5.log(`Found ${allQueues.length} queues`);
3293
- logger$5.log(`Found ${allTopics.length} topics`);
3290
+ logger$4.log(`Found ${allEndpoints.length} endpoints`);
3291
+ logger$4.log(`Found ${allFunctions.length} functions`);
3292
+ logger$4.log(`Found ${allCrons.length} crons`);
3293
+ logger$4.log(`Found ${allSubscribers.length} subscribers`);
3294
+ logger$4.log(`Found ${allQueues.length} queues`);
3295
+ logger$4.log(`Found ${allTopics.length} topics`);
3294
3296
  if (!input.serveEmpty && allEndpoints.length === 0 && allFunctions.length === 0 && allCrons.length === 0 && allSubscribers.length === 0 && allQueues.length === 0 && allTopics.length === 0) {
3295
- logger$5.log("No endpoints, functions, crons, subscribers, queues, or topics found to process");
3297
+ logger$4.log("No endpoints, functions, crons, subscribers, queues, or topics found to process");
3296
3298
  return {};
3297
3299
  }
3298
3300
  if (production?.subscribers === "include" && derived.surface) {
@@ -3305,7 +3307,7 @@ async function buildApp(input) {
3305
3307
  [allQueues.length, "queue consumer"],
3306
3308
  [allSubscribers.length, "subscriber"]
3307
3309
  ].filter(([n]) => n > 0).map(([n, what]) => `${n} ${what}${n === 1 ? "" : "s"}`);
3308
- if (left.length > 0) logger$5.log(`Serving ${derived.surface.id} only: leaving out ${left.join(", ")} — they run in a Worker's image`);
3310
+ if (left.length > 0) logger$4.log(`Serving ${derived.surface.id} only: leaving out ${left.join(", ")} — they run in a Worker's image`);
3309
3311
  }
3310
3312
  const result = await buildForTarget(target, buildContext, appRoot, workspaceRoot, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, queueGenerator, topicGenerator, allEndpoints, allFunctions, allCrons, allSubscribers, allQueues, allTopics, enableOpenApi, input.skipBundle ?? false, input.stage);
3311
3313
  await generateOpenApi(allEndpoints.map(({ construct }) => construct), {
@@ -3328,7 +3330,7 @@ async function buildForTarget(target, context, appRoot, workspaceRoot, endpointG
3328
3330
  force: true
3329
3331
  });
3330
3332
  await mkdir(outputDir, { recursive: true });
3331
- logger$5.log(`\nGenerating handlers for ${target}`);
3333
+ logger$4.log(`\nGenerating handlers for ${target}`);
3332
3334
  const options = {
3333
3335
  target,
3334
3336
  root: workspaceRoot
@@ -3344,7 +3346,7 @@ async function buildForTarget(target, context, appRoot, workspaceRoot, endpointG
3344
3346
  queueGenerator.build(context, queues, outputDir, options),
3345
3347
  topicGenerator.build(context, topics, outputDir, options)
3346
3348
  ]);
3347
- logger$5.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers, ${queueInfos.length} queues, ${topicInfos.length} topics for ${target}`);
3349
+ logger$4.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers, ${queueInfos.length} queues, ${topicInfos.length} topics for ${target}`);
3348
3350
  const fields = {
3349
3351
  ...context.surface ? { surface: context.surface.id } : {},
3350
3352
  functions: functionInfos,
@@ -3367,8 +3369,8 @@ async function buildForTarget(target, context, appRoot, workspaceRoot, endpointG
3367
3369
  };
3368
3370
  let masterKey;
3369
3371
  if (context.production?.bundle && !skipBundle) {
3370
- logger$5.log(`\n📦 Bundling production server...`);
3371
- const { bundleServer } = await import("./bundler-DYgm9xsd.mjs");
3372
+ logger$4.log(`\n📦 Bundling production server...`);
3373
+ const { bundleServer } = await import("./bundler-D_14JLZF.mjs");
3372
3374
  const allConstructs = [
3373
3375
  ...endpoints.map((e) => e.construct),
3374
3376
  ...functions.map((f) => f.construct),
@@ -3385,13 +3387,13 @@ async function buildForTarget(target, context, appRoot, workspaceRoot, endpointG
3385
3387
  stage,
3386
3388
  constructs: allConstructs
3387
3389
  })).masterKey;
3388
- logger$5.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
3390
+ logger$4.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
3389
3391
  if (masterKey) {
3390
3392
  const keyPath = join(outputDir, MASTER_KEY_FILE);
3391
3393
  await writeFile(keyPath, `${masterKey}\n`, { mode: 384 });
3392
3394
  await chmod(keyPath, 384);
3393
- logger$5.log(`\n🔐 Secrets encrypted for deployment`);
3394
- logger$5.log(` Run with GKM_MASTER_KEY set to the key in ${relative(appRoot, keyPath)} (fingerprint ${keyFingerprint(masterKey)})`);
3395
+ logger$4.log(`\n🔐 Secrets encrypted for deployment`);
3396
+ logger$4.log(` Run with GKM_MASTER_KEY set to the key in ${relative(appRoot, keyPath)} (fingerprint ${keyFingerprint(masterKey)})`);
3395
3397
  }
3396
3398
  }
3397
3399
  return {
@@ -3492,12 +3494,12 @@ async function workspaceBuildCommand(workspace, options) {
3492
3494
  const apps = Object.entries(workspace.apps);
3493
3495
  const backendApps = apps.filter(([, app]) => app.type === "backend");
3494
3496
  const frontendApps = apps.filter(([, app]) => app.type === "web");
3495
- logger$5.log(`\n🏗️ Building workspace: ${workspace.name}`);
3496
- logger$5.log(` Backend apps: ${backendApps.map(([name]) => name).join(", ") || "none"}`);
3497
- logger$5.log(` Frontend apps: ${frontendApps.map(([name]) => name).join(", ") || "none"}`);
3498
- if (options.production) logger$5.log(` 🏭 Production mode enabled`);
3497
+ logger$4.log(`\n🏗️ Building workspace: ${workspace.name}`);
3498
+ logger$4.log(` Backend apps: ${backendApps.map(([name]) => name).join(", ") || "none"}`);
3499
+ logger$4.log(` Frontend apps: ${frontendApps.map(([name]) => name).join(", ") || "none"}`);
3500
+ if (options.production) logger$4.log(` 🏭 Production mode enabled`);
3499
3501
  const buildOrder = getAppBuildOrder(workspace);
3500
- logger$5.log(` Build order: ${buildOrder.join(" → ")}`);
3502
+ logger$4.log(` Build order: ${buildOrder.join(" → ")}`);
3501
3503
  try {
3502
3504
  const target = options.provider ?? providerOf(workspace);
3503
3505
  const builds = [];
@@ -3505,7 +3507,7 @@ async function workspaceBuildCommand(workspace, options) {
3505
3507
  const app = workspace.apps[appName];
3506
3508
  const config = getAppGkmConfig(workspace, appName);
3507
3509
  if (!app || app.type !== "backend" || !config) continue;
3508
- logger$5.log(`\n⚙️ ${appName}`);
3510
+ logger$4.log(`\n⚙️ ${appName}`);
3509
3511
  const output = await buildOneApp({
3510
3512
  config,
3511
3513
  options,
@@ -3530,8 +3532,8 @@ async function workspaceBuildCommand(workspace, options) {
3530
3532
  const pm = detectPackageManager();
3531
3533
  const [turboBin, turboArgs] = getTurboArgs(pm, filters, { only: true });
3532
3534
  if (filters.length > 0) {
3533
- logger$5.log(`\n📦 Using ${pm} with Turbo for the other apps...\n`);
3534
- logger$5.log(`Running: ${[turboBin, ...turboArgs].join(" ")}`);
3535
+ logger$4.log(`\n📦 Using ${pm} with Turbo for the other apps...\n`);
3536
+ logger$4.log(`Running: ${[turboBin, ...turboArgs].join(" ")}`);
3535
3537
  const sandbox = options.sandbox ?? new LocalSandbox({
3536
3538
  root: workspace.root,
3537
3539
  passEnv: ["TURBO_TOKEN"]
@@ -3556,15 +3558,15 @@ async function workspaceBuildCommand(workspace, options) {
3556
3558
  outputPath
3557
3559
  });
3558
3560
  }
3559
- logger$5.log(`\n✅ Workspace build complete!`);
3560
- logger$5.log(`\n📋 Build Summary:`);
3561
+ logger$4.log(`\n✅ Workspace build complete!`);
3562
+ logger$4.log(`\n📋 Build Summary:`);
3561
3563
  for (const result of results) {
3562
3564
  const icon = result.type === "backend" ? "⚙️" : result.type === "mobile" ? "📱" : "🌐";
3563
- logger$5.log(` ${icon} ${result.appName}: ${result.outputPath || "built"}`);
3565
+ logger$4.log(` ${icon} ${result.appName}: ${result.outputPath || "built"}`);
3564
3566
  }
3565
3567
  } catch (error) {
3566
3568
  const errorMessage = error instanceof Error ? error.message : "Build failed";
3567
- logger$5.log(`\n❌ Build failed: ${errorMessage}`);
3569
+ logger$4.log(`\n❌ Build failed: ${errorMessage}`);
3568
3570
  for (const [appName, app] of apps) results.push({
3569
3571
  appName,
3570
3572
  type: app.type,
@@ -3594,1169 +3596,17 @@ function formatRoutes(routes) {
3594
3596
  return Array.isArray(routes) ? routes.join(", ") : routes;
3595
3597
  }
3596
3598
  //#endregion
3597
- //#region src/migrate/provider.ts
3598
- /**
3599
- * One construct's migrations and seeds, read from its folders.
3600
- *
3601
- * Two kinds of file, told apart by extension rather than by configuration: a
3602
- * script exports `up` (and may export `down`) — or `seed(db, { stage })`, in
3603
- * `seeds/` — and a
3604
- * `.sql` file is run whole. The second is what makes a folder independent of
3605
- * the client the app uses — Better Auth's schema is SQL whatever the
3606
- * application queries with.
3607
- */
3608
- const SCRIPTS = /* @__PURE__ */ new Set([
3609
- ".ts",
3610
- ".mts",
3611
- ".js",
3612
- ".mjs"
3613
- ]);
3614
- /** Two files in one folder that would be one migration. */
3615
- var DuplicateMigration = class extends Error {
3616
- folder;
3617
- migration;
3618
- constructor(folder, migration) {
3619
- super(`${folder} has two migrations named '${migration}' — a script and a .sql file with the same name. A migration is recorded by name, so only one of them could ever run; rename or remove the other.`);
3620
- this.folder = folder;
3621
- this.migration = migration;
3622
- this.name = "DuplicateMigration";
3623
- }
3624
- };
3625
- /** A script in a migrations folder that exports no `up`. */
3626
- var MigrationHasNoUp = class extends Error {
3627
- file;
3628
- constructor(file) {
3629
- super(`${file} is in a migrations folder but exports no \`up\` function. Export \`async function up(db: Kysely<unknown>)\`, or move the file out of the folder if it is not a migration.`);
3630
- this.file = file;
3631
- this.name = "MigrationHasNoUp";
3632
- }
3633
- };
3634
- /** A script in a seeds folder that exports no `seed`. */
3635
- var SeedHasNoSeed = class extends Error {
3636
- file;
3637
- constructor(file) {
3638
- super(`${file} is in a seeds folder but exports no \`seed\` function. Export \`async function seed(db, { stage })\`, or move the file out of the folder if it is not a seed.`);
3639
- this.file = file;
3640
- this.name = "SeedHasNoSeed";
3641
- }
3642
- };
3643
- /**
3644
- * The migration files in a folder, by name, without reading them.
3645
- *
3646
- * Also what `gkm dev` compares against a construct's history to report what
3647
- * is pending, which is why it is separate from loading them.
3648
- */
3649
- async function migrationFiles(folder) {
3650
- const entries = await readdir(folder, { withFileTypes: true }).catch((error) => {
3651
- if (error.code === "ENOENT") return [];
3652
- throw error;
3653
- });
3654
- const files = /* @__PURE__ */ new Map();
3655
- for (const entry of entries) {
3656
- if (!entry.isFile() || !isMigration(entry.name)) continue;
3657
- const name = entry.name.slice(0, -extname(entry.name).length);
3658
- if (files.has(name)) throw new DuplicateMigration(folder, name);
3659
- files.set(name, join(folder, entry.name));
3660
- }
3661
- return files;
3662
- }
3663
- /** A Kysely provider over one construct's folder. */
3664
- function folderProvider(folder, raw) {
3665
- return { async getMigrations() {
3666
- const migrations = {};
3667
- for (const [name, file] of await migrationFiles(folder)) migrations[name] = extname(file) === ".sql" ? await sqlMigration(file, raw) : await scriptMigration(file);
3668
- return migrations;
3669
- } };
3670
- }
3671
- /**
3672
- * The seeds in a folder, loaded, in name order.
3673
- *
3674
- * Every one runs on every pass — there is no history to consult — so the order
3675
- * is the only thing between two seeds where one reads what another writes.
3676
- * Name them to sort that way.
3677
- */
3678
- async function loadSeeds(folder, raw) {
3679
- const files = [...await migrationFiles(folder)].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
3680
- const seeds = [];
3681
- for (const [name, file] of files) seeds.push({
3682
- name,
3683
- run: extname(file) === ".sql" ? (await sqlMigration(file, raw)).up : await scriptSeed(file)
3684
- });
3685
- return seeds;
3686
- }
3687
- /**
3688
- * A seed script, imported as it is on disk now.
3689
- *
3690
- * Keyed by its modification time: a seed is run again after it is edited, and
3691
- * a process that has imported it once would otherwise run the old one.
3692
- */
3693
- async function scriptSeed(file) {
3694
- const url = pathToFileURL(file);
3695
- url.searchParams.set("mtime", String((await stat(file)).mtimeMs));
3696
- const module = await import(url.href);
3697
- if (typeof module.seed !== "function") throw new SeedHasNoSeed(file);
3698
- return module.seed;
3699
- }
3700
- function isMigration(file) {
3701
- if (file.endsWith(".d.ts")) return false;
3702
- if (/\.(spec|test)\.[cm]?[jt]s$/.test(file)) return false;
3703
- const extension = extname(file);
3704
- return extension === ".sql" || SCRIPTS.has(extension);
3705
- }
3706
- async function scriptMigration(file) {
3707
- const module = await import(pathToFileURL(file).href);
3708
- if (typeof module.up !== "function") throw new MigrationHasNoUp(file);
3709
- return {
3710
- up: module.up,
3711
- ...typeof module.down === "function" ? { down: module.down } : {}
3712
- };
3713
- }
3714
- /**
3715
- * A `.sql` file, run whole.
3716
- *
3717
- * One query rather than split on `;`: with no parameters, Postgres takes
3718
- * several statements in one message, and splitting by hand breaks on the first
3719
- * `;` inside a string or a function body. There is no `down` — a generated
3720
- * schema is rolled back by a migration that says how, not by guessing.
3721
- */
3722
- async function sqlMigration(file, raw) {
3723
- const statements = await readFile(file, "utf-8");
3724
- return { up: async (db) => {
3725
- await raw.raw(statements).execute(db);
3726
- } };
3727
- }
3728
- //#endregion
3729
- //#region src/migrate/databases.ts
3730
- /**
3731
- * Bring each construct's schema up to its migrations folder.
3732
- *
3733
- * Which constructs, in what order, from which folder, is `migrationTargets`'
3734
- * answer and nobody else's. What is decided here is only how one target is
3735
- * run: as its owner, in its own schema, with a history of its own.
3736
- */
3737
- /** A folder under `db/` that names no construct. */
3738
- var UnknownMigrationFolder = class extends Error {
3739
- folder;
3740
- known;
3741
- constructor(folder, known) {
3742
- super(`${folder} names no database construct, so its migrations would never run. ${known.length > 0 ? `The folders that do: ${known.join(", ")}.` : "This project declares no database."} Rename the folder after the construct it belongs to.`);
3743
- this.folder = folder;
3744
- this.known = known;
3745
- this.name = "UnknownMigrationFolder";
3746
- }
3747
- };
3748
- /** A construct was asked for by a name that takes no migrations. */
3749
- var NoSuchMigrationTarget = class extends Error {
3750
- construct;
3751
- known;
3752
- constructor(construct, known) {
3753
- super(`'${construct}' is not a database construct. ` + (known.length > 0 ? `The ones with migrations: ${known.join(", ")}.` : "This project declares no database."));
3754
- this.construct = construct;
3755
- this.known = known;
3756
- this.name = "NoSuchMigrationTarget";
3757
- }
3758
- };
3759
- /**
3760
- * A migration left directly in a construct's folder, where nothing reads it.
3761
- *
3762
- * Migrations moved into `migrations/` so seeds could sit beside them. A file
3763
- * still at the old level would otherwise never run, and nothing would say so.
3764
- */
3765
- var MigrationsOutsideFolder = class extends Error {
3766
- folder;
3767
- files;
3768
- constructor(folder, files) {
3769
- super(`${folder} has ${files.join(", ")} directly inside it, where nothing runs them. Migrations live in ${folder}/migrations/ and seeds in ${folder}/seeds/ — move ${files.length === 1 ? "it" : "them"} there.`);
3770
- this.folder = folder;
3771
- this.files = files;
3772
- this.name = "MigrationsOutsideFolder";
3773
- }
3774
- };
3775
- /** A folder inside a construct's folder that is neither `migrations` nor `seeds`. */
3776
- var UnknownDatabaseFolder = class extends Error {
3777
- folder;
3778
- constructor(folder) {
3779
- super(`${folder} is neither migrations/ nor seeds/, so nothing in it would ever run. A construct's folder holds those two; rename or remove it.`);
3780
- this.folder = folder;
3781
- this.name = "UnknownDatabaseFolder";
3782
- }
3783
- };
3784
- /** A seed failed; its own writes are rolled back, the ones before it stay. */
3785
- var SeedFailed = class extends Error {
3786
- construct;
3787
- seed;
3788
- cause;
3789
- constructor(construct, seed, cause) {
3790
- super(`${construct}: seed '${seed}' failed — ${cause instanceof Error ? cause.message : String(cause)}. Its writes were rolled back; fix it and run it again.`);
3791
- this.construct = construct;
3792
- this.seed = seed;
3793
- this.cause = cause;
3794
- this.name = "SeedFailed";
3795
- }
3796
- };
3797
- /** A construct with roles has no owner URL to migrate with. */
3798
- var NoOwnerCredential = class extends Error {
3799
- construct;
3800
- key;
3801
- constructor(construct, key) {
3802
- super(`${construct} has no ${key} to migrate with. Migrations run as the owner role — the runtime one cannot create a table — and nothing resolved its URL. Is the stage reconciled?`);
3803
- this.construct = construct;
3804
- this.key = key;
3805
- this.name = "NoOwnerCredential";
3806
- }
3807
- };
3808
- /** A migration failed; the ones before it in the run stay applied. */
3809
- var MigrationFailed = class extends Error {
3810
- construct;
3811
- migration;
3812
- cause;
3813
- constructor(construct, migration, cause) {
3814
- super(`${construct}: migration ${migration ? `'${migration}' ` : ""}failed — ${cause instanceof Error ? cause.message : String(cause)}`);
3815
- this.construct = construct;
3816
- this.migration = migration;
3817
- this.cause = cause;
3818
- this.name = "MigrationFailed";
3819
- }
3820
- };
3821
- /** The project's Kysely could not be found from where a database is declared. */
3822
- var KyselyNotFound = class extends Error {
3823
- from;
3824
- constructor(from) {
3825
- super(`Could not resolve 'kysely' from ${from}. Migrations run with the project's own Kysely — install it where the database is declared.`);
3826
- this.from = from;
3827
- this.name = "KyselyNotFound";
3828
- }
3829
- };
3830
- /** Apply every pending migration, construct by construct, parents first. */
3831
- async function migrateDatabases(options) {
3832
- const runs = [];
3833
- for (const target of await targetsFor(options)) {
3834
- if ((await migrationFiles(join(options.root, target.migrations))).size === 0) {
3835
- runs.push({
3836
- target,
3837
- applied: []
3838
- });
3839
- continue;
3840
- }
3841
- const applied = await withMigrator(options, target, async (migrator) => {
3842
- const { error, results = [] } = await migrator.migrateToLatest();
3843
- if (error) {
3844
- const failed = results.find((result) => result.status === "Error");
3845
- throw new MigrationFailed(target.id, failed?.migrationName, error);
3846
- }
3847
- return results.filter((result) => result.status === "Success").map((result) => result.migrationName);
3848
- });
3849
- runs.push({
3850
- target,
3851
- applied
3852
- });
3853
- }
3854
- return runs;
3855
- }
3856
- /**
3857
- * Run every construct's seeds, parents first — each one, every time, on every
3858
- * stage, production included. Each is handed the stage, so a seed decides
3859
- * what belongs where.
3860
- *
3861
- * Seeds are reference data kept up to date by being run again, so there is no
3862
- * history: a seed is an upsert, and a changed one is applied by the next run.
3863
- * Each runs in its own transaction, as the construct's owner, so a failure
3864
- * leaves nothing half-written. Assumes the schema is migrated —
3865
- * {@link migrateAndSeed} is what makes that true.
3866
- */
3867
- async function seedDatabases(options) {
3868
- const runs = [];
3869
- for (const target of await targetsFor(options)) {
3870
- const folder = join(options.root, target.seeds);
3871
- if ((await migrationFiles(folder)).size === 0) {
3872
- runs.push({
3873
- target,
3874
- seeded: []
3875
- });
3876
- continue;
3877
- }
3878
- const seeded = await withOwner(options, target, async (db, kysely) => {
3879
- const names = [];
3880
- for (const seed of await loadSeeds(folder, kysely.sql)) {
3881
- try {
3882
- await db.transaction().execute((trx) => seed.run(trx, { stage: options.stage }));
3883
- } catch (error) {
3884
- throw new SeedFailed(target.id, seed.name, error);
3885
- }
3886
- names.push(seed.name);
3887
- }
3888
- return names;
3889
- });
3890
- runs.push({
3891
- target,
3892
- seeded
3893
- });
3894
- }
3895
- return runs;
3896
- }
3897
- /**
3898
- * Migrate, then seed: a seed only ever runs against the schema it was written
3899
- * for.
3900
- *
3901
- * With `only`, the constructs it lives in are migrated too — a tenant's seed
3902
- * cannot run before the database that owns its schema exists — and only it is
3903
- * seeded.
3904
- */
3905
- async function migrateAndSeed(options) {
3906
- const migrations = [];
3907
- for (const target of await withParents(options)) migrations.push(...await migrateDatabases({
3908
- ...options,
3909
- only: target.id
3910
- }));
3911
- return {
3912
- migrations,
3913
- seeds: await seedDatabases(options)
3914
- };
3915
- }
3916
- /** The targets `only` names, after the ones it lives in; all without it. */
3917
- async function withParents(options) {
3918
- const chosen = await targetsFor(options);
3919
- if (!options.only) return chosen;
3920
- const all = migrationTargets(options.manifest);
3921
- const ids = /* @__PURE__ */ new Set();
3922
- for (const target of chosen) {
3923
- let current = target;
3924
- while (current) {
3925
- ids.add(current.id);
3926
- const parent = current.of;
3927
- current = parent ? all.find((t) => t.id === parent) : void 0;
3928
- }
3929
- }
3930
- return all.filter((target) => ids.has(target.id));
3931
- }
3932
- /**
3933
- * What each construct's folder has that its history does not — without
3934
- * applying any of it. What `gkm dev` reports.
3935
- */
3936
- async function pendingMigrations(options) {
3937
- const found = [];
3938
- for (const target of await targetsFor(options)) {
3939
- if ((await migrationFiles(join(options.root, target.migrations))).size === 0) continue;
3940
- const pending = await withMigrator(options, target, async (migrator) => (await migrator.getMigrations()).filter((migration) => migration.executedAt === void 0).map((migration) => migration.name));
3941
- if (pending.length > 0) found.push({
3942
- target,
3943
- pending
3944
- });
3945
- }
3946
- return found;
3947
- }
3948
- /**
3949
- * The targets this run covers, with every folder under `db/` accounted for.
3950
- *
3951
- * A folder that names no construct is an error rather than something to skip:
3952
- * a typo in its name is otherwise a migration that silently never runs.
3953
- */
3954
- async function targetsFor(options) {
3955
- const targets = migrationTargets(options.manifest);
3956
- const folders = targets.map((target) => target.folder);
3957
- for (const folder of await foldersIn(join(options.root, MIGRATIONS_ROOT))) {
3958
- const relative = `${MIGRATIONS_ROOT}/${folder}`;
3959
- if (!folders.includes(relative)) throw new UnknownMigrationFolder(relative, folders);
3960
- await checkLayout(options.root, relative);
3961
- }
3962
- if (!options.only) return targets;
3963
- const id = canonicalId(options.only);
3964
- const chosen = targets.filter((target) => target.id === id);
3965
- if (chosen.length === 0) throw new NoSuchMigrationTarget(options.only, targets.map((target) => target.id));
3966
- return chosen;
3967
- }
3968
- /**
3969
- * A construct's folder holds `migrations/` and `seeds/`, and nothing else —
3970
- * anything beside them is a file or folder nothing would ever run.
3971
- */
3972
- async function checkLayout(root, folder) {
3973
- const entries = await readdir(join(root, folder), { withFileTypes: true });
3974
- const loose = entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).map((entry) => entry.name);
3975
- if (loose.length > 0) throw new MigrationsOutsideFolder(folder, loose);
3976
- for (const entry of entries) if (entry.isDirectory() && !DATABASE_FOLDERS.includes(entry.name)) throw new UnknownDatabaseFolder(`${folder}/${entry.name}`);
3977
- }
3978
- async function foldersIn(dir) {
3979
- return (await readdir(dir, { withFileTypes: true }).catch((error) => {
3980
- if (error.code === "ENOENT") return [];
3981
- throw error;
3982
- })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
3983
- }
3984
- /**
3985
- * A migrator for one target, connected as its owner.
3986
- *
3987
- * No `migrationTableSchema`: the owner role's `search_path` is pinned to the
3988
- * construct's schema, so the history lands there — `app.kysely_migration` for
3989
- * the database, `auth_database.kysely_migration` for its tenant — and two
3990
- * constructs in one Postgres never share one.
3991
- *
3992
- * Unordered migrations are allowed. Files are timestamped, so a branch merged
3993
- * after another's migration ran carries an older one; refusing it would make
3994
- * every such merge a manual repair.
3995
- */
3996
- async function withMigrator(options, target, run) {
3997
- return withOwner(options, target, (db, { Migrator, sql }) => run(new Migrator({
3998
- db,
3999
- provider: folderProvider(join(options.root, target.migrations), sql),
4000
- allowUnorderedMigrations: true
4001
- })));
4002
- }
4003
- /**
4004
- * One target's database, connected as its owner and closed afterwards — what
4005
- * migrations and seeds both run as.
4006
- */
4007
- async function withOwner(options, target, run) {
4008
- const kysely = await kyselyFrom(options.sources?.[target.id]?.file ?? (target.of ? options.sources?.[target.of]?.file : void 0) ?? join(options.root, "package.json"));
4009
- const db = new kysely.Kysely({ dialect: new kysely.PostgresDialect({ pool: new pg.Pool(poolConfig(ownerUrl(options, target))) }) });
4010
- try {
4011
- return await run(db, kysely);
4012
- } finally {
4013
- await db.destroy();
4014
- }
4015
- }
4016
- /**
4017
- * The owner's URL, with no fallback to the runtime one.
4018
- *
4019
- * The runtime role can create nothing, so falling back would only move the
4020
- * failure to the first `CREATE TABLE` and make it read like a permissions
4021
- * bug. The one exception is a database declared `roles: false`, where there is
4022
- * no owner — both URLs are the master credential by design.
4023
- */
4024
- function ownerUrl(options, target) {
4025
- const key = provideKey(target.id, "ownerUrl");
4026
- const owner = options.env[key];
4027
- if (owner) return owner;
4028
- const database = options.manifest[target.of ?? target.id];
4029
- if (database?.kind === "database" && database.roles === false) {
4030
- const url = options.env[provideKey(target.id, "url")];
4031
- if (url) return url;
4032
- }
4033
- throw new NoOwnerCredential(target.id, key);
4034
- }
4035
- /**
4036
- * Pool settings for a URL, with `?search_path=` as the startup option Postgres
4037
- * reads. Only a `roles: false` URL carries it — a role pins its own.
4038
- */
4039
- function poolConfig(url) {
4040
- const parsed = new URL(url);
4041
- const searchPath = parsed.searchParams.get("search_path");
4042
- if (!searchPath) return { connectionString: url };
4043
- parsed.searchParams.delete("search_path");
4044
- return {
4045
- connectionString: parsed.toString(),
4046
- options: `-c search_path=${searchPath}`
4047
- };
4048
- }
4049
- /** Kysely as the project resolves it from `from`. */
4050
- async function kyselyFrom(from) {
4051
- const require = createRequire(from);
4052
- try {
4053
- const [main, migration] = await Promise.all([import(pathToFileURL(require.resolve("kysely")).href), import(pathToFileURL(require.resolve("kysely/migration")).href)]);
4054
- return {
4055
- Kysely: main.Kysely,
4056
- PostgresDialect: main.PostgresDialect,
4057
- sql: main.sql,
4058
- Migrator: migration.Migrator
4059
- };
4060
- } catch {
4061
- throw new KyselyNotFound(from);
4062
- }
4063
- }
4064
- //#endregion
4065
- //#region src/compose/docker.ts
4066
- /**
4067
- * The Docker operations `gkm compose` needs, behind an interface.
4068
- *
4069
- * Small on purpose, like reconcile's: the rules about *when* each runs — the
4070
- * registry is asked before anything is pulled, nothing is started for a tag
4071
- * that is missing — are asserted against a fake, and this file is only the
4072
- * wiring to the real CLI.
4073
- *
4074
- * Every call is an argument array. Image refs, tags and paths come from the
4075
- * workspace, and none of them reaches a shell.
4076
- */
4077
- /** How long a stack has to pass its health checks. */
4078
- const WAIT_TIMEOUT_S = 180;
4079
- /** `docker compose port` printed nothing a port can be read from. */
4080
- var ServicePortUnknown = class extends Error {
4081
- service;
4082
- inside;
4083
- output;
4084
- constructor(service, inside, output) {
4085
- super(`'${service}' publishes no host port for ${inside} (docker said: ${output.trim() || "nothing"}). Is it running? \`docker compose ps\` shows the stack.`);
4086
- this.service = service;
4087
- this.inside = inside;
4088
- this.output = output;
4089
- this.name = "ServicePortUnknown";
4090
- }
4091
- };
4092
- function compose(stack, args) {
4093
- return [
4094
- "compose",
4095
- "-p",
4096
- stack.project,
4097
- "-f",
4098
- stack.file,
4099
- ...args
4100
- ];
4101
- }
4102
- /** Run a program and keep everything it said, whatever it exits with. */
4103
- function capture(command, args, timeoutMs = 6e4) {
4104
- return new Promise((resolve, reject) => {
4105
- const child = spawn(command, [...args], {
4106
- stdio: [
4107
- "ignore",
4108
- "pipe",
4109
- "pipe"
4110
- ],
4111
- shell: false
4112
- });
4113
- let stdout = "";
4114
- let stderr = "";
4115
- child.stdout.on("data", (chunk) => {
4116
- stdout += chunk.toString();
4117
- });
4118
- child.stderr.on("data", (chunk) => {
4119
- stderr += chunk.toString();
4120
- });
4121
- const timer = setTimeout(() => child.kill("SIGKILL"), timeoutMs);
4122
- timer.unref?.();
4123
- child.on("error", (error) => {
4124
- clearTimeout(timer);
4125
- reject(error);
4126
- });
4127
- child.on("close", (code) => {
4128
- clearTimeout(timer);
4129
- resolve({
4130
- code,
4131
- stdout,
4132
- stderr
4133
- });
4134
- });
4135
- });
4136
- }
4137
- /**
4138
- * Whether a failed `docker manifest inspect` means the tag is not there, as
4139
- * opposed to the registry not answering.
4140
- *
4141
- * Read off the message because that is all the CLI gives. Anything not
4142
- * recognisably "no such tag" is treated as the registry being unreachable:
4143
- * a registry that refused credentials must not be reported as a tag that was
4144
- * never pushed, or the fix offered would be the wrong one.
4145
- */
4146
- function isMissingManifest(output) {
4147
- return /no such manifest|manifest unknown|manifest for .* not found|not found: manifest/i.test(output);
4148
- }
4149
- const dockerCompose = {
4150
- async lookup(ref) {
4151
- const { code, stdout, stderr } = await capture("docker", [
4152
- "manifest",
4153
- "inspect",
4154
- ref
4155
- ]);
4156
- if (code === 0) return {
4157
- ref,
4158
- status: "found"
4159
- };
4160
- const output = `${stderr}\n${stdout}`.trim();
4161
- return isMissingManifest(output) ? {
4162
- ref,
4163
- status: "missing"
4164
- } : {
4165
- ref,
4166
- status: "unreachable",
4167
- detail: output
4168
- };
4169
- },
4170
- async build(stack, services) {
4171
- await run$1("docker", compose(stack, ["build", ...services]), {
4172
- cwd: stack.cwd,
4173
- env: {
4174
- ...process.env,
4175
- DOCKER_BUILDKIT: "1"
4176
- }
4177
- });
4178
- },
4179
- async pull(stack, services) {
4180
- await run$1("docker", compose(stack, ["pull", ...services]), { cwd: stack.cwd });
4181
- },
4182
- async up(stack, services) {
4183
- await run$1("docker", compose(stack, [
4184
- "up",
4185
- "-d",
4186
- "--wait",
4187
- `--wait-timeout=${WAIT_TIMEOUT_S}`,
4188
- ...services ? [] : ["--remove-orphans"],
4189
- ...services ?? []
4190
- ]), { cwd: stack.cwd });
4191
- },
4192
- async down(stack) {
4193
- await run$1("docker", compose(stack, ["down"]), { cwd: stack.cwd });
4194
- },
4195
- async port(stack, service, inside) {
4196
- const output = await runOutput("docker", compose(stack, [
4197
- "port",
4198
- service,
4199
- String(inside)
4200
- ]), { cwd: stack.cwd });
4201
- const port = Number(output.trim().split(":").pop());
4202
- if (!Number.isInteger(port) || port <= 0) throw new ServicePortUnknown(service, inside, output);
4203
- return port;
4204
- },
4205
- async copyOut(stack, service, from, to) {
4206
- await mkdir(dirname(to), { recursive: true });
4207
- await run$1("docker", compose(stack, [
4208
- "cp",
4209
- `${service}:${from}`,
4210
- to
4211
- ]), { cwd: stack.cwd });
4212
- },
4213
- async digest(ref) {
4214
- const { code, stdout } = await capture("docker", [
4215
- "image",
4216
- "inspect",
4217
- "--format={{json .RepoDigests}} {{.Id}}",
4218
- ref
4219
- ]);
4220
- if (code !== 0) return void 0;
4221
- const [json = "[]", id] = stdout.trim().split(" ");
4222
- const repository = ref.split("@")[0].replace(/:[\w][\w.-]*$/, "");
4223
- try {
4224
- const digests = JSON.parse(json);
4225
- const match = Array.isArray(digests) ? digests.find((entry) => typeof entry === "string" && entry.startsWith(`${repository}@`)) : void 0;
4226
- if (match) return match.slice(repository.length + 1);
4227
- } catch {}
4228
- return id || void 0;
4229
- }
4230
- };
4231
- //#endregion
4232
- //#region src/compose/images.ts
4233
- /**
4234
- * The tag a site's image carries for a release.
4235
- *
4236
- * A site's public URLs are inlined when it is built, so one bundle serves one
4237
- * stage: `v1.4.0-production` and `v1.4.0-staging` are two images of the same
4238
- * commit. A backend reads its URLs at runtime, so one image serves every stage
4239
- * and its tag is the release's alone.
4240
- */
4241
- function siteTag(tag, stage) {
4242
- return `${tag}-${stage}`;
4243
- }
4244
- /** Images a tag names that the registry does not have. */
4245
- var ImageTagNotFound = class extends Error {
4246
- tag;
4247
- refs;
4248
- constructor(tag, refs) {
4249
- super(`The registry has no image for ${refs.length === 1 ? "this app" : `${refs.length} apps`} at '${tag}':\n` + refs.map((ref) => ` - ${ref}`).join("\n") + `\nNothing was pulled or started. Push '${tag}' for every app from CI, pick a tag that exists, or run without --tag to build from this checkout.`);
4250
- this.tag = tag;
4251
- this.refs = refs;
4252
- this.name = "ImageTagNotFound";
4253
- }
4254
- };
4255
- /** The registry did not answer, or did not let this machine in. */
4256
- var RegistryUnreachable = class extends Error {
4257
- ref;
4258
- detail;
4259
- constructor(ref, detail) {
4260
- super(`Could not ask the registry about ${ref}: ${detail || "no answer"}\nNothing was pulled or started. Check the registry is reachable and that this machine is logged in to it (\`docker login <registry>\`).`);
4261
- this.ref = ref;
4262
- this.detail = detail;
4263
- this.name = "RegistryUnreachable";
4264
- }
4265
- };
4266
- /**
4267
- * Assert every image exists, before anything else happens.
4268
- *
4269
- * Asked all at once and reported all at once: a release missing two images is
4270
- * one problem with two names, and finding the second only after fixing the
4271
- * first is a slower way to learn the same thing.
4272
- *
4273
- * @throws {RegistryUnreachable} when the registry cannot say
4274
- * @throws {ImageTagNotFound} listing every missing image
4275
- */
4276
- async function assertImagesExist(docker, tag, images) {
4277
- const lookups = await Promise.all(images.map((image) => docker.lookup(image.ref)));
4278
- const unreachable = lookups.find((lookup) => lookup.status === "unreachable");
4279
- if (unreachable?.status === "unreachable") throw new RegistryUnreachable(unreachable.ref, unreachable.detail);
4280
- const missing = lookups.filter((lookup) => lookup.status === "missing").map((lookup) => lookup.ref);
4281
- if (missing.length > 0) throw new ImageTagNotFound(tag, missing);
4282
- }
4283
- //#endregion
4284
- //#region src/compose/caddyfile.ts
4285
- /** The Caddyfile for a stack's edge. */
4286
- function edgeCaddyfile(sites, options) {
4287
- const global = options.local ? `{
4288
- # No ACME and no public DNS: the CA is Caddy's own and the names resolve
4289
- # to this machine.
4290
- local_certs
4291
- auto_https disable_redirects
4292
- }` : options.email ? `{
4293
- email ${options.email}
4294
- }` : "";
4295
- const blocks = sites.map(({ host, upstream }) => `https://${host} {${options.local ? `
4296
- tls internal
4297
- ` : ""}
4298
- # Host and X-Forwarded-* are passed through as Caddy does by default, so
4299
- # an app builds redirects, cookies and links on the address a caller used.
4300
- reverse_proxy ${upstream} {
4301
- # Never buffer: a streamed response — server-sent events, a Next.js
4302
- # RSC payload — reaches the client as each chunk is written.
4303
- flush_interval -1
4304
- }
4305
- }`);
4306
- return `${HEADER}${global ? `${global}\n\n` : ""}${blocks.join("\n\n")}\n`;
4307
- }
4308
- const HEADER = `# Generated by gkm compose from the construct manifest — do not edit.
4309
- #
4310
- # One host per API and site, each in front of its own container.
4311
- `;
4312
- //#endregion
4313
- //#region src/compose/stack.ts
4314
- /**
4315
- * The stack a workspace runs as for one stage — pure.
4316
- *
4317
- * Everything `gkm compose` writes is decided here, as data: the compose file,
4318
- * the Caddyfile, one env file per backend, and the image each app runs. The
4319
- * command around it reads the workspace, asks the registry, writes the files
4320
- * and starts Docker; none of those decide anything, so all of it can be
4321
- * asserted without a daemon.
4322
- *
4323
- * The values come from the same derivations every other target uses. The
4324
- * plan is reconcile's `planFor`, the keys an app reads are `appEnvKeys`, and
4325
- * every URL is `envFor`'s — asked twice, once with each app at its public
4326
- * address and once on the compose network, because a browser and a sibling
4327
- * service reach the same API by different names.
4328
- */
4329
- /** Where a stack's files are written, relative to the workspace root. */
4330
- function stackDir(stage) {
4331
- return `.gkm/compose/${stage}`;
4332
- }
4333
- /**
4334
- * The compose project a stage's stack runs as.
4335
- *
4336
- * The deploy identity's scope, so two workspaces with one name in different
4337
- * namespaces never share containers or volumes — and the stage, so a
4338
- * machine can run two stages side by side.
4339
- */
4340
- function composeProject(identity) {
4341
- return `${identity.scope}-${identity.stage}`;
4342
- }
4343
- /** The ports the edge is published on, and the variables that move them. */
4344
- const EDGE_PORT_ENV = {
4345
- https: "GKM_COMPOSE_HTTPS_PORT",
4346
- http: "GKM_COMPOSE_HTTP_PORT"
4347
- };
4348
- /**
4349
- * Containers this target does not run.
4350
- *
4351
- * Object storage is the stage's own — an externally hosted bucket whose URL
4352
- * the stage's secrets hold — and so is a deployed stage's mail. The AWS
4353
- * emulator never applies: a stack is a server target, whose events are
4354
- * pg-boss.
4355
- */
4356
- const NOT_RUN = {
4357
- minio: true,
4358
- localstack: true,
4359
- caddy: true
4360
- };
4361
- /** Kinds whose `<ID>_URL` a stage must be given, because nothing here runs them. */
4362
- const EXTERNAL_KINDS = {
4363
- objects: true,
4364
- "file-server": true
4365
- };
4366
- /** The S3 client's credentials, read beside a bucket's URL when it has them. */
4367
- const STORAGE_KEYS = [
4368
- "AWS_ACCESS_KEY_ID",
4369
- "AWS_SECRET_ACCESS_KEY",
4370
- "AWS_REGION"
4371
- ];
4372
- /** A bucket the stack needs and the stage was not given. */
4373
- var BucketNotConfigured = class extends Error {
4374
- app;
4375
- id;
4376
- key;
4377
- stage;
4378
- constructor(app, id, key, stage) {
4379
- super(`'${app}' uses the bucket '${id}', and gkm compose runs no object storage: the stage needs an externally hosted bucket. Set its URL in the stage's secrets: gkm secrets:set ${key} 's3://…' --stage ${stage}`);
4380
- this.app = app;
4381
- this.id = id;
4382
- this.key = key;
4383
- this.stage = stage;
4384
- this.name = "BucketNotConfigured";
4385
- }
4386
- };
4387
- /** A value only the stage's secrets can hold, and they do not. */
4388
- var StageSecretMissing = class extends Error {
4389
- app;
4390
- key;
4391
- stage;
4392
- constructor(app, key, stage) {
4393
- super(`'${app}' reads ${key}, and the stage '${stage}' has none. Nothing in the stack can derive it; set it in the stage's secrets: gkm secrets:set ${key} '…' --stage ${stage}`);
4394
- this.app = app;
4395
- this.key = key;
4396
- this.stage = stage;
4397
- this.name = "StageSecretMissing";
4398
- }
4399
- };
4400
- /** A deployed stage's secrets with no seed to derive its passwords from. */
4401
- var StageSeedMissing = class extends Error {
4402
- stage;
4403
- constructor(stage) {
4404
- super(`The stage '${stage}' has no seed in its secrets, and a deployed stage's database passwords are derived from it. Run gkm compose without --dry-run once, or gkm deploy, to generate it.`);
4405
- this.stage = stage;
4406
- this.name = "StageSeedMissing";
4407
- }
4408
- };
4409
- /** A workspace with no API and no site to run. */
4410
- var NothingToCompose = class extends Error {
4411
- root;
4412
- constructor(root) {
4413
- super(`${root} declares no RestApi and no site, so a stack would run nothing. Workers and mobile apps are not run by gkm compose yet.`);
4414
- this.root = root;
4415
- this.name = "NothingToCompose";
4416
- }
4417
- };
4418
- /** A value that cannot be written to an env file a line at a time. */
4419
- var EnvValueMultiline = class extends Error {
4420
- app;
4421
- key;
4422
- constructor(app, key) {
4423
- super(`${key} for '${app}' spans more than one line, and an env file holds one value per line. Store it on one line — base64, or JSON — and decode it where it is read.`);
4424
- this.app = app;
4425
- this.key = key;
4426
- this.name = "EnvValueMultiline";
4427
- }
4428
- };
4429
- /** The services a stack's apps wait on and the edge waits on in turn. */
4430
- const HEALTHY = { condition: "service_healthy" };
4431
- /**
4432
- * An app's health check: its own server, asked on 127.0.0.1 — not
4433
- * `localhost`, which resolves to `::1` first in an Alpine container while a
4434
- * server may listen on IPv4 alone.
4435
- */
4436
- function probe(port, path) {
4437
- return {
4438
- test: [
4439
- "CMD",
4440
- "wget",
4441
- "-q",
4442
- "-O",
4443
- "/dev/null",
4444
- `http://127.0.0.1:${port}${path}`
4445
- ],
4446
- interval: "5s",
4447
- timeout: "3s",
4448
- retries: 20,
4449
- start_period: "10s"
4450
- };
4451
- }
4452
- /** The stack for one stage. */
4453
- function composeStack(input) {
4454
- const { workspace, manifest, stage, identity } = input;
4455
- const local = stage === workspace.stages.local;
4456
- const project = composeProject(identity);
4457
- const custom = input.secrets?.custom ?? {};
4458
- const https = input.ports?.https ?? 443;
4459
- const http = input.ports?.http ?? 80;
4460
- const plan = planFor(manifest, stage, provisionOrder(manifest), {
4461
- localStage: workspace.stages.local,
4462
- events: DEFAULT_EVENTS.server,
4463
- cache: DEFAULT_CACHE.server,
4464
- edge: false
4465
- });
4466
- const byId = new Map(plan.resources.map((r) => [r.id, r]));
4467
- const seed = local ? void 0 : input.secrets?.seed;
4468
- if (!local && !seed) throw new StageSeedMissing(stage);
4469
- const credential = {
4470
- master: local ? "geekmidas" : localRolePassword(workspace.name, plan, "master", seed),
4471
- ...seed ? { seed } : {}
4472
- };
4473
- const infra = plan.containers.filter((c) => !NOT_RUN[c]).filter((c) => local || c !== "mailpit").sort();
4474
- const apps = stackApps(workspace, manifest, plan, {
4475
- local,
4476
- stage,
4477
- https,
4478
- identity,
4479
- images: input.images
4480
- });
4481
- if (apps.length === 0) throw new NothingToCompose(workspace.root);
4482
- const derivation = {
4483
- project: workspace.name,
4484
- master: credential.master,
4485
- ...credential.seed ? { seed: credential.seed } : {}
4486
- };
4487
- const outside = networkEnv(plan, {
4488
- ...derivation,
4489
- addresses: Object.fromEntries(apps.map((app) => [app.id, app.url]))
4490
- });
4491
- const inside = networkEnv(plan, {
4492
- ...derivation,
4493
- addresses: Object.fromEntries(apps.map((app) => [app.id, `http://${app.name}:${app.port}`]))
4494
- });
4495
- const owners = /* @__PURE__ */ new Map();
4496
- for (const resource of plan.resources) {
4497
- owners.set(resource.envKey, resource);
4498
- if (resource.kind === "email") {
4499
- owners.set(provideKey(resource.id, "from"), resource);
4500
- owners.set(provideKey(resource.id, "inboxUrl"), resource);
4501
- }
4502
- if (resource.kind === "external-api") owners.set(provideKey(resource.id, "credentials"), resource);
4503
- }
4504
- const surfaceUrls = new Map(plan.resources.filter((r) => r.kind === "rest-api" || r.kind === "site").map((r) => [r.envKey, r.id]));
4505
- const appById = new Map(apps.map((app) => [app.id, app]));
4506
- /**
4507
- * What a key resolves to for one app, from the stage's secrets where only
4508
- * they can hold it, and the derivations otherwise.
4509
- */
4510
- const valueFor = (app, key, owner) => {
4511
- if (owner && EXTERNAL_KINDS[owner.kind] && key === owner.envKey) {
4512
- const value = custom[key];
4513
- if (value === void 0) throw new BucketNotConfigured(app.name, owner.id, key, stage);
4514
- return value;
4515
- }
4516
- if (owner && suppliedBy(owner, key, local)) {
4517
- const value = custom[key];
4518
- if (value === void 0) throw new StageSecretMissing(app.name, key, stage);
4519
- return value;
4520
- }
4521
- if (custom[key] !== void 0) return custom[key];
4522
- const surface = surfaceUrls.get(key);
4523
- if (app.kind === "rest-api" && surface && surface !== app.id) return inside[key];
4524
- return outside[key];
4525
- };
4526
- for (const app of apps) {
4527
- if (app.kind === "site") {
4528
- const site = manifest[app.id];
4529
- if (site?.kind !== "site") continue;
4530
- const args = {};
4531
- for (const [key, source] of Object.entries(publicEnvFor(site, manifest))) {
4532
- const value = valueFor(app, source, owners.get(source));
4533
- if (value !== void 0) args[key] = value;
4534
- }
4535
- if (app.build && Object.keys(args).length > 0) app.build.args = args;
4536
- continue;
4537
- }
4538
- const keys = appEnvKeys(manifest, app.name, input.runnables) ?? /* @__PURE__ */ new Set();
4539
- const values = {};
4540
- for (const key of [...keys].sort()) {
4541
- const owner = owners.get(key) ?? storageOwner(key, plan);
4542
- if (!local && owner?.kind === "email" && key.endsWith("_INBOX_URL")) continue;
4543
- if (STORAGE_KEYS.includes(key)) {
4544
- if (custom[key] !== void 0) values[key] = custom[key];
4545
- continue;
4546
- }
4547
- const value = valueFor(app, key, owner);
4548
- if (value !== void 0) values[key] = value;
4549
- }
4550
- const trusted = provideKey(app.id, "trustedOrigins");
4551
- if (keys.has(trusted)) {
4552
- const internal = (byId.get(app.id)?.callers ?? []).map((caller) => appById.get(caller)).filter((caller) => caller?.kind === "rest-api").map((caller) => `http://${caller.name}:${caller.port}`);
4553
- const origins = [...(values[trusted] ?? "").split(",").filter(Boolean), ...internal];
4554
- values[trusted] = [...new Set(origins)].join(",");
4555
- }
4556
- app.env = {
4557
- ...values,
4558
- NODE_ENV: "production",
4559
- PORT: String(app.port),
4560
- STAGE: stage
4561
- };
4562
- for (const [key, value] of Object.entries(app.env)) if (/[\r\n]/.test(value)) throw new EnvValueMultiline(app.name, key);
4563
- }
4564
- const compose = stackFile({
4565
- project,
4566
- plan,
4567
- infra,
4568
- apps,
4569
- master: credential.master,
4570
- https,
4571
- http
4572
- });
4573
- const caddyfile = edgeCaddyfile(apps.map((app) => ({
4574
- host: app.host,
4575
- upstream: `${app.name}:${app.port}`
4576
- })), { local });
4577
- const dockerfiles = {};
4578
- for (const app of apps) {
4579
- if (!app.build) continue;
4580
- dockerfiles[app.build.dockerfile] = app.kind === "site" ? siteDockerfile(app.name, workspace.apps[app.name], {
4581
- turboPackage: input.packages?.[app.name] ?? app.name,
4582
- packageManager: input.packageManager ?? "pnpm",
4583
- manifest
4584
- }) : generateSlimDockerfile({
4585
- imageName: app.name,
4586
- baseImage: "node:22-alpine",
4587
- port: app.port,
4588
- healthCheckPath: "/health",
4589
- prebuilt: true,
4590
- packageManager: input.packageManager ?? "pnpm",
4591
- bundle: `${app.path}/.gkm/server/dist/server.mjs`
4592
- });
4593
- }
4594
- return {
4595
- project,
4596
- stage,
4597
- local,
4598
- apps,
4599
- infra,
4600
- compose,
4601
- caddyfile,
4602
- dockerfiles,
4603
- plan,
4604
- credential
4605
- };
4606
- /** Whether only the stage's secrets can supply `key` to `owner`. */
4607
- function suppliedBy(owner, key, isLocal) {
4608
- if (owner.kind === "credential") return true;
4609
- if (owner.kind === "external-api") return key === provideKey(owner.id, "credentials");
4610
- if (isLocal) return false;
4611
- if (owner.kind === "secret" || owner.kind === "encryption") return true;
4612
- return owner.kind === "email" && !key.endsWith("_INBOX_URL");
4613
- }
4614
- }
4615
- /** Which bucket an S3 credential key belongs to — the first one declared. */
4616
- function storageOwner(key, plan) {
4617
- if (!STORAGE_KEYS.includes(key)) return void 0;
4618
- return plan.resources.find((r) => r.kind === "objects");
4619
- }
4620
- /** The apps a stack runs: every API and every site, at its stage address. */
4621
- function stackApps(workspace, manifest, plan, options) {
4622
- const apps = [];
4623
- const resources = new Map(plan.resources.map((r) => [r.id, r]));
4624
- for (const [id, declaration] of Object.entries(manifest)) {
4625
- if (declaration.kind !== "rest-api" && declaration.kind !== "site") continue;
4626
- const name = appKey(id);
4627
- const app = workspace.apps[name];
4628
- const resource = resources.get(id);
4629
- if (!app || !resource || !app.port) continue;
4630
- const host = hostOf(name, app, workspace, resource, options);
4631
- const tag = declaration.kind === "site" ? siteTag(options.images.tag, options.stage) : options.images.tag;
4632
- const ref = validateImageRef(imageRef(options.identity, name, options.images.registry, tag));
4633
- apps.push({
4634
- app: name,
4635
- name,
4636
- id,
4637
- kind: declaration.kind,
4638
- path: app.path,
4639
- port: app.port,
4640
- host,
4641
- url: `https://${host}${options.local && options.https !== 443 ? `:${options.https}` : ""}`,
4642
- ref,
4643
- tag,
4644
- ...options.images.mode === "build" ? { build: { dockerfile: `${stackDir(options.stage)}/Dockerfile.${name}` } } : {}
4645
- });
4646
- }
4647
- return apps.sort((a, b) => a.name.localeCompare(b.name));
4648
- }
4649
- /**
4650
- * The host an app answers on for a stage: the `gkm dev` edge's
4651
- * `*.localhost` names for the local stage, and the stage's domain — by the
4652
- * same rule a deploy follows — for any other.
4653
- */
4654
- function hostOf(name, app, workspace, resource, options) {
4655
- if (options.local) return hostFor(resource, workspace.name);
4656
- return resolveHost(name, app, options.stage, workspace.deploy?.domains, isMainFrontendApp(name, app, workspace.apps));
4657
- }
4658
- /** The compose document: the infrastructure, the apps, and the edge. */
4659
- function stackFile(options) {
4660
- const { project, plan, infra, apps } = options;
4661
- const derived = composeFor({
4662
- ...plan,
4663
- containers: [...infra]
4664
- }, {
4665
- project,
4666
- ports: Object.fromEntries(portKeys(infra, plan.fakes).map((key) => [key, 0]))
4667
- });
4668
- const services = {};
4669
- const volumes = {
4670
- ...derived.volumes,
4671
- "caddy-data": {},
4672
- "caddy-config": {}
4673
- };
4674
- for (const name of infra) {
4675
- const { ports: _ports, depends_on, profiles: _profiles, ...service } = derived.services[name];
4676
- services[name] = {
4677
- ...service,
4678
- ...depends_on?.length ? { depends_on: Object.fromEntries(depends_on.map((dep) => [dep, HEALTHY])) } : {}
4679
- };
4680
- }
4681
- const postgres = services.postgres;
4682
- if (postgres) {
4683
- postgres.environment = {
4684
- ...postgres.environment,
4685
- POSTGRES_PASSWORD: options.master
4686
- };
4687
- postgres.ports = ["127.0.0.1::5432"];
4688
- }
4689
- for (const app of apps) services[app.name] = {
4690
- image: app.ref,
4691
- ...app.build ? { build: {
4692
- context: "../../..",
4693
- dockerfile: app.build.dockerfile,
4694
- ...app.build.args ? { args: app.build.args } : {}
4695
- } } : {},
4696
- restart: "unless-stopped",
4697
- ...app.kind === "rest-api" ? {
4698
- env_file: [{
4699
- path: `./${app.name}.env`,
4700
- format: "raw"
4701
- }],
4702
- ...infra.length ? { depends_on: Object.fromEntries(infra.map((name) => [name, HEALTHY])) } : {}
4703
- } : {},
4704
- healthcheck: probe(app.port, app.kind === "site" ? "/" : "/health")
4705
- };
4706
- services.caddy = {
4707
- image: DEFAULT_IMAGES.caddy,
4708
- restart: "unless-stopped",
4709
- ports: [`${options.https}:443`, `${options.http}:80`],
4710
- volumes: [
4711
- "./Caddyfile:/etc/caddy/Caddyfile:ro",
4712
- "caddy-data:/data",
4713
- "caddy-config:/config"
4714
- ],
4715
- depends_on: Object.fromEntries(apps.map((app) => [app.name, HEALTHY])),
4716
- healthcheck: {
4717
- test: [
4718
- "CMD",
4719
- "caddy",
4720
- "version"
4721
- ],
4722
- interval: "10s",
4723
- timeout: "5s",
4724
- retries: 5
4725
- }
4726
- };
4727
- return {
4728
- name: project,
4729
- services,
4730
- volumes
4731
- };
4732
- }
4733
- /** Render an app's env file: one `KEY=value` per line, read raw by compose. */
4734
- function envFile(env) {
4735
- return `${Object.entries(env).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${key}=${value}`).join("\n")}\n`;
4736
- }
4737
- //#endregion
4738
3599
  //#region src/compose/index.ts
4739
3600
  /**
4740
3601
  * `gkm compose` — a workspace's APIs and sites for one stage, as one Docker
4741
3602
  * Compose stack behind Caddy.
4742
3603
  *
4743
- * The order is the point of this file, and the rules it keeps are these:
4744
- *
4745
- * - A tag is checked before anything happens. Every app's image is asked of
4746
- * the registry first; one missing image stops the run naming them all, and
4747
- * nothing is written, pulled or started.
4748
- * - The databases exist before the apps do. Postgres comes up alone, its
4749
- * databases, roles and grants are created and its migrations applied from
4750
- * this machine, and only then do the apps start — so an app never boots
4751
- * against a schema that is not there yet.
4752
- * - What a stage runs is recorded. Each app's tag and the digest it resolved
4753
- * to are written to the stage's state, so "what is this stage running" has
4754
- * an exact answer.
4755
- *
4756
- * Docker, the registry, the build and the clock are injected, so the rules are
4757
- * asserted without a daemon; the defaults are the real ones.
3604
+ * A convenience over `gkm deploy --target compose`: the stage defaults to the
3605
+ * local one, `--build`/`--pull` choose the images whatever `--tag` says, and
3606
+ * `--down` stops the stack. Everything else — the order, the image check, the
3607
+ * state, the health checks — is the compose target's, run through the same
3608
+ * `deploy()` as any other target, so there is one way a stack comes up.
4758
3609
  */
4759
- const logger$4 = console;
4760
3610
  /** `--build` and `--pull` together ask for two different things. */
4761
3611
  var ComposeModeConflict = class extends Error {
4762
3612
  constructor() {
@@ -4764,320 +3614,52 @@ var ComposeModeConflict = class extends Error {
4764
3614
  this.name = "ComposeModeConflict";
4765
3615
  }
4766
3616
  };
4767
- /** A build with no commit to tag its images with. */
4768
- var NoGitRevision = class extends Error {
4769
- root;
4770
- constructor(root) {
4771
- super(`Images built here are tagged with the commit they were built from, and ${root} is not in a git repository with a commit. Commit, or pass --tag to name the images yourself.`);
4772
- this.root = root;
4773
- this.name = "NoGitRevision";
4774
- }
4775
- };
4776
- /** A port variable that does not hold a port. */
4777
- var EdgePortInvalid = class extends Error {
4778
- variable;
4779
- value;
4780
- constructor(variable, value) {
4781
- super(`${variable}=${value} is not a port. Set it to a number between 1 and 65535, or unset it for the default.`);
4782
- this.variable = variable;
4783
- this.value = value;
4784
- this.name = "EdgePortInvalid";
4785
- }
4786
- };
4787
- /** The commit HEAD is at, short — and `-dirty` when the tree has changes. */
4788
- async function gitRevision(root) {
4789
- let sha;
4790
- try {
4791
- sha = (await runOutput("git", [
4792
- "rev-parse",
4793
- "--short",
4794
- "HEAD"
4795
- ], { cwd: root })).trim();
4796
- } catch {
4797
- throw new NoGitRevision(root);
4798
- }
4799
- if (!sha) throw new NoGitRevision(root);
4800
- return (await runOutput("git", ["status", "--porcelain"], { cwd: root }).catch(() => "")).trim() ? `${sha}-dirty` : sha;
4801
- }
4802
- /**
4803
- * Bundle a backend with the gkm that is running — the same build a deploy
4804
- * makes, so the image holds one file and no dependencies.
4805
- */
4806
- async function bundleWithThisCli(appRoot) {
4807
- const entry = process.argv[1];
4808
- if (!entry) return;
4809
- await run$1(process.execPath, [
4810
- ...process.execArgv,
4811
- entry,
4812
- "build",
4813
- "--provider",
4814
- "server",
4815
- "--production"
4816
- ], { cwd: appRoot });
4817
- }
4818
- const defaultDeps = {
4819
- docker: dockerCompose,
4820
- bundle: bundleWithThisCli,
4821
- revision: gitRevision
4822
- };
4823
- /** The edge's ports, from the environment — 443 and 80 when unset. */
4824
- function edgePorts(env = process.env) {
4825
- const read = (variable, fallback) => {
4826
- const value = env[variable];
4827
- if (value === void 0 || value === "") return fallback;
4828
- const port = Number(value);
4829
- if (!Number.isInteger(port) || port < 1 || port > 65535) throw new EdgePortInvalid(variable, value);
4830
- return port;
4831
- };
4832
- return {
4833
- https: read(EDGE_PORT_ENV.https, 443),
4834
- http: read(EDGE_PORT_ENV.http, 80)
4835
- };
4836
- }
4837
3617
  /** `gkm compose`. */
4838
3618
  async function composeCommand(options = {}, deps = {}) {
4839
- const { docker, bundle, revision } = {
4840
- ...defaultDeps,
4841
- ...deps
4842
- };
4843
- const env = deps.env ?? process.env;
4844
3619
  if (options.build && options.pull) throw new ComposeModeConflict();
4845
- const { workspace } = await loadWorkspaceConfig(options.cwd);
4846
- const root = workspace.root;
3620
+ const cwd = resolve(options.cwd ?? process.cwd());
3621
+ const { workspace } = await loadWorkspaceConfig(cwd);
4847
3622
  const stage = options.stage ?? workspace.stages.local;
4848
- if (!(stage === workspace.stages.local)) assertDeployedStage(workspace.stages, stage);
4849
- const identity = deployIdentity(workspace, stage);
4850
- const dir = join(root, stackDir(stage));
4851
- const file = join(dir, "docker-compose.yml");
4852
- const ref = {
4853
- project: composeProject(identity),
4854
- file,
4855
- cwd: root
4856
- };
4857
3623
  if (options.down) {
4858
- await docker.down(ref);
4859
- logger$4.log(`🛑 Stopped ${ref.project}. Its volumes are kept.`);
3624
+ const ref = {
3625
+ project: composeProject(deployIdentity(workspace, stage)),
3626
+ file: join(workspace.root, stackDir(stage), "docker-compose.yml"),
3627
+ cwd: workspace.root
3628
+ };
3629
+ await (deps.docker ?? dockerCompose).down(ref);
3630
+ console.log(`🛑 Stopped ${ref.project}. Its volumes are kept.`);
4860
3631
  return;
4861
3632
  }
4862
- const sources = {};
4863
- const runnables = {};
4864
- const manifest = await discover({
4865
- patterns: constructGlobs(workspace),
4866
- cwd: root,
4867
- sources,
4868
- runnables
4869
- });
4870
- const mode = options.build || !options.pull && !options.tag ? "build" : "pull";
4871
- const tag = options.tag ?? (mode === "pull" ? "latest" : await revision(root));
4872
- const { secrets, keep } = await stageSecrets(workspace, manifest, stage);
4873
- const packageManager = detectPackageManager$1(root);
4874
- const stack = composeStack({
4875
- workspace,
4876
- manifest,
4877
- runnables,
3633
+ let finished;
3634
+ const mode = options.build ? "build" : options.pull ? "pull" : void 0;
3635
+ const tag = options.tag ?? (options.pull ? "latest" : void 0);
3636
+ const result = await deploy$1({
3637
+ cwd,
4878
3638
  stage,
4879
- identity,
4880
- images: {
4881
- mode,
4882
- tag,
4883
- ...workspace.deploy?.dokploy?.registry ? { registry: workspace.deploy.dokploy.registry } : {}
4884
- },
4885
- secrets,
4886
- ports: edgePorts(env),
4887
- packageManager,
4888
- packages: Object.fromEntries(Object.entries(workspace.apps).map(([name, app]) => [name, appPackageName$1(root, name, app.path)]))
4889
- });
4890
- if (mode === "pull" && !options.dryRun) await assertImagesExist(docker, tag, stack.apps);
4891
- if (options.dryRun) keep.skip();
4892
- else await keep.write();
4893
- const files = await writeStack(root, dir, stack);
4894
- printPlan(stack, mode, files);
4895
- if (options.dryRun) return {
4896
- stack,
4897
- files
4898
- };
4899
- const store = deps.state ? await deps.state(workspace) : await createStateStore({
4900
- config: workspace.state,
4901
- workspaceRoot: root,
4902
- workspaceName: workspace.name
4903
- });
4904
- const lock = await store.lock(stage, { operation: "compose" });
4905
- try {
4906
- const apps = stack.apps.map((app) => app.name);
4907
- if (mode === "build") {
4908
- for (const app of stack.apps.filter((a) => a.kind === "rest-api")) {
4909
- logger$4.log(`\n📦 Bundling ${app.name}…`);
4910
- await bundle(join(root, app.path));
3639
+ target: "compose",
3640
+ targets: { compose: composeTarget({
3641
+ ...deps,
3642
+ ...mode ? { mode } : {},
3643
+ report: (done) => {
3644
+ finished = done;
4911
3645
  }
4912
- logger$4.log("\n🐳 Building images…");
4913
- await docker.build(ref, apps);
4914
- } else {
4915
- logger$4.log(`\n🐳 Pulling ${tag}…`);
4916
- await docker.pull(ref, apps);
3646
+ }) },
3647
+ ...tag ? { tag } : {},
3648
+ ...options.dryRun ? { dryRun: true } : {},
3649
+ logger: {
3650
+ info: (message) => console.log(message),
3651
+ warn: (message) => console.warn(message),
3652
+ error: (message) => console.error(message)
4917
3653
  }
4918
- if (stack.infra.length > 0) {
4919
- logger$4.log(`\n🗄️ Starting ${stack.infra.join(", ")}…`);
4920
- await docker.up(ref, stack.infra);
4921
- }
4922
- if (stack.infra.includes("postgres")) await prepareDatabases(stack, workspace, manifest, sources, {
4923
- docker,
4924
- ref,
4925
- sql: deps.sql ?? pgClient,
4926
- migrate: deps.migrate ?? migrateDatabases
4927
- });
4928
- logger$4.log("\n🚀 Starting the stack…");
4929
- await docker.up(ref);
4930
- if (stack.local) await docker.copyOut(ref, "caddy", "/data/caddy/pki/authorities/local/root.crt", join(dir, "caddy-root.crt")).catch(() => {});
4931
- const images = await recordImages(store, stack, docker, mode);
4932
- logger$4.log(`\n✅ ${ref.project} is running:`);
4933
- for (const app of stack.apps) logger$4.log(` ${app.name.padEnd(12)} ${app.url}`);
4934
- if (stack.local) logger$4.log(`\n🔐 Certificates are from Caddy's local CA: NODE_EXTRA_CA_CERTS=${join(stackDir(stage), "caddy-root.crt")}`);
4935
- return {
4936
- stack,
4937
- files,
4938
- images
4939
- };
4940
- } finally {
4941
- await lock.release();
4942
- }
4943
- }
4944
- /**
4945
- * The stage's secrets — and, for a deployed stage, everything it generates
4946
- * once: its seed, and each declared secret and keyring. Written back before
4947
- * anything is derived from them, so the next run derives the same passwords.
4948
- */
4949
- async function stageSecrets(workspace, manifest, stage) {
4950
- const store = await secretsStoreFor(workspace, stage);
4951
- const stored = await store.read(stage);
4952
- const nothing = {
4953
- write: async () => {},
4954
- skip: () => {}
4955
- };
4956
- if (stage === workspace.stages.local) return {
4957
- secrets: stored,
4958
- keep: nothing
4959
- };
4960
- const { secrets, generated } = withGeneratedSecrets(stored ?? initStageSecrets(stage), manifest);
4961
- if (generated.length === 0) return {
4962
- secrets,
4963
- keep: nothing
4964
- };
3654
+ }).result;
3655
+ if (!finished) return void 0;
4965
3656
  return {
4966
- secrets,
4967
- keep: {
4968
- async write() {
4969
- await store.write(stage, secrets);
4970
- logger$4.log(`🔑 Generated for "${stage}" (${store.name}): ${generated.join(", ")}`);
4971
- },
4972
- skip() {
4973
- logger$4.log(`🔑 "${stage}" has no ${generated.join(", ")} yet; this dry run used values it did not keep.`);
4974
- }
4975
- }
3657
+ stack: finished.stack,
3658
+ files: finished.files,
3659
+ ...options.dryRun ? {} : { images: finished.images },
3660
+ deploy: result
4976
3661
  };
4977
3662
  }
4978
- /** Write the stack's files: owner-only, since the env files hold secrets. */
4979
- async function writeStack(root, dir, stack) {
4980
- await mkdir(dir, {
4981
- recursive: true,
4982
- mode: 448
4983
- });
4984
- await chmod(dir, 448);
4985
- const files = [];
4986
- const write = async (path, content, mode = 420) => {
4987
- await mkdir(dirname(path), { recursive: true });
4988
- await writeFile(path, content, { mode });
4989
- await chmod(path, mode);
4990
- files.push(path);
4991
- };
4992
- await write(join(dir, "docker-compose.yml"), composeYaml(stack));
4993
- await write(join(dir, "Caddyfile"), stack.caddyfile);
4994
- for (const app of stack.apps) if (app.env) await write(join(dir, `${app.name}.env`), envFile(app.env), 384);
4995
- for (const [path, content] of Object.entries(stack.dockerfiles)) await write(join(root, path), content);
4996
- await ignoreStacks(root);
4997
- return files;
4998
- }
4999
- /** Make sure the build context leaves `.gkm/compose` out. */
5000
- async function ignoreStacks(root) {
5001
- const path = join(root, ".dockerignore");
5002
- if (!existsSync(path)) {
5003
- await writeFile(path, generateDockerignore());
5004
- return;
5005
- }
5006
- const current = await readFile(path, "utf-8");
5007
- if (/^\/?\.gkm\/compose\/?$|^\/?\.gkm\/?$/m.test(current)) return;
5008
- await appendFile(path, `${current.endsWith("\n") ? "" : "\n"}\n# gkm compose's stacks: each app's env file holds its stage's secrets\n.gkm/compose\n`);
5009
- }
5010
- function composeYaml(stack) {
5011
- return `# Generated by gkm compose from the construct manifest — do not edit.
5012
- # The ${stack.stage} stage's APIs and sites behind one Caddy. Each backend
5013
- # reads exactly the keys in its own env file beside this one.
5014
- #
5015
- # gkm compose${stack.local ? "" : ` --stage ${stack.stage}`} start or update it
5016
- # gkm compose${stack.local ? "" : ` --stage ${stack.stage}`} --down stop it
5017
- ${stringify(stack.compose, {
5018
- lineWidth: 0,
5019
- aliasDuplicateObjects: false
5020
- })}`;
5021
- }
5022
- /** Create what the plan names in the stack's Postgres, then migrate it. */
5023
- async function prepareDatabases(stack, workspace, manifest, sources, deps) {
5024
- const port = await deps.docker.port(deps.ref, "postgres", 5432);
5025
- const statements = postgresStatements(stack.plan, workspace.name, stack.credential.seed);
5026
- if (statements.length > 0) {
5027
- logger$4.log("🗄️ Creating databases, roles and grants…");
5028
- await applyPostgres(deps.sql(port, stack.credential.master), statements);
5029
- }
5030
- const env = envFor(stack.plan, {
5031
- ports: { [primaryPortKey("postgres")]: port },
5032
- project: workspace.name,
5033
- master: stack.credential.master,
5034
- ...stack.credential.seed ? { seed: stack.credential.seed } : {}
5035
- });
5036
- const runs = await deps.migrate({
5037
- root: workspace.root,
5038
- manifest,
5039
- sources,
5040
- env
5041
- });
5042
- for (const { target, applied } of runs) if (applied.length > 0) logger$4.log(`🗄️ ${target.migrations}: applied ${applied.length}`);
5043
- }
5044
- /**
5045
- * Record each app's image in the stage's state: the tag it ran and the digest
5046
- * the tag resolved to — or, for an image built here and never pushed, its
5047
- * content id.
5048
- */
5049
- async function recordImages(store, stack, docker, mode) {
5050
- if (!await store.read(stack.stage)) await store.write(stack.stage, createEmptyState(stack.stage, "", ""), { expectedVersion: null });
5051
- const images = {};
5052
- for (const app of stack.apps) {
5053
- const digest = await docker.digest(app.ref);
5054
- images[app.name] = {
5055
- ref: app.ref,
5056
- tag: app.tag,
5057
- ...digest ? { digest } : {}
5058
- };
5059
- await store.putResource(stack.stage, {
5060
- key: `compose:${app.name}`,
5061
- type: "compose-image",
5062
- status: "ready",
5063
- ...digest ? { id: digest } : {},
5064
- data: {
5065
- project: stack.project,
5066
- ref: app.ref,
5067
- tag: app.tag,
5068
- source: mode,
5069
- ...digest ? { digest } : {}
5070
- }
5071
- });
5072
- }
5073
- return images;
5074
- }
5075
- function printPlan(stack, mode, files) {
5076
- logger$4.log(`\n🧱 ${stack.project} — stage ${stack.stage}`);
5077
- for (const app of stack.apps) logger$4.log(` ${app.name.padEnd(12)} ${app.url.padEnd(40)} ${mode === "build" ? "build" : "pull"} ${app.ref}`);
5078
- if (stack.infra.length > 0) logger$4.log(` infrastructure: ${stack.infra.join(", ")}`);
5079
- logger$4.log(`\n📝 Wrote ${files.length} file(s) under ${stackDir(stack.stage)}/`);
5080
- }
5081
3663
  /**
5082
3664
  * Format a fatal error for display.
5083
3665
  * Always includes the full stack trace since these are process-ending errors.
@@ -6483,25 +5065,25 @@ function loadPackageJson() {
6483
5065
  * Run: pnpm --filter @geekmidas/cli sync-versions
6484
5066
  */
6485
5067
  const GEEKMIDAS_VERSIONS = {
6486
- "@geekmidas/audit": "~10.0.0-alpha.64",
6487
- "@geekmidas/auth": "~10.0.0-alpha.64",
6488
- "@geekmidas/cache": "~10.0.0-alpha.64",
6489
- "@geekmidas/client": "~10.0.0-alpha.64",
6490
- "@geekmidas/cloud": "~10.0.0-alpha.64",
6491
- "@geekmidas/constructs": "~10.0.0-alpha.64",
6492
- "@geekmidas/db": "~10.0.0-alpha.64",
6493
- "@geekmidas/emailkit": "~10.0.0-alpha.64",
6494
- "@geekmidas/envkit": "~10.0.0-alpha.64",
6495
- "@geekmidas/errors": "~10.0.0-alpha.64",
6496
- "@geekmidas/events": "~10.0.0-alpha.64",
6497
- "@geekmidas/logger": "~10.0.0-alpha.64",
6498
- "@geekmidas/manifest": "~10.0.0-alpha.64",
6499
- "@geekmidas/rate-limit": "~10.0.0-alpha.64",
6500
- "@geekmidas/schema": "~10.0.0-alpha.64",
6501
- "@geekmidas/services": "~10.0.0-alpha.64",
6502
- "@geekmidas/storage": "~10.0.0-alpha.64",
6503
- "@geekmidas/telescope": "~10.0.0-alpha.64",
6504
- "@geekmidas/testkit": "~10.0.0-alpha.64",
5068
+ "@geekmidas/audit": "~10.0.0-alpha.65",
5069
+ "@geekmidas/auth": "~10.0.0-alpha.65",
5070
+ "@geekmidas/cache": "~10.0.0-alpha.65",
5071
+ "@geekmidas/client": "~10.0.0-alpha.65",
5072
+ "@geekmidas/cloud": "~10.0.0-alpha.65",
5073
+ "@geekmidas/constructs": "~10.0.0-alpha.65",
5074
+ "@geekmidas/db": "~10.0.0-alpha.65",
5075
+ "@geekmidas/emailkit": "~10.0.0-alpha.65",
5076
+ "@geekmidas/envkit": "~10.0.0-alpha.65",
5077
+ "@geekmidas/errors": "~10.0.0-alpha.65",
5078
+ "@geekmidas/events": "~10.0.0-alpha.65",
5079
+ "@geekmidas/logger": "~10.0.0-alpha.65",
5080
+ "@geekmidas/manifest": "~10.0.0-alpha.65",
5081
+ "@geekmidas/rate-limit": "~10.0.0-alpha.65",
5082
+ "@geekmidas/schema": "~10.0.0-alpha.65",
5083
+ "@geekmidas/services": "~10.0.0-alpha.65",
5084
+ "@geekmidas/storage": "~10.0.0-alpha.65",
5085
+ "@geekmidas/telescope": "~10.0.0-alpha.65",
5086
+ "@geekmidas/testkit": "~10.0.0-alpha.65",
6505
5087
  "@geekmidas/cli": `~${loadPackageJson().version}`
6506
5088
  };
6507
5089
  //#endregion
@@ -11183,7 +9765,7 @@ const AWS_REGION = /^[a-z]{2}(-gov)?-[a-z]+-\d$/;
11183
9765
  */
11184
9766
  async function initCommand(projectName, options = {}) {
11185
9767
  const cwd = process.cwd();
11186
- const detectedPkgManager = detectPackageManager$2(cwd);
9768
+ const detectedPkgManager = detectPackageManager$1(cwd);
11187
9769
  if (options.deploy && !deployTargetChoices.some((choice) => choice.value === options.deploy)) throw new UnknownDeployTarget(options.deploy, deployTargetChoices.map((c) => c.value));
11188
9770
  if (options.region && !AWS_REGION.test(options.region)) throw new NotAnAwsRegion(options.region);
11189
9771
  if (options.stages || options.localStage || options.protectedStage) {
@@ -12264,7 +10846,7 @@ function testDatabases(reconciled) {
12264
10846
  * starts without the reference data the app needs.
12265
10847
  */
12266
10848
  async function migrateTestStage(workspace, env) {
12267
- const { migrateAndSeed } = await import("./migrate-B4gngTLe.mjs");
10849
+ const { migrateAndSeed } = await import("./migrate-B2qL5wj4.mjs");
12268
10850
  const { discover } = await import("./discover-LMrqsWSU.mjs").then((n) => n.i);
12269
10851
  const sources = {};
12270
10852
  const manifest = await discover({
@@ -12423,7 +11005,7 @@ function withVersion(range, version) {
12423
11005
  }
12424
11006
  async function upgradeCommand(options = {}) {
12425
11007
  const cwd = process.cwd();
12426
- const pm = detectPackageManager$2(cwd);
11008
+ const pm = detectPackageManager$1(cwd);
12427
11009
  const root = findWorkspaceRoot(cwd, pm);
12428
11010
  logger.log("\n📦 Scanning workspace for @geekmidas packages...\n");
12429
11011
  logger.log(` Package manager: ${pm}`);
@@ -12695,7 +11277,7 @@ program.command("migrate").description("Apply each database construct's migratio
12695
11277
  try {
12696
11278
  const globalOptions = program.opts();
12697
11279
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
12698
- const { migrateCommand } = await import("./migrate-B4gngTLe.mjs");
11280
+ const { migrateCommand } = await import("./migrate-B2qL5wj4.mjs");
12699
11281
  await migrateCommand({
12700
11282
  ...options,
12701
11283
  ...construct ? { construct } : {}
@@ -12709,7 +11291,7 @@ program.command("seed").description("Migrate, then run each database construct's
12709
11291
  try {
12710
11292
  const globalOptions = program.opts();
12711
11293
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
12712
- const { seedCommand } = await import("./migrate-B4gngTLe.mjs");
11294
+ const { seedCommand } = await import("./migrate-B2qL5wj4.mjs");
12713
11295
  await seedCommand({
12714
11296
  ...options,
12715
11297
  ...construct ? { construct } : {}
@@ -12723,7 +11305,7 @@ program.command("migration").description("Write a construct's next migration: an
12723
11305
  try {
12724
11306
  const globalOptions = program.opts();
12725
11307
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
12726
- const { migrationCommand } = await import("./migrate-B4gngTLe.mjs");
11308
+ const { migrationCommand } = await import("./migrate-B2qL5wj4.mjs");
12727
11309
  await migrationCommand({
12728
11310
  construct,
12729
11311
  ...name ? { name } : {}
@@ -12831,7 +11413,7 @@ program.command("encryption:rotate").description("Add a new current key to an En
12831
11413
  try {
12832
11414
  const globalOptions = program.opts();
12833
11415
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
12834
- const { encryptionRotateCommand } = await import("./encryption-CKHjMpCr.mjs");
11416
+ const { encryptionRotateCommand } = await import("./encryption-DF_hgIpl.mjs");
12835
11417
  await encryptionRotateCommand(construct, options);
12836
11418
  } catch (error) {
12837
11419
  console.error(formatError(error));
@@ -12842,7 +11424,7 @@ program.command("encryption:retire").description("Remove an old key from an Encr
12842
11424
  try {
12843
11425
  const globalOptions = program.opts();
12844
11426
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
12845
- const { encryptionRetireCommand } = await import("./encryption-CKHjMpCr.mjs");
11427
+ const { encryptionRetireCommand } = await import("./encryption-DF_hgIpl.mjs");
12846
11428
  await encryptionRetireCommand(construct, key, options);
12847
11429
  } catch (error) {
12848
11430
  console.error(formatError(error));
@@ -12885,7 +11467,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
12885
11467
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
12886
11468
  const { loadWorkspaceConfig } = await import("./config.mjs");
12887
11469
  const { reconcileMissingSecrets } = await import("./reconcile--I7V4Klj.mjs");
12888
- const { secretsStoreFor } = await import("./store-BaaonhkJ.mjs").then((n) => n.r);
11470
+ const { secretsStoreFor } = await import("./store-CLiHnqOY.mjs").then((n) => n.r);
12889
11471
  const { workspace } = await loadWorkspaceConfig();
12890
11472
  const stage = options.stage ?? workspace.stages.local;
12891
11473
  const store = await secretsStoreFor(workspace, stage);
@@ -12894,7 +11476,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
12894
11476
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
12895
11477
  process.exit(1);
12896
11478
  }
12897
- const { derivedContainers } = await import("./sniffer-49KPbDgq.mjs").then((n) => n.G);
11479
+ const { derivedContainers } = await import("./state-B8YwmUZU.mjs").then((n) => n.A);
12898
11480
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
12899
11481
  if (!result) {
12900
11482
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
@@ -12908,8 +11490,8 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
12908
11490
  process.exit(1);
12909
11491
  }
12910
11492
  });
12911
- program.command("deploy").description("Deploy a stage through its target").option("--target <name>", "Deploy target: dokploy, sst, or one named in deploy.targets (default: deploy.default)").option("--provider <provider>", "[DEPRECATED] Use --target instead").requiredOption("--stage <stage>", "Deployment stage (e.g., production, staging)").option("--tag <tag>", "Image tag (default: stage-timestamp)").option("--skip-push", "Skip pushing image to registry").option("--skip-build", "Skip build step (use existing build)").option("--json", "Write events as JSON lines instead of progress; never prompts").option("--dry-run", "Show what would be created or reused; change, build and push nothing").action(async (options) => {
12912
- const { deployCli } = await import("./cli-Bogz-zEj.mjs");
11493
+ program.command("deploy").description("Deploy a stage through its target").option("--target <name>", "Deploy target: dokploy, compose, sst, or one named in deploy.targets (default: deploy.default)").option("--provider <provider>", "[DEPRECATED] Use --target instead").requiredOption("--stage <stage>", "Deployment stage (e.g., production, staging)").option("--tag <tag>", "Image tag (default: stage-timestamp; compose: the commit). Compose pulls a given tag").option("--skip-push", "Skip pushing image to registry").option("--skip-build", "Skip build step (use existing build)").option("--json", "Write events as JSON lines instead of progress; never prompts").option("--dry-run", "Show what would be created or reused; change, build and push nothing").action(async (options) => {
11494
+ const { deployCli } = await import("./cli-P56vvrDQ.mjs");
12913
11495
  const globalOptions = program.opts();
12914
11496
  const code = await deployCli({
12915
11497
  cwd: resolve(globalOptions.cwd ?? process.cwd()),
@@ -12942,7 +11524,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
12942
11524
  try {
12943
11525
  const globalOptions = program.opts();
12944
11526
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
12945
- const { deployGithubCommand } = await import("./github-D-byNI7T.mjs");
11527
+ const { deployGithubCommand } = await import("./github-B0YYE98x.mjs");
12946
11528
  await deployGithubCommand(options);
12947
11529
  } catch (error) {
12948
11530
  console.error(formatError(error));
@@ -13077,6 +11659,6 @@ program.command("upgrade").description("Upgrade @geekmidas/cli (with --all, ever
13077
11659
  });
13078
11660
  program.parse();
13079
11661
  //#endregion
13080
- export { DuplicateMigration as _, KyselyNotFound as a, NoOwnerCredential as c, UnknownDatabaseFolder as d, UnknownMigrationFolder as f, seedDatabases as g, pendingMigrations as h, formatError as i, NoSuchMigrationTarget as l, migrateDatabases as m, slug as n, MigrationFailed as o, migrateAndSeed as p, stamp as r, MigrationsOutsideFolder as s, generateFullstackCustomSecrets as t, SeedFailed as u, MigrationHasNoUp as v, SeedHasNoSeed as y };
11662
+ export { formatError as i, slug as n, stamp as r, generateFullstackCustomSecrets as t };
13081
11663
 
13082
11664
  //# sourceMappingURL=index.mjs.map