@geekmidas/cli 10.0.0-alpha.37 → 10.0.0-alpha.39

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 (114) hide show
  1. package/README.md +27 -26
  2. package/dist/{HostingerProvider-GF6NAZAK.mjs → HostingerProvider-Bh6K7QYz.mjs} +2 -2
  3. package/dist/{HostingerProvider-GF6NAZAK.mjs.map → HostingerProvider-Bh6K7QYz.mjs.map} +1 -1
  4. package/dist/{OpenApiTsGenerator-D1nhN98m.cjs → OpenApiTsGenerator-CeEI4ll9.cjs} +3 -3
  5. package/dist/OpenApiTsGenerator-CeEI4ll9.cjs.map +1 -0
  6. package/dist/{OpenApiTsGenerator-hDINScnz.mjs → OpenApiTsGenerator-DQqOuSW8.mjs} +3 -3
  7. package/dist/OpenApiTsGenerator-DQqOuSW8.mjs.map +1 -0
  8. package/dist/{ssm-FxeSAt35.cjs → aws-DkHdTV-p.cjs} +7 -6
  9. package/dist/aws-DkHdTV-p.cjs.map +1 -0
  10. package/dist/{ssm-BKN1Cg4z.mjs → aws-DojzQnLd.mjs} +7 -6
  11. package/dist/aws-DojzQnLd.mjs.map +1 -0
  12. package/dist/{bundler-D9IPNvDt.mjs → bundler--fyybf1N.mjs} +13 -7
  13. package/dist/bundler--fyybf1N.mjs.map +1 -0
  14. package/dist/{bundler-CYF5mWi9.cjs → bundler-By6_sXiL.cjs} +12 -6
  15. package/dist/bundler-By6_sXiL.cjs.map +1 -0
  16. package/dist/{config-Dss1WwtH.mjs → config-5watB1D0.mjs} +95 -71
  17. package/dist/config-5watB1D0.mjs.map +1 -0
  18. package/dist/{config-DgAHy8nY.cjs → config-CJ-PAb7M.cjs} +116 -74
  19. package/dist/config-CJ-PAb7M.cjs.map +1 -0
  20. package/dist/config.cjs +6 -3
  21. package/dist/config.d.cts +40 -19
  22. package/dist/config.d.cts.map +1 -1
  23. package/dist/config.d.mts +40 -19
  24. package/dist/config.d.mts.map +1 -1
  25. package/dist/config.mjs +3 -3
  26. package/dist/{discover-CroTIm0K.cjs → discover-B0f_kuNN.cjs} +2 -1
  27. package/dist/{discover-CroTIm0K.cjs.map → discover-B0f_kuNN.cjs.map} +1 -1
  28. package/dist/{discover-CMxesskG.mjs → discover-BLhf7V_J.mjs} +2 -1
  29. package/dist/{discover-CMxesskG.mjs.map → discover-BLhf7V_J.mjs.map} +1 -1
  30. package/dist/{github-tZmMZ7td.cjs → github-BAr8WGkR.cjs} +10 -13
  31. package/dist/github-BAr8WGkR.cjs.map +1 -0
  32. package/dist/{github-C99BMxf0.mjs → github-D5MthUlR.mjs} +8 -11
  33. package/dist/github-D5MthUlR.mjs.map +1 -0
  34. package/dist/{index-CAp22ul5.d.cts → index-CLH1DgsO.d.cts} +2 -2
  35. package/dist/{index-CAp22ul5.d.cts.map → index-CLH1DgsO.d.cts.map} +1 -1
  36. package/dist/{index-DxXMtQNT.d.mts → index-CST2jOPH.d.mts} +2 -2
  37. package/dist/{index-DxXMtQNT.d.mts.map → index-CST2jOPH.d.mts.map} +1 -1
  38. package/dist/index.cjs +1015 -353
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.mjs +1003 -341
  41. package/dist/index.mjs.map +1 -1
  42. package/dist/{migrate-_2J3lFT5.mjs → migrate-BJR912RE.mjs} +9 -7
  43. package/dist/migrate-BJR912RE.mjs.map +1 -0
  44. package/dist/{migrate-Cgga-OGi.cjs → migrate-Bu8POqsR.cjs} +8 -6
  45. package/dist/migrate-Bu8POqsR.cjs.map +1 -0
  46. package/dist/openapi.cjs +2 -2
  47. package/dist/openapi.d.cts +1 -1
  48. package/dist/openapi.d.mts +1 -1
  49. package/dist/openapi.mjs +2 -2
  50. package/dist/{ready-CXhoutix.cjs → ready-B0WVtkjR.cjs} +96 -6
  51. package/dist/ready-B0WVtkjR.cjs.map +1 -0
  52. package/dist/{ready-D1BCiaYU.mjs → ready-BXmLS4VB.mjs} +92 -8
  53. package/dist/ready-BXmLS4VB.mjs.map +1 -0
  54. package/dist/reconcile/public.cjs +1 -1
  55. package/dist/reconcile/public.d.cts +1 -1
  56. package/dist/reconcile/public.d.mts +1 -1
  57. package/dist/reconcile/public.mjs +1 -1
  58. package/dist/reconcile--I7V4Klj.mjs +34 -0
  59. package/dist/reconcile--I7V4Klj.mjs.map +1 -0
  60. package/dist/reconcile-CuCQBa5-.cjs +34 -0
  61. package/dist/reconcile-CuCQBa5-.cjs.map +1 -0
  62. package/dist/storage-BzaUxmqe.mjs +124 -0
  63. package/dist/storage-BzaUxmqe.mjs.map +1 -0
  64. package/dist/storage-CSGoC3bK.cjs +152 -0
  65. package/dist/storage-CSGoC3bK.cjs.map +1 -0
  66. package/dist/store-DQgxT_WR.cjs +211 -0
  67. package/dist/store-DQgxT_WR.cjs.map +1 -0
  68. package/dist/store-DeGATCS-.mjs +182 -0
  69. package/dist/store-DeGATCS-.mjs.map +1 -0
  70. package/dist/{types-BTiu5kwo.d.mts → types-B0EVAF6U.d.mts} +24 -10
  71. package/dist/types-B0EVAF6U.d.mts.map +1 -0
  72. package/dist/{types-Bj_FpeTc.d.cts → types-CHKCUKM0.d.cts} +24 -10
  73. package/dist/types-CHKCUKM0.d.cts.map +1 -0
  74. package/dist/vitest.cjs +1 -1
  75. package/dist/vitest.mjs +1 -1
  76. package/dist/workspace/index.cjs +1 -1
  77. package/dist/workspace/index.d.cts +2 -2
  78. package/dist/workspace/index.d.mts +2 -2
  79. package/dist/workspace/index.mjs +1 -1
  80. package/dist/{workspace-BtXCL1C8.mjs → workspace-CrFbItPz.mjs} +111 -39
  81. package/dist/workspace-CrFbItPz.mjs.map +1 -0
  82. package/dist/{workspace-CsL76tpc.mjs → workspace-DOmIhiau.mjs} +2 -2
  83. package/dist/{workspace-CsL76tpc.mjs.map → workspace-DOmIhiau.mjs.map} +1 -1
  84. package/dist/{workspace-DUhLejv4.cjs → workspace-DZ1G4zow.cjs} +121 -37
  85. package/dist/workspace-DZ1G4zow.cjs.map +1 -0
  86. package/dist/{workspace-CAJk6UmC.cjs → workspace-RYSYtHP7.cjs} +2 -2
  87. package/dist/{workspace-CAJk6UmC.cjs.map → workspace-RYSYtHP7.cjs.map} +1 -1
  88. package/package.json +14 -14
  89. package/dist/OpenApiTsGenerator-D1nhN98m.cjs.map +0 -1
  90. package/dist/OpenApiTsGenerator-hDINScnz.mjs.map +0 -1
  91. package/dist/bundler-CYF5mWi9.cjs.map +0 -1
  92. package/dist/bundler-D9IPNvDt.mjs.map +0 -1
  93. package/dist/config-DgAHy8nY.cjs.map +0 -1
  94. package/dist/config-Dss1WwtH.mjs.map +0 -1
  95. package/dist/github-C99BMxf0.mjs.map +0 -1
  96. package/dist/github-tZmMZ7td.cjs.map +0 -1
  97. package/dist/migrate-Cgga-OGi.cjs.map +0 -1
  98. package/dist/migrate-_2J3lFT5.mjs.map +0 -1
  99. package/dist/ready-CXhoutix.cjs.map +0 -1
  100. package/dist/ready-D1BCiaYU.mjs.map +0 -1
  101. package/dist/reconcile-BxZTV2vl.cjs +0 -332
  102. package/dist/reconcile-BxZTV2vl.cjs.map +0 -1
  103. package/dist/reconcile-yNq6Shau.mjs +0 -279
  104. package/dist/reconcile-yNq6Shau.mjs.map +0 -1
  105. package/dist/ssm-BKN1Cg4z.mjs.map +0 -1
  106. package/dist/ssm-FxeSAt35.cjs.map +0 -1
  107. package/dist/storage-BjTLjDDp.mjs +0 -297
  108. package/dist/storage-BjTLjDDp.mjs.map +0 -1
  109. package/dist/storage-C2JC31V9.cjs +0 -344
  110. package/dist/storage-C2JC31V9.cjs.map +0 -1
  111. package/dist/types-BTiu5kwo.d.mts.map +0 -1
  112. package/dist/types-Bj_FpeTc.d.cts.map +0 -1
  113. package/dist/workspace-BtXCL1C8.mjs.map +0 -1
  114. package/dist/workspace-DUhLejv4.cjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  import { n as __require, t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
3
- import { c as isPartitionedRoutes, i as ConstructGenerator, t as discover } from "./discover-CMxesskG.mjs";
4
- import { a as loadConfig, c as parseModuleConfig, i as loadAppConfig, l as appKey, r as getAppNameFromCwd, s as loadWorkspaceConfig, u as derivedApps } from "./config-Dss1WwtH.mjs";
5
- import { D as assertDeployedStage, E as TEST_STAGE, O as deployedProblems, T as InvalidStages, a as getAppBuildOrder, b as getDeployTargetError, k as stageProblems, m as stripPublicPrefix, o as getAppGkmConfig, p as getPublicEnvPrefix, t as allConstructGlobs, x as isDeployTargetSupported } from "./workspace-CsL76tpc.mjs";
6
- import { c as getKeyPath, i as setCustomSecret, n as readStageSecrets, o as toEmbeddableSecrets, r as secretsExist, s as writeStageSecrets, t as maskPassword } from "./storage-BjTLjDDp.mjs";
7
- import { C as loadEnvFiles, S as isPortAvailable, T as prepareEntryCredentials, _ as providerOf, b as createEntryWrapper, c as forgetState, d as pgClient, f as resourceName, g as eventsBackendFor, h as cacheBackendFor, i as reconcileWorkspace, l as applyPostgres, m as primaryPortKey, n as constructGlobs, o as COMPOSE_PATH, p as rootSite, r as derivedContainers, s as LOCAL_CA_PATH, t as backendsOf, u as quoteIdentifier, v as storageBackendFor, w as loadSecretsForApp, x as findAvailablePort, y as createCredentialsPreload } from "./workspace-BtXCL1C8.mjs";
8
- import { n as EndpointGenerator, r as runtimeFor } from "./OpenApiTsGenerator-hDINScnz.mjs";
3
+ import { c as isPartitionedRoutes, i as ConstructGenerator, t as discover } from "./discover-BLhf7V_J.mjs";
4
+ import { d as parseModuleConfig, f as appKey, l as loadWorkspaceConfig, n as NotABackendApp, o as loadAppConfig, p as derivedApps, r as NotInAnApp, s as loadConfig, t as ConfigNotFound, u as loadWorkspaceSettings } from "./config-5watB1D0.mjs";
5
+ import { D as assertDeployedStage, E as TEST_STAGE, O as deployedProblems, T as InvalidStages, a as getAppBuildOrder, b as getDeployTargetError, k as stageProblems, m as stripPublicPrefix, o as getAppGkmConfig, p as getPublicEnvPrefix, t as allConstructGlobs, x as isDeployTargetSupported } from "./workspace-DOmIhiau.mjs";
6
+ import { a as withCustomSecret, i as toEmbeddableSecrets, n as maskPassword, t as initStageSecrets } from "./storage-BzaUxmqe.mjs";
7
+ import { a as getKeyPath, i as FileSecretsStore, n as secretsStoreFor } from "./store-DeGATCS-.mjs";
8
+ import { C as findAvailablePort, D as prepareEntryCredentials, E as loadSecretsForApp, S as createEntryWrapper, T as loadEnvFiles, _ as cacheBackendFor, a as reconcileWorkspace, b as storageBackendFor, c as LOCAL_CA_PATH, d as quoteIdentifier, f as pgClient, g as primaryPortKey, h as fakeKey, i as derivedContainers, l as forgetState, m as rootSite, n as backendsOf, p as resourceName, r as constructGlobs, s as COMPOSE_PATH, t as FAKE_ENV, u as applyPostgres, v as eventsBackendFor, w as isPortAvailable, x as createCredentialsPreload, y as providerOf } from "./workspace-CrFbItPz.mjs";
9
+ import { n as EndpointGenerator, r as runtimeFor } from "./OpenApiTsGenerator-DQqOuSW8.mjs";
9
10
  import { generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi.mjs";
10
- import { a as writeTestHarness, i as TEST_MANIFEST_ENV, n as TEST_READY_FILE, o as cacheBackendsIn, s as driversFor, t as TEST_READY_ENV } from "./ready-D1BCiaYU.mjs";
11
+ import { a as writeTestHarness, i as TEST_MANIFEST_ENV, n as TEST_READY_FILE, o as cacheBackendsIn, s as driversFor, t as TEST_READY_ENV } from "./ready-BXmLS4VB.mjs";
11
12
  import { i as withRoutes, r as withCompute } from "./emit-Bzq7JsMP.mjs";
12
- import { a as createStageSecrets, c as generateServiceCredentials, i as writeDockerEnvFromSecrets, l as rotateServicePassword, o as generateConnectionUrls, r as generateFullstackCustomSecrets, s as generateSecurePassword, t as reconcileMissingSecrets } from "./reconcile-yNq6Shau.mjs";
13
13
  import { createRequire } from "node:module";
14
14
  import { Command } from "commander";
15
15
  import { stdin, stdout } from "node:process";
@@ -19,7 +19,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
19
19
  import { homedir } from "node:os";
20
20
  import { basename, dirname, isAbsolute, join, parse, relative, resolve } from "node:path";
21
21
  import { execFile, execSync, spawn } from "node:child_process";
22
- import { cacheTable, canonicalId, cloudName, cookieDomain, dependentsOf, kebabCase, migrationFolder, mobileOrigins, provideKey, provisionOrder, schemeBase, scopedName, serviceKey } from "@geekmidas/manifest";
22
+ import { cacheTable, canonicalId, cloudName, cookieDomain, dependentsOf, externalApiUrl, kebabCase, migrationFolder, mobileOrigins, provideKey, provisionOrder, schemeBase, scopedName, serviceKey } from "@geekmidas/manifest";
23
23
  import fg from "fast-glob";
24
24
  import chokidar from "chokidar";
25
25
  import { createCipheriv, createHash, randomBytes } from "node:crypto";
@@ -30,6 +30,10 @@ import { parse as parse$1, parseDocument } from "yaml";
30
30
  import { promisify } from "node:util";
31
31
  import { cacheTableStatements } from "@geekmidas/cache/postgres";
32
32
  import prompts from "prompts";
33
+ import { createServer } from "http";
34
+ import { Http2ServerRequest } from "http2";
35
+ import { Readable } from "stream";
36
+ import crypto from "crypto";
33
37
  import { Cron } from "@geekmidas/constructs/crons";
34
38
  import { Function } from "@geekmidas/constructs/functions";
35
39
  import { Queue } from "@geekmidas/constructs/queue";
@@ -37,7 +41,7 @@ import { Subscriber } from "@geekmidas/constructs/subscribers";
37
41
  import { Topic } from "@geekmidas/constructs/topic";
38
42
  import { lookup } from "node:dns/promises";
39
43
  //#region package.json
40
- var version = "10.0.0-alpha.37";
44
+ var version = "10.0.0-alpha.39";
41
45
  //#endregion
42
46
  //#region src/auth/credentials.ts
43
47
  /**
@@ -578,6 +582,485 @@ var UnsupportedPlatform = class extends Error {
578
582
  }
579
583
  };
580
584
  //#endregion
585
+ //#region ../../node_modules/.pnpm/@hono+node-server@1.19.5_hono@4.13.8/node_modules/@hono/node-server/dist/index.mjs
586
+ var RequestError = class extends Error {
587
+ constructor(message, options) {
588
+ super(message, options);
589
+ this.name = "RequestError";
590
+ }
591
+ };
592
+ var toRequestError = (e) => {
593
+ if (e instanceof RequestError) return e;
594
+ return new RequestError(e.message, { cause: e });
595
+ };
596
+ var GlobalRequest = global.Request;
597
+ var Request = class extends GlobalRequest {
598
+ constructor(input, options) {
599
+ if (typeof input === "object" && getRequestCache in input) input = input[getRequestCache]();
600
+ if (typeof options?.body?.getReader !== "undefined") options.duplex ??= "half";
601
+ super(input, options);
602
+ }
603
+ };
604
+ var newHeadersFromIncoming = (incoming) => {
605
+ const headerRecord = [];
606
+ const rawHeaders = incoming.rawHeaders;
607
+ for (let i = 0; i < rawHeaders.length; i += 2) {
608
+ const { [i]: key, [i + 1]: value } = rawHeaders;
609
+ if (key.charCodeAt(0) !== 58) headerRecord.push([key, value]);
610
+ }
611
+ return new Headers(headerRecord);
612
+ };
613
+ var wrapBodyStream = Symbol("wrapBodyStream");
614
+ var newRequestFromIncoming = (method, url, headers, incoming, abortController) => {
615
+ const init = {
616
+ method,
617
+ headers,
618
+ signal: abortController.signal
619
+ };
620
+ if (method === "TRACE") {
621
+ init.method = "GET";
622
+ const req = new Request(url, init);
623
+ Object.defineProperty(req, "method", { get() {
624
+ return "TRACE";
625
+ } });
626
+ return req;
627
+ }
628
+ if (!(method === "GET" || method === "HEAD")) {
629
+ if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) init.body = new ReadableStream({ start(controller) {
630
+ controller.enqueue(incoming.rawBody);
631
+ controller.close();
632
+ } });
633
+ else if (incoming[wrapBodyStream]) {
634
+ let reader;
635
+ init.body = new ReadableStream({ async pull(controller) {
636
+ try {
637
+ reader ||= Readable.toWeb(incoming).getReader();
638
+ const { done, value } = await reader.read();
639
+ if (done) controller.close();
640
+ else controller.enqueue(value);
641
+ } catch (error) {
642
+ controller.error(error);
643
+ }
644
+ } });
645
+ } else init.body = Readable.toWeb(incoming);
646
+ }
647
+ return new Request(url, init);
648
+ };
649
+ var getRequestCache = Symbol("getRequestCache");
650
+ var requestCache = Symbol("requestCache");
651
+ var incomingKey = Symbol("incomingKey");
652
+ var urlKey = Symbol("urlKey");
653
+ var headersKey = Symbol("headersKey");
654
+ var abortControllerKey = Symbol("abortControllerKey");
655
+ var requestPrototype = {
656
+ get method() {
657
+ return this[incomingKey].method || "GET";
658
+ },
659
+ get url() {
660
+ return this[urlKey];
661
+ },
662
+ get headers() {
663
+ return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]);
664
+ },
665
+ [Symbol("getAbortController")]() {
666
+ this[getRequestCache]();
667
+ return this[abortControllerKey];
668
+ },
669
+ [getRequestCache]() {
670
+ this[abortControllerKey] ||= new AbortController();
671
+ return this[requestCache] ||= newRequestFromIncoming(this.method, this[urlKey], this.headers, this[incomingKey], this[abortControllerKey]);
672
+ }
673
+ };
674
+ [
675
+ "body",
676
+ "bodyUsed",
677
+ "cache",
678
+ "credentials",
679
+ "destination",
680
+ "integrity",
681
+ "mode",
682
+ "redirect",
683
+ "referrer",
684
+ "referrerPolicy",
685
+ "signal",
686
+ "keepalive"
687
+ ].forEach((k) => {
688
+ Object.defineProperty(requestPrototype, k, { get() {
689
+ return this[getRequestCache]()[k];
690
+ } });
691
+ });
692
+ [
693
+ "arrayBuffer",
694
+ "blob",
695
+ "clone",
696
+ "formData",
697
+ "json",
698
+ "text"
699
+ ].forEach((k) => {
700
+ Object.defineProperty(requestPrototype, k, { value: function() {
701
+ return this[getRequestCache]()[k]();
702
+ } });
703
+ });
704
+ Object.setPrototypeOf(requestPrototype, Request.prototype);
705
+ var newRequest = (incoming, defaultHostname) => {
706
+ const req = Object.create(requestPrototype);
707
+ req[incomingKey] = incoming;
708
+ const incomingUrl = incoming.url || "";
709
+ if (incomingUrl[0] !== "/" && (incomingUrl.startsWith("http://") || incomingUrl.startsWith("https://"))) {
710
+ if (incoming instanceof Http2ServerRequest) throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
711
+ try {
712
+ req[urlKey] = new URL(incomingUrl).href;
713
+ } catch (e) {
714
+ throw new RequestError("Invalid absolute URL", { cause: e });
715
+ }
716
+ return req;
717
+ }
718
+ const host = (incoming instanceof Http2ServerRequest ? incoming.authority : incoming.headers.host) || defaultHostname;
719
+ if (!host) throw new RequestError("Missing host header");
720
+ let scheme;
721
+ if (incoming instanceof Http2ServerRequest) {
722
+ scheme = incoming.scheme;
723
+ if (!(scheme === "http" || scheme === "https")) throw new RequestError("Unsupported scheme");
724
+ } else scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
725
+ const url = new URL(`${scheme}://${host}${incomingUrl}`);
726
+ if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) throw new RequestError("Invalid host header");
727
+ req[urlKey] = url.href;
728
+ return req;
729
+ };
730
+ var responseCache = Symbol("responseCache");
731
+ var getResponseCache = Symbol("getResponseCache");
732
+ var cacheKey = Symbol("cache");
733
+ var GlobalResponse = global.Response;
734
+ var Response2 = class _Response {
735
+ #body;
736
+ #init;
737
+ [getResponseCache]() {
738
+ delete this[cacheKey];
739
+ return this[responseCache] ||= new GlobalResponse(this.#body, this.#init);
740
+ }
741
+ constructor(body, init) {
742
+ let headers;
743
+ this.#body = body;
744
+ if (init instanceof _Response) {
745
+ const cachedGlobalResponse = init[responseCache];
746
+ if (cachedGlobalResponse) {
747
+ this.#init = cachedGlobalResponse;
748
+ this[getResponseCache]();
749
+ return;
750
+ } else {
751
+ this.#init = init.#init;
752
+ headers = new Headers(init.#init.headers);
753
+ }
754
+ } else this.#init = init;
755
+ if (typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) {
756
+ headers ||= init?.headers || { "content-type": "text/plain; charset=UTF-8" };
757
+ this[cacheKey] = [
758
+ init?.status || 200,
759
+ body,
760
+ headers
761
+ ];
762
+ }
763
+ }
764
+ get headers() {
765
+ const cache = this[cacheKey];
766
+ if (cache) {
767
+ if (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2]);
768
+ return cache[2];
769
+ }
770
+ return this[getResponseCache]().headers;
771
+ }
772
+ get status() {
773
+ return this[cacheKey]?.[0] ?? this[getResponseCache]().status;
774
+ }
775
+ get ok() {
776
+ const status = this.status;
777
+ return status >= 200 && status < 300;
778
+ }
779
+ };
780
+ [
781
+ "body",
782
+ "bodyUsed",
783
+ "redirected",
784
+ "statusText",
785
+ "trailers",
786
+ "type",
787
+ "url"
788
+ ].forEach((k) => {
789
+ Object.defineProperty(Response2.prototype, k, { get() {
790
+ return this[getResponseCache]()[k];
791
+ } });
792
+ });
793
+ [
794
+ "arrayBuffer",
795
+ "blob",
796
+ "clone",
797
+ "formData",
798
+ "json",
799
+ "text"
800
+ ].forEach((k) => {
801
+ Object.defineProperty(Response2.prototype, k, { value: function() {
802
+ return this[getResponseCache]()[k]();
803
+ } });
804
+ });
805
+ Object.setPrototypeOf(Response2, GlobalResponse);
806
+ Object.setPrototypeOf(Response2.prototype, GlobalResponse.prototype);
807
+ async function readWithoutBlocking(readPromise) {
808
+ return Promise.race([readPromise, Promise.resolve().then(() => Promise.resolve(void 0))]);
809
+ }
810
+ function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromise) {
811
+ const cancel = (error) => {
812
+ reader.cancel(error).catch(() => {});
813
+ };
814
+ writable.on("close", cancel);
815
+ writable.on("error", cancel);
816
+ (currentReadPromise ?? reader.read()).then(flow, handleStreamError);
817
+ return reader.closed.finally(() => {
818
+ writable.off("close", cancel);
819
+ writable.off("error", cancel);
820
+ });
821
+ function handleStreamError(error) {
822
+ if (error) writable.destroy(error);
823
+ }
824
+ function onDrain() {
825
+ reader.read().then(flow, handleStreamError);
826
+ }
827
+ function flow({ done, value }) {
828
+ try {
829
+ if (done) writable.end();
830
+ else if (!writable.write(value)) writable.once("drain", onDrain);
831
+ else return reader.read().then(flow, handleStreamError);
832
+ } catch (e) {
833
+ handleStreamError(e);
834
+ }
835
+ }
836
+ }
837
+ function writeFromReadableStream(stream, writable) {
838
+ if (stream.locked) throw new TypeError("ReadableStream is locked.");
839
+ else if (writable.destroyed) return;
840
+ return writeFromReadableStreamDefaultReader(stream.getReader(), writable);
841
+ }
842
+ var buildOutgoingHttpHeaders = (headers) => {
843
+ const res = {};
844
+ if (!(headers instanceof Headers)) headers = new Headers(headers ?? void 0);
845
+ const cookies = [];
846
+ for (const [k, v] of headers) if (k === "set-cookie") cookies.push(v);
847
+ else res[k] = v;
848
+ if (cookies.length > 0) res["set-cookie"] = cookies;
849
+ res["content-type"] ??= "text/plain; charset=UTF-8";
850
+ return res;
851
+ };
852
+ var X_ALREADY_SENT = "x-hono-already-sent";
853
+ var webFetch = global.fetch;
854
+ if (typeof global.crypto === "undefined") global.crypto = crypto;
855
+ global.fetch = (info, init) => {
856
+ init = {
857
+ compress: false,
858
+ ...init
859
+ };
860
+ return webFetch(info, init);
861
+ };
862
+ var outgoingEnded = Symbol("outgoingEnded");
863
+ var handleRequestError = () => new Response(null, { status: 400 });
864
+ var handleFetchError = (e) => new Response(null, { status: e instanceof Error && (e.name === "TimeoutError" || e.constructor.name === "TimeoutError") ? 504 : 500 });
865
+ var handleResponseError = (e, outgoing) => {
866
+ const err = e instanceof Error ? e : new Error("unknown error", { cause: e });
867
+ if (err.code === "ERR_STREAM_PREMATURE_CLOSE") console.info("The user aborted a request.");
868
+ else {
869
+ console.error(e);
870
+ if (!outgoing.headersSent) outgoing.writeHead(500, { "Content-Type": "text/plain" });
871
+ outgoing.end(`Error: ${err.message}`);
872
+ outgoing.destroy(err);
873
+ }
874
+ };
875
+ var flushHeaders = (outgoing) => {
876
+ if ("flushHeaders" in outgoing && outgoing.writable) outgoing.flushHeaders();
877
+ };
878
+ var responseViaCache = async (res, outgoing) => {
879
+ let [status, body, header] = res[cacheKey];
880
+ if (header instanceof Headers) header = buildOutgoingHttpHeaders(header);
881
+ if (typeof body === "string") header["Content-Length"] = Buffer.byteLength(body);
882
+ else if (body instanceof Uint8Array) header["Content-Length"] = body.byteLength;
883
+ else if (body instanceof Blob) header["Content-Length"] = body.size;
884
+ outgoing.writeHead(status, header);
885
+ if (typeof body === "string" || body instanceof Uint8Array) outgoing.end(body);
886
+ else if (body instanceof Blob) outgoing.end(new Uint8Array(await body.arrayBuffer()));
887
+ else {
888
+ flushHeaders(outgoing);
889
+ await writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));
890
+ }
891
+ outgoing[outgoingEnded]?.();
892
+ };
893
+ var isPromise = (res) => typeof res.then === "function";
894
+ var responseViaResponseObject = async (res, outgoing, options = {}) => {
895
+ if (isPromise(res)) {
896
+ if (options.errorHandler) try {
897
+ res = await res;
898
+ } catch (err) {
899
+ const errRes = await options.errorHandler(err);
900
+ if (!errRes) return;
901
+ res = errRes;
902
+ }
903
+ else res = await res.catch(handleFetchError);
904
+ }
905
+ if (cacheKey in res) return responseViaCache(res, outgoing);
906
+ const resHeaderRecord = buildOutgoingHttpHeaders(res.headers);
907
+ if (res.body) {
908
+ const reader = res.body.getReader();
909
+ const values = [];
910
+ let done = false;
911
+ let currentReadPromise = void 0;
912
+ if (resHeaderRecord["transfer-encoding"] !== "chunked") {
913
+ let maxReadCount = 2;
914
+ for (let i = 0; i < maxReadCount; i++) {
915
+ currentReadPromise ||= reader.read();
916
+ const chunk = await readWithoutBlocking(currentReadPromise).catch((e) => {
917
+ console.error(e);
918
+ done = true;
919
+ });
920
+ if (!chunk) {
921
+ if (i === 1) {
922
+ await new Promise((resolve) => setTimeout(resolve));
923
+ maxReadCount = 3;
924
+ continue;
925
+ }
926
+ break;
927
+ }
928
+ currentReadPromise = void 0;
929
+ if (chunk.value) values.push(chunk.value);
930
+ if (chunk.done) {
931
+ done = true;
932
+ break;
933
+ }
934
+ }
935
+ if (done && !("content-length" in resHeaderRecord)) resHeaderRecord["content-length"] = values.reduce((acc, value) => acc + value.length, 0);
936
+ }
937
+ outgoing.writeHead(res.status, resHeaderRecord);
938
+ values.forEach((value) => {
939
+ outgoing.write(value);
940
+ });
941
+ if (done) outgoing.end();
942
+ else {
943
+ if (values.length === 0) flushHeaders(outgoing);
944
+ await writeFromReadableStreamDefaultReader(reader, outgoing, currentReadPromise);
945
+ }
946
+ } else if (resHeaderRecord[X_ALREADY_SENT]) {} else {
947
+ outgoing.writeHead(res.status, resHeaderRecord);
948
+ outgoing.end();
949
+ }
950
+ outgoing[outgoingEnded]?.();
951
+ };
952
+ var getRequestListener = (fetchCallback, options = {}) => {
953
+ const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
954
+ if (options.overrideGlobalObjects !== false && global.Request !== Request) {
955
+ Object.defineProperty(global, "Request", { value: Request });
956
+ Object.defineProperty(global, "Response", { value: Response2 });
957
+ }
958
+ return async (incoming, outgoing) => {
959
+ let res, req;
960
+ try {
961
+ req = newRequest(incoming, options.hostname);
962
+ let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
963
+ if (!incomingEnded) {
964
+ incoming[wrapBodyStream] = true;
965
+ incoming.on("end", () => {
966
+ incomingEnded = true;
967
+ });
968
+ if (incoming instanceof Http2ServerRequest) outgoing[outgoingEnded] = () => {
969
+ if (!incomingEnded) setTimeout(() => {
970
+ if (!incomingEnded) setTimeout(() => {
971
+ incoming.destroy();
972
+ outgoing.destroy();
973
+ });
974
+ });
975
+ };
976
+ }
977
+ outgoing.on("close", () => {
978
+ if (req[abortControllerKey]) {
979
+ if (incoming.errored) req[abortControllerKey].abort(incoming.errored.toString());
980
+ else if (!outgoing.writableFinished) req[abortControllerKey].abort("Client connection prematurely closed.");
981
+ }
982
+ if (!incomingEnded) setTimeout(() => {
983
+ if (!incomingEnded) setTimeout(() => {
984
+ incoming.destroy();
985
+ });
986
+ });
987
+ });
988
+ res = fetchCallback(req, {
989
+ incoming,
990
+ outgoing
991
+ });
992
+ if (cacheKey in res) return responseViaCache(res, outgoing);
993
+ } catch (e) {
994
+ if (!res) {
995
+ if (options.errorHandler) {
996
+ res = await options.errorHandler(req ? e : toRequestError(e));
997
+ if (!res) return;
998
+ } else if (!req) res = handleRequestError();
999
+ else res = handleFetchError(e);
1000
+ } else return handleResponseError(e, outgoing);
1001
+ }
1002
+ try {
1003
+ return await responseViaResponseObject(res, outgoing, options);
1004
+ } catch (e) {
1005
+ return handleResponseError(e, outgoing);
1006
+ }
1007
+ };
1008
+ };
1009
+ var createAdaptorServer = (options) => {
1010
+ const fetchCallback = options.fetch;
1011
+ const requestListener = getRequestListener(fetchCallback, {
1012
+ hostname: options.hostname,
1013
+ overrideGlobalObjects: options.overrideGlobalObjects,
1014
+ autoCleanupIncoming: options.autoCleanupIncoming
1015
+ });
1016
+ return (options.createServer || createServer)(options.serverOptions || {}, requestListener);
1017
+ };
1018
+ var serve = (options, listeningListener) => {
1019
+ const server = createAdaptorServer(options);
1020
+ server.listen(options?.port ?? 3e3, options.hostname, () => {
1021
+ const serverInfo = server.address();
1022
+ listeningListener && listeningListener(serverInfo);
1023
+ });
1024
+ return server;
1025
+ };
1026
+ //#endregion
1027
+ //#region src/dev/fakes.ts
1028
+ /**
1029
+ * External APIs' app fakes, served for `gkm dev`.
1030
+ *
1031
+ * An image fake runs with the other containers. An app fake is a fetch handler
1032
+ * in `test/fakes/<id>.ts`, and nothing runs it unless this does: each one
1033
+ * listens on the port its key was allocated, which is the port the stage's
1034
+ * `<ID>_URL` already names.
1035
+ */
1036
+ /**
1037
+ * Serve every app fake on its allocated port. Image fakes are skipped — their
1038
+ * containers are already up.
1039
+ */
1040
+ async function serveFakes(fakes, ports) {
1041
+ const served = [];
1042
+ for (const [id, local] of Object.entries(fakes)) {
1043
+ if (local.image) continue;
1044
+ const port = ports[fakeKey(id)];
1045
+ if (port === void 0) continue;
1046
+ const { handler } = (await import(pathToFileURL(local.file).href)).default;
1047
+ const server = serve({
1048
+ fetch: (request) => handler.fetch(request),
1049
+ port
1050
+ });
1051
+ served.push({
1052
+ id,
1053
+ port,
1054
+ server
1055
+ });
1056
+ }
1057
+ return served;
1058
+ }
1059
+ /** Stop every served fake. */
1060
+ function closeFakes(served) {
1061
+ for (const { server } of served) server.close();
1062
+ }
1063
+ //#endregion
581
1064
  //#region src/exec/index.ts
582
1065
  const logger$11 = console;
583
1066
  /** `gkm exec` was given nothing to run. */
@@ -729,41 +1212,28 @@ async function devCommand(options) {
729
1212
  if (options.entry) return entryDevCommand(options);
730
1213
  const defaultEnv = loadEnvFiles(".env");
731
1214
  if (defaultEnv.loaded.length > 0) logger$10.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
732
- const appName = getAppNameFromCwd();
733
- let config;
734
- let appRoot = process.cwd();
735
- let secretsRoot = process.cwd();
736
- let workspaceAppName;
737
- let workspaceAppPort;
738
- let workspace;
739
- if (appName) try {
740
- const appConfig = await loadAppConfig();
741
- config = appConfig.gkmConfig;
742
- appRoot = appConfig.appRoot;
743
- secretsRoot = appConfig.workspaceRoot;
744
- workspaceAppName = appConfig.appName;
745
- workspaceAppPort = appConfig.app.port;
746
- workspace = appConfig.workspace;
747
- if (appConfig.app.entry) {
748
- logger$10.log(`📄 Using entry point: ${appConfig.app.entry}`);
749
- return entryDevCommand({
750
- ...options,
751
- entry: appConfig.app.entry,
752
- port: workspaceAppPort,
753
- portExplicit: true
754
- });
755
- }
756
- } catch {
757
- const loadedConfig = await loadWorkspaceConfig();
758
- if (loadedConfig.type === "workspace") return workspaceDevCommand(loadedConfig.workspace, options);
759
- config = loadedConfig.raw;
760
- workspace = loadedConfig.workspace;
761
- }
762
- else {
763
- const loadedConfig = await loadWorkspaceConfig();
764
- if (loadedConfig.type === "workspace") return workspaceDevCommand(loadedConfig.workspace, options);
765
- config = loadedConfig.raw;
766
- workspace = loadedConfig.workspace;
1215
+ const appConfig = await loadAppConfig().catch((error) => {
1216
+ if (error instanceof NotInAnApp || error instanceof NotABackendApp) return;
1217
+ throw error;
1218
+ });
1219
+ if (!appConfig) {
1220
+ const { workspace: everything } = await loadWorkspaceConfig();
1221
+ return workspaceDevCommand(everything, options);
1222
+ }
1223
+ const config = appConfig.gkmConfig;
1224
+ const appRoot = appConfig.appRoot;
1225
+ const secretsRoot = appConfig.workspaceRoot;
1226
+ const workspaceAppName = appConfig.appName;
1227
+ const workspaceAppPort = appConfig.app.port;
1228
+ const workspace = appConfig.workspace;
1229
+ if (appConfig.app.entry) {
1230
+ logger$10.log(`📄 Using entry point: ${appConfig.app.entry}`);
1231
+ return entryDevCommand({
1232
+ ...options,
1233
+ entry: appConfig.app.entry,
1234
+ port: workspaceAppPort,
1235
+ portExplicit: true
1236
+ });
767
1237
  }
768
1238
  if (config.env) {
769
1239
  const { loaded, missing } = loadEnvFiles(config.env, appRoot);
@@ -793,9 +1263,12 @@ async function devCommand(options) {
793
1263
  const runtime = config.runtime ?? "node";
794
1264
  let secretsJsonPath;
795
1265
  let publicUrl;
796
- const appSecrets = await loadSecretsForApp(secretsRoot, config.stages.local, workspaceAppName);
1266
+ const appSecrets = await loadSecretsForApp(workspace ? await secretsStoreFor(workspace, config.stages.local) : new FileSecretsStore(secretsRoot), config.stages.local, workspaceAppName);
797
1267
  if (workspace) {
798
- const reconciled = await reconcileWorkspace(workspace, { stage: workspace.stages.local });
1268
+ const reconciled = await reconcileWorkspace(workspace, {
1269
+ stage: workspace.stages.local,
1270
+ ...options.fake ? { fake: true } : {}
1271
+ });
799
1272
  if (reconciled.changed && reconciled.plan.containers.length > 0) {
800
1273
  logger$10.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
801
1274
  for (const [container, address] of Object.entries(reconciled.addresses)) logger$10.log(` ${container}: ${address}`);
@@ -816,7 +1289,7 @@ async function devCommand(options) {
816
1289
  devServer.publicUrl = publicUrl;
817
1290
  await devServer.start();
818
1291
  const hooksFile = (config.hooks?.server?.split("#"))?.[0];
819
- const normalizedPatterns = [config.constructs, ...hooksFile ? [hooksFile.endsWith(".ts") ? hooksFile : `${hooksFile}.ts`] : []].flat().filter((p) => typeof p === "string").map((p) => p.startsWith("./") ? p.slice(2) : p);
1292
+ const normalizedPatterns = [config.constructs, ...hooksFile ? [hooksFile.endsWith(".ts") ? hooksFile : `${hooksFile}.ts`] : []].flat().filter((p) => typeof p === "string").map((p) => isAbsolute(p) ? relative(appRoot, p) : p.replace(/^\.\//, ""));
820
1293
  const resolvedFiles = await fg(normalizedPatterns, {
821
1294
  cwd: appRoot,
822
1295
  absolute: false,
@@ -1040,10 +1513,8 @@ async function validateFrontendApps(workspace) {
1040
1513
  async function loadDevSecrets(workspace) {
1041
1514
  if (!workspace.secrets.enabled) return {};
1042
1515
  const stage = workspace.stages.local;
1043
- if (secretsExist(stage, workspace.root)) {
1044
- const secrets = await readStageSecrets(stage, workspace.root);
1045
- if (secrets) return toEmbeddableSecrets(secrets);
1046
- }
1516
+ const secrets = await (await secretsStoreFor(workspace, stage)).read(stage);
1517
+ if (secrets) return toEmbeddableSecrets(secrets);
1047
1518
  return {};
1048
1519
  }
1049
1520
  /**
@@ -1077,7 +1548,10 @@ async function workspaceDevCommand(workspace, options) {
1077
1548
  if (hasErrors) throw new Error("Frontend app validation failed. Fix the issues above and try again.");
1078
1549
  }
1079
1550
  const rawSecrets = await loadDevSecrets(workspace);
1080
- const reconciled = await reconcileWorkspace(workspace, { stage: workspace.stages.local });
1551
+ const reconciled = await reconcileWorkspace(workspace, {
1552
+ stage: workspace.stages.local,
1553
+ ...options.fake ? { fake: true } : {}
1554
+ });
1081
1555
  if (reconciled.changed && reconciled.plan.containers.length > 0) {
1082
1556
  logger$10.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
1083
1557
  for (const [container, address] of Object.entries(reconciled.addresses)) logger$10.log(` ${container}: ${address}`);
@@ -1086,10 +1560,12 @@ async function workspaceDevCommand(workspace, options) {
1086
1560
  ...rawSecrets,
1087
1561
  ...reconciled.env
1088
1562
  };
1563
+ const fakes = options.fake ? await serveFakes(reconciled.fakes, reconciled.ports) : [];
1564
+ for (const { id, port } of fakes) logger$10.log(`🎭 ${id} fake: http://localhost:${port}`);
1089
1565
  if (options.migrate || options.seed) {
1090
- const { prepareDevDatabases } = await import("./migrate-_2J3lFT5.mjs");
1566
+ const { prepareDevDatabases } = await import("./migrate-BJR912RE.mjs");
1091
1567
  await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
1092
- } else await import("./migrate-_2J3lFT5.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
1568
+ } else await import("./migrate-BJR912RE.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
1093
1569
  logger$10.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
1094
1570
  });
1095
1571
  const appUrls = {};
@@ -1148,7 +1624,8 @@ async function workspaceDevCommand(workspace, options) {
1148
1624
  ...process.env,
1149
1625
  ...secretsEnv,
1150
1626
  NODE_ENV: "development",
1151
- ...configPath ? { GKM_CONFIG_PATH: configPath } : {}
1627
+ ...configPath ? { GKM_CONFIG_PATH: configPath } : {},
1628
+ ...options.fake ? { [FAKE_ENV]: "1" } : {}
1152
1629
  };
1153
1630
  const scoped = options.app ? [options.app] : options.filter ? [] : Object.keys(workspace.apps);
1154
1631
  const held = [];
@@ -1177,6 +1654,7 @@ async function workspaceDevCommand(workspace, options) {
1177
1654
  if (isShuttingDown) return;
1178
1655
  isShuttingDown = true;
1179
1656
  logger$10.log("\n🛑 Shutting down workspace...");
1657
+ closeFakes(fakes);
1180
1658
  const pid = turboProcess.pid;
1181
1659
  if (pid) try {
1182
1660
  process.kill(-pid, "SIGTERM");
@@ -2244,10 +2722,10 @@ export async function setupSubscribers(
2244
2722
  //#endregion
2245
2723
  //#region src/generators/TopicGenerator.ts
2246
2724
  /**
2247
- * Discovers `t` topics into `manifest.topics`. A topic is a *resource*, not a
2248
- * function — it has no handler to generate. Producers reach it via its derived
2249
- * publisher (`topic.publisher`, whose connection string is sniffed onto the
2250
- * producing construct) and subscribers bind via `s.topic(topic)`; this generator
2725
+ * Discovers topics (`new Topic(…)`) into `manifest.topics`. A topic is a
2726
+ * *resource*, not a function — it has no handler to generate. Producers name it
2727
+ * with `.event(topic, …)` or `.dependsOn([topic])`, and subscribers bind with
2728
+ * `worker.topic(topic)`; this generator
2251
2729
  * only records the topic + its event contract so infra can provision the SNS
2252
2730
  * topic. Identical output for every provider (locally pg-boss routes by event
2253
2731
  * name, so there's nothing to run).
@@ -2809,7 +3287,7 @@ async function buildForProvider(provider, context, appRoot, discovered, endpoint
2809
3287
  let masterKey;
2810
3288
  if (context.production?.bundle && !skipBundle) {
2811
3289
  logger$8.log(`\n📦 Bundling production server...`);
2812
- const { bundleServer } = await import("./bundler-D9IPNvDt.mjs");
3290
+ const { bundleServer } = await import("./bundler--fyybf1N.mjs");
2813
3291
  const allConstructs = [
2814
3292
  ...endpoints.map((e) => e.construct),
2815
3293
  ...functions.map((f) => f.construct),
@@ -3293,17 +3771,43 @@ const PROVISIONERS = {
3293
3771
  return broker(declaration.id, context);
3294
3772
  },
3295
3773
  /**
3296
- * A signing key, generated once and remembered.
3774
+ * A signing key, generated once and remembered in the stage's store.
3297
3775
  *
3298
- * Regenerating it on every deploy would invalidate every live session, which
3299
- * is why the value comes from state when there is one. Nothing is created in
3300
- * Dokploy: a secret has no address.
3776
+ * Random, never derived: a key computed from the project and stage names
3777
+ * signs sessions anyone reading the repo could forge. The deploy generates
3778
+ * it before provisioning (`withGeneratedSecrets`) and every later deploy
3779
+ * reads the same one, so live sessions survive a redeploy. Nothing is
3780
+ * created in Dokploy: a secret has no address.
3301
3781
  */
3302
3782
  secret: async (declaration, context) => {
3303
3783
  if (declaration.kind !== "secret") throw new WrongKind(declaration.kind);
3304
3784
  const key = declaration.provides?.[0];
3305
3785
  if (!key) return { provides: {} };
3306
- return { provides: { [key]: context.secrets?.[key] ?? derivedPassword(context, `secret:${declaration.id}`) } };
3786
+ return { provides: { [key]: supplied(declaration.id, key, context) } };
3787
+ },
3788
+ /**
3789
+ * A third party's credentials, as the stage was given them.
3790
+ *
3791
+ * Nothing is created and nothing derived: the value was issued by somebody
3792
+ * else. A stage that was never given one fails the deploy here, naming the
3793
+ * command that sets it — not the first request that needs it.
3794
+ */
3795
+ credential: async (declaration, context) => {
3796
+ if (declaration.kind !== "credential") throw new WrongKind(declaration.kind);
3797
+ const key = provideKey(declaration.id, "credentials");
3798
+ return { provides: { [key]: supplied(declaration.id, key, context) } };
3799
+ },
3800
+ /**
3801
+ * An API somebody else runs: the URL this stage calls it at, and the
3802
+ * credentials it was issued for this stage.
3803
+ */
3804
+ "external-api": async (declaration, context) => {
3805
+ if (declaration.kind !== "external-api") throw new WrongKind(declaration.kind);
3806
+ const credentials = provideKey(declaration.id, "credentials");
3807
+ return { provides: {
3808
+ [provideKey(declaration.id, "url")]: externalApiUrl(declaration, context.stage),
3809
+ [credentials]: supplied(declaration.id, credentials, context)
3810
+ } };
3307
3811
  },
3308
3812
  /**
3309
3813
  * A surface, at the address whatever created its domain assigned.
@@ -3490,14 +3994,16 @@ function soleDatabase(context) {
3490
3994
  return databases[0];
3491
3995
  }
3492
3996
  /**
3493
- * A password derived from the project, the stage and the role.
3997
+ * A password derived from the stage's seed, the project, the stage and the role.
3494
3998
  *
3495
- * Derived rather than random for the reason the local target derives its own: a
3496
- * redeploy must not lock the running application out of its own database, and
3497
- * two projects must not share a credential.
3999
+ * Derived rather than stored per role so a redeploy hands the running
4000
+ * application the password it already has, and two projects never share one.
4001
+ * The seed is what makes it secret: random, generated once and kept in the
4002
+ * stage's store. Without it the inputs are all in the repo, and so was every
4003
+ * production password.
3498
4004
  */
3499
4005
  function derivedPassword(context, role) {
3500
- return createHash("sha256").update(`${context.project}:${context.stage}:role:${role}`).digest("base64url").slice(0, 32);
4006
+ return createHash("sha256").update(`${context.seed}:${context.project}:${context.stage}:role:${role}`).digest("base64url").slice(0, 32);
3501
4007
  }
3502
4008
  /** A connection string for a role on a cluster. */
3503
4009
  function postgresUrl(role, password, host, database) {
@@ -3508,6 +4014,29 @@ function quoted(name) {
3508
4014
  return name.split(".").map((part) => `"${part.replace(/"/g, "\"\"")}"`).join(".");
3509
4015
  }
3510
4016
  /** A provisioner was handed a declaration of the wrong kind. */
4017
+ /**
4018
+ * A value only the stage's own secrets can hold.
4019
+ *
4020
+ * @throws {MissingSuppliedSecret} when the stage was never given it.
4021
+ */
4022
+ function supplied(id, key, context) {
4023
+ const value = context.supplied?.[key];
4024
+ if (value === void 0) throw new MissingSuppliedSecret(id, key, context.stage);
4025
+ return value;
4026
+ }
4027
+ /** A construct needs a value the stage was never given. */
4028
+ var MissingSuppliedSecret = class extends Error {
4029
+ id;
4030
+ key;
4031
+ stage;
4032
+ constructor(id, key, stage) {
4033
+ super(`'${id}' needs ${key}, and the stage '${stage}' has none. Nothing can derive it; set it in the stage's secrets: gkm secrets:set ${key} '…' --stage ${stage}`);
4034
+ this.id = id;
4035
+ this.key = key;
4036
+ this.stage = stage;
4037
+ this.name = "MissingSuppliedSecret";
4038
+ }
4039
+ };
3511
4040
  var WrongKind = class extends Error {
3512
4041
  kind;
3513
4042
  constructor(kind) {
@@ -3630,7 +4159,8 @@ async function provisionDeclared(options) {
3630
4159
  events: eventsBackendFor(providerOf(workspace)),
3631
4160
  storage: storageBackendFor(providerOf(workspace)),
3632
4161
  addresses: surfaceAddresses(workspace, manifest, options.appUrls),
3633
- ...options.secrets ? { secrets: options.secrets } : {},
4162
+ seed: options.seed,
4163
+ ...options.supplied ? { supplied: options.supplied } : {},
3634
4164
  deferred: [],
3635
4165
  clusters: {}
3636
4166
  };
@@ -3826,7 +4356,7 @@ async function createDnsProvider(options) {
3826
4356
  if (isDnsProvider(config.provider)) return config.provider;
3827
4357
  const provider = config.provider;
3828
4358
  if (provider === "hostinger") {
3829
- const { HostingerProvider } = await import("./HostingerProvider-GF6NAZAK.mjs");
4359
+ const { HostingerProvider } = await import("./HostingerProvider-Bh6K7QYz.mjs");
3830
4360
  return new HostingerProvider();
3831
4361
  }
3832
4362
  if (provider === "route53") {
@@ -4654,14 +5184,9 @@ exec "$@"
4654
5184
  */
4655
5185
  function resolveDockerConfig(config) {
4656
5186
  const docker = config.docker ?? {};
4657
- let defaultImageName = "api";
4658
- try {
4659
- const pkg = __require(`${process.cwd()}/package.json`);
4660
- if (pkg.name) defaultImageName = pkg.name.replace(/^@[^/]+\//, "");
4661
- } catch {}
4662
5187
  return {
4663
5188
  registry: docker.registry ?? "",
4664
- imageName: docker.imageName ?? defaultImageName,
5189
+ imageName: docker.imageName ?? config.name ?? "api",
4665
5190
  baseImage: docker.baseImage ?? "node:22-alpine",
4666
5191
  port: docker.port ?? 3e3
4667
5192
  };
@@ -6254,6 +6779,55 @@ function validateEnvVars(requiredVars, context) {
6254
6779
  };
6255
6780
  }
6256
6781
  //#endregion
6782
+ //#region src/deploy/generated.ts
6783
+ /**
6784
+ * The values a deployed stage generates once and keeps: what nothing can
6785
+ * provision and nobody else issues.
6786
+ *
6787
+ * - **A seed**, which salts every password a deploy derives — a database's
6788
+ * master and roles, a bucket's root user. Derived from repo facts alone
6789
+ * (project, stage, construct id), those passwords were computable by anyone
6790
+ * who could read the repo.
6791
+ * - **Each `secret` construct's value** — an auth server's signing secret —
6792
+ * random, rather than derived.
6793
+ *
6794
+ * Both are generated on the first deploy that needs them and written to the
6795
+ * stage's store, so every later deploy reads the same values: sessions survive
6796
+ * a redeploy, and a role's password does not move. A value set by hand with
6797
+ * `gkm secrets:set` is kept, never replaced.
6798
+ */
6799
+ /**
6800
+ * The stage's secrets with its seed and every declared `secret`'s value
6801
+ * present, generating what is missing. Pure: the caller writes the result
6802
+ * back to the stage's store when `generated` is not empty.
6803
+ */
6804
+ function withGeneratedSecrets(secrets, manifest) {
6805
+ const generated = [];
6806
+ const custom = { ...secrets.custom };
6807
+ for (const declaration of Object.values(manifest)) {
6808
+ if (declaration?.kind !== "secret") continue;
6809
+ const key = declaration.provides?.[0];
6810
+ if (!key || custom[key]) continue;
6811
+ custom[key] = random();
6812
+ generated.push(key);
6813
+ }
6814
+ const seed = secrets.seed ?? random();
6815
+ if (!secrets.seed) generated.push("seed");
6816
+ return {
6817
+ secrets: generated.length ? {
6818
+ ...secrets,
6819
+ seed,
6820
+ custom,
6821
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
6822
+ } : secrets,
6823
+ generated
6824
+ };
6825
+ }
6826
+ /** 256 bits, URL-safe. */
6827
+ function random() {
6828
+ return randomBytes(32).toString("base64url");
6829
+ }
6830
+ //#endregion
6257
6831
  //#region src/deploy/StateProvider.ts
6258
6832
  /**
6259
6833
  * Check if value is a StateProvider implementation.
@@ -7072,10 +7646,13 @@ async function workspaceDeployCommand(configured, options) {
7072
7646
  if (dokployApps.length === 0) throw new Error("No apps to deploy. All selected apps have unsupported deploy targets.");
7073
7647
  appsToDeployNames = dokployApps;
7074
7648
  logger$4.log("\n🔐 Loading secrets and analyzing environment requirements...");
7075
- const stageSecrets = await readStageSecrets(stage, workspace.root);
7076
- if (!stageSecrets) {
7077
- logger$4.log(` ⚠️ No secrets found for stage "${stage}"`);
7078
- logger$4.log(` Run "gkm secrets:init --stage ${stage}" to create secrets`);
7649
+ const secretsStore = await secretsStoreFor(workspace, stage);
7650
+ const stored = await secretsStore.read(stage);
7651
+ if (!stored) logger$4.log(` ⚠️ No secrets found for stage "${stage}"; starting them`);
7652
+ const { secrets: stageSecrets, generated } = withGeneratedSecrets(stored ?? initStageSecrets(stage), manifest);
7653
+ if (generated.length > 0) {
7654
+ await secretsStore.write(stage, stageSecrets);
7655
+ logger$4.log(` 🔑 Generated for "${stage}" (${secretsStore.name}): ${generated.join(", ")}`);
7079
7656
  }
7080
7657
  const sniffedApps = await sniffAllApps(workspace.apps, workspace.root);
7081
7658
  const encryptedSecrets = stageSecrets ? prepareSecretsForAllApps(stageSecrets, sniffedApps) : /* @__PURE__ */ new Map();
@@ -7203,6 +7780,8 @@ async function workspaceDeployCommand(configured, options) {
7203
7780
  environmentId,
7204
7781
  stage,
7205
7782
  appUrls,
7783
+ supplied: stageSecrets.custom,
7784
+ seed: stageSecrets.seed,
7206
7785
  manifest
7207
7786
  });
7208
7787
  declaredEnv = declared.env;
@@ -7963,6 +8542,172 @@ function printStateDetails(state) {
7963
8542
  }
7964
8543
  }
7965
8544
  //#endregion
8545
+ //#region src/secrets/generator.ts
8546
+ /**
8547
+ * Generate a secure random password using URL-safe base64 characters.
8548
+ * @param length Password length (default: 32)
8549
+ */
8550
+ function generateSecurePassword(length = 32) {
8551
+ return randomBytes(Math.ceil(length * 3 / 4)).toString("base64url").slice(0, length);
8552
+ }
8553
+ /** Default service configurations (localhost for local dev via Docker port mapping) */
8554
+ const SERVICE_DEFAULTS = {
8555
+ postgres: {
8556
+ host: "localhost",
8557
+ port: 5432,
8558
+ username: "app",
8559
+ database: "app"
8560
+ },
8561
+ redis: {
8562
+ host: "localhost",
8563
+ port: 6379,
8564
+ username: "default"
8565
+ },
8566
+ minio: {
8567
+ host: "localhost",
8568
+ port: 9e3,
8569
+ username: "app",
8570
+ bucket: "app"
8571
+ },
8572
+ mailpit: {
8573
+ host: "localhost",
8574
+ port: 1025,
8575
+ username: "app"
8576
+ },
8577
+ localstack: {
8578
+ host: "localhost",
8579
+ port: 4566,
8580
+ username: "localstack",
8581
+ region: "us-east-1"
8582
+ }
8583
+ };
8584
+ /** Default credentials for pgboss (not a Docker service, reuses postgres) */
8585
+ const PGBOSS_DEFAULTS = {
8586
+ host: "localhost",
8587
+ port: 5432,
8588
+ username: "pgboss",
8589
+ database: "app"
8590
+ };
8591
+ /**
8592
+ * Generate credentials for a specific service.
8593
+ */
8594
+ function generateServiceCredentials(service) {
8595
+ return {
8596
+ ...SERVICE_DEFAULTS[service],
8597
+ password: generateSecurePassword()
8598
+ };
8599
+ }
8600
+ /**
8601
+ * Generate credentials for multiple services.
8602
+ */
8603
+ function generateServicesCredentials(services) {
8604
+ const result = {};
8605
+ for (const service of services) result[service] = generateServiceCredentials(service);
8606
+ return result;
8607
+ }
8608
+ /**
8609
+ * Generate connection URL for PostgreSQL.
8610
+ */
8611
+ function generatePostgresUrl(creds) {
8612
+ const { username, password, host, port, database } = creds;
8613
+ return `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
8614
+ }
8615
+ /**
8616
+ * Generate connection URL for Redis.
8617
+ */
8618
+ function generateRedisUrl(creds) {
8619
+ const { password, host, port } = creds;
8620
+ return `redis://:${encodeURIComponent(password)}@${host}:${port}`;
8621
+ }
8622
+ /**
8623
+ * Generate endpoint URL for MinIO (S3-compatible).
8624
+ */
8625
+ function generateMinioEndpoint(creds) {
8626
+ const { host, port } = creds;
8627
+ return `http://${host}:${port}`;
8628
+ }
8629
+ /**
8630
+ * Generate connection URL for pg-boss (uses PostgreSQL protocol).
8631
+ * Format: pgboss://user:pass@host:port/db?schema=pgboss
8632
+ */
8633
+ function generatePgBossUrl(creds) {
8634
+ const { username, password, host, port, database } = creds;
8635
+ return `pgboss://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}?schema=pgboss`;
8636
+ }
8637
+ /**
8638
+ * Generate connection URLs from service credentials.
8639
+ */
8640
+ function generateConnectionUrls(services) {
8641
+ const urls = {};
8642
+ if (services.postgres) urls.DATABASE_URL = generatePostgresUrl(services.postgres);
8643
+ if (services.redis) urls.REDIS_URL = generateRedisUrl(services.redis);
8644
+ if (services.minio) urls.STORAGE_ENDPOINT = generateMinioEndpoint(services.minio);
8645
+ if (services.pgboss) {
8646
+ const pgbossUrl = generatePgBossUrl(services.pgboss);
8647
+ urls.EVENT_PUBLISHER_CONNECTION_STRING = pgbossUrl;
8648
+ urls.EVENT_SUBSCRIBER_CONNECTION_STRING = pgbossUrl;
8649
+ }
8650
+ if (services.mailpit) {
8651
+ urls.SMTP_HOST = services.mailpit.host;
8652
+ urls.SMTP_PORT = String(services.mailpit.port);
8653
+ }
8654
+ return urls;
8655
+ }
8656
+ /**
8657
+ * Create a new StageSecrets object with generated credentials.
8658
+ * @param stage - The deployment stage (e.g., 'development', 'production')
8659
+ * @param services - List of services to generate credentials for
8660
+ * @param options - Optional configuration
8661
+ * @param options.projectName - Project name used to derive the database name (e.g., 'myapp' → 'myapp_dev')
8662
+ */
8663
+ function createStageSecrets(stage, services, options) {
8664
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8665
+ const serviceCredentials = generateServicesCredentials(services);
8666
+ if (options?.projectName) {
8667
+ if (serviceCredentials.postgres) serviceCredentials.postgres.database = `${options.projectName.replace(/-/g, "_")}_dev`;
8668
+ if (serviceCredentials.minio) {
8669
+ serviceCredentials.minio.bucket = options.projectName;
8670
+ serviceCredentials.minio.username = options.projectName;
8671
+ }
8672
+ }
8673
+ if (serviceCredentials.postgres && !serviceCredentials.pgboss) serviceCredentials.pgboss = {
8674
+ ...PGBOSS_DEFAULTS,
8675
+ password: generateSecurePassword(),
8676
+ host: serviceCredentials.postgres.host,
8677
+ port: serviceCredentials.postgres.port,
8678
+ database: serviceCredentials.postgres.database
8679
+ };
8680
+ return {
8681
+ stage,
8682
+ createdAt: now,
8683
+ updatedAt: now,
8684
+ services: serviceCredentials,
8685
+ urls: generateConnectionUrls(serviceCredentials),
8686
+ custom: {}
8687
+ };
8688
+ }
8689
+ /**
8690
+ * Rotate password for a specific service.
8691
+ */
8692
+ function rotateServicePassword(secrets, service) {
8693
+ const currentCreds = secrets.services[service];
8694
+ if (!currentCreds) throw new Error(`Service "${service}" not configured in secrets`);
8695
+ const newCreds = {
8696
+ ...currentCreds,
8697
+ password: generateSecurePassword()
8698
+ };
8699
+ const newServices = {
8700
+ ...secrets.services,
8701
+ [service]: newCreds
8702
+ };
8703
+ return {
8704
+ ...secrets,
8705
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
8706
+ services: newServices,
8707
+ urls: generateConnectionUrls(newServices)
8708
+ };
8709
+ }
8710
+ //#endregion
7966
8711
  //#region src/migrate/names.ts
7967
8712
  /**
7968
8713
  * What a migration file is called: a UTC timestamp, then a name.
@@ -8798,26 +9543,26 @@ function loadPackageJson() {
8798
9543
  * Run: pnpm --filter @geekmidas/cli sync-versions
8799
9544
  */
8800
9545
  const GEEKMIDAS_VERSIONS = {
8801
- "@geekmidas/audit": "~10.0.0-alpha.37",
8802
- "@geekmidas/auth": "~10.0.0-alpha.37",
8803
- "@geekmidas/cache": "~10.0.0-alpha.37",
8804
- "@geekmidas/client": "~10.0.0-alpha.37",
8805
- "@geekmidas/cloud": "~10.0.0-alpha.37",
8806
- "@geekmidas/constructs": "~10.0.0-alpha.37",
8807
- "@geekmidas/db": "~10.0.0-alpha.37",
8808
- "@geekmidas/emailkit": "~10.0.0-alpha.37",
8809
- "@geekmidas/envkit": "~10.0.0-alpha.37",
8810
- "@geekmidas/errors": "~10.0.0-alpha.37",
8811
- "@geekmidas/events": "~10.0.0-alpha.37",
8812
- "@geekmidas/logger": "~10.0.0-alpha.37",
8813
- "@geekmidas/manifest": "~10.0.0-alpha.37",
8814
- "@geekmidas/rate-limit": "~10.0.0-alpha.37",
8815
- "@geekmidas/schema": "~10.0.0-alpha.37",
8816
- "@geekmidas/services": "~10.0.0-alpha.37",
8817
- "@geekmidas/storage": "~10.0.0-alpha.37",
8818
- "@geekmidas/studio": "~10.0.0-alpha.37",
8819
- "@geekmidas/telescope": "~10.0.0-alpha.37",
8820
- "@geekmidas/testkit": "~10.0.0-alpha.37",
9546
+ "@geekmidas/audit": "~10.0.0-alpha.39",
9547
+ "@geekmidas/auth": "~10.0.0-alpha.39",
9548
+ "@geekmidas/cache": "~10.0.0-alpha.39",
9549
+ "@geekmidas/client": "~10.0.0-alpha.39",
9550
+ "@geekmidas/cloud": "~10.0.0-alpha.39",
9551
+ "@geekmidas/constructs": "~10.0.0-alpha.39",
9552
+ "@geekmidas/db": "~10.0.0-alpha.39",
9553
+ "@geekmidas/emailkit": "~10.0.0-alpha.39",
9554
+ "@geekmidas/envkit": "~10.0.0-alpha.39",
9555
+ "@geekmidas/errors": "~10.0.0-alpha.39",
9556
+ "@geekmidas/events": "~10.0.0-alpha.39",
9557
+ "@geekmidas/logger": "~10.0.0-alpha.39",
9558
+ "@geekmidas/manifest": "~10.0.0-alpha.39",
9559
+ "@geekmidas/rate-limit": "~10.0.0-alpha.39",
9560
+ "@geekmidas/schema": "~10.0.0-alpha.39",
9561
+ "@geekmidas/services": "~10.0.0-alpha.39",
9562
+ "@geekmidas/storage": "~10.0.0-alpha.39",
9563
+ "@geekmidas/studio": "~10.0.0-alpha.39",
9564
+ "@geekmidas/telescope": "~10.0.0-alpha.39",
9565
+ "@geekmidas/testkit": "~10.0.0-alpha.39",
8821
9566
  "@geekmidas/cli": `~${loadPackageJson().version}`
8822
9567
  };
8823
9568
  //#endregion
@@ -9389,16 +10134,7 @@ function deploy(options) {
9389
10134
  role-to-assume: \${{ vars.AWS_ROLE_ARN }}
9390
10135
  aws-region: ${options.region}
9391
10136
  ` : "";
9392
- const stageSecrets = sst ? `${credentials}
9393
- # The stage's secrets, from SSM in the account the role belongs to —
9394
- # pushed there once with \`gkm secrets:push --stage <stage> --profile …\`.
9395
- # The stage reaches the shell as a variable, never pasted into the script:
9396
- # on a manual run it is whatever was typed.
9397
- - name: Stage secrets
9398
- run: ${pm.exec} gkm secrets:pull --stage "$STAGE"
9399
- env:
9400
- STAGE: \${{ matrix.stage }}
9401
- ` : `
10137
+ const stageSecrets = sst ? credentials : `
9402
10138
  # The stage's secrets are kept in the encrypted .gkm/secrets/<stage>.json,
9403
10139
  # and each environment holds its key as GKM_SECRETS_KEY. .gkm/ is
9404
10140
  # gitignored, so a checkout has the key but not the file: set
@@ -10824,7 +11560,7 @@ function secretsStore(options) {
10824
11560
  if (options.deployTarget !== "sst" || !options.region) return "";
10825
11561
  return `
10826
11562
  // Deployed stages keep their secrets in SSM, in the account each stage
10827
- // deploys to: \`gkm secrets:push --stage <stage> --profile <profile>\`.
11563
+ // deploys to: \`gkm secrets:set <KEY> '…' --stage <stage>\` writes there.
10828
11564
  store: { provider: 'ssm', region: '${options.region}' },`;
10829
11565
  }
10830
11566
  /**
@@ -11809,22 +12545,24 @@ export const config = envParser
11809
12545
  },
11810
12546
  {
11811
12547
  path: "src/constructs/topics.ts",
11812
- content: `import { t } from '@geekmidas/constructs/topic';
12548
+ content: `import { Topic } from '@geekmidas/constructs/topic';
11813
12549
  import { z } from 'zod';
11814
12550
 
11815
12551
  /**
11816
12552
  * The \`users\` topic: pub/sub fan-out, any number of subscribers.
11817
12553
  *
11818
- * The event map is the contract. It types \`users.publisher\` and every
11819
- * subscriber that binds with \`.topic(users)\`, so an event cannot be published
11820
- * in one shape and read in another.
12554
+ * The event map is the contract. It types every \`.event(users, …)\` that
12555
+ * publishes to it and every subscriber that binds with \`worker.topic(users)\`,
12556
+ * so an event cannot be published in one shape and read in another.
11821
12557
  */
11822
- export const users = t.topic('users').events({
11823
- 'user.created': z.object({ userId: z.string(), email: z.email() }),
11824
- 'user.updated': z.object({
11825
- userId: z.string(),
11826
- changes: z.record(z.string(), z.unknown()),
11827
- }),
12558
+ export const users = new Topic('users', {
12559
+ events: {
12560
+ 'user.created': z.object({ userId: z.string(), email: z.email() }),
12561
+ 'user.updated': z.object({
12562
+ userId: z.string(),
12563
+ changes: z.record(z.string(), z.unknown()),
12564
+ }),
12565
+ },
11828
12566
  });
11829
12567
  `
11830
12568
  },
@@ -14732,7 +15470,7 @@ async function initCommand(projectName, options = {}) {
14732
15470
  customSecrets.BETTER_AUTH_TRUSTED_ORIGINS = "http://localhost:3000,http://localhost:3001";
14733
15471
  }
14734
15472
  devSecrets.custom = customSecrets;
14735
- await writeStageSecrets(devSecrets, targetDir);
15473
+ await new FileSecretsStore(targetDir).write(local, devSecrets);
14736
15474
  const keyPath = getKeyPath(local, name);
14737
15475
  console.log(` Secrets: .gkm/secrets/${local}.json (encrypted)`);
14738
15476
  console.log(` Key: ${keyPath}\n`);
@@ -14885,15 +15623,104 @@ var NotAnAwsRegion = class extends Error {
14885
15623
  }
14886
15624
  };
14887
15625
  //#endregion
15626
+ //#region src/setup/fullstack-secrets.ts
15627
+ /**
15628
+ * Generate a secure random password for database users.
15629
+ * Uses a combination of timestamp and random bytes for uniqueness.
15630
+ */
15631
+ function generateDbPassword() {
15632
+ return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;
15633
+ }
15634
+ /**
15635
+ * Generate database URL for an app.
15636
+ * All apps connect to the same database, but use different users/schemas.
15637
+ */
15638
+ function generateDbUrl(appName, password, projectName, host = "localhost", port = 5432) {
15639
+ return `postgresql://${appName.replace(/-/g, "_")}:${password}@${host}:${port}/${`${projectName.replace(/-/g, "_")}_dev`}`;
15640
+ }
15641
+ /**
15642
+ * Generate fullstack-aware custom secrets for a workspace.
15643
+ *
15644
+ * Generates:
15645
+ * - Common secrets: PORT, LOG_LEVEL, JWT_SECRET (no `NODE_ENV` — the command
15646
+ * decides that; see `gkm init`)
15647
+ * - Per-app database passwords and URLs for backend apps with db service
15648
+ * - Better-auth secrets for apps using the better-auth framework
15649
+ */
15650
+ function generateFullstackCustomSecrets(workspace, containers = []) {
15651
+ const hasDb = containers.includes("postgres");
15652
+ const customs = {
15653
+ PORT: "3000",
15654
+ LOG_LEVEL: "debug",
15655
+ JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
15656
+ };
15657
+ if (!hasDb) return customs;
15658
+ const frontendPorts = [];
15659
+ for (const [appName, appConfig] of Object.entries(workspace.apps)) {
15660
+ if (appConfig.type === "web") {
15661
+ frontendPorts.push(appConfig.port);
15662
+ const upperName = appName.toUpperCase();
15663
+ customs[`${upperName}_URL`] = `http://localhost:${appConfig.port}`;
15664
+ continue;
15665
+ }
15666
+ if (appConfig.type === "mobile") continue;
15667
+ const password = generateDbPassword();
15668
+ const upperName = appName.toUpperCase();
15669
+ customs[`${upperName}_DATABASE_URL`] = generateDbUrl(appName, password, workspace.name);
15670
+ customs[`${upperName}_DB_PASSWORD`] = password;
15671
+ if (appConfig.framework === "better-auth") {
15672
+ customs.AUTH_PORT = String(appConfig.port);
15673
+ customs.AUTH_URL = `http://localhost:${appConfig.port}`;
15674
+ customs.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${generateSecurePassword(16)}`;
15675
+ customs.BETTER_AUTH_URL = `http://localhost:${appConfig.port}`;
15676
+ }
15677
+ }
15678
+ if (customs.BETTER_AUTH_SECRET) customs.BETTER_AUTH_TRUSTED_ORIGINS = Object.values(workspace.apps).map((a) => a.port).map((p) => `http://localhost:${p}`).join(",");
15679
+ return customs;
15680
+ }
15681
+ /**
15682
+ * Extract *_DB_PASSWORD keys from secrets and write docker/.env.
15683
+ *
15684
+ * The docker/.env file contains database passwords that the PostgreSQL
15685
+ * init script reads to create per-app database users.
15686
+ */
15687
+ async function writeDockerEnvFromSecrets(secrets, workspaceRoot) {
15688
+ const dbPasswordEntries = Object.entries(secrets.custom).filter(([key]) => key.endsWith("_DB_PASSWORD"));
15689
+ if (secrets.services.pgboss && !dbPasswordEntries.some(([key]) => key === "PGBOSS_DB_PASSWORD")) dbPasswordEntries.push(["PGBOSS_DB_PASSWORD", secrets.services.pgboss.password]);
15690
+ if (dbPasswordEntries.length === 0) return;
15691
+ const envContent = `# Auto-generated docker environment file
15692
+ # Contains database passwords for docker-compose postgres init
15693
+ # This file is gitignored - do not commit to version control
15694
+ ${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join("\n")}
15695
+ `;
15696
+ const envPath = join(workspaceRoot, "docker", ".env");
15697
+ await mkdir(dirname(envPath), { recursive: true });
15698
+ await writeFile(envPath, envContent);
15699
+ }
15700
+ //#endregion
14888
15701
  //#region src/secrets/index.ts
14889
15702
  const logger$2 = console;
14890
15703
  /**
15704
+ * The store the stage named on the command line keeps its secrets in — SSM in
15705
+ * its account, for a deployed stage kept there. Outside any workspace nothing
15706
+ * names a store, so it is the file.
15707
+ */
15708
+ async function storeFor(stage) {
15709
+ try {
15710
+ return await secretsStoreFor(await loadWorkspaceSettings(), stage);
15711
+ } catch (error) {
15712
+ if (error instanceof ConfigNotFound) return new FileSecretsStore(process.cwd());
15713
+ throw error;
15714
+ }
15715
+ }
15716
+ /**
14891
15717
  * Initialize secrets for a stage.
14892
15718
  * Generates secure random passwords for configured services.
14893
15719
  */
14894
15720
  async function secretsInitCommand(options) {
14895
15721
  const { stage, force } = options;
14896
- if (!force && secretsExist(stage)) {
15722
+ const store = await storeFor(stage);
15723
+ if (!force && await store.read(stage)) {
14897
15724
  logger$2.error(`Secrets already exist for stage "${stage}". Use --force to overwrite.`);
14898
15725
  process.exit(1);
14899
15726
  }
@@ -14909,9 +15736,9 @@ async function secretsInitCommand(options) {
14909
15736
  } catch {}
14910
15737
  const secrets = createStageSecrets(stage, [], { projectName });
14911
15738
  if (workspaceSecrets) secrets.custom = workspaceSecrets;
14912
- await writeStageSecrets(secrets);
15739
+ await store.write(stage, secrets);
14913
15740
  logger$2.log(`\n✓ Secrets initialized for stage "${stage}"`);
14914
- logger$2.log(` Location: .gkm/secrets/${stage}.json`);
15741
+ logger$2.log(` Store: ${store.name}`);
14915
15742
  if (secrets.urls.DATABASE_URL) logger$2.log(`\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);
14916
15743
  if (secrets.urls.REDIS_URL) logger$2.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);
14917
15744
  if (secrets.urls.STORAGE_ENDPOINT) logger$2.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);
@@ -14946,20 +15773,21 @@ async function secretsSetCommand(key, value, options) {
14946
15773
  process.exit(1);
14947
15774
  }
14948
15775
  }
14949
- try {
14950
- await setCustomSecret(stage, key, secretValue);
14951
- logger$2.log(`\n✓ Secret "${key}" set for stage "${stage}"`);
14952
- } catch (error) {
14953
- logger$2.error(error instanceof Error ? error.message : "Failed to set secret");
15776
+ const store = await storeFor(stage);
15777
+ const secrets = await store.read(stage);
15778
+ if (!secrets) {
15779
+ logger$2.error(`Secrets not found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
14954
15780
  process.exit(1);
14955
15781
  }
15782
+ await store.write(stage, withCustomSecret(secrets, key, secretValue));
15783
+ logger$2.log(`\n✓ Secret "${key}" set for stage "${stage}" (${store.name})`);
14956
15784
  }
14957
15785
  /**
14958
15786
  * Show secrets for a stage.
14959
15787
  */
14960
15788
  async function secretsShowCommand(options) {
14961
15789
  const { stage, reveal } = options;
14962
- const secrets = await readStageSecrets(stage);
15790
+ const secrets = await (await storeFor(stage)).read(stage);
14963
15791
  if (!secrets) {
14964
15792
  logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
14965
15793
  process.exit(1);
@@ -14992,7 +15820,8 @@ async function secretsShowCommand(options) {
14992
15820
  */
14993
15821
  async function secretsRotateCommand(options) {
14994
15822
  const { stage, service } = options;
14995
- const secrets = await readStageSecrets(stage);
15823
+ const store = await storeFor(stage);
15824
+ const secrets = await store.read(stage);
14996
15825
  if (!secrets) {
14997
15826
  logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
14998
15827
  process.exit(1);
@@ -15003,13 +15832,13 @@ async function secretsRotateCommand(options) {
15003
15832
  process.exit(1);
15004
15833
  }
15005
15834
  const updated = rotateServicePassword(secrets, service);
15006
- await writeStageSecrets(updated);
15835
+ await store.write(stage, updated);
15007
15836
  logger$2.log(`\n✓ Password rotated for ${service} in stage "${stage}"`);
15008
15837
  } else {
15009
15838
  let updated = secrets;
15010
15839
  const services = Object.keys(secrets.services);
15011
15840
  for (const svc of services) updated = rotateServicePassword(updated, svc);
15012
- await writeStageSecrets(updated);
15841
+ await store.write(stage, updated);
15013
15842
  logger$2.log(`\n✓ Passwords rotated for all services in stage "${stage}": ${services.join(", ")}`);
15014
15843
  }
15015
15844
  logger$2.log(`\nUse "gkm secrets:show --stage ${stage}" to view new values`);
@@ -15033,7 +15862,8 @@ async function secretsImportCommand(file, options) {
15033
15862
  logger$2.error(`Failed to parse JSON file: ${error instanceof Error ? error.message : "Invalid JSON"}`);
15034
15863
  process.exit(1);
15035
15864
  }
15036
- const secrets = await readStageSecrets(stage);
15865
+ const store = await storeFor(stage);
15866
+ const secrets = await store.read(stage);
15037
15867
  if (!secrets) {
15038
15868
  logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
15039
15869
  process.exit(1);
@@ -15047,7 +15877,7 @@ async function secretsImportCommand(file, options) {
15047
15877
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
15048
15878
  custom: updatedCustom
15049
15879
  };
15050
- await writeStageSecrets(updated);
15880
+ await store.write(stage, updated);
15051
15881
  const importedCount = Object.keys(importedSecrets).length;
15052
15882
  const totalCount = Object.keys(updatedCustom).length;
15053
15883
  logger$2.log(`\n✓ Imported ${importedCount} secrets for stage "${stage}"`);
@@ -15068,121 +15898,6 @@ function maskUrl(url) {
15068
15898
  }
15069
15899
  }
15070
15900
  //#endregion
15071
- //#region src/secrets/store.ts
15072
- /** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */
15073
- var FileSecretsStore = class {
15074
- root;
15075
- constructor(root) {
15076
- this.root = root;
15077
- }
15078
- pull(stage) {
15079
- return readStageSecrets(stage, this.root);
15080
- }
15081
- push(_stage, secrets) {
15082
- return writeStageSecrets(secrets, this.root);
15083
- }
15084
- };
15085
- /**
15086
- * The store a stage's secrets live in.
15087
- *
15088
- * The local stage is always the file: its secrets belong to the machine
15089
- * running `gkm dev`. A deployed stage uses `secrets.store`.
15090
- */
15091
- async function secretsStoreFor(workspace, stage, options = {}) {
15092
- const configured = workspace.secrets.store ?? "file";
15093
- if (stage === workspace.stages.local || configured === "file") return new FileSecretsStore(workspace.root);
15094
- if (configured.provider !== "ssm") return configured.provider;
15095
- const { SsmSecretsStore } = await import("./ssm-BKN1Cg4z.mjs");
15096
- return new SsmSecretsStore({
15097
- project: workspace.name,
15098
- region: configured.region,
15099
- ...options.profile ? { profile: options.profile } : {}
15100
- });
15101
- }
15102
- /** Whether a stage's secrets live somewhere other than this machine. */
15103
- function isRemoteStore(workspace, stage) {
15104
- const configured = workspace.secrets.store ?? "file";
15105
- return stage !== workspace.stages.local && configured !== "file";
15106
- }
15107
- //#endregion
15108
- //#region src/secrets/transfer.ts
15109
- /**
15110
- * Moving a deployed stage's secrets between this machine and its store.
15111
- *
15112
- * `push` sends the encrypted local copy to the stage's store; `pull` brings it
15113
- * back and writes the local copy a deploy reads — generating a key for it if
15114
- * the machine has none, which is what a fresh CI runner needs. Both reconcile
15115
- * the keys the workspace now derives, so a store never lags a new construct.
15116
- */
15117
- var transfer_exports = /* @__PURE__ */ __exportAll({
15118
- NoLocalSecrets: () => NoLocalSecrets,
15119
- NoRemoteSecretsStore: () => NoRemoteSecretsStore,
15120
- NoStoredSecrets: () => NoStoredSecrets,
15121
- pullStageSecrets: () => pullStageSecrets,
15122
- pushStageSecrets: () => pushStageSecrets
15123
- });
15124
- /** A push or pull for a stage with nowhere to push or pull from. */
15125
- var NoRemoteSecretsStore = class extends Error {
15126
- stage;
15127
- constructor(stage) {
15128
- super(`Stage "${stage}" keeps its secrets in the local file, so there is nothing to push or pull. Set secrets.store in gkm.config.ts (e.g. { provider: 'ssm', region: 'eu-west-1' }) for deployed stages.`);
15129
- this.stage = stage;
15130
- this.name = "NoRemoteSecretsStore";
15131
- }
15132
- };
15133
- /** A push for a stage whose secrets this machine does not have. */
15134
- var NoLocalSecrets = class extends Error {
15135
- stage;
15136
- constructor(stage) {
15137
- super(`No secrets for stage "${stage}" on this machine. Run gkm secrets:init --stage ${stage} first.`);
15138
- this.stage = stage;
15139
- this.name = "NoLocalSecrets";
15140
- }
15141
- };
15142
- /** A pull for a stage the store holds nothing for. */
15143
- var NoStoredSecrets = class extends Error {
15144
- stage;
15145
- constructor(stage) {
15146
- super(`The store holds no secrets for stage "${stage}". Push them first: gkm secrets:push --stage ${stage}.`);
15147
- this.stage = stage;
15148
- this.name = "NoStoredSecrets";
15149
- }
15150
- };
15151
- async function reconcile(secrets, workspace, stage) {
15152
- const { derivedContainers } = await import("./workspace-BtXCL1C8.mjs").then((n) => n.a);
15153
- const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
15154
- return result ? {
15155
- secrets: result.secrets,
15156
- addedKeys: result.addedKeys
15157
- } : {
15158
- secrets,
15159
- addedKeys: []
15160
- };
15161
- }
15162
- function remoteOnly(workspace, stage) {
15163
- assertDeployedStage(workspace.stages, stage);
15164
- if (!isRemoteStore(workspace, stage)) throw new NoRemoteSecretsStore(stage);
15165
- }
15166
- /** Send this machine's secrets for a deployed stage to its store. */
15167
- async function pushStageSecrets(workspace, stage, options = {}) {
15168
- remoteOnly(workspace, stage);
15169
- const local = await readStageSecrets(stage, workspace.root);
15170
- if (!local) throw new NoLocalSecrets(stage);
15171
- const result = await reconcile(local, workspace, stage);
15172
- if (result.addedKeys.length) await writeStageSecrets(result.secrets, workspace.root);
15173
- await (await secretsStoreFor(workspace, stage, options)).push(stage, result.secrets);
15174
- return result;
15175
- }
15176
- /** Bring a deployed stage's secrets from its store into the local copy. */
15177
- async function pullStageSecrets(workspace, stage, options = {}) {
15178
- remoteOnly(workspace, stage);
15179
- const stored = await (await secretsStoreFor(workspace, stage, options)).pull(stage);
15180
- if (!stored) throw new NoStoredSecrets(stage);
15181
- const result = await reconcile(stored, workspace, stage);
15182
- await writeStageSecrets(result.secrets, workspace.root);
15183
- return result;
15184
- }
15185
- //#endregion
15186
15901
  //#region src/setup/index.ts
15187
15902
  var setup_exports = /* @__PURE__ */ __exportAll({
15188
15903
  createFreshWorkspaceSecrets: () => createFreshWorkspaceSecrets,
@@ -15264,30 +15979,18 @@ async function reconcileLocal(workspace, stage, options) {
15264
15979
  async function resolveSecrets(stage, workspace, options) {
15265
15980
  if (options.force) {
15266
15981
  logger$1.log("🔐 Generating fresh secrets (--force)...");
15267
- return generateFreshSecrets(stage, workspace, options);
15268
- }
15269
- if (secretsExist(stage, workspace.root)) {
15270
- logger$1.log("🔐 Using existing local secrets");
15271
- const secrets = await readStageSecrets(stage, workspace.root);
15272
- if (secrets) {
15273
- const reconciled = reconcileSecrets(secrets, workspace, await derivedContainers(workspace, stage));
15274
- if (reconciled) await writeStageSecrets(reconciled, workspace.root);
15275
- return reconciled ?? secrets;
15276
- }
15982
+ return generateFreshSecrets(stage, workspace);
15277
15983
  }
15278
- if (isRemoteStore(workspace, stage)) {
15279
- logger$1.log("☁️ Checking the secrets store...");
15280
- try {
15281
- const { secrets } = await pullStageSecrets(workspace, stage);
15282
- logger$1.log("✅ Pulled secrets from the store");
15283
- return secrets;
15284
- } catch (error) {
15285
- if (error instanceof NoStoredSecrets) logger$1.log(" The store holds none yet");
15286
- else logger$1.warn(`⚠️ Could not pull from the store: ${error.message}`);
15287
- }
15984
+ const store = await secretsStoreFor(workspace, stage);
15985
+ const secrets = await store.read(stage);
15986
+ if (secrets) {
15987
+ logger$1.log("🔐 Using existing secrets");
15988
+ const reconciled = reconcileSecrets(secrets, workspace, await derivedContainers(workspace, stage));
15989
+ if (reconciled) await store.write(stage, reconciled);
15990
+ return reconciled ?? secrets;
15288
15991
  }
15289
15992
  logger$1.log("🔐 Generating fresh development secrets...");
15290
- return generateFreshSecrets(stage, workspace, options);
15993
+ return generateFreshSecrets(stage, workspace);
15291
15994
  }
15292
15995
  /**
15293
15996
  * Reconcile existing secrets with expected workspace-derived keys.
@@ -15381,8 +16084,7 @@ function reconcileSecrets(secrets, workspace, containers = []) {
15381
16084
  * @internal Exported for reuse by `gkm test` auto-setup.
15382
16085
  */
15383
16086
  function createFreshWorkspaceSecrets(stage, workspace, containers = []) {
15384
- const serviceNames = credentialedServices(containers);
15385
- const secrets = createStageSecrets(stage, serviceNames, { projectName: workspace.name });
16087
+ const secrets = createStageSecrets(stage, credentialedServices(containers), { projectName: workspace.name });
15386
16088
  if (Object.keys(workspace.apps).length > 1) secrets.custom = generateFullstackCustomSecrets(workspace, containers);
15387
16089
  else secrets.custom = {
15388
16090
  PORT: "3000",
@@ -15397,38 +16099,26 @@ function createFreshWorkspaceSecrets(stage, workspace, containers = []) {
15397
16099
  *
15398
16100
  * Powers `gkm test --auto-setup` / `GKM_AUTO_SETUP`: CI can run without a
15399
16101
  * committed secrets file or a shared encryption key — the stage is regenerated
15400
- * from the committed `gkm.config.ts`, and `writeStageSecrets` mints a local key.
16102
+ * from the committed `gkm.config.ts`, and the file store mints a local key.
15401
16103
  *
15402
16104
  * @returns true if fresh secrets were generated, false if existing ones were kept
15403
16105
  */
15404
16106
  async function ensureStageSecrets(stage, cwd = process.cwd()) {
15405
16107
  const { workspace } = await loadWorkspaceConfig(cwd);
15406
- if (secretsExist(stage, workspace.root)) return false;
16108
+ const store = await secretsStoreFor(workspace, stage);
16109
+ if (await store.read(stage)) return false;
15407
16110
  const secrets = createFreshWorkspaceSecrets(stage, workspace, await derivedContainers(workspace, stage));
15408
- await writeStageSecrets(secrets, workspace.root);
16111
+ await store.write(stage, secrets);
15409
16112
  return true;
15410
16113
  }
15411
16114
  /**
15412
16115
  * Generate fresh secrets for the workspace.
15413
16116
  */
15414
- async function generateFreshSecrets(stage, workspace, options) {
16117
+ async function generateFreshSecrets(stage, workspace) {
15415
16118
  const secrets = createFreshWorkspaceSecrets(stage, workspace, await derivedContainers(workspace, stage));
15416
- await writeStageSecrets(secrets, workspace.root);
15417
- logger$1.log(` Secrets written to .gkm/secrets/${stage}.json`);
15418
- if (isRemoteStore(workspace, stage) && !options.yes) {
15419
- const { shouldPush } = await prompts({
15420
- type: "confirm",
15421
- name: "shouldPush",
15422
- message: `Push the "${stage}" secrets to its store?`,
15423
- initial: true
15424
- });
15425
- if (shouldPush) try {
15426
- await pushStageSecrets(workspace, stage);
15427
- logger$1.log("☁️ Secrets pushed to the store");
15428
- } catch (error) {
15429
- logger$1.warn(`⚠️ Could not push to the store: ${error.message}`);
15430
- }
15431
- }
16119
+ const store = await secretsStoreFor(workspace, stage);
16120
+ await store.write(stage, secrets);
16121
+ logger$1.log(` Secrets written to the "${stage}" store (${store.name})`);
15432
16122
  return secrets;
15433
16123
  }
15434
16124
  /**
@@ -15621,8 +16311,8 @@ function testDatabases(reconciled) {
15621
16311
  * starts without the reference data the app needs.
15622
16312
  */
15623
16313
  async function migrateTestStage(workspace, env) {
15624
- const { migrateAndSeed } = await import("./migrate-_2J3lFT5.mjs");
15625
- const { discover } = await import("./discover-CMxesskG.mjs").then((n) => n.n);
16314
+ const { migrateAndSeed } = await import("./migrate-BJR912RE.mjs");
16315
+ const { discover } = await import("./discover-BLhf7V_J.mjs").then((n) => n.n);
15626
16316
  const sources = {};
15627
16317
  const manifest = await discover({
15628
16318
  patterns: constructGlobs(workspace),
@@ -16021,7 +16711,7 @@ program.command("build").description("Build handlers and the manifest from decla
16021
16711
  process.exit(1);
16022
16712
  }
16023
16713
  });
16024
- program.command("dev").description("Reconcile constructs and start the dev server with automatic reload").option("-p, --port <port>", "Port to run the development server on").option("--entry <file>", "Entry file to run (bypasses gkm config)").option("--watch", "Watch for file changes (default: true with --entry)").option("--no-watch", "Disable file watching").option("--enable-openapi", "Enable OpenAPI documentation for development server", true).option("--migrate", "Apply pending migrations before the apps start").option("--seed", "Migrate, then run the seeds, before the apps start").action(async (options) => {
16714
+ program.command("dev").description("Reconcile constructs and start the dev server with automatic reload").option("-p, --port <port>", "Port to run the development server on").option("--entry <file>", "Entry file to run (bypasses gkm config)").option("--watch", "Watch for file changes (default: true with --entry)").option("--no-watch", "Disable file watching").option("--enable-openapi", "Enable OpenAPI documentation for development server", true).option("--migrate", "Apply pending migrations before the apps start").option("--seed", "Migrate, then run the seeds, before the apps start").option("--fake", "Call each external API’s fake (test/fakes/<id>.ts) instead of the provider").action(async (options) => {
16025
16715
  try {
16026
16716
  const globalOptions = program.opts();
16027
16717
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
@@ -16032,7 +16722,8 @@ program.command("dev").description("Reconcile constructs and start the dev serve
16032
16722
  entry: options.entry,
16033
16723
  watch: options.watch,
16034
16724
  migrate: options.migrate,
16035
- seed: options.seed
16725
+ seed: options.seed,
16726
+ fake: options.fake
16036
16727
  });
16037
16728
  } catch (error) {
16038
16729
  console.error(formatError(error));
@@ -16066,7 +16757,7 @@ program.command("migrate").description("Apply each database construct's migratio
16066
16757
  try {
16067
16758
  const globalOptions = program.opts();
16068
16759
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16069
- const { migrateCommand } = await import("./migrate-_2J3lFT5.mjs");
16760
+ const { migrateCommand } = await import("./migrate-BJR912RE.mjs");
16070
16761
  await migrateCommand({
16071
16762
  ...options,
16072
16763
  ...construct ? { construct } : {}
@@ -16080,7 +16771,7 @@ program.command("seed").description("Migrate, then run each database construct's
16080
16771
  try {
16081
16772
  const globalOptions = program.opts();
16082
16773
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16083
- const { seedCommand } = await import("./migrate-_2J3lFT5.mjs");
16774
+ const { seedCommand } = await import("./migrate-BJR912RE.mjs");
16084
16775
  await seedCommand({
16085
16776
  ...options,
16086
16777
  ...construct ? { construct } : {}
@@ -16094,7 +16785,7 @@ program.command("migration").description("Write a construct's next migration: an
16094
16785
  try {
16095
16786
  const globalOptions = program.opts();
16096
16787
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16097
- const { migrationCommand } = await import("./migrate-_2J3lFT5.mjs");
16788
+ const { migrationCommand } = await import("./migrate-BJR912RE.mjs");
16098
16789
  await migrationCommand({
16099
16790
  construct,
16100
16791
  ...name ? { name } : {}
@@ -16218,57 +16909,28 @@ program.command("secrets:import").description("Import secrets from a JSON file")
16218
16909
  process.exit(1);
16219
16910
  }
16220
16911
  });
16221
- program.command("secrets:push").description("Send a deployed stage's secrets to its store (secrets.store)").requiredOption("--stage <stage>", "A deployed stage").option("--profile <profile>", "AWS profile for the stage's account").action(async (options) => {
16222
- try {
16223
- const globalOptions = program.opts();
16224
- if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16225
- const { loadWorkspaceConfig } = await import("./config.mjs");
16226
- const { pushStageSecrets } = await Promise.resolve().then(() => transfer_exports);
16227
- const { workspace } = await loadWorkspaceConfig();
16228
- const { addedKeys } = await pushStageSecrets(workspace, options.stage, { profile: options.profile });
16229
- for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
16230
- console.log(`\n✓ Secrets pushed for stage "${options.stage}"`);
16231
- } catch (error) {
16232
- console.error(formatError(error));
16233
- process.exit(1);
16234
- }
16235
- });
16236
- program.command("secrets:pull").description("Bring a deployed stage's secrets from its store into the local copy").requiredOption("--stage <stage>", "A deployed stage").option("--profile <profile>", "AWS profile for the stage's account").action(async (options) => {
16237
- try {
16238
- const globalOptions = program.opts();
16239
- if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16240
- const { loadWorkspaceConfig } = await import("./config.mjs");
16241
- const { pullStageSecrets } = await Promise.resolve().then(() => transfer_exports);
16242
- const { workspace } = await loadWorkspaceConfig();
16243
- const { addedKeys } = await pullStageSecrets(workspace, options.stage, { profile: options.profile });
16244
- for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
16245
- console.log(`\n✓ Secrets pulled for stage "${options.stage}"`);
16246
- } catch (error) {
16247
- console.error(formatError(error));
16248
- process.exit(1);
16249
- }
16250
- });
16251
16912
  program.command("secrets:reconcile").description("Backfill missing custom secrets from workspace config").option("--stage <stage>", "Stage name (default: stages.local)").action(async (options) => {
16252
16913
  try {
16253
16914
  const globalOptions = program.opts();
16254
16915
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16255
16916
  const { loadWorkspaceConfig } = await import("./config.mjs");
16256
- const { reconcileMissingSecrets } = await import("./reconcile-yNq6Shau.mjs").then((n) => n.n);
16257
- const { readStageSecrets, writeStageSecrets } = await import("./storage-BjTLjDDp.mjs").then((n) => n.a);
16917
+ const { reconcileMissingSecrets } = await import("./reconcile--I7V4Klj.mjs");
16918
+ const { secretsStoreFor } = await import("./store-DeGATCS-.mjs").then((n) => n.r);
16258
16919
  const { workspace } = await loadWorkspaceConfig();
16259
16920
  const stage = options.stage ?? workspace.stages.local;
16260
- const secrets = await readStageSecrets(stage, workspace.root);
16921
+ const store = await secretsStoreFor(workspace, stage);
16922
+ const secrets = await store.read(stage);
16261
16923
  if (!secrets) {
16262
16924
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
16263
16925
  process.exit(1);
16264
16926
  }
16265
- const { derivedContainers } = await import("./workspace-BtXCL1C8.mjs").then((n) => n.a);
16927
+ const { derivedContainers } = await import("./workspace-CrFbItPz.mjs").then((n) => n.o);
16266
16928
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
16267
16929
  if (!result) {
16268
16930
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
16269
16931
  return;
16270
16932
  }
16271
- await writeStageSecrets(result.secrets, workspace.root);
16933
+ await store.write(stage, result.secrets);
16272
16934
  console.log(`\n✓ Reconciled ${result.addedKeys.length} missing secret(s) for stage "${stage}":`);
16273
16935
  for (const key of result.addedKeys) console.log(` + ${key}`);
16274
16936
  } catch (error) {
@@ -16321,7 +16983,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
16321
16983
  try {
16322
16984
  const globalOptions = program.opts();
16323
16985
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16324
- const { deployGithubCommand } = await import("./github-C99BMxf0.mjs");
16986
+ const { deployGithubCommand } = await import("./github-D5MthUlR.mjs");
16325
16987
  await deployGithubCommand(options);
16326
16988
  } catch (error) {
16327
16989
  console.error(formatError(error));
@@ -16446,6 +17108,6 @@ program.command("upgrade").description("Upgrade @geekmidas/cli (with --all, ever
16446
17108
  });
16447
17109
  program.parse();
16448
17110
  //#endregion
16449
- export { encryption_exports as a, stamp as i, isRemoteStore as n, getHostingerToken as o, slug as r, pushStageSecrets as t };
17111
+ export { getHostingerToken as a, encryption_exports as i, slug as n, stamp as r, generateFullstackCustomSecrets as t };
16450
17112
 
16451
17113
  //# sourceMappingURL=index.mjs.map