@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.cjs CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
4
- const require_discover = require("./discover-CroTIm0K.cjs");
5
- const require_config = require("./config-DgAHy8nY.cjs");
6
- const require_workspace = require("./workspace-CAJk6UmC.cjs");
7
- const require_storage = require("./storage-C2JC31V9.cjs");
8
- const require_workspace$1 = require("./workspace-DUhLejv4.cjs");
9
- const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-D1nhN98m.cjs");
4
+ const require_discover = require("./discover-B0f_kuNN.cjs");
5
+ const require_config = require("./config-CJ-PAb7M.cjs");
6
+ const require_workspace = require("./workspace-RYSYtHP7.cjs");
7
+ const require_storage = require("./storage-CSGoC3bK.cjs");
8
+ const require_store = require("./store-DQgxT_WR.cjs");
9
+ const require_workspace$1 = require("./workspace-DZ1G4zow.cjs");
10
+ const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-CeEI4ll9.cjs");
10
11
  const require_openapi = require("./openapi.cjs");
11
- const require_ready = require("./ready-CXhoutix.cjs");
12
+ const require_ready = require("./ready-B0WVtkjR.cjs");
12
13
  const require_emit = require("./emit-BdoPFjZf.cjs");
13
- const require_reconcile = require("./reconcile-BxZTV2vl.cjs");
14
14
  let commander = require("commander");
15
15
  let node_process = require("node:process");
16
16
  let node_readline_promises = require("node:readline/promises");
@@ -35,6 +35,11 @@ let node_util = require("node:util");
35
35
  let _geekmidas_cache_postgres = require("@geekmidas/cache/postgres");
36
36
  let prompts = require("prompts");
37
37
  prompts = require_rolldown_runtime.__toESM(prompts, 1);
38
+ let http = require("http");
39
+ let http2 = require("http2");
40
+ let stream = require("stream");
41
+ let crypto = require("crypto");
42
+ crypto = require_rolldown_runtime.__toESM(crypto, 1);
38
43
  let _geekmidas_constructs_crons = require("@geekmidas/constructs/crons");
39
44
  let _geekmidas_constructs_functions = require("@geekmidas/constructs/functions");
40
45
  let _geekmidas_constructs_queue = require("@geekmidas/constructs/queue");
@@ -42,7 +47,7 @@ let _geekmidas_constructs_subscribers = require("@geekmidas/constructs/subscribe
42
47
  let _geekmidas_constructs_topic = require("@geekmidas/constructs/topic");
43
48
  let node_dns_promises = require("node:dns/promises");
44
49
  //#region package.json
45
- var version = "10.0.0-alpha.37";
50
+ var version = "10.0.0-alpha.39";
46
51
  //#endregion
47
52
  //#region src/auth/credentials.ts
48
53
  /**
@@ -583,6 +588,485 @@ var UnsupportedPlatform = class extends Error {
583
588
  }
584
589
  };
585
590
  //#endregion
591
+ //#region ../../node_modules/.pnpm/@hono+node-server@1.19.5_hono@4.13.8/node_modules/@hono/node-server/dist/index.mjs
592
+ var RequestError = class extends Error {
593
+ constructor(message, options) {
594
+ super(message, options);
595
+ this.name = "RequestError";
596
+ }
597
+ };
598
+ var toRequestError = (e) => {
599
+ if (e instanceof RequestError) return e;
600
+ return new RequestError(e.message, { cause: e });
601
+ };
602
+ var GlobalRequest = global.Request;
603
+ var Request = class extends GlobalRequest {
604
+ constructor(input, options) {
605
+ if (typeof input === "object" && getRequestCache in input) input = input[getRequestCache]();
606
+ if (typeof options?.body?.getReader !== "undefined") options.duplex ??= "half";
607
+ super(input, options);
608
+ }
609
+ };
610
+ var newHeadersFromIncoming = (incoming) => {
611
+ const headerRecord = [];
612
+ const rawHeaders = incoming.rawHeaders;
613
+ for (let i = 0; i < rawHeaders.length; i += 2) {
614
+ const { [i]: key, [i + 1]: value } = rawHeaders;
615
+ if (key.charCodeAt(0) !== 58) headerRecord.push([key, value]);
616
+ }
617
+ return new Headers(headerRecord);
618
+ };
619
+ var wrapBodyStream = Symbol("wrapBodyStream");
620
+ var newRequestFromIncoming = (method, url, headers, incoming, abortController) => {
621
+ const init = {
622
+ method,
623
+ headers,
624
+ signal: abortController.signal
625
+ };
626
+ if (method === "TRACE") {
627
+ init.method = "GET";
628
+ const req = new Request(url, init);
629
+ Object.defineProperty(req, "method", { get() {
630
+ return "TRACE";
631
+ } });
632
+ return req;
633
+ }
634
+ if (!(method === "GET" || method === "HEAD")) {
635
+ if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) init.body = new ReadableStream({ start(controller) {
636
+ controller.enqueue(incoming.rawBody);
637
+ controller.close();
638
+ } });
639
+ else if (incoming[wrapBodyStream]) {
640
+ let reader;
641
+ init.body = new ReadableStream({ async pull(controller) {
642
+ try {
643
+ reader ||= stream.Readable.toWeb(incoming).getReader();
644
+ const { done, value } = await reader.read();
645
+ if (done) controller.close();
646
+ else controller.enqueue(value);
647
+ } catch (error) {
648
+ controller.error(error);
649
+ }
650
+ } });
651
+ } else init.body = stream.Readable.toWeb(incoming);
652
+ }
653
+ return new Request(url, init);
654
+ };
655
+ var getRequestCache = Symbol("getRequestCache");
656
+ var requestCache = Symbol("requestCache");
657
+ var incomingKey = Symbol("incomingKey");
658
+ var urlKey = Symbol("urlKey");
659
+ var headersKey = Symbol("headersKey");
660
+ var abortControllerKey = Symbol("abortControllerKey");
661
+ var requestPrototype = {
662
+ get method() {
663
+ return this[incomingKey].method || "GET";
664
+ },
665
+ get url() {
666
+ return this[urlKey];
667
+ },
668
+ get headers() {
669
+ return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]);
670
+ },
671
+ [Symbol("getAbortController")]() {
672
+ this[getRequestCache]();
673
+ return this[abortControllerKey];
674
+ },
675
+ [getRequestCache]() {
676
+ this[abortControllerKey] ||= new AbortController();
677
+ return this[requestCache] ||= newRequestFromIncoming(this.method, this[urlKey], this.headers, this[incomingKey], this[abortControllerKey]);
678
+ }
679
+ };
680
+ [
681
+ "body",
682
+ "bodyUsed",
683
+ "cache",
684
+ "credentials",
685
+ "destination",
686
+ "integrity",
687
+ "mode",
688
+ "redirect",
689
+ "referrer",
690
+ "referrerPolicy",
691
+ "signal",
692
+ "keepalive"
693
+ ].forEach((k) => {
694
+ Object.defineProperty(requestPrototype, k, { get() {
695
+ return this[getRequestCache]()[k];
696
+ } });
697
+ });
698
+ [
699
+ "arrayBuffer",
700
+ "blob",
701
+ "clone",
702
+ "formData",
703
+ "json",
704
+ "text"
705
+ ].forEach((k) => {
706
+ Object.defineProperty(requestPrototype, k, { value: function() {
707
+ return this[getRequestCache]()[k]();
708
+ } });
709
+ });
710
+ Object.setPrototypeOf(requestPrototype, Request.prototype);
711
+ var newRequest = (incoming, defaultHostname) => {
712
+ const req = Object.create(requestPrototype);
713
+ req[incomingKey] = incoming;
714
+ const incomingUrl = incoming.url || "";
715
+ if (incomingUrl[0] !== "/" && (incomingUrl.startsWith("http://") || incomingUrl.startsWith("https://"))) {
716
+ if (incoming instanceof http2.Http2ServerRequest) throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
717
+ try {
718
+ req[urlKey] = new URL(incomingUrl).href;
719
+ } catch (e) {
720
+ throw new RequestError("Invalid absolute URL", { cause: e });
721
+ }
722
+ return req;
723
+ }
724
+ const host = (incoming instanceof http2.Http2ServerRequest ? incoming.authority : incoming.headers.host) || defaultHostname;
725
+ if (!host) throw new RequestError("Missing host header");
726
+ let scheme;
727
+ if (incoming instanceof http2.Http2ServerRequest) {
728
+ scheme = incoming.scheme;
729
+ if (!(scheme === "http" || scheme === "https")) throw new RequestError("Unsupported scheme");
730
+ } else scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
731
+ const url = new URL(`${scheme}://${host}${incomingUrl}`);
732
+ if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) throw new RequestError("Invalid host header");
733
+ req[urlKey] = url.href;
734
+ return req;
735
+ };
736
+ var responseCache = Symbol("responseCache");
737
+ var getResponseCache = Symbol("getResponseCache");
738
+ var cacheKey = Symbol("cache");
739
+ var GlobalResponse = global.Response;
740
+ var Response2 = class _Response {
741
+ #body;
742
+ #init;
743
+ [getResponseCache]() {
744
+ delete this[cacheKey];
745
+ return this[responseCache] ||= new GlobalResponse(this.#body, this.#init);
746
+ }
747
+ constructor(body, init) {
748
+ let headers;
749
+ this.#body = body;
750
+ if (init instanceof _Response) {
751
+ const cachedGlobalResponse = init[responseCache];
752
+ if (cachedGlobalResponse) {
753
+ this.#init = cachedGlobalResponse;
754
+ this[getResponseCache]();
755
+ return;
756
+ } else {
757
+ this.#init = init.#init;
758
+ headers = new Headers(init.#init.headers);
759
+ }
760
+ } else this.#init = init;
761
+ if (typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) {
762
+ headers ||= init?.headers || { "content-type": "text/plain; charset=UTF-8" };
763
+ this[cacheKey] = [
764
+ init?.status || 200,
765
+ body,
766
+ headers
767
+ ];
768
+ }
769
+ }
770
+ get headers() {
771
+ const cache = this[cacheKey];
772
+ if (cache) {
773
+ if (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2]);
774
+ return cache[2];
775
+ }
776
+ return this[getResponseCache]().headers;
777
+ }
778
+ get status() {
779
+ return this[cacheKey]?.[0] ?? this[getResponseCache]().status;
780
+ }
781
+ get ok() {
782
+ const status = this.status;
783
+ return status >= 200 && status < 300;
784
+ }
785
+ };
786
+ [
787
+ "body",
788
+ "bodyUsed",
789
+ "redirected",
790
+ "statusText",
791
+ "trailers",
792
+ "type",
793
+ "url"
794
+ ].forEach((k) => {
795
+ Object.defineProperty(Response2.prototype, k, { get() {
796
+ return this[getResponseCache]()[k];
797
+ } });
798
+ });
799
+ [
800
+ "arrayBuffer",
801
+ "blob",
802
+ "clone",
803
+ "formData",
804
+ "json",
805
+ "text"
806
+ ].forEach((k) => {
807
+ Object.defineProperty(Response2.prototype, k, { value: function() {
808
+ return this[getResponseCache]()[k]();
809
+ } });
810
+ });
811
+ Object.setPrototypeOf(Response2, GlobalResponse);
812
+ Object.setPrototypeOf(Response2.prototype, GlobalResponse.prototype);
813
+ async function readWithoutBlocking(readPromise) {
814
+ return Promise.race([readPromise, Promise.resolve().then(() => Promise.resolve(void 0))]);
815
+ }
816
+ function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromise) {
817
+ const cancel = (error) => {
818
+ reader.cancel(error).catch(() => {});
819
+ };
820
+ writable.on("close", cancel);
821
+ writable.on("error", cancel);
822
+ (currentReadPromise ?? reader.read()).then(flow, handleStreamError);
823
+ return reader.closed.finally(() => {
824
+ writable.off("close", cancel);
825
+ writable.off("error", cancel);
826
+ });
827
+ function handleStreamError(error) {
828
+ if (error) writable.destroy(error);
829
+ }
830
+ function onDrain() {
831
+ reader.read().then(flow, handleStreamError);
832
+ }
833
+ function flow({ done, value }) {
834
+ try {
835
+ if (done) writable.end();
836
+ else if (!writable.write(value)) writable.once("drain", onDrain);
837
+ else return reader.read().then(flow, handleStreamError);
838
+ } catch (e) {
839
+ handleStreamError(e);
840
+ }
841
+ }
842
+ }
843
+ function writeFromReadableStream(stream$1, writable) {
844
+ if (stream$1.locked) throw new TypeError("ReadableStream is locked.");
845
+ else if (writable.destroyed) return;
846
+ return writeFromReadableStreamDefaultReader(stream$1.getReader(), writable);
847
+ }
848
+ var buildOutgoingHttpHeaders = (headers) => {
849
+ const res = {};
850
+ if (!(headers instanceof Headers)) headers = new Headers(headers ?? void 0);
851
+ const cookies = [];
852
+ for (const [k, v] of headers) if (k === "set-cookie") cookies.push(v);
853
+ else res[k] = v;
854
+ if (cookies.length > 0) res["set-cookie"] = cookies;
855
+ res["content-type"] ??= "text/plain; charset=UTF-8";
856
+ return res;
857
+ };
858
+ var X_ALREADY_SENT = "x-hono-already-sent";
859
+ var webFetch = global.fetch;
860
+ if (typeof global.crypto === "undefined") global.crypto = crypto.default;
861
+ global.fetch = (info, init) => {
862
+ init = {
863
+ compress: false,
864
+ ...init
865
+ };
866
+ return webFetch(info, init);
867
+ };
868
+ var outgoingEnded = Symbol("outgoingEnded");
869
+ var handleRequestError = () => new Response(null, { status: 400 });
870
+ var handleFetchError = (e) => new Response(null, { status: e instanceof Error && (e.name === "TimeoutError" || e.constructor.name === "TimeoutError") ? 504 : 500 });
871
+ var handleResponseError = (e, outgoing) => {
872
+ const err = e instanceof Error ? e : new Error("unknown error", { cause: e });
873
+ if (err.code === "ERR_STREAM_PREMATURE_CLOSE") console.info("The user aborted a request.");
874
+ else {
875
+ console.error(e);
876
+ if (!outgoing.headersSent) outgoing.writeHead(500, { "Content-Type": "text/plain" });
877
+ outgoing.end(`Error: ${err.message}`);
878
+ outgoing.destroy(err);
879
+ }
880
+ };
881
+ var flushHeaders = (outgoing) => {
882
+ if ("flushHeaders" in outgoing && outgoing.writable) outgoing.flushHeaders();
883
+ };
884
+ var responseViaCache = async (res, outgoing) => {
885
+ let [status, body, header] = res[cacheKey];
886
+ if (header instanceof Headers) header = buildOutgoingHttpHeaders(header);
887
+ if (typeof body === "string") header["Content-Length"] = Buffer.byteLength(body);
888
+ else if (body instanceof Uint8Array) header["Content-Length"] = body.byteLength;
889
+ else if (body instanceof Blob) header["Content-Length"] = body.size;
890
+ outgoing.writeHead(status, header);
891
+ if (typeof body === "string" || body instanceof Uint8Array) outgoing.end(body);
892
+ else if (body instanceof Blob) outgoing.end(new Uint8Array(await body.arrayBuffer()));
893
+ else {
894
+ flushHeaders(outgoing);
895
+ await writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));
896
+ }
897
+ outgoing[outgoingEnded]?.();
898
+ };
899
+ var isPromise = (res) => typeof res.then === "function";
900
+ var responseViaResponseObject = async (res, outgoing, options = {}) => {
901
+ if (isPromise(res)) {
902
+ if (options.errorHandler) try {
903
+ res = await res;
904
+ } catch (err) {
905
+ const errRes = await options.errorHandler(err);
906
+ if (!errRes) return;
907
+ res = errRes;
908
+ }
909
+ else res = await res.catch(handleFetchError);
910
+ }
911
+ if (cacheKey in res) return responseViaCache(res, outgoing);
912
+ const resHeaderRecord = buildOutgoingHttpHeaders(res.headers);
913
+ if (res.body) {
914
+ const reader = res.body.getReader();
915
+ const values = [];
916
+ let done = false;
917
+ let currentReadPromise = void 0;
918
+ if (resHeaderRecord["transfer-encoding"] !== "chunked") {
919
+ let maxReadCount = 2;
920
+ for (let i = 0; i < maxReadCount; i++) {
921
+ currentReadPromise ||= reader.read();
922
+ const chunk = await readWithoutBlocking(currentReadPromise).catch((e) => {
923
+ console.error(e);
924
+ done = true;
925
+ });
926
+ if (!chunk) {
927
+ if (i === 1) {
928
+ await new Promise((resolve) => setTimeout(resolve));
929
+ maxReadCount = 3;
930
+ continue;
931
+ }
932
+ break;
933
+ }
934
+ currentReadPromise = void 0;
935
+ if (chunk.value) values.push(chunk.value);
936
+ if (chunk.done) {
937
+ done = true;
938
+ break;
939
+ }
940
+ }
941
+ if (done && !("content-length" in resHeaderRecord)) resHeaderRecord["content-length"] = values.reduce((acc, value) => acc + value.length, 0);
942
+ }
943
+ outgoing.writeHead(res.status, resHeaderRecord);
944
+ values.forEach((value) => {
945
+ outgoing.write(value);
946
+ });
947
+ if (done) outgoing.end();
948
+ else {
949
+ if (values.length === 0) flushHeaders(outgoing);
950
+ await writeFromReadableStreamDefaultReader(reader, outgoing, currentReadPromise);
951
+ }
952
+ } else if (resHeaderRecord[X_ALREADY_SENT]) {} else {
953
+ outgoing.writeHead(res.status, resHeaderRecord);
954
+ outgoing.end();
955
+ }
956
+ outgoing[outgoingEnded]?.();
957
+ };
958
+ var getRequestListener = (fetchCallback, options = {}) => {
959
+ const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
960
+ if (options.overrideGlobalObjects !== false && global.Request !== Request) {
961
+ Object.defineProperty(global, "Request", { value: Request });
962
+ Object.defineProperty(global, "Response", { value: Response2 });
963
+ }
964
+ return async (incoming, outgoing) => {
965
+ let res, req;
966
+ try {
967
+ req = newRequest(incoming, options.hostname);
968
+ let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
969
+ if (!incomingEnded) {
970
+ incoming[wrapBodyStream] = true;
971
+ incoming.on("end", () => {
972
+ incomingEnded = true;
973
+ });
974
+ if (incoming instanceof http2.Http2ServerRequest) outgoing[outgoingEnded] = () => {
975
+ if (!incomingEnded) setTimeout(() => {
976
+ if (!incomingEnded) setTimeout(() => {
977
+ incoming.destroy();
978
+ outgoing.destroy();
979
+ });
980
+ });
981
+ };
982
+ }
983
+ outgoing.on("close", () => {
984
+ if (req[abortControllerKey]) {
985
+ if (incoming.errored) req[abortControllerKey].abort(incoming.errored.toString());
986
+ else if (!outgoing.writableFinished) req[abortControllerKey].abort("Client connection prematurely closed.");
987
+ }
988
+ if (!incomingEnded) setTimeout(() => {
989
+ if (!incomingEnded) setTimeout(() => {
990
+ incoming.destroy();
991
+ });
992
+ });
993
+ });
994
+ res = fetchCallback(req, {
995
+ incoming,
996
+ outgoing
997
+ });
998
+ if (cacheKey in res) return responseViaCache(res, outgoing);
999
+ } catch (e) {
1000
+ if (!res) {
1001
+ if (options.errorHandler) {
1002
+ res = await options.errorHandler(req ? e : toRequestError(e));
1003
+ if (!res) return;
1004
+ } else if (!req) res = handleRequestError();
1005
+ else res = handleFetchError(e);
1006
+ } else return handleResponseError(e, outgoing);
1007
+ }
1008
+ try {
1009
+ return await responseViaResponseObject(res, outgoing, options);
1010
+ } catch (e) {
1011
+ return handleResponseError(e, outgoing);
1012
+ }
1013
+ };
1014
+ };
1015
+ var createAdaptorServer = (options) => {
1016
+ const fetchCallback = options.fetch;
1017
+ const requestListener = getRequestListener(fetchCallback, {
1018
+ hostname: options.hostname,
1019
+ overrideGlobalObjects: options.overrideGlobalObjects,
1020
+ autoCleanupIncoming: options.autoCleanupIncoming
1021
+ });
1022
+ return (options.createServer || http.createServer)(options.serverOptions || {}, requestListener);
1023
+ };
1024
+ var serve = (options, listeningListener) => {
1025
+ const server = createAdaptorServer(options);
1026
+ server.listen(options?.port ?? 3e3, options.hostname, () => {
1027
+ const serverInfo = server.address();
1028
+ listeningListener && listeningListener(serverInfo);
1029
+ });
1030
+ return server;
1031
+ };
1032
+ //#endregion
1033
+ //#region src/dev/fakes.ts
1034
+ /**
1035
+ * External APIs' app fakes, served for `gkm dev`.
1036
+ *
1037
+ * An image fake runs with the other containers. An app fake is a fetch handler
1038
+ * in `test/fakes/<id>.ts`, and nothing runs it unless this does: each one
1039
+ * listens on the port its key was allocated, which is the port the stage's
1040
+ * `<ID>_URL` already names.
1041
+ */
1042
+ /**
1043
+ * Serve every app fake on its allocated port. Image fakes are skipped — their
1044
+ * containers are already up.
1045
+ */
1046
+ async function serveFakes(fakes, ports) {
1047
+ const served = [];
1048
+ for (const [id, local] of Object.entries(fakes)) {
1049
+ if (local.image) continue;
1050
+ const port = ports[require_workspace$1.fakeKey(id)];
1051
+ if (port === void 0) continue;
1052
+ const { handler } = (await import((0, node_url.pathToFileURL)(local.file).href)).default;
1053
+ const server = serve({
1054
+ fetch: (request) => handler.fetch(request),
1055
+ port
1056
+ });
1057
+ served.push({
1058
+ id,
1059
+ port,
1060
+ server
1061
+ });
1062
+ }
1063
+ return served;
1064
+ }
1065
+ /** Stop every served fake. */
1066
+ function closeFakes(served) {
1067
+ for (const { server } of served) server.close();
1068
+ }
1069
+ //#endregion
586
1070
  //#region src/exec/index.ts
587
1071
  const logger$11 = console;
588
1072
  /** `gkm exec` was given nothing to run. */
@@ -734,41 +1218,28 @@ async function devCommand(options) {
734
1218
  if (options.entry) return entryDevCommand(options);
735
1219
  const defaultEnv = require_workspace$1.loadEnvFiles(".env");
736
1220
  if (defaultEnv.loaded.length > 0) logger$10.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
737
- const appName = require_config.getAppNameFromCwd();
738
- let config;
739
- let appRoot = process.cwd();
740
- let secretsRoot = process.cwd();
741
- let workspaceAppName;
742
- let workspaceAppPort;
743
- let workspace;
744
- if (appName) try {
745
- const appConfig = await require_config.loadAppConfig();
746
- config = appConfig.gkmConfig;
747
- appRoot = appConfig.appRoot;
748
- secretsRoot = appConfig.workspaceRoot;
749
- workspaceAppName = appConfig.appName;
750
- workspaceAppPort = appConfig.app.port;
751
- workspace = appConfig.workspace;
752
- if (appConfig.app.entry) {
753
- logger$10.log(`📄 Using entry point: ${appConfig.app.entry}`);
754
- return entryDevCommand({
755
- ...options,
756
- entry: appConfig.app.entry,
757
- port: workspaceAppPort,
758
- portExplicit: true
759
- });
760
- }
761
- } catch {
762
- const loadedConfig = await require_config.loadWorkspaceConfig();
763
- if (loadedConfig.type === "workspace") return workspaceDevCommand(loadedConfig.workspace, options);
764
- config = loadedConfig.raw;
765
- workspace = loadedConfig.workspace;
766
- }
767
- else {
768
- const loadedConfig = await require_config.loadWorkspaceConfig();
769
- if (loadedConfig.type === "workspace") return workspaceDevCommand(loadedConfig.workspace, options);
770
- config = loadedConfig.raw;
771
- workspace = loadedConfig.workspace;
1221
+ const appConfig = await require_config.loadAppConfig().catch((error) => {
1222
+ if (error instanceof require_config.NotInAnApp || error instanceof require_config.NotABackendApp) return;
1223
+ throw error;
1224
+ });
1225
+ if (!appConfig) {
1226
+ const { workspace: everything } = await require_config.loadWorkspaceConfig();
1227
+ return workspaceDevCommand(everything, options);
1228
+ }
1229
+ const config = appConfig.gkmConfig;
1230
+ const appRoot = appConfig.appRoot;
1231
+ const secretsRoot = appConfig.workspaceRoot;
1232
+ const workspaceAppName = appConfig.appName;
1233
+ const workspaceAppPort = appConfig.app.port;
1234
+ const workspace = appConfig.workspace;
1235
+ if (appConfig.app.entry) {
1236
+ logger$10.log(`📄 Using entry point: ${appConfig.app.entry}`);
1237
+ return entryDevCommand({
1238
+ ...options,
1239
+ entry: appConfig.app.entry,
1240
+ port: workspaceAppPort,
1241
+ portExplicit: true
1242
+ });
772
1243
  }
773
1244
  if (config.env) {
774
1245
  const { loaded, missing } = require_workspace$1.loadEnvFiles(config.env, appRoot);
@@ -798,9 +1269,12 @@ async function devCommand(options) {
798
1269
  const runtime = config.runtime ?? "node";
799
1270
  let secretsJsonPath;
800
1271
  let publicUrl;
801
- const appSecrets = await require_workspace$1.loadSecretsForApp(secretsRoot, config.stages.local, workspaceAppName);
1272
+ const appSecrets = await require_workspace$1.loadSecretsForApp(workspace ? await require_store.secretsStoreFor(workspace, config.stages.local) : new require_store.FileSecretsStore(secretsRoot), config.stages.local, workspaceAppName);
802
1273
  if (workspace) {
803
- const reconciled = await require_workspace$1.reconcileWorkspace(workspace, { stage: workspace.stages.local });
1274
+ const reconciled = await require_workspace$1.reconcileWorkspace(workspace, {
1275
+ stage: workspace.stages.local,
1276
+ ...options.fake ? { fake: true } : {}
1277
+ });
804
1278
  if (reconciled.changed && reconciled.plan.containers.length > 0) {
805
1279
  logger$10.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
806
1280
  for (const [container, address] of Object.entries(reconciled.addresses)) logger$10.log(` ${container}: ${address}`);
@@ -821,7 +1295,7 @@ async function devCommand(options) {
821
1295
  devServer.publicUrl = publicUrl;
822
1296
  await devServer.start();
823
1297
  const hooksFile = (config.hooks?.server?.split("#"))?.[0];
824
- 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);
1298
+ const normalizedPatterns = [config.constructs, ...hooksFile ? [hooksFile.endsWith(".ts") ? hooksFile : `${hooksFile}.ts`] : []].flat().filter((p) => typeof p === "string").map((p) => (0, node_path.isAbsolute)(p) ? (0, node_path.relative)(appRoot, p) : p.replace(/^\.\//, ""));
825
1299
  const resolvedFiles = await (0, fast_glob.default)(normalizedPatterns, {
826
1300
  cwd: appRoot,
827
1301
  absolute: false,
@@ -1045,10 +1519,8 @@ async function validateFrontendApps(workspace) {
1045
1519
  async function loadDevSecrets(workspace) {
1046
1520
  if (!workspace.secrets.enabled) return {};
1047
1521
  const stage = workspace.stages.local;
1048
- if (require_storage.secretsExist(stage, workspace.root)) {
1049
- const secrets = await require_storage.readStageSecrets(stage, workspace.root);
1050
- if (secrets) return require_storage.toEmbeddableSecrets(secrets);
1051
- }
1522
+ const secrets = await (await require_store.secretsStoreFor(workspace, stage)).read(stage);
1523
+ if (secrets) return require_storage.toEmbeddableSecrets(secrets);
1052
1524
  return {};
1053
1525
  }
1054
1526
  /**
@@ -1082,7 +1554,10 @@ async function workspaceDevCommand(workspace, options) {
1082
1554
  if (hasErrors) throw new Error("Frontend app validation failed. Fix the issues above and try again.");
1083
1555
  }
1084
1556
  const rawSecrets = await loadDevSecrets(workspace);
1085
- const reconciled = await require_workspace$1.reconcileWorkspace(workspace, { stage: workspace.stages.local });
1557
+ const reconciled = await require_workspace$1.reconcileWorkspace(workspace, {
1558
+ stage: workspace.stages.local,
1559
+ ...options.fake ? { fake: true } : {}
1560
+ });
1086
1561
  if (reconciled.changed && reconciled.plan.containers.length > 0) {
1087
1562
  logger$10.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
1088
1563
  for (const [container, address] of Object.entries(reconciled.addresses)) logger$10.log(` ${container}: ${address}`);
@@ -1091,10 +1566,12 @@ async function workspaceDevCommand(workspace, options) {
1091
1566
  ...rawSecrets,
1092
1567
  ...reconciled.env
1093
1568
  };
1569
+ const fakes = options.fake ? await serveFakes(reconciled.fakes, reconciled.ports) : [];
1570
+ for (const { id, port } of fakes) logger$10.log(`🎭 ${id} fake: http://localhost:${port}`);
1094
1571
  if (options.migrate || options.seed) {
1095
- const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-Cgga-OGi.cjs"));
1572
+ const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-Bu8POqsR.cjs"));
1096
1573
  await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
1097
- } else await Promise.resolve().then(() => require("./migrate-Cgga-OGi.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
1574
+ } else await Promise.resolve().then(() => require("./migrate-Bu8POqsR.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
1098
1575
  logger$10.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
1099
1576
  });
1100
1577
  const appUrls = {};
@@ -1153,7 +1630,8 @@ async function workspaceDevCommand(workspace, options) {
1153
1630
  ...process.env,
1154
1631
  ...secretsEnv,
1155
1632
  NODE_ENV: "development",
1156
- ...configPath ? { GKM_CONFIG_PATH: configPath } : {}
1633
+ ...configPath ? { GKM_CONFIG_PATH: configPath } : {},
1634
+ ...options.fake ? { [require_workspace$1.FAKE_ENV]: "1" } : {}
1157
1635
  };
1158
1636
  const scoped = options.app ? [options.app] : options.filter ? [] : Object.keys(workspace.apps);
1159
1637
  const held = [];
@@ -1182,6 +1660,7 @@ async function workspaceDevCommand(workspace, options) {
1182
1660
  if (isShuttingDown) return;
1183
1661
  isShuttingDown = true;
1184
1662
  logger$10.log("\n🛑 Shutting down workspace...");
1663
+ closeFakes(fakes);
1185
1664
  const pid = turboProcess.pid;
1186
1665
  if (pid) try {
1187
1666
  process.kill(-pid, "SIGTERM");
@@ -2249,10 +2728,10 @@ export async function setupSubscribers(
2249
2728
  //#endregion
2250
2729
  //#region src/generators/TopicGenerator.ts
2251
2730
  /**
2252
- * Discovers `t` topics into `manifest.topics`. A topic is a *resource*, not a
2253
- * function — it has no handler to generate. Producers reach it via its derived
2254
- * publisher (`topic.publisher`, whose connection string is sniffed onto the
2255
- * producing construct) and subscribers bind via `s.topic(topic)`; this generator
2731
+ * Discovers topics (`new Topic(…)`) into `manifest.topics`. A topic is a
2732
+ * *resource*, not a function — it has no handler to generate. Producers name it
2733
+ * with `.event(topic, …)` or `.dependsOn([topic])`, and subscribers bind with
2734
+ * `worker.topic(topic)`; this generator
2256
2735
  * only records the topic + its event contract so infra can provision the SNS
2257
2736
  * topic. Identical output for every provider (locally pg-boss routes by event
2258
2737
  * name, so there's nothing to run).
@@ -2814,7 +3293,7 @@ async function buildForProvider(provider, context, appRoot, discovered, endpoint
2814
3293
  let masterKey;
2815
3294
  if (context.production?.bundle && !skipBundle) {
2816
3295
  logger$8.log(`\n📦 Bundling production server...`);
2817
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-CYF5mWi9.cjs"));
3296
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-By6_sXiL.cjs"));
2818
3297
  const allConstructs = [
2819
3298
  ...endpoints.map((e) => e.construct),
2820
3299
  ...functions.map((f) => f.construct),
@@ -3298,17 +3777,43 @@ const PROVISIONERS = {
3298
3777
  return broker(declaration.id, context);
3299
3778
  },
3300
3779
  /**
3301
- * A signing key, generated once and remembered.
3780
+ * A signing key, generated once and remembered in the stage's store.
3302
3781
  *
3303
- * Regenerating it on every deploy would invalidate every live session, which
3304
- * is why the value comes from state when there is one. Nothing is created in
3305
- * Dokploy: a secret has no address.
3782
+ * Random, never derived: a key computed from the project and stage names
3783
+ * signs sessions anyone reading the repo could forge. The deploy generates
3784
+ * it before provisioning (`withGeneratedSecrets`) and every later deploy
3785
+ * reads the same one, so live sessions survive a redeploy. Nothing is
3786
+ * created in Dokploy: a secret has no address.
3306
3787
  */
3307
3788
  secret: async (declaration, context) => {
3308
3789
  if (declaration.kind !== "secret") throw new WrongKind(declaration.kind);
3309
3790
  const key = declaration.provides?.[0];
3310
3791
  if (!key) return { provides: {} };
3311
- return { provides: { [key]: context.secrets?.[key] ?? derivedPassword(context, `secret:${declaration.id}`) } };
3792
+ return { provides: { [key]: supplied(declaration.id, key, context) } };
3793
+ },
3794
+ /**
3795
+ * A third party's credentials, as the stage was given them.
3796
+ *
3797
+ * Nothing is created and nothing derived: the value was issued by somebody
3798
+ * else. A stage that was never given one fails the deploy here, naming the
3799
+ * command that sets it — not the first request that needs it.
3800
+ */
3801
+ credential: async (declaration, context) => {
3802
+ if (declaration.kind !== "credential") throw new WrongKind(declaration.kind);
3803
+ const key = (0, _geekmidas_manifest.provideKey)(declaration.id, "credentials");
3804
+ return { provides: { [key]: supplied(declaration.id, key, context) } };
3805
+ },
3806
+ /**
3807
+ * An API somebody else runs: the URL this stage calls it at, and the
3808
+ * credentials it was issued for this stage.
3809
+ */
3810
+ "external-api": async (declaration, context) => {
3811
+ if (declaration.kind !== "external-api") throw new WrongKind(declaration.kind);
3812
+ const credentials = (0, _geekmidas_manifest.provideKey)(declaration.id, "credentials");
3813
+ return { provides: {
3814
+ [(0, _geekmidas_manifest.provideKey)(declaration.id, "url")]: (0, _geekmidas_manifest.externalApiUrl)(declaration, context.stage),
3815
+ [credentials]: supplied(declaration.id, credentials, context)
3816
+ } };
3312
3817
  },
3313
3818
  /**
3314
3819
  * A surface, at the address whatever created its domain assigned.
@@ -3495,14 +4000,16 @@ function soleDatabase(context) {
3495
4000
  return databases[0];
3496
4001
  }
3497
4002
  /**
3498
- * A password derived from the project, the stage and the role.
4003
+ * A password derived from the stage's seed, the project, the stage and the role.
3499
4004
  *
3500
- * Derived rather than random for the reason the local target derives its own: a
3501
- * redeploy must not lock the running application out of its own database, and
3502
- * two projects must not share a credential.
4005
+ * Derived rather than stored per role so a redeploy hands the running
4006
+ * application the password it already has, and two projects never share one.
4007
+ * The seed is what makes it secret: random, generated once and kept in the
4008
+ * stage's store. Without it the inputs are all in the repo, and so was every
4009
+ * production password.
3503
4010
  */
3504
4011
  function derivedPassword(context, role) {
3505
- return (0, node_crypto.createHash)("sha256").update(`${context.project}:${context.stage}:role:${role}`).digest("base64url").slice(0, 32);
4012
+ return (0, node_crypto.createHash)("sha256").update(`${context.seed}:${context.project}:${context.stage}:role:${role}`).digest("base64url").slice(0, 32);
3506
4013
  }
3507
4014
  /** A connection string for a role on a cluster. */
3508
4015
  function postgresUrl(role, password, host, database) {
@@ -3513,6 +4020,29 @@ function quoted(name) {
3513
4020
  return name.split(".").map((part) => `"${part.replace(/"/g, "\"\"")}"`).join(".");
3514
4021
  }
3515
4022
  /** A provisioner was handed a declaration of the wrong kind. */
4023
+ /**
4024
+ * A value only the stage's own secrets can hold.
4025
+ *
4026
+ * @throws {MissingSuppliedSecret} when the stage was never given it.
4027
+ */
4028
+ function supplied(id, key, context) {
4029
+ const value = context.supplied?.[key];
4030
+ if (value === void 0) throw new MissingSuppliedSecret(id, key, context.stage);
4031
+ return value;
4032
+ }
4033
+ /** A construct needs a value the stage was never given. */
4034
+ var MissingSuppliedSecret = class extends Error {
4035
+ id;
4036
+ key;
4037
+ stage;
4038
+ constructor(id, key, stage) {
4039
+ 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}`);
4040
+ this.id = id;
4041
+ this.key = key;
4042
+ this.stage = stage;
4043
+ this.name = "MissingSuppliedSecret";
4044
+ }
4045
+ };
3516
4046
  var WrongKind = class extends Error {
3517
4047
  kind;
3518
4048
  constructor(kind) {
@@ -3635,7 +4165,8 @@ async function provisionDeclared(options) {
3635
4165
  events: require_workspace$1.eventsBackendFor(require_workspace$1.providerOf(workspace)),
3636
4166
  storage: require_workspace$1.storageBackendFor(require_workspace$1.providerOf(workspace)),
3637
4167
  addresses: surfaceAddresses(workspace, manifest, options.appUrls),
3638
- ...options.secrets ? { secrets: options.secrets } : {},
4168
+ seed: options.seed,
4169
+ ...options.supplied ? { supplied: options.supplied } : {},
3639
4170
  deferred: [],
3640
4171
  clusters: {}
3641
4172
  };
@@ -4665,14 +5196,9 @@ exec "$@"
4665
5196
  */
4666
5197
  function resolveDockerConfig(config) {
4667
5198
  const docker = config.docker ?? {};
4668
- let defaultImageName = "api";
4669
- try {
4670
- const pkg = require(`${process.cwd()}/package.json`);
4671
- if (pkg.name) defaultImageName = pkg.name.replace(/^@[^/]+\//, "");
4672
- } catch {}
4673
5199
  return {
4674
5200
  registry: docker.registry ?? "",
4675
- imageName: docker.imageName ?? defaultImageName,
5201
+ imageName: docker.imageName ?? config.name ?? "api",
4676
5202
  baseImage: docker.baseImage ?? "node:22-alpine",
4677
5203
  port: docker.port ?? 3e3
4678
5204
  };
@@ -6265,6 +6791,55 @@ function validateEnvVars(requiredVars, context) {
6265
6791
  };
6266
6792
  }
6267
6793
  //#endregion
6794
+ //#region src/deploy/generated.ts
6795
+ /**
6796
+ * The values a deployed stage generates once and keeps: what nothing can
6797
+ * provision and nobody else issues.
6798
+ *
6799
+ * - **A seed**, which salts every password a deploy derives — a database's
6800
+ * master and roles, a bucket's root user. Derived from repo facts alone
6801
+ * (project, stage, construct id), those passwords were computable by anyone
6802
+ * who could read the repo.
6803
+ * - **Each `secret` construct's value** — an auth server's signing secret —
6804
+ * random, rather than derived.
6805
+ *
6806
+ * Both are generated on the first deploy that needs them and written to the
6807
+ * stage's store, so every later deploy reads the same values: sessions survive
6808
+ * a redeploy, and a role's password does not move. A value set by hand with
6809
+ * `gkm secrets:set` is kept, never replaced.
6810
+ */
6811
+ /**
6812
+ * The stage's secrets with its seed and every declared `secret`'s value
6813
+ * present, generating what is missing. Pure: the caller writes the result
6814
+ * back to the stage's store when `generated` is not empty.
6815
+ */
6816
+ function withGeneratedSecrets(secrets, manifest) {
6817
+ const generated = [];
6818
+ const custom = { ...secrets.custom };
6819
+ for (const declaration of Object.values(manifest)) {
6820
+ if (declaration?.kind !== "secret") continue;
6821
+ const key = declaration.provides?.[0];
6822
+ if (!key || custom[key]) continue;
6823
+ custom[key] = random();
6824
+ generated.push(key);
6825
+ }
6826
+ const seed = secrets.seed ?? random();
6827
+ if (!secrets.seed) generated.push("seed");
6828
+ return {
6829
+ secrets: generated.length ? {
6830
+ ...secrets,
6831
+ seed,
6832
+ custom,
6833
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
6834
+ } : secrets,
6835
+ generated
6836
+ };
6837
+ }
6838
+ /** 256 bits, URL-safe. */
6839
+ function random() {
6840
+ return (0, node_crypto.randomBytes)(32).toString("base64url");
6841
+ }
6842
+ //#endregion
6268
6843
  //#region src/deploy/StateProvider.ts
6269
6844
  /**
6270
6845
  * Check if value is a StateProvider implementation.
@@ -7083,10 +7658,13 @@ async function workspaceDeployCommand(configured, options) {
7083
7658
  if (dokployApps.length === 0) throw new Error("No apps to deploy. All selected apps have unsupported deploy targets.");
7084
7659
  appsToDeployNames = dokployApps;
7085
7660
  logger$4.log("\n🔐 Loading secrets and analyzing environment requirements...");
7086
- const stageSecrets = await require_storage.readStageSecrets(stage, workspace.root);
7087
- if (!stageSecrets) {
7088
- logger$4.log(` ⚠️ No secrets found for stage "${stage}"`);
7089
- logger$4.log(` Run "gkm secrets:init --stage ${stage}" to create secrets`);
7661
+ const secretsStore = await require_store.secretsStoreFor(workspace, stage);
7662
+ const stored = await secretsStore.read(stage);
7663
+ if (!stored) logger$4.log(` ⚠️ No secrets found for stage "${stage}"; starting them`);
7664
+ const { secrets: stageSecrets, generated } = withGeneratedSecrets(stored ?? require_storage.initStageSecrets(stage), manifest);
7665
+ if (generated.length > 0) {
7666
+ await secretsStore.write(stage, stageSecrets);
7667
+ logger$4.log(` 🔑 Generated for "${stage}" (${secretsStore.name}): ${generated.join(", ")}`);
7090
7668
  }
7091
7669
  const sniffedApps = await sniffAllApps(workspace.apps, workspace.root);
7092
7670
  const encryptedSecrets = stageSecrets ? prepareSecretsForAllApps(stageSecrets, sniffedApps) : /* @__PURE__ */ new Map();
@@ -7214,6 +7792,8 @@ async function workspaceDeployCommand(configured, options) {
7214
7792
  environmentId,
7215
7793
  stage,
7216
7794
  appUrls,
7795
+ supplied: stageSecrets.custom,
7796
+ seed: stageSecrets.seed,
7217
7797
  manifest
7218
7798
  });
7219
7799
  declaredEnv = declared.env;
@@ -7974,6 +8554,172 @@ function printStateDetails(state) {
7974
8554
  }
7975
8555
  }
7976
8556
  //#endregion
8557
+ //#region src/secrets/generator.ts
8558
+ /**
8559
+ * Generate a secure random password using URL-safe base64 characters.
8560
+ * @param length Password length (default: 32)
8561
+ */
8562
+ function generateSecurePassword(length = 32) {
8563
+ return (0, node_crypto.randomBytes)(Math.ceil(length * 3 / 4)).toString("base64url").slice(0, length);
8564
+ }
8565
+ /** Default service configurations (localhost for local dev via Docker port mapping) */
8566
+ const SERVICE_DEFAULTS = {
8567
+ postgres: {
8568
+ host: "localhost",
8569
+ port: 5432,
8570
+ username: "app",
8571
+ database: "app"
8572
+ },
8573
+ redis: {
8574
+ host: "localhost",
8575
+ port: 6379,
8576
+ username: "default"
8577
+ },
8578
+ minio: {
8579
+ host: "localhost",
8580
+ port: 9e3,
8581
+ username: "app",
8582
+ bucket: "app"
8583
+ },
8584
+ mailpit: {
8585
+ host: "localhost",
8586
+ port: 1025,
8587
+ username: "app"
8588
+ },
8589
+ localstack: {
8590
+ host: "localhost",
8591
+ port: 4566,
8592
+ username: "localstack",
8593
+ region: "us-east-1"
8594
+ }
8595
+ };
8596
+ /** Default credentials for pgboss (not a Docker service, reuses postgres) */
8597
+ const PGBOSS_DEFAULTS = {
8598
+ host: "localhost",
8599
+ port: 5432,
8600
+ username: "pgboss",
8601
+ database: "app"
8602
+ };
8603
+ /**
8604
+ * Generate credentials for a specific service.
8605
+ */
8606
+ function generateServiceCredentials(service) {
8607
+ return {
8608
+ ...SERVICE_DEFAULTS[service],
8609
+ password: generateSecurePassword()
8610
+ };
8611
+ }
8612
+ /**
8613
+ * Generate credentials for multiple services.
8614
+ */
8615
+ function generateServicesCredentials(services) {
8616
+ const result = {};
8617
+ for (const service of services) result[service] = generateServiceCredentials(service);
8618
+ return result;
8619
+ }
8620
+ /**
8621
+ * Generate connection URL for PostgreSQL.
8622
+ */
8623
+ function generatePostgresUrl(creds) {
8624
+ const { username, password, host, port, database } = creds;
8625
+ return `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
8626
+ }
8627
+ /**
8628
+ * Generate connection URL for Redis.
8629
+ */
8630
+ function generateRedisUrl(creds) {
8631
+ const { password, host, port } = creds;
8632
+ return `redis://:${encodeURIComponent(password)}@${host}:${port}`;
8633
+ }
8634
+ /**
8635
+ * Generate endpoint URL for MinIO (S3-compatible).
8636
+ */
8637
+ function generateMinioEndpoint(creds) {
8638
+ const { host, port } = creds;
8639
+ return `http://${host}:${port}`;
8640
+ }
8641
+ /**
8642
+ * Generate connection URL for pg-boss (uses PostgreSQL protocol).
8643
+ * Format: pgboss://user:pass@host:port/db?schema=pgboss
8644
+ */
8645
+ function generatePgBossUrl(creds) {
8646
+ const { username, password, host, port, database } = creds;
8647
+ return `pgboss://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}?schema=pgboss`;
8648
+ }
8649
+ /**
8650
+ * Generate connection URLs from service credentials.
8651
+ */
8652
+ function generateConnectionUrls(services) {
8653
+ const urls = {};
8654
+ if (services.postgres) urls.DATABASE_URL = generatePostgresUrl(services.postgres);
8655
+ if (services.redis) urls.REDIS_URL = generateRedisUrl(services.redis);
8656
+ if (services.minio) urls.STORAGE_ENDPOINT = generateMinioEndpoint(services.minio);
8657
+ if (services.pgboss) {
8658
+ const pgbossUrl = generatePgBossUrl(services.pgboss);
8659
+ urls.EVENT_PUBLISHER_CONNECTION_STRING = pgbossUrl;
8660
+ urls.EVENT_SUBSCRIBER_CONNECTION_STRING = pgbossUrl;
8661
+ }
8662
+ if (services.mailpit) {
8663
+ urls.SMTP_HOST = services.mailpit.host;
8664
+ urls.SMTP_PORT = String(services.mailpit.port);
8665
+ }
8666
+ return urls;
8667
+ }
8668
+ /**
8669
+ * Create a new StageSecrets object with generated credentials.
8670
+ * @param stage - The deployment stage (e.g., 'development', 'production')
8671
+ * @param services - List of services to generate credentials for
8672
+ * @param options - Optional configuration
8673
+ * @param options.projectName - Project name used to derive the database name (e.g., 'myapp' → 'myapp_dev')
8674
+ */
8675
+ function createStageSecrets(stage, services, options) {
8676
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8677
+ const serviceCredentials = generateServicesCredentials(services);
8678
+ if (options?.projectName) {
8679
+ if (serviceCredentials.postgres) serviceCredentials.postgres.database = `${options.projectName.replace(/-/g, "_")}_dev`;
8680
+ if (serviceCredentials.minio) {
8681
+ serviceCredentials.minio.bucket = options.projectName;
8682
+ serviceCredentials.minio.username = options.projectName;
8683
+ }
8684
+ }
8685
+ if (serviceCredentials.postgres && !serviceCredentials.pgboss) serviceCredentials.pgboss = {
8686
+ ...PGBOSS_DEFAULTS,
8687
+ password: generateSecurePassword(),
8688
+ host: serviceCredentials.postgres.host,
8689
+ port: serviceCredentials.postgres.port,
8690
+ database: serviceCredentials.postgres.database
8691
+ };
8692
+ return {
8693
+ stage,
8694
+ createdAt: now,
8695
+ updatedAt: now,
8696
+ services: serviceCredentials,
8697
+ urls: generateConnectionUrls(serviceCredentials),
8698
+ custom: {}
8699
+ };
8700
+ }
8701
+ /**
8702
+ * Rotate password for a specific service.
8703
+ */
8704
+ function rotateServicePassword(secrets, service) {
8705
+ const currentCreds = secrets.services[service];
8706
+ if (!currentCreds) throw new Error(`Service "${service}" not configured in secrets`);
8707
+ const newCreds = {
8708
+ ...currentCreds,
8709
+ password: generateSecurePassword()
8710
+ };
8711
+ const newServices = {
8712
+ ...secrets.services,
8713
+ [service]: newCreds
8714
+ };
8715
+ return {
8716
+ ...secrets,
8717
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
8718
+ services: newServices,
8719
+ urls: generateConnectionUrls(newServices)
8720
+ };
8721
+ }
8722
+ //#endregion
7977
8723
  //#region src/migrate/names.ts
7978
8724
  /**
7979
8725
  * What a migration file is called: a UTC timestamp, then a name.
@@ -8809,26 +9555,26 @@ function loadPackageJson() {
8809
9555
  * Run: pnpm --filter @geekmidas/cli sync-versions
8810
9556
  */
8811
9557
  const GEEKMIDAS_VERSIONS = {
8812
- "@geekmidas/audit": "~10.0.0-alpha.37",
8813
- "@geekmidas/auth": "~10.0.0-alpha.37",
8814
- "@geekmidas/cache": "~10.0.0-alpha.37",
8815
- "@geekmidas/client": "~10.0.0-alpha.37",
8816
- "@geekmidas/cloud": "~10.0.0-alpha.37",
8817
- "@geekmidas/constructs": "~10.0.0-alpha.37",
8818
- "@geekmidas/db": "~10.0.0-alpha.37",
8819
- "@geekmidas/emailkit": "~10.0.0-alpha.37",
8820
- "@geekmidas/envkit": "~10.0.0-alpha.37",
8821
- "@geekmidas/errors": "~10.0.0-alpha.37",
8822
- "@geekmidas/events": "~10.0.0-alpha.37",
8823
- "@geekmidas/logger": "~10.0.0-alpha.37",
8824
- "@geekmidas/manifest": "~10.0.0-alpha.37",
8825
- "@geekmidas/rate-limit": "~10.0.0-alpha.37",
8826
- "@geekmidas/schema": "~10.0.0-alpha.37",
8827
- "@geekmidas/services": "~10.0.0-alpha.37",
8828
- "@geekmidas/storage": "~10.0.0-alpha.37",
8829
- "@geekmidas/studio": "~10.0.0-alpha.37",
8830
- "@geekmidas/telescope": "~10.0.0-alpha.37",
8831
- "@geekmidas/testkit": "~10.0.0-alpha.37",
9558
+ "@geekmidas/audit": "~10.0.0-alpha.39",
9559
+ "@geekmidas/auth": "~10.0.0-alpha.39",
9560
+ "@geekmidas/cache": "~10.0.0-alpha.39",
9561
+ "@geekmidas/client": "~10.0.0-alpha.39",
9562
+ "@geekmidas/cloud": "~10.0.0-alpha.39",
9563
+ "@geekmidas/constructs": "~10.0.0-alpha.39",
9564
+ "@geekmidas/db": "~10.0.0-alpha.39",
9565
+ "@geekmidas/emailkit": "~10.0.0-alpha.39",
9566
+ "@geekmidas/envkit": "~10.0.0-alpha.39",
9567
+ "@geekmidas/errors": "~10.0.0-alpha.39",
9568
+ "@geekmidas/events": "~10.0.0-alpha.39",
9569
+ "@geekmidas/logger": "~10.0.0-alpha.39",
9570
+ "@geekmidas/manifest": "~10.0.0-alpha.39",
9571
+ "@geekmidas/rate-limit": "~10.0.0-alpha.39",
9572
+ "@geekmidas/schema": "~10.0.0-alpha.39",
9573
+ "@geekmidas/services": "~10.0.0-alpha.39",
9574
+ "@geekmidas/storage": "~10.0.0-alpha.39",
9575
+ "@geekmidas/studio": "~10.0.0-alpha.39",
9576
+ "@geekmidas/telescope": "~10.0.0-alpha.39",
9577
+ "@geekmidas/testkit": "~10.0.0-alpha.39",
8832
9578
  "@geekmidas/cli": `~${loadPackageJson().version}`
8833
9579
  };
8834
9580
  //#endregion
@@ -9400,16 +10146,7 @@ function deploy(options) {
9400
10146
  role-to-assume: \${{ vars.AWS_ROLE_ARN }}
9401
10147
  aws-region: ${options.region}
9402
10148
  ` : "";
9403
- const stageSecrets = sst ? `${credentials}
9404
- # The stage's secrets, from SSM in the account the role belongs to —
9405
- # pushed there once with \`gkm secrets:push --stage <stage> --profile …\`.
9406
- # The stage reaches the shell as a variable, never pasted into the script:
9407
- # on a manual run it is whatever was typed.
9408
- - name: Stage secrets
9409
- run: ${pm.exec} gkm secrets:pull --stage "$STAGE"
9410
- env:
9411
- STAGE: \${{ matrix.stage }}
9412
- ` : `
10149
+ const stageSecrets = sst ? credentials : `
9413
10150
  # The stage's secrets are kept in the encrypted .gkm/secrets/<stage>.json,
9414
10151
  # and each environment holds its key as GKM_SECRETS_KEY. .gkm/ is
9415
10152
  # gitignored, so a checkout has the key but not the file: set
@@ -10835,7 +11572,7 @@ function secretsStore(options) {
10835
11572
  if (options.deployTarget !== "sst" || !options.region) return "";
10836
11573
  return `
10837
11574
  // Deployed stages keep their secrets in SSM, in the account each stage
10838
- // deploys to: \`gkm secrets:push --stage <stage> --profile <profile>\`.
11575
+ // deploys to: \`gkm secrets:set <KEY> '…' --stage <stage>\` writes there.
10839
11576
  store: { provider: 'ssm', region: '${options.region}' },`;
10840
11577
  }
10841
11578
  /**
@@ -11820,22 +12557,24 @@ export const config = envParser
11820
12557
  },
11821
12558
  {
11822
12559
  path: "src/constructs/topics.ts",
11823
- content: `import { t } from '@geekmidas/constructs/topic';
12560
+ content: `import { Topic } from '@geekmidas/constructs/topic';
11824
12561
  import { z } from 'zod';
11825
12562
 
11826
12563
  /**
11827
12564
  * The \`users\` topic: pub/sub fan-out, any number of subscribers.
11828
12565
  *
11829
- * The event map is the contract. It types \`users.publisher\` and every
11830
- * subscriber that binds with \`.topic(users)\`, so an event cannot be published
11831
- * in one shape and read in another.
12566
+ * The event map is the contract. It types every \`.event(users, …)\` that
12567
+ * publishes to it and every subscriber that binds with \`worker.topic(users)\`,
12568
+ * so an event cannot be published in one shape and read in another.
11832
12569
  */
11833
- export const users = t.topic('users').events({
11834
- 'user.created': z.object({ userId: z.string(), email: z.email() }),
11835
- 'user.updated': z.object({
11836
- userId: z.string(),
11837
- changes: z.record(z.string(), z.unknown()),
11838
- }),
12570
+ export const users = new Topic('users', {
12571
+ events: {
12572
+ 'user.created': z.object({ userId: z.string(), email: z.email() }),
12573
+ 'user.updated': z.object({
12574
+ userId: z.string(),
12575
+ changes: z.record(z.string(), z.unknown()),
12576
+ }),
12577
+ },
11839
12578
  });
11840
12579
  `
11841
12580
  },
@@ -14729,7 +15468,7 @@ async function initCommand(projectName, options = {}) {
14729
15468
  }
14730
15469
  console.log("🔐 Initializing encrypted secrets...\n");
14731
15470
  const local = templateOptions.stages.local;
14732
- const devSecrets = require_reconcile.createStageSecrets(local, [], { projectName: name });
15471
+ const devSecrets = createStageSecrets(local, [], { projectName: name });
14733
15472
  const customSecrets = {
14734
15473
  PORT: "3000",
14735
15474
  LOG_LEVEL: "debug",
@@ -14743,8 +15482,8 @@ async function initCommand(projectName, options = {}) {
14743
15482
  customSecrets.BETTER_AUTH_TRUSTED_ORIGINS = "http://localhost:3000,http://localhost:3001";
14744
15483
  }
14745
15484
  devSecrets.custom = customSecrets;
14746
- await require_storage.writeStageSecrets(devSecrets, targetDir);
14747
- const keyPath = require_storage.getKeyPath(local, name);
15485
+ await new require_store.FileSecretsStore(targetDir).write(local, devSecrets);
15486
+ const keyPath = require_store.getKeyPath(local, name);
14748
15487
  console.log(` Secrets: .gkm/secrets/${local}.json (encrypted)`);
14749
15488
  console.log(` Key: ${keyPath}\n`);
14750
15489
  if (!options.skipInstall) {
@@ -14896,15 +15635,104 @@ var NotAnAwsRegion = class extends Error {
14896
15635
  }
14897
15636
  };
14898
15637
  //#endregion
15638
+ //#region src/setup/fullstack-secrets.ts
15639
+ /**
15640
+ * Generate a secure random password for database users.
15641
+ * Uses a combination of timestamp and random bytes for uniqueness.
15642
+ */
15643
+ function generateDbPassword() {
15644
+ return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;
15645
+ }
15646
+ /**
15647
+ * Generate database URL for an app.
15648
+ * All apps connect to the same database, but use different users/schemas.
15649
+ */
15650
+ function generateDbUrl(appName, password, projectName, host = "localhost", port = 5432) {
15651
+ return `postgresql://${appName.replace(/-/g, "_")}:${password}@${host}:${port}/${`${projectName.replace(/-/g, "_")}_dev`}`;
15652
+ }
15653
+ /**
15654
+ * Generate fullstack-aware custom secrets for a workspace.
15655
+ *
15656
+ * Generates:
15657
+ * - Common secrets: PORT, LOG_LEVEL, JWT_SECRET (no `NODE_ENV` — the command
15658
+ * decides that; see `gkm init`)
15659
+ * - Per-app database passwords and URLs for backend apps with db service
15660
+ * - Better-auth secrets for apps using the better-auth framework
15661
+ */
15662
+ function generateFullstackCustomSecrets(workspace, containers = []) {
15663
+ const hasDb = containers.includes("postgres");
15664
+ const customs = {
15665
+ PORT: "3000",
15666
+ LOG_LEVEL: "debug",
15667
+ JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
15668
+ };
15669
+ if (!hasDb) return customs;
15670
+ const frontendPorts = [];
15671
+ for (const [appName, appConfig] of Object.entries(workspace.apps)) {
15672
+ if (appConfig.type === "web") {
15673
+ frontendPorts.push(appConfig.port);
15674
+ const upperName = appName.toUpperCase();
15675
+ customs[`${upperName}_URL`] = `http://localhost:${appConfig.port}`;
15676
+ continue;
15677
+ }
15678
+ if (appConfig.type === "mobile") continue;
15679
+ const password = generateDbPassword();
15680
+ const upperName = appName.toUpperCase();
15681
+ customs[`${upperName}_DATABASE_URL`] = generateDbUrl(appName, password, workspace.name);
15682
+ customs[`${upperName}_DB_PASSWORD`] = password;
15683
+ if (appConfig.framework === "better-auth") {
15684
+ customs.AUTH_PORT = String(appConfig.port);
15685
+ customs.AUTH_URL = `http://localhost:${appConfig.port}`;
15686
+ customs.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${generateSecurePassword(16)}`;
15687
+ customs.BETTER_AUTH_URL = `http://localhost:${appConfig.port}`;
15688
+ }
15689
+ }
15690
+ if (customs.BETTER_AUTH_SECRET) customs.BETTER_AUTH_TRUSTED_ORIGINS = Object.values(workspace.apps).map((a) => a.port).map((p) => `http://localhost:${p}`).join(",");
15691
+ return customs;
15692
+ }
15693
+ /**
15694
+ * Extract *_DB_PASSWORD keys from secrets and write docker/.env.
15695
+ *
15696
+ * The docker/.env file contains database passwords that the PostgreSQL
15697
+ * init script reads to create per-app database users.
15698
+ */
15699
+ async function writeDockerEnvFromSecrets(secrets, workspaceRoot) {
15700
+ const dbPasswordEntries = Object.entries(secrets.custom).filter(([key]) => key.endsWith("_DB_PASSWORD"));
15701
+ if (secrets.services.pgboss && !dbPasswordEntries.some(([key]) => key === "PGBOSS_DB_PASSWORD")) dbPasswordEntries.push(["PGBOSS_DB_PASSWORD", secrets.services.pgboss.password]);
15702
+ if (dbPasswordEntries.length === 0) return;
15703
+ const envContent = `# Auto-generated docker environment file
15704
+ # Contains database passwords for docker-compose postgres init
15705
+ # This file is gitignored - do not commit to version control
15706
+ ${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join("\n")}
15707
+ `;
15708
+ const envPath = (0, node_path.join)(workspaceRoot, "docker", ".env");
15709
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(envPath), { recursive: true });
15710
+ await (0, node_fs_promises.writeFile)(envPath, envContent);
15711
+ }
15712
+ //#endregion
14899
15713
  //#region src/secrets/index.ts
14900
15714
  const logger$2 = console;
14901
15715
  /**
15716
+ * The store the stage named on the command line keeps its secrets in — SSM in
15717
+ * its account, for a deployed stage kept there. Outside any workspace nothing
15718
+ * names a store, so it is the file.
15719
+ */
15720
+ async function storeFor(stage) {
15721
+ try {
15722
+ return await require_store.secretsStoreFor(await require_config.loadWorkspaceSettings(), stage);
15723
+ } catch (error) {
15724
+ if (error instanceof require_config.ConfigNotFound) return new require_store.FileSecretsStore(process.cwd());
15725
+ throw error;
15726
+ }
15727
+ }
15728
+ /**
14902
15729
  * Initialize secrets for a stage.
14903
15730
  * Generates secure random passwords for configured services.
14904
15731
  */
14905
15732
  async function secretsInitCommand(options) {
14906
15733
  const { stage, force } = options;
14907
- if (!force && require_storage.secretsExist(stage)) {
15734
+ const store = await storeFor(stage);
15735
+ if (!force && await store.read(stage)) {
14908
15736
  logger$2.error(`Secrets already exist for stage "${stage}". Use --force to overwrite.`);
14909
15737
  process.exit(1);
14910
15738
  }
@@ -14914,15 +15742,15 @@ async function secretsInitCommand(options) {
14914
15742
  const loaded = await require_config.loadWorkspaceConfig();
14915
15743
  projectName = loaded.workspace.name;
14916
15744
  if (Object.keys(loaded.workspace.apps).length > 1) {
14917
- workspaceSecrets = require_reconcile.generateFullstackCustomSecrets(loaded.workspace);
15745
+ workspaceSecrets = generateFullstackCustomSecrets(loaded.workspace);
14918
15746
  logger$2.log(" Detected workspace mode — generating per-app secrets");
14919
15747
  }
14920
15748
  } catch {}
14921
- const secrets = require_reconcile.createStageSecrets(stage, [], { projectName });
15749
+ const secrets = createStageSecrets(stage, [], { projectName });
14922
15750
  if (workspaceSecrets) secrets.custom = workspaceSecrets;
14923
- await require_storage.writeStageSecrets(secrets);
15751
+ await store.write(stage, secrets);
14924
15752
  logger$2.log(`\n✓ Secrets initialized for stage "${stage}"`);
14925
- logger$2.log(` Location: .gkm/secrets/${stage}.json`);
15753
+ logger$2.log(` Store: ${store.name}`);
14926
15754
  if (secrets.urls.DATABASE_URL) logger$2.log(`\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);
14927
15755
  if (secrets.urls.REDIS_URL) logger$2.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);
14928
15756
  if (secrets.urls.STORAGE_ENDPOINT) logger$2.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);
@@ -14957,20 +15785,21 @@ async function secretsSetCommand(key, value, options) {
14957
15785
  process.exit(1);
14958
15786
  }
14959
15787
  }
14960
- try {
14961
- await require_storage.setCustomSecret(stage, key, secretValue);
14962
- logger$2.log(`\n✓ Secret "${key}" set for stage "${stage}"`);
14963
- } catch (error) {
14964
- logger$2.error(error instanceof Error ? error.message : "Failed to set secret");
15788
+ const store = await storeFor(stage);
15789
+ const secrets = await store.read(stage);
15790
+ if (!secrets) {
15791
+ logger$2.error(`Secrets not found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
14965
15792
  process.exit(1);
14966
15793
  }
15794
+ await store.write(stage, require_storage.withCustomSecret(secrets, key, secretValue));
15795
+ logger$2.log(`\n✓ Secret "${key}" set for stage "${stage}" (${store.name})`);
14967
15796
  }
14968
15797
  /**
14969
15798
  * Show secrets for a stage.
14970
15799
  */
14971
15800
  async function secretsShowCommand(options) {
14972
15801
  const { stage, reveal } = options;
14973
- const secrets = await require_storage.readStageSecrets(stage);
15802
+ const secrets = await (await storeFor(stage)).read(stage);
14974
15803
  if (!secrets) {
14975
15804
  logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
14976
15805
  process.exit(1);
@@ -15003,7 +15832,8 @@ async function secretsShowCommand(options) {
15003
15832
  */
15004
15833
  async function secretsRotateCommand(options) {
15005
15834
  const { stage, service } = options;
15006
- const secrets = await require_storage.readStageSecrets(stage);
15835
+ const store = await storeFor(stage);
15836
+ const secrets = await store.read(stage);
15007
15837
  if (!secrets) {
15008
15838
  logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
15009
15839
  process.exit(1);
@@ -15013,14 +15843,14 @@ async function secretsRotateCommand(options) {
15013
15843
  logger$2.error(`Service "${service}" not configured in stage "${stage}"`);
15014
15844
  process.exit(1);
15015
15845
  }
15016
- const updated = require_reconcile.rotateServicePassword(secrets, service);
15017
- await require_storage.writeStageSecrets(updated);
15846
+ const updated = rotateServicePassword(secrets, service);
15847
+ await store.write(stage, updated);
15018
15848
  logger$2.log(`\n✓ Password rotated for ${service} in stage "${stage}"`);
15019
15849
  } else {
15020
15850
  let updated = secrets;
15021
15851
  const services = Object.keys(secrets.services);
15022
- for (const svc of services) updated = require_reconcile.rotateServicePassword(updated, svc);
15023
- await require_storage.writeStageSecrets(updated);
15852
+ for (const svc of services) updated = rotateServicePassword(updated, svc);
15853
+ await store.write(stage, updated);
15024
15854
  logger$2.log(`\n✓ Passwords rotated for all services in stage "${stage}": ${services.join(", ")}`);
15025
15855
  }
15026
15856
  logger$2.log(`\nUse "gkm secrets:show --stage ${stage}" to view new values`);
@@ -15044,7 +15874,8 @@ async function secretsImportCommand(file, options) {
15044
15874
  logger$2.error(`Failed to parse JSON file: ${error instanceof Error ? error.message : "Invalid JSON"}`);
15045
15875
  process.exit(1);
15046
15876
  }
15047
- const secrets = await require_storage.readStageSecrets(stage);
15877
+ const store = await storeFor(stage);
15878
+ const secrets = await store.read(stage);
15048
15879
  if (!secrets) {
15049
15880
  logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
15050
15881
  process.exit(1);
@@ -15058,7 +15889,7 @@ async function secretsImportCommand(file, options) {
15058
15889
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
15059
15890
  custom: updatedCustom
15060
15891
  };
15061
- await require_storage.writeStageSecrets(updated);
15892
+ await store.write(stage, updated);
15062
15893
  const importedCount = Object.keys(importedSecrets).length;
15063
15894
  const totalCount = Object.keys(updatedCustom).length;
15064
15895
  logger$2.log(`\n✓ Imported ${importedCount} secrets for stage "${stage}"`);
@@ -15079,121 +15910,6 @@ function maskUrl(url) {
15079
15910
  }
15080
15911
  }
15081
15912
  //#endregion
15082
- //#region src/secrets/store.ts
15083
- /** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */
15084
- var FileSecretsStore = class {
15085
- root;
15086
- constructor(root) {
15087
- this.root = root;
15088
- }
15089
- pull(stage) {
15090
- return require_storage.readStageSecrets(stage, this.root);
15091
- }
15092
- push(_stage, secrets) {
15093
- return require_storage.writeStageSecrets(secrets, this.root);
15094
- }
15095
- };
15096
- /**
15097
- * The store a stage's secrets live in.
15098
- *
15099
- * The local stage is always the file: its secrets belong to the machine
15100
- * running `gkm dev`. A deployed stage uses `secrets.store`.
15101
- */
15102
- async function secretsStoreFor(workspace, stage, options = {}) {
15103
- const configured = workspace.secrets.store ?? "file";
15104
- if (stage === workspace.stages.local || configured === "file") return new FileSecretsStore(workspace.root);
15105
- if (configured.provider !== "ssm") return configured.provider;
15106
- const { SsmSecretsStore } = await Promise.resolve().then(() => require("./ssm-FxeSAt35.cjs"));
15107
- return new SsmSecretsStore({
15108
- project: workspace.name,
15109
- region: configured.region,
15110
- ...options.profile ? { profile: options.profile } : {}
15111
- });
15112
- }
15113
- /** Whether a stage's secrets live somewhere other than this machine. */
15114
- function isRemoteStore(workspace, stage) {
15115
- const configured = workspace.secrets.store ?? "file";
15116
- return stage !== workspace.stages.local && configured !== "file";
15117
- }
15118
- //#endregion
15119
- //#region src/secrets/transfer.ts
15120
- /**
15121
- * Moving a deployed stage's secrets between this machine and its store.
15122
- *
15123
- * `push` sends the encrypted local copy to the stage's store; `pull` brings it
15124
- * back and writes the local copy a deploy reads — generating a key for it if
15125
- * the machine has none, which is what a fresh CI runner needs. Both reconcile
15126
- * the keys the workspace now derives, so a store never lags a new construct.
15127
- */
15128
- var transfer_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
15129
- NoLocalSecrets: () => NoLocalSecrets,
15130
- NoRemoteSecretsStore: () => NoRemoteSecretsStore,
15131
- NoStoredSecrets: () => NoStoredSecrets,
15132
- pullStageSecrets: () => pullStageSecrets,
15133
- pushStageSecrets: () => pushStageSecrets
15134
- });
15135
- /** A push or pull for a stage with nowhere to push or pull from. */
15136
- var NoRemoteSecretsStore = class extends Error {
15137
- stage;
15138
- constructor(stage) {
15139
- 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.`);
15140
- this.stage = stage;
15141
- this.name = "NoRemoteSecretsStore";
15142
- }
15143
- };
15144
- /** A push for a stage whose secrets this machine does not have. */
15145
- var NoLocalSecrets = class extends Error {
15146
- stage;
15147
- constructor(stage) {
15148
- super(`No secrets for stage "${stage}" on this machine. Run gkm secrets:init --stage ${stage} first.`);
15149
- this.stage = stage;
15150
- this.name = "NoLocalSecrets";
15151
- }
15152
- };
15153
- /** A pull for a stage the store holds nothing for. */
15154
- var NoStoredSecrets = class extends Error {
15155
- stage;
15156
- constructor(stage) {
15157
- super(`The store holds no secrets for stage "${stage}". Push them first: gkm secrets:push --stage ${stage}.`);
15158
- this.stage = stage;
15159
- this.name = "NoStoredSecrets";
15160
- }
15161
- };
15162
- async function reconcile(secrets, workspace, stage) {
15163
- const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-DUhLejv4.cjs")).then((n) => n.workspace_exports);
15164
- const result = require_reconcile.reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
15165
- return result ? {
15166
- secrets: result.secrets,
15167
- addedKeys: result.addedKeys
15168
- } : {
15169
- secrets,
15170
- addedKeys: []
15171
- };
15172
- }
15173
- function remoteOnly(workspace, stage) {
15174
- require_workspace.assertDeployedStage(workspace.stages, stage);
15175
- if (!isRemoteStore(workspace, stage)) throw new NoRemoteSecretsStore(stage);
15176
- }
15177
- /** Send this machine's secrets for a deployed stage to its store. */
15178
- async function pushStageSecrets(workspace, stage, options = {}) {
15179
- remoteOnly(workspace, stage);
15180
- const local = await require_storage.readStageSecrets(stage, workspace.root);
15181
- if (!local) throw new NoLocalSecrets(stage);
15182
- const result = await reconcile(local, workspace, stage);
15183
- if (result.addedKeys.length) await require_storage.writeStageSecrets(result.secrets, workspace.root);
15184
- await (await secretsStoreFor(workspace, stage, options)).push(stage, result.secrets);
15185
- return result;
15186
- }
15187
- /** Bring a deployed stage's secrets from its store into the local copy. */
15188
- async function pullStageSecrets(workspace, stage, options = {}) {
15189
- remoteOnly(workspace, stage);
15190
- const stored = await (await secretsStoreFor(workspace, stage, options)).pull(stage);
15191
- if (!stored) throw new NoStoredSecrets(stage);
15192
- const result = await reconcile(stored, workspace, stage);
15193
- await require_storage.writeStageSecrets(result.secrets, workspace.root);
15194
- return result;
15195
- }
15196
- //#endregion
15197
15913
  //#region src/setup/index.ts
15198
15914
  var setup_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
15199
15915
  createFreshWorkspaceSecrets: () => createFreshWorkspaceSecrets,
@@ -15233,7 +15949,7 @@ async function setupCommand(options = {}) {
15233
15949
  }
15234
15950
  const containers = await require_workspace$1.derivedContainers(workspace, stage);
15235
15951
  if (isMultiApp && containers.includes("postgres")) {
15236
- await require_reconcile.writeDockerEnvFromSecrets(secrets, workspace.root);
15952
+ await writeDockerEnvFromSecrets(secrets, workspace.root);
15237
15953
  logger$1.log("📄 Generated docker/.env with database passwords");
15238
15954
  }
15239
15955
  if (!options.skipDocker) {
@@ -15275,30 +15991,18 @@ async function reconcileLocal(workspace, stage, options) {
15275
15991
  async function resolveSecrets(stage, workspace, options) {
15276
15992
  if (options.force) {
15277
15993
  logger$1.log("🔐 Generating fresh secrets (--force)...");
15278
- return generateFreshSecrets(stage, workspace, options);
15279
- }
15280
- if (require_storage.secretsExist(stage, workspace.root)) {
15281
- logger$1.log("🔐 Using existing local secrets");
15282
- const secrets = await require_storage.readStageSecrets(stage, workspace.root);
15283
- if (secrets) {
15284
- const reconciled = reconcileSecrets(secrets, workspace, await require_workspace$1.derivedContainers(workspace, stage));
15285
- if (reconciled) await require_storage.writeStageSecrets(reconciled, workspace.root);
15286
- return reconciled ?? secrets;
15287
- }
15994
+ return generateFreshSecrets(stage, workspace);
15288
15995
  }
15289
- if (isRemoteStore(workspace, stage)) {
15290
- logger$1.log("☁️ Checking the secrets store...");
15291
- try {
15292
- const { secrets } = await pullStageSecrets(workspace, stage);
15293
- logger$1.log("✅ Pulled secrets from the store");
15294
- return secrets;
15295
- } catch (error) {
15296
- if (error instanceof NoStoredSecrets) logger$1.log(" The store holds none yet");
15297
- else logger$1.warn(`⚠️ Could not pull from the store: ${error.message}`);
15298
- }
15996
+ const store = await require_store.secretsStoreFor(workspace, stage);
15997
+ const secrets = await store.read(stage);
15998
+ if (secrets) {
15999
+ logger$1.log("🔐 Using existing secrets");
16000
+ const reconciled = reconcileSecrets(secrets, workspace, await require_workspace$1.derivedContainers(workspace, stage));
16001
+ if (reconciled) await store.write(stage, reconciled);
16002
+ return reconciled ?? secrets;
15299
16003
  }
15300
16004
  logger$1.log("🔐 Generating fresh development secrets...");
15301
- return generateFreshSecrets(stage, workspace, options);
16005
+ return generateFreshSecrets(stage, workspace);
15302
16006
  }
15303
16007
  /**
15304
16008
  * Reconcile existing secrets with expected workspace-derived keys.
@@ -15328,7 +16032,7 @@ function reconcileSecrets(secrets, workspace, containers = []) {
15328
16032
  let changed = false;
15329
16033
  let result = { ...secrets };
15330
16034
  for (const name of credentialedServices(containers)) if (!result.services[name]) {
15331
- const creds = require_reconcile.generateServiceCredentials(name);
16035
+ const creds = generateServiceCredentials(name);
15332
16036
  if (name === "minio") {
15333
16037
  creds.bucket = workspace.name;
15334
16038
  creds.username = workspace.name;
@@ -15340,7 +16044,7 @@ function reconcileSecrets(secrets, workspace, containers = []) {
15340
16044
  [name]: creds
15341
16045
  }
15342
16046
  };
15343
- result.urls = require_reconcile.generateConnectionUrls(result.services);
16047
+ result.urls = generateConnectionUrls(result.services);
15344
16048
  logger$1.log(` 🔄 Adding missing service credentials: ${name}`);
15345
16049
  changed = true;
15346
16050
  }
@@ -15353,17 +16057,17 @@ function reconcileSecrets(secrets, workspace, containers = []) {
15353
16057
  host: result.services.postgres.host,
15354
16058
  port: result.services.postgres.port,
15355
16059
  username: "pgboss",
15356
- password: require_reconcile.generateSecurePassword(),
16060
+ password: generateSecurePassword(),
15357
16061
  database: result.services.postgres.database ?? "app"
15358
16062
  }
15359
16063
  }
15360
16064
  };
15361
- result.urls = require_reconcile.generateConnectionUrls(result.services);
16065
+ result.urls = generateConnectionUrls(result.services);
15362
16066
  logger$1.log(" 🔄 Adding missing service credentials: pgboss");
15363
16067
  changed = true;
15364
16068
  }
15365
16069
  if (Object.keys(workspace.apps).length > 1) {
15366
- const expected = require_reconcile.generateFullstackCustomSecrets(workspace, containers);
16070
+ const expected = generateFullstackCustomSecrets(workspace, containers);
15367
16071
  const missing = {};
15368
16072
  for (const [key, value] of Object.entries(expected)) if (!(key in result.custom)) missing[key] = value;
15369
16073
  if (Object.keys(missing).length > 0) {
@@ -15392,9 +16096,8 @@ function reconcileSecrets(secrets, workspace, containers = []) {
15392
16096
  * @internal Exported for reuse by `gkm test` auto-setup.
15393
16097
  */
15394
16098
  function createFreshWorkspaceSecrets(stage, workspace, containers = []) {
15395
- const serviceNames = credentialedServices(containers);
15396
- const secrets = require_reconcile.createStageSecrets(stage, serviceNames, { projectName: workspace.name });
15397
- if (Object.keys(workspace.apps).length > 1) secrets.custom = require_reconcile.generateFullstackCustomSecrets(workspace, containers);
16099
+ const secrets = createStageSecrets(stage, credentialedServices(containers), { projectName: workspace.name });
16100
+ if (Object.keys(workspace.apps).length > 1) secrets.custom = generateFullstackCustomSecrets(workspace, containers);
15398
16101
  else secrets.custom = {
15399
16102
  PORT: "3000",
15400
16103
  LOG_LEVEL: "debug",
@@ -15408,38 +16111,26 @@ function createFreshWorkspaceSecrets(stage, workspace, containers = []) {
15408
16111
  *
15409
16112
  * Powers `gkm test --auto-setup` / `GKM_AUTO_SETUP`: CI can run without a
15410
16113
  * committed secrets file or a shared encryption key — the stage is regenerated
15411
- * from the committed `gkm.config.ts`, and `writeStageSecrets` mints a local key.
16114
+ * from the committed `gkm.config.ts`, and the file store mints a local key.
15412
16115
  *
15413
16116
  * @returns true if fresh secrets were generated, false if existing ones were kept
15414
16117
  */
15415
16118
  async function ensureStageSecrets(stage, cwd = process.cwd()) {
15416
16119
  const { workspace } = await require_config.loadWorkspaceConfig(cwd);
15417
- if (require_storage.secretsExist(stage, workspace.root)) return false;
16120
+ const store = await require_store.secretsStoreFor(workspace, stage);
16121
+ if (await store.read(stage)) return false;
15418
16122
  const secrets = createFreshWorkspaceSecrets(stage, workspace, await require_workspace$1.derivedContainers(workspace, stage));
15419
- await require_storage.writeStageSecrets(secrets, workspace.root);
16123
+ await store.write(stage, secrets);
15420
16124
  return true;
15421
16125
  }
15422
16126
  /**
15423
16127
  * Generate fresh secrets for the workspace.
15424
16128
  */
15425
- async function generateFreshSecrets(stage, workspace, options) {
16129
+ async function generateFreshSecrets(stage, workspace) {
15426
16130
  const secrets = createFreshWorkspaceSecrets(stage, workspace, await require_workspace$1.derivedContainers(workspace, stage));
15427
- await require_storage.writeStageSecrets(secrets, workspace.root);
15428
- logger$1.log(` Secrets written to .gkm/secrets/${stage}.json`);
15429
- if (isRemoteStore(workspace, stage) && !options.yes) {
15430
- const { shouldPush } = await (0, prompts.default)({
15431
- type: "confirm",
15432
- name: "shouldPush",
15433
- message: `Push the "${stage}" secrets to its store?`,
15434
- initial: true
15435
- });
15436
- if (shouldPush) try {
15437
- await pushStageSecrets(workspace, stage);
15438
- logger$1.log("☁️ Secrets pushed to the store");
15439
- } catch (error) {
15440
- logger$1.warn(`⚠️ Could not push to the store: ${error.message}`);
15441
- }
15442
- }
16131
+ const store = await require_store.secretsStoreFor(workspace, stage);
16132
+ await store.write(stage, secrets);
16133
+ logger$1.log(` Secrets written to the "${stage}" store (${store.name})`);
15443
16134
  return secrets;
15444
16135
  }
15445
16136
  /**
@@ -15632,8 +16323,8 @@ function testDatabases(reconciled) {
15632
16323
  * starts without the reference data the app needs.
15633
16324
  */
15634
16325
  async function migrateTestStage(workspace, env) {
15635
- const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-Cgga-OGi.cjs"));
15636
- const { discover } = await Promise.resolve().then(() => require("./discover-CroTIm0K.cjs")).then((n) => n.discover_exports);
16326
+ const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-Bu8POqsR.cjs"));
16327
+ const { discover } = await Promise.resolve().then(() => require("./discover-B0f_kuNN.cjs")).then((n) => n.discover_exports);
15637
16328
  const sources = {};
15638
16329
  const manifest = await discover({
15639
16330
  patterns: require_workspace$1.constructGlobs(workspace),
@@ -16032,7 +16723,7 @@ program.command("build").description("Build handlers and the manifest from decla
16032
16723
  process.exit(1);
16033
16724
  }
16034
16725
  });
16035
- 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) => {
16726
+ 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) => {
16036
16727
  try {
16037
16728
  const globalOptions = program.opts();
16038
16729
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
@@ -16043,7 +16734,8 @@ program.command("dev").description("Reconcile constructs and start the dev serve
16043
16734
  entry: options.entry,
16044
16735
  watch: options.watch,
16045
16736
  migrate: options.migrate,
16046
- seed: options.seed
16737
+ seed: options.seed,
16738
+ fake: options.fake
16047
16739
  });
16048
16740
  } catch (error) {
16049
16741
  console.error(formatError(error));
@@ -16077,7 +16769,7 @@ program.command("migrate").description("Apply each database construct's migratio
16077
16769
  try {
16078
16770
  const globalOptions = program.opts();
16079
16771
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16080
- const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-Cgga-OGi.cjs"));
16772
+ const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-Bu8POqsR.cjs"));
16081
16773
  await migrateCommand({
16082
16774
  ...options,
16083
16775
  ...construct ? { construct } : {}
@@ -16091,7 +16783,7 @@ program.command("seed").description("Migrate, then run each database construct's
16091
16783
  try {
16092
16784
  const globalOptions = program.opts();
16093
16785
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16094
- const { seedCommand } = await Promise.resolve().then(() => require("./migrate-Cgga-OGi.cjs"));
16786
+ const { seedCommand } = await Promise.resolve().then(() => require("./migrate-Bu8POqsR.cjs"));
16095
16787
  await seedCommand({
16096
16788
  ...options,
16097
16789
  ...construct ? { construct } : {}
@@ -16105,7 +16797,7 @@ program.command("migration").description("Write a construct's next migration: an
16105
16797
  try {
16106
16798
  const globalOptions = program.opts();
16107
16799
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16108
- const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-Cgga-OGi.cjs"));
16800
+ const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-Bu8POqsR.cjs"));
16109
16801
  await migrationCommand({
16110
16802
  construct,
16111
16803
  ...name ? { name } : {}
@@ -16229,57 +16921,28 @@ program.command("secrets:import").description("Import secrets from a JSON file")
16229
16921
  process.exit(1);
16230
16922
  }
16231
16923
  });
16232
- 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) => {
16233
- try {
16234
- const globalOptions = program.opts();
16235
- if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16236
- const { loadWorkspaceConfig } = await Promise.resolve().then(() => require("./config.cjs"));
16237
- const { pushStageSecrets } = await Promise.resolve().then(() => transfer_exports);
16238
- const { workspace } = await loadWorkspaceConfig();
16239
- const { addedKeys } = await pushStageSecrets(workspace, options.stage, { profile: options.profile });
16240
- for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
16241
- console.log(`\n✓ Secrets pushed for stage "${options.stage}"`);
16242
- } catch (error) {
16243
- console.error(formatError(error));
16244
- process.exit(1);
16245
- }
16246
- });
16247
- 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) => {
16248
- try {
16249
- const globalOptions = program.opts();
16250
- if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16251
- const { loadWorkspaceConfig } = await Promise.resolve().then(() => require("./config.cjs"));
16252
- const { pullStageSecrets } = await Promise.resolve().then(() => transfer_exports);
16253
- const { workspace } = await loadWorkspaceConfig();
16254
- const { addedKeys } = await pullStageSecrets(workspace, options.stage, { profile: options.profile });
16255
- for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
16256
- console.log(`\n✓ Secrets pulled for stage "${options.stage}"`);
16257
- } catch (error) {
16258
- console.error(formatError(error));
16259
- process.exit(1);
16260
- }
16261
- });
16262
16924
  program.command("secrets:reconcile").description("Backfill missing custom secrets from workspace config").option("--stage <stage>", "Stage name (default: stages.local)").action(async (options) => {
16263
16925
  try {
16264
16926
  const globalOptions = program.opts();
16265
16927
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16266
16928
  const { loadWorkspaceConfig } = await Promise.resolve().then(() => require("./config.cjs"));
16267
- const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-BxZTV2vl.cjs")).then((n) => n.reconcile_exports);
16268
- const { readStageSecrets, writeStageSecrets } = await Promise.resolve().then(() => require("./storage-C2JC31V9.cjs")).then((n) => n.storage_exports);
16929
+ const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CuCQBa5-.cjs"));
16930
+ const { secretsStoreFor } = await Promise.resolve().then(() => require("./store-DQgxT_WR.cjs")).then((n) => n.store_exports);
16269
16931
  const { workspace } = await loadWorkspaceConfig();
16270
16932
  const stage = options.stage ?? workspace.stages.local;
16271
- const secrets = await readStageSecrets(stage, workspace.root);
16933
+ const store = await secretsStoreFor(workspace, stage);
16934
+ const secrets = await store.read(stage);
16272
16935
  if (!secrets) {
16273
16936
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
16274
16937
  process.exit(1);
16275
16938
  }
16276
- const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-DUhLejv4.cjs")).then((n) => n.workspace_exports);
16939
+ const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-DZ1G4zow.cjs")).then((n) => n.workspace_exports);
16277
16940
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
16278
16941
  if (!result) {
16279
16942
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
16280
16943
  return;
16281
16944
  }
16282
- await writeStageSecrets(result.secrets, workspace.root);
16945
+ await store.write(stage, result.secrets);
16283
16946
  console.log(`\n✓ Reconciled ${result.addedKeys.length} missing secret(s) for stage "${stage}":`);
16284
16947
  for (const key of result.addedKeys) console.log(` + ${key}`);
16285
16948
  } catch (error) {
@@ -16332,7 +16995,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
16332
16995
  try {
16333
16996
  const globalOptions = program.opts();
16334
16997
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16335
- const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-tZmMZ7td.cjs"));
16998
+ const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-BAr8WGkR.cjs"));
16336
16999
  await deployGithubCommand(options);
16337
17000
  } catch (error) {
16338
17001
  console.error(formatError(error));
@@ -16463,9 +17126,8 @@ Object.defineProperty(exports, "encryption_exports", {
16463
17126
  return encryption_exports;
16464
17127
  }
16465
17128
  });
17129
+ exports.generateFullstackCustomSecrets = generateFullstackCustomSecrets;
16466
17130
  exports.getHostingerToken = getHostingerToken;
16467
- exports.isRemoteStore = isRemoteStore;
16468
- exports.pushStageSecrets = pushStageSecrets;
16469
17131
  exports.slug = slug;
16470
17132
  exports.stamp = stamp;
16471
17133