@geekmidas/cli 10.0.0-alpha.73 → 10.0.0-alpha.74

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 (111) hide show
  1. package/dist/{SubscriberGenerator-DZk_pKDh.mjs → SubscriberGenerator-5looQ3gB.mjs} +18 -2
  2. package/dist/SubscriberGenerator-5looQ3gB.mjs.map +1 -0
  3. package/dist/{SubscriberGenerator-Bixm28fN.cjs → SubscriberGenerator-CajwOa5W.cjs} +29 -1
  4. package/dist/SubscriberGenerator-CajwOa5W.cjs.map +1 -0
  5. package/dist/{add-BiPlBUq3.mjs → add-BTH7WaH3.mjs} +2 -2
  6. package/dist/{add-BiPlBUq3.mjs.map → add-BTH7WaH3.mjs.map} +1 -1
  7. package/dist/{add-C1ojB2Yy.cjs → add-Djrr12Xc.cjs} +2 -2
  8. package/dist/{add-C1ojB2Yy.cjs.map → add-Djrr12Xc.cjs.map} +1 -1
  9. package/dist/{bundler-C2LZoLdP.mjs → bundler-BUojOkof.mjs} +5 -5
  10. package/dist/{bundler-C2LZoLdP.mjs.map → bundler-BUojOkof.mjs.map} +1 -1
  11. package/dist/{bundler-C35h0WKt.cjs → bundler-utHuESvL.cjs} +5 -5
  12. package/dist/{bundler-C35h0WKt.cjs.map → bundler-utHuESvL.cjs.map} +1 -1
  13. package/dist/{cli-DJVL2sL9.cjs → cli-Bx0sUzxH.cjs} +3 -3
  14. package/dist/{cli-DJVL2sL9.cjs.map → cli-Bx0sUzxH.cjs.map} +1 -1
  15. package/dist/{cli-BfdfyTF6.mjs → cli-CyYceKTS.mjs} +3 -3
  16. package/dist/{cli-BfdfyTF6.mjs.map → cli-CyYceKTS.mjs.map} +1 -1
  17. package/dist/{compose-9II-0IsK.mjs → compose-DZp8DJ0A.mjs} +202 -10
  18. package/dist/compose-DZp8DJ0A.mjs.map +1 -0
  19. package/dist/{compose-CNnw8meq.cjs → compose-iuw8wfHk.cjs} +202 -10
  20. package/dist/compose-iuw8wfHk.cjs.map +1 -0
  21. package/dist/{config-RLuR2o9A.d.cts → config-B38YWicj.d.cts} +3 -3
  22. package/dist/{config-B8OR_TrO.d.mts.map → config-B38YWicj.d.cts.map} +1 -1
  23. package/dist/{config-B8OR_TrO.d.mts → config-DA0kRTQe.d.mts} +3 -3
  24. package/dist/{config-RLuR2o9A.d.cts.map → config-DA0kRTQe.d.mts.map} +1 -1
  25. package/dist/config.d.cts +3 -3
  26. package/dist/config.d.mts +3 -3
  27. package/dist/deploy/public.cjs +5 -5
  28. package/dist/deploy/public.d.cts +3 -3
  29. package/dist/deploy/public.d.mts +3 -3
  30. package/dist/deploy/public.mjs +5 -5
  31. package/dist/{deploy-nmov4j0B.mjs → deploy-C17K7Imy.mjs} +3 -3
  32. package/dist/{deploy-nmov4j0B.mjs.map → deploy-C17K7Imy.mjs.map} +1 -1
  33. package/dist/{deploy-BitZLdnY.cjs → deploy-eWorhvQp.cjs} +3 -3
  34. package/dist/{deploy-BitZLdnY.cjs.map → deploy-eWorhvQp.cjs.map} +1 -1
  35. package/dist/{dokploy-CZ1vZt21.mjs → dokploy-DKa49Nqm.mjs} +2 -2
  36. package/dist/{dokploy-CZ1vZt21.mjs.map → dokploy-DKa49Nqm.mjs.map} +1 -1
  37. package/dist/{dokploy-Ce_VTytN.cjs → dokploy-DKzcI-RL.cjs} +2 -2
  38. package/dist/{dokploy-Ce_VTytN.cjs.map → dokploy-DKzcI-RL.cjs.map} +1 -1
  39. package/dist/drivers-BdvtCKc7.cjs +200 -0
  40. package/dist/drivers-BdvtCKc7.cjs.map +1 -0
  41. package/dist/drivers-Ch1rJitn.mjs +177 -0
  42. package/dist/drivers-Ch1rJitn.mjs.map +1 -0
  43. package/dist/{encryption-CDGRdcMr.cjs → encryption-DSkJdoyk.cjs} +2 -2
  44. package/dist/{encryption-CDGRdcMr.cjs.map → encryption-DSkJdoyk.cjs.map} +1 -1
  45. package/dist/{encryption-CmfPICSN.mjs → encryption-sTMz3aWi.mjs} +2 -2
  46. package/dist/{encryption-CmfPICSN.mjs.map → encryption-sTMz3aWi.mjs.map} +1 -1
  47. package/dist/{engine-B37qBO5W.mjs → engine-B1b4I34Z.mjs} +2 -2
  48. package/dist/{engine-B37qBO5W.mjs.map → engine-B1b4I34Z.mjs.map} +1 -1
  49. package/dist/{engine-DL2-PW6m.cjs → engine-Bh76sAHy.cjs} +2 -2
  50. package/dist/{engine-DL2-PW6m.cjs.map → engine-Bh76sAHy.cjs.map} +1 -1
  51. package/dist/{github-C6qF8kHg.cjs → github-BvN3CfxM.cjs} +2 -2
  52. package/dist/{github-C6qF8kHg.cjs.map → github-BvN3CfxM.cjs.map} +1 -1
  53. package/dist/{github-hjHHm7jM.mjs → github-C9ODoyZU.mjs} +2 -2
  54. package/dist/{github-hjHHm7jM.mjs.map → github-C9ODoyZU.mjs.map} +1 -1
  55. package/dist/{index-DHyTqGG3.d.mts → index-B-1lLGzy.d.mts} +2 -2
  56. package/dist/{index-DHyTqGG3.d.mts.map → index-B-1lLGzy.d.mts.map} +1 -1
  57. package/dist/{index-Dg-UZ4Hm.d.cts → index-vMidfqhU.d.cts} +2 -2
  58. package/dist/{index-Dg-UZ4Hm.d.cts.map → index-vMidfqhU.d.cts.map} +1 -1
  59. package/dist/index.cjs +59 -47
  60. package/dist/index.cjs.map +1 -1
  61. package/dist/index.mjs +57 -45
  62. package/dist/index.mjs.map +1 -1
  63. package/dist/{migrate-kA_j1TPK.cjs → migrate-B0FCZNkp.cjs} +2 -2
  64. package/dist/{migrate-kA_j1TPK.cjs.map → migrate-B0FCZNkp.cjs.map} +1 -1
  65. package/dist/{migrate-CFFC3esE.mjs → migrate-CTtJhGV9.mjs} +2 -2
  66. package/dist/{migrate-CFFC3esE.mjs.map → migrate-CTtJhGV9.mjs.map} +1 -1
  67. package/dist/openapi.d.cts +1 -1
  68. package/dist/openapi.d.mts +1 -1
  69. package/dist/{ready-BIuMw6wH.cjs → ready-BB9xS4S9.cjs} +6 -188
  70. package/dist/ready-BB9xS4S9.cjs.map +1 -0
  71. package/dist/{ready-C0U0NYfB.mjs → ready-ZZoNskrr.mjs} +5 -169
  72. package/dist/ready-ZZoNskrr.mjs.map +1 -0
  73. package/dist/reconcile/public.d.cts +1 -1
  74. package/dist/reconcile/public.d.mts +1 -1
  75. package/dist/{resolve-CV4eQkDa.mjs → resolve-DSAXj_47.mjs} +3 -3
  76. package/dist/{resolve-CV4eQkDa.mjs.map → resolve-DSAXj_47.mjs.map} +1 -1
  77. package/dist/{resolve-E5RfY0bm.cjs → resolve-D_BU-wKA.cjs} +3 -3
  78. package/dist/{resolve-E5RfY0bm.cjs.map → resolve-D_BU-wKA.cjs.map} +1 -1
  79. package/dist/{rollback-C-V-Rf4a.mjs → rollback-CWYTUhfW.mjs} +3 -3
  80. package/dist/{rollback-C-V-Rf4a.mjs.map → rollback-CWYTUhfW.mjs.map} +1 -1
  81. package/dist/{rollback-QGwomD64.cjs → rollback-DJy3t696.cjs} +3 -3
  82. package/dist/{rollback-QGwomD64.cjs.map → rollback-DJy3t696.cjs.map} +1 -1
  83. package/dist/{runtime-CWYLIeCU.d.mts → runtime-BzZD5HHi.d.cts} +2 -2
  84. package/dist/{runtime-CWYLIeCU.d.mts.map → runtime-BzZD5HHi.d.cts.map} +1 -1
  85. package/dist/{runtime-CmuQOT6A.d.cts → runtime-Cm6J6L7n.d.mts} +2 -2
  86. package/dist/{runtime-CmuQOT6A.d.cts.map → runtime-Cm6J6L7n.d.mts.map} +1 -1
  87. package/dist/{state-DMjFPOhf.cjs → state-DCwRWyh-.cjs} +16 -11
  88. package/dist/state-DCwRWyh-.cjs.map +1 -0
  89. package/dist/{state-DtS2HEfi.mjs → state-DpkxSwzI.mjs} +16 -11
  90. package/dist/state-DpkxSwzI.mjs.map +1 -0
  91. package/dist/target/public.cjs +1 -1
  92. package/dist/target/public.d.cts +2 -2
  93. package/dist/target/public.d.mts +2 -2
  94. package/dist/target/public.mjs +1 -1
  95. package/dist/{types-BLdCL4fB.d.mts → types-Dp9lgx_S.d.mts} +6 -2
  96. package/dist/{types-BLdCL4fB.d.mts.map → types-Dp9lgx_S.d.mts.map} +1 -1
  97. package/dist/{types-DwCQCkC-.d.cts → types-TzCChNrA.d.cts} +6 -2
  98. package/dist/{types-DwCQCkC-.d.cts.map → types-TzCChNrA.d.cts.map} +1 -1
  99. package/dist/vitest.cjs +1 -1
  100. package/dist/vitest.mjs +1 -1
  101. package/dist/workspace/index.d.cts +2 -2
  102. package/dist/workspace/index.d.mts +2 -2
  103. package/package.json +15 -15
  104. package/dist/SubscriberGenerator-Bixm28fN.cjs.map +0 -1
  105. package/dist/SubscriberGenerator-DZk_pKDh.mjs.map +0 -1
  106. package/dist/compose-9II-0IsK.mjs.map +0 -1
  107. package/dist/compose-CNnw8meq.cjs.map +0 -1
  108. package/dist/ready-BIuMw6wH.cjs.map +0 -1
  109. package/dist/ready-C0U0NYfB.mjs.map +0 -1
  110. package/dist/state-DMjFPOhf.cjs.map +0 -1
  111. package/dist/state-DtS2HEfi.mjs.map +0 -1
@@ -0,0 +1,200 @@
1
+ const require_SubscriberGenerator = require("./SubscriberGenerator-CajwOa5W.cjs");
2
+ let node_path = require("node:path");
3
+ let node_fs = require("node:fs");
4
+ //#region src/generators/drivers.ts
5
+ /**
6
+ * Driver registration for generated entry points.
7
+ *
8
+ * A construct declares that it needs object storage and is handed one URL; the
9
+ * scheme in that URL picks the driver that builds the client. Which drivers
10
+ * exist is therefore the *entry point's* decision, not the construct's and not
11
+ * the application's — an app that never talks to S3 should never resolve the AWS
12
+ * SDK, and application code that registers a driver has named a provider in the
13
+ * one layer this design keeps provider-free.
14
+ *
15
+ * So the generated entries register: `gkm dev`'s server for local development,
16
+ * and each Lambda handler for its own target. Registration is an explicit call
17
+ * rather than an import side effect, because a side-effecting module is exactly
18
+ * what a bundler is entitled to drop.
19
+ */
20
+ /** The `s3://` driver, which serves MinIO locally and S3 deployed. */
21
+ const S3 = {
22
+ imports: `import { registerStorageDriver } from '@geekmidas/storage';\nimport { s3Driver } from '@geekmidas/storage/aws';`,
23
+ setup: "registerStorageDriver(s3Driver);"
24
+ };
25
+ const NONE = {
26
+ imports: "",
27
+ setup: ""
28
+ };
29
+ /** The Redis wire protocol's drivers: both schemes, plain and TLS. */
30
+ const REDIS = {
31
+ imports: `import { registerCacheDriver } from '@geekmidas/cache';\nimport { redisCacheDriver, redissCacheDriver } from '@geekmidas/cache/redis';`,
32
+ setup: "registerCacheDriver(redisCacheDriver);\nregisterCacheDriver(redissCacheDriver);"
33
+ };
34
+ /**
35
+ * The cache driver for a backend — exactly one, never all three.
36
+ *
37
+ * Keyed off the *backend* rather than off a dependency, because unlike storage
38
+ * the backend already says which driver is right: a project caching in Postgres
39
+ * resolves `pg` and never `ioredis`, and one caching in Upstash resolves
40
+ * neither. That is the whole reason the drivers live behind separate subpaths.
41
+ *
42
+ * Both HTTP schemes are registered for Upstash, because the local proxy speaks
43
+ * `http://` and Upstash speaks `https://`, and an entry that registered one
44
+ * would work in exactly one of the two places.
45
+ */
46
+ const CACHE_DRIVERS = {
47
+ upstash: {
48
+ imports: `import { registerCacheDriver } from '@geekmidas/cache';\nimport { upstashCacheDriver, upstashInsecureCacheDriver } from '@geekmidas/cache/upstash';`,
49
+ setup: "registerCacheDriver(upstashCacheDriver);\nregisterCacheDriver(upstashInsecureCacheDriver);"
50
+ },
51
+ elasticache: REDIS,
52
+ redis: REDIS,
53
+ db: {
54
+ imports: `import { registerCacheDriver } from '@geekmidas/cache';\nimport { postgresCacheDriver } from '@geekmidas/cache/postgres';`,
55
+ setup: "registerCacheDriver(postgresCacheDriver);"
56
+ }
57
+ };
58
+ /**
59
+ * The events drivers for a broker — only its own, so a bundle never resolves
60
+ * another broker's client library.
61
+ *
62
+ * Keyed off the backend, as the cache is: the target already says which broker
63
+ * carries the app's topics and queues. SNS registers SQS beside it, because on
64
+ * AWS a topic is `sns://` and a queue is `sqs://`, and an entry registering one
65
+ * would publish to topics and fail on the first queue.
66
+ */
67
+ const EVENTS_DRIVERS = {
68
+ pgboss: {
69
+ imports: `import { registerEventsDriver } from '@geekmidas/events';\nimport { pgbossEventsDriver } from '@geekmidas/events/pgboss';`,
70
+ setup: "registerEventsDriver(pgbossEventsDriver);"
71
+ },
72
+ sns: {
73
+ imports: `import { registerEventsDriver } from '@geekmidas/events';\nimport { snsEventsDriver } from '@geekmidas/events/sns';\nimport { sqsEventsDriver } from '@geekmidas/events/sqs';`,
74
+ setup: "registerEventsDriver(snsEventsDriver);\nregisterEventsDriver(sqsEventsDriver);"
75
+ },
76
+ rabbitmq: {
77
+ imports: `import { registerEventsDriver } from '@geekmidas/events';\nimport { rabbitmqEventsDriver } from '@geekmidas/events/rabbitmq';`,
78
+ setup: "registerEventsDriver(rabbitmqEventsDriver);"
79
+ }
80
+ };
81
+ /**
82
+ * Everything a generated entry should register: object storage, the cache,
83
+ * and the events broker.
84
+ *
85
+ * Merged here rather than threaded separately, because a generated file has one
86
+ * import block and one setup block whatever fills them.
87
+ */
88
+ function driversFor(options) {
89
+ const backends = listOf(options.cache);
90
+ const brokers = listOf(options.events);
91
+ const parts = [
92
+ storageDriversFor(options.appRoot),
93
+ ...[...new Set(backends)].map((backend) => CACHE_DRIVERS[backend] ?? CACHE_DRIVERS[require_SubscriberGenerator.DEFAULT_CACHE.aws]),
94
+ ...[...new Set(brokers)].map((broker) => EVENTS_DRIVERS[broker])
95
+ ].filter((part) => part.imports || part.setup);
96
+ if (parts.length === 0) return NONE;
97
+ return {
98
+ imports: parts.map((part) => part.imports).join("\n"),
99
+ setup: parts.map((part) => part.setup).join("\n")
100
+ };
101
+ }
102
+ function listOf(value) {
103
+ if (value === false || value === void 0) return [];
104
+ return typeof value === "string" ? [value] : value;
105
+ }
106
+ /**
107
+ * Which events brokers an app's entry has to speak: the target's, when the app
108
+ * declares a `Topic` or a `Queue` — and none at all when it declares neither,
109
+ * so a project without events never resolves `@geekmidas/events`.
110
+ *
111
+ * A worker on pg-boss counts too: a server schedules its crons through the
112
+ * pg-boss broker, which the plan provisions for a declared worker beside a
113
+ * declared database (`workerBroker` in `reconcile/plan.ts`) whether or not
114
+ * anything publishes.
115
+ */
116
+ function eventsBackendsIn(manifest, configured) {
117
+ const kinds = new Set(Object.values(manifest).map((d) => d.kind));
118
+ return kinds.has("topic") || kinds.has("queue") || configured === "pgboss" && kinds.has("worker") && kinds.has("database") ? [configured] : [];
119
+ }
120
+ /**
121
+ * Which cache backends an app's entry actually has to speak.
122
+ *
123
+ * The config answers this for a cache that named nowhere, and the *declaration*
124
+ * answers it for one that named a database — `orders.cache('Sessions')` is a
125
+ * statement about the application, so a deployment cannot move that cache and
126
+ * the entry cannot register a driver for somewhere else.
127
+ *
128
+ * Reading only the config is how an entry ends up registering the Upstash
129
+ * driver for a URL the target composed as `postgres://`, which fails at the
130
+ * first request with `UnregisteredCacheScheme` and a stack that points at the
131
+ * cache rather than at the config that disagreed.
132
+ */
133
+ function cacheBackendsIn(manifest, configured) {
134
+ const caches = Object.values(manifest).filter((d) => d.kind === "cache");
135
+ return [...new Set(caches.map((c) => c.of && !require_SubscriberGenerator.holdsEveryCache(configured) ? "db" : configured))];
136
+ }
137
+ /**
138
+ * The drivers an app's entry points should register.
139
+ *
140
+ * Keyed off the dependency rather than off a declared bucket, because the entry
141
+ * is generated before discovery has run on the watcher's rebuild path — and an
142
+ * app that installed `@geekmidas/storage` has already paid for the SDK it would
143
+ * otherwise resolve lazily.
144
+ *
145
+ * "Installed" the way Node resolves it: the app's own `package.json`, or any
146
+ * directory above it. A workspace that lists `@geekmidas/storage` once, at its
147
+ * root, is the common case — and reading only the app's file registered no S3
148
+ * driver there, so the first service to inject a bucket threw
149
+ * `UnregisteredStorageScheme`. A queue whose consumer depended on one logged
150
+ * that once and was never polled.
151
+ */
152
+ function storageDriversFor(appRoot) {
153
+ return installedFrom(appRoot, "@geekmidas/storage") ? S3 : NONE;
154
+ }
155
+ /**
156
+ * Whether `pkg` is a dependency of the app at `appRoot` the way Node resolves
157
+ * it: listed in the app's own `package.json`, or in any directory above it.
158
+ */
159
+ function installedFrom(appRoot, pkg) {
160
+ for (let dir = (0, node_path.resolve)(appRoot);; dir = (0, node_path.dirname)(dir)) {
161
+ if (lists((0, node_path.join)(dir, "package.json"), pkg)) return true;
162
+ if ((0, node_path.dirname)(dir) === dir) return false;
163
+ }
164
+ }
165
+ function lists(manifest, dependency) {
166
+ if (!(0, node_fs.existsSync)(manifest)) return false;
167
+ try {
168
+ const pkg = JSON.parse((0, node_fs.readFileSync)(manifest, "utf8"));
169
+ return Boolean(pkg.dependencies?.[dependency] ?? pkg.devDependencies?.[dependency]);
170
+ } catch {
171
+ return false;
172
+ }
173
+ }
174
+ //#endregion
175
+ Object.defineProperty(exports, "cacheBackendsIn", {
176
+ enumerable: true,
177
+ get: function() {
178
+ return cacheBackendsIn;
179
+ }
180
+ });
181
+ Object.defineProperty(exports, "driversFor", {
182
+ enumerable: true,
183
+ get: function() {
184
+ return driversFor;
185
+ }
186
+ });
187
+ Object.defineProperty(exports, "eventsBackendsIn", {
188
+ enumerable: true,
189
+ get: function() {
190
+ return eventsBackendsIn;
191
+ }
192
+ });
193
+ Object.defineProperty(exports, "installedFrom", {
194
+ enumerable: true,
195
+ get: function() {
196
+ return installedFrom;
197
+ }
198
+ });
199
+
200
+ //# sourceMappingURL=drivers-BdvtCKc7.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drivers-BdvtCKc7.cjs","names":["DEFAULT_CACHE","holdsEveryCache","resolve","dirname","join","existsSync","readFileSync"],"sources":["../src/generators/drivers.ts"],"sourcesContent":["/**\n * Driver registration for generated entry points.\n *\n * A construct declares that it needs object storage and is handed one URL; the\n * scheme in that URL picks the driver that builds the client. Which drivers\n * exist is therefore the *entry point's* decision, not the construct's and not\n * the application's — an app that never talks to S3 should never resolve the AWS\n * SDK, and application code that registers a driver has named a provider in the\n * one layer this design keeps provider-free.\n *\n * So the generated entries register: `gkm dev`'s server for local development,\n * and each Lambda handler for its own target. Registration is an explicit call\n * rather than an import side effect, because a side-effecting module is exactly\n * what a bundler is entitled to drop.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport {\n\ttype CacheBackend,\n\tDEFAULT_CACHE,\n\ttype EventsBackend,\n\tholdsEveryCache,\n} from '../types.js';\n\n/** The `s3://` driver, which serves MinIO locally and S3 deployed. */\nconst S3 = {\n\timports: `import { registerStorageDriver } from '@geekmidas/storage';\\nimport { s3Driver } from '@geekmidas/storage/aws';`,\n\tsetup: 'registerStorageDriver(s3Driver);',\n};\n\n/** What a generated entry needs in order to register the drivers it uses. */\nexport interface RuntimeDrivers {\n\t/** Import lines, or `''` when there is nothing to register. */\n\timports: string;\n\t/** The registration calls, or `''`. */\n\tsetup: string;\n}\n\n/** @deprecated The same shape, from when only storage had drivers. */\nexport type StorageDrivers = RuntimeDrivers;\n\nconst NONE: RuntimeDrivers = { imports: '', setup: '' };\n\n/** The Redis wire protocol's drivers: both schemes, plain and TLS. */\nconst REDIS: RuntimeDrivers = {\n\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { redisCacheDriver, redissCacheDriver } from '@geekmidas/cache/redis';`,\n\tsetup:\n\t\t'registerCacheDriver(redisCacheDriver);\\nregisterCacheDriver(redissCacheDriver);',\n};\n\n/**\n * The cache driver for a backend — exactly one, never all three.\n *\n * Keyed off the *backend* rather than off a dependency, because unlike storage\n * the backend already says which driver is right: a project caching in Postgres\n * resolves `pg` and never `ioredis`, and one caching in Upstash resolves\n * neither. That is the whole reason the drivers live behind separate subpaths.\n *\n * Both HTTP schemes are registered for Upstash, because the local proxy speaks\n * `http://` and Upstash speaks `https://`, and an entry that registered one\n * would work in exactly one of the two places.\n */\nconst CACHE_DRIVERS: Record<CacheBackend, RuntimeDrivers> = {\n\tupstash: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { upstashCacheDriver, upstashInsecureCacheDriver } from '@geekmidas/cache/upstash';`,\n\t\tsetup:\n\t\t\t'registerCacheDriver(upstashCacheDriver);\\nregisterCacheDriver(upstashInsecureCacheDriver);',\n\t},\n\telasticache: REDIS,\n\t// The stack's own Redis speaks the same wire protocol — and a managed\n\t// Redis set in its place may be `rediss://`.\n\tredis: REDIS,\n\tdb: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { postgresCacheDriver } from '@geekmidas/cache/postgres';`,\n\t\tsetup: 'registerCacheDriver(postgresCacheDriver);',\n\t},\n};\n\n/**\n * The events drivers for a broker — only its own, so a bundle never resolves\n * another broker's client library.\n *\n * Keyed off the backend, as the cache is: the target already says which broker\n * carries the app's topics and queues. SNS registers SQS beside it, because on\n * AWS a topic is `sns://` and a queue is `sqs://`, and an entry registering one\n * would publish to topics and fail on the first queue.\n */\nconst EVENTS_DRIVERS: Record<EventsBackend, RuntimeDrivers> = {\n\tpgboss: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { pgbossEventsDriver } from '@geekmidas/events/pgboss';`,\n\t\tsetup: 'registerEventsDriver(pgbossEventsDriver);',\n\t},\n\tsns: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { snsEventsDriver } from '@geekmidas/events/sns';\\nimport { sqsEventsDriver } from '@geekmidas/events/sqs';`,\n\t\tsetup:\n\t\t\t'registerEventsDriver(snsEventsDriver);\\nregisterEventsDriver(sqsEventsDriver);',\n\t},\n\trabbitmq: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { rabbitmqEventsDriver } from '@geekmidas/events/rabbitmq';`,\n\t\tsetup: 'registerEventsDriver(rabbitmqEventsDriver);',\n\t},\n};\n\n/**\n * Everything a generated entry should register: object storage, the cache,\n * and the events broker.\n *\n * Merged here rather than threaded separately, because a generated file has one\n * import block and one setup block whatever fills them.\n */\nexport function driversFor(options: {\n\tappRoot: string;\n\t/**\n\t * The cache backends actually in play — normally one, and more than one only\n\t * where an app declares two caches that live in different places.\n\t *\n\t * Absent or empty means nothing declared a cache, so no cache driver is\n\t * registered at all.\n\t */\n\tcache?: CacheBackend | readonly CacheBackend[] | false;\n\t/**\n\t * The events brokers in play — see {@link eventsBackendsIn}. Absent or\n\t * empty means nothing declared a topic or a queue, so no events driver is\n\t * registered and `@geekmidas/events` is never resolved.\n\t */\n\tevents?: EventsBackend | readonly EventsBackend[] | false;\n}): RuntimeDrivers {\n\tconst backends = listOf(options.cache);\n\tconst brokers = listOf(options.events);\n\n\tconst parts = [\n\t\tstorageDriversFor(options.appRoot),\n\t\t...[...new Set(backends)].map(\n\t\t\t(backend) => CACHE_DRIVERS[backend] ?? CACHE_DRIVERS[DEFAULT_CACHE.aws],\n\t\t),\n\t\t...[...new Set(brokers)].map((broker) => EVENTS_DRIVERS[broker]),\n\t].filter((part) => part.imports || part.setup);\n\n\tif (parts.length === 0) return NONE;\n\n\treturn {\n\t\timports: parts.map((part) => part.imports).join('\\n'),\n\t\tsetup: parts.map((part) => part.setup).join('\\n'),\n\t};\n}\n\nfunction listOf<T extends string>(\n\tvalue: T | readonly T[] | false | undefined,\n): readonly T[] {\n\tif (value === false || value === undefined) return [];\n\treturn typeof value === 'string' ? [value] : (value as readonly T[]);\n}\n\n/**\n * Which events brokers an app's entry has to speak: the target's, when the app\n * declares a `Topic` or a `Queue` — and none at all when it declares neither,\n * so a project without events never resolves `@geekmidas/events`.\n *\n * A worker on pg-boss counts too: a server schedules its crons through the\n * pg-boss broker, which the plan provisions for a declared worker beside a\n * declared database (`workerBroker` in `reconcile/plan.ts`) whether or not\n * anything publishes.\n */\nexport function eventsBackendsIn(\n\tmanifest: Record<string, { kind: string }>,\n\tconfigured: EventsBackend,\n): EventsBackend[] {\n\tconst kinds = new Set(Object.values(manifest).map((d) => d.kind));\n\tconst declared =\n\t\tkinds.has('topic') ||\n\t\tkinds.has('queue') ||\n\t\t(configured === 'pgboss' && kinds.has('worker') && kinds.has('database'));\n\n\treturn declared ? [configured] : [];\n}\n\n/**\n * Which cache backends an app's entry actually has to speak.\n *\n * The config answers this for a cache that named nowhere, and the *declaration*\n * answers it for one that named a database — `orders.cache('Sessions')` is a\n * statement about the application, so a deployment cannot move that cache and\n * the entry cannot register a driver for somewhere else.\n *\n * Reading only the config is how an entry ends up registering the Upstash\n * driver for a URL the target composed as `postgres://`, which fails at the\n * first request with `UnregisteredCacheScheme` and a stack that points at the\n * cache rather than at the config that disagreed.\n */\nexport function cacheBackendsIn(\n\tmanifest: Record<string, { kind: string; of?: string }>,\n\tconfigured: CacheBackend,\n): CacheBackend[] {\n\tconst caches = Object.values(manifest).filter((d) => d.kind === 'cache');\n\n\t// The stack's own Redis holds every cache, one that named a database too.\n\treturn [\n\t\t...new Set(\n\t\t\tcaches.map((c) =>\n\t\t\t\tc.of && !holdsEveryCache(configured) ? 'db' : configured,\n\t\t\t),\n\t\t),\n\t];\n}\n\n/**\n * The drivers an app's entry points should register.\n *\n * Keyed off the dependency rather than off a declared bucket, because the entry\n * is generated before discovery has run on the watcher's rebuild path — and an\n * app that installed `@geekmidas/storage` has already paid for the SDK it would\n * otherwise resolve lazily.\n *\n * \"Installed\" the way Node resolves it: the app's own `package.json`, or any\n * directory above it. A workspace that lists `@geekmidas/storage` once, at its\n * root, is the common case — and reading only the app's file registered no S3\n * driver there, so the first service to inject a bucket threw\n * `UnregisteredStorageScheme`. A queue whose consumer depended on one logged\n * that once and was never polled.\n */\nexport function storageDriversFor(appRoot: string): RuntimeDrivers {\n\treturn installedFrom(appRoot, '@geekmidas/storage') ? S3 : NONE;\n}\n\n/**\n * Whether `pkg` is a dependency of the app at `appRoot` the way Node resolves\n * it: listed in the app's own `package.json`, or in any directory above it.\n */\nexport function installedFrom(appRoot: string, pkg: string): boolean {\n\tfor (let dir = resolve(appRoot); ; dir = dirname(dir)) {\n\t\tif (lists(join(dir, 'package.json'), pkg)) return true;\n\t\tif (dirname(dir) === dir) return false;\n\t}\n}\n\nfunction lists(manifest: string, dependency: string): boolean {\n\tif (!existsSync(manifest)) return false;\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(manifest, 'utf8')) as {\n\t\t\tdependencies?: Record<string, string>;\n\t\t\tdevDependencies?: Record<string, string>;\n\t\t};\n\n\t\treturn Boolean(\n\t\t\tpkg.dependencies?.[dependency] ?? pkg.devDependencies?.[dependency],\n\t\t);\n\t} catch {\n\t\t// An unreadable package.json is the build's problem, not this function's.\n\t\treturn false;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA0BA,MAAM,KAAK;CACV,SAAS;CACT,OAAO;AACR;AAaA,MAAM,OAAuB;CAAE,SAAS;CAAI,OAAO;AAAG;;AAGtD,MAAM,QAAwB;CAC7B,SAAS;CACT,OACC;AACF;;;;;;;;;;;;;AAcA,MAAM,gBAAsD;CAC3D,SAAS;EACR,SAAS;EACT,OACC;CACF;CACA,aAAa;CAGb,OAAO;CACP,IAAI;EACH,SAAS;EACT,OAAO;CACR;AACD;;;;;;;;;;AAWA,MAAM,iBAAwD;CAC7D,QAAQ;EACP,SAAS;EACT,OAAO;CACR;CACA,KAAK;EACJ,SAAS;EACT,OACC;CACF;CACA,UAAU;EACT,SAAS;EACT,OAAO;CACR;AACD;;;;;;;;AASA,SAAgB,WAAW,SAgBR;CAClB,MAAM,WAAW,OAAO,QAAQ,KAAK;CACrC,MAAM,UAAU,OAAO,QAAQ,MAAM;CAErC,MAAM,QAAQ;EACb,kBAAkB,QAAQ,OAAO;EACjC,GAAG,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,KACxB,YAAY,cAAc,YAAY,cAAcA,4BAAAA,cAAc,IACpE;EACA,GAAG,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,WAAW,eAAe,OAAO;CAChE,CAAC,CAAC,QAAQ,SAAS,KAAK,WAAW,KAAK,KAAK;CAE7C,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,OAAO;EACN,SAAS,MAAM,KAAK,SAAS,KAAK,OAAO,CAAC,CAAC,KAAK,IAAI;EACpD,OAAO,MAAM,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI;CACjD;AACD;AAEA,SAAS,OACR,OACe;CACf,IAAI,UAAU,SAAS,UAAU,KAAA,GAAW,OAAO,CAAC;CACpD,OAAO,OAAO,UAAU,WAAW,CAAC,KAAK,IAAK;AAC/C;;;;;;;;;;;AAYA,SAAgB,iBACf,UACA,YACkB;CAClB,MAAM,QAAQ,IAAI,IAAI,OAAO,OAAO,QAAQ,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI,CAAC;CAMhE,OAJC,MAAM,IAAI,OAAO,KACjB,MAAM,IAAI,OAAO,KAChB,eAAe,YAAY,MAAM,IAAI,QAAQ,KAAK,MAAM,IAAI,UAAU,IAEtD,CAAC,UAAU,IAAI,CAAC;AACnC;;;;;;;;;;;;;;AAeA,SAAgB,gBACf,UACA,YACiB;CACjB,MAAM,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC,QAAQ,MAAM,EAAE,SAAS,OAAO;CAGvE,OAAO,CACN,GAAG,IAAI,IACN,OAAO,KAAK,MACX,EAAE,MAAM,CAACC,4BAAAA,gBAAgB,UAAU,IAAI,OAAO,UAC/C,CACD,CACD;AACD;;;;;;;;;;;;;;;;AAiBA,SAAgB,kBAAkB,SAAiC;CAClE,OAAO,cAAc,SAAS,oBAAoB,IAAI,KAAK;AAC5D;;;;;AAMA,SAAgB,cAAc,SAAiB,KAAsB;CACpE,KAAK,IAAI,OAAA,GAAMC,UAAAA,QAAAA,CAAQ,OAAO,IAAK,OAAA,GAAMC,UAAAA,QAAAA,CAAQ,GAAG,GAAG;EACtD,IAAI,OAAA,GAAMC,UAAAA,KAAAA,CAAK,KAAK,cAAc,GAAG,GAAG,GAAG,OAAO;EAClD,KAAA,GAAID,UAAAA,QAAAA,CAAQ,GAAG,MAAM,KAAK,OAAO;CAClC;AACD;AAEA,SAAS,MAAM,UAAkB,YAA6B;CAC7D,IAAI,EAAA,GAACE,QAAAA,WAAAA,CAAW,QAAQ,GAAG,OAAO;CAElC,IAAI;EACH,MAAM,MAAM,KAAK,OAAA,GAAMC,QAAAA,aAAAA,CAAa,UAAU,MAAM,CAAC;EAKrD,OAAO,QACN,IAAI,eAAe,eAAe,IAAI,kBAAkB,WACzD;CACD,QAAQ;EAEP,OAAO;CACR;AACD"}
@@ -0,0 +1,177 @@
1
+ import { o as holdsEveryCache, r as DEFAULT_CACHE } from "./SubscriberGenerator-5looQ3gB.mjs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ //#region src/generators/drivers.ts
5
+ /**
6
+ * Driver registration for generated entry points.
7
+ *
8
+ * A construct declares that it needs object storage and is handed one URL; the
9
+ * scheme in that URL picks the driver that builds the client. Which drivers
10
+ * exist is therefore the *entry point's* decision, not the construct's and not
11
+ * the application's — an app that never talks to S3 should never resolve the AWS
12
+ * SDK, and application code that registers a driver has named a provider in the
13
+ * one layer this design keeps provider-free.
14
+ *
15
+ * So the generated entries register: `gkm dev`'s server for local development,
16
+ * and each Lambda handler for its own target. Registration is an explicit call
17
+ * rather than an import side effect, because a side-effecting module is exactly
18
+ * what a bundler is entitled to drop.
19
+ */
20
+ /** The `s3://` driver, which serves MinIO locally and S3 deployed. */
21
+ const S3 = {
22
+ imports: `import { registerStorageDriver } from '@geekmidas/storage';\nimport { s3Driver } from '@geekmidas/storage/aws';`,
23
+ setup: "registerStorageDriver(s3Driver);"
24
+ };
25
+ const NONE = {
26
+ imports: "",
27
+ setup: ""
28
+ };
29
+ /** The Redis wire protocol's drivers: both schemes, plain and TLS. */
30
+ const REDIS = {
31
+ imports: `import { registerCacheDriver } from '@geekmidas/cache';\nimport { redisCacheDriver, redissCacheDriver } from '@geekmidas/cache/redis';`,
32
+ setup: "registerCacheDriver(redisCacheDriver);\nregisterCacheDriver(redissCacheDriver);"
33
+ };
34
+ /**
35
+ * The cache driver for a backend — exactly one, never all three.
36
+ *
37
+ * Keyed off the *backend* rather than off a dependency, because unlike storage
38
+ * the backend already says which driver is right: a project caching in Postgres
39
+ * resolves `pg` and never `ioredis`, and one caching in Upstash resolves
40
+ * neither. That is the whole reason the drivers live behind separate subpaths.
41
+ *
42
+ * Both HTTP schemes are registered for Upstash, because the local proxy speaks
43
+ * `http://` and Upstash speaks `https://`, and an entry that registered one
44
+ * would work in exactly one of the two places.
45
+ */
46
+ const CACHE_DRIVERS = {
47
+ upstash: {
48
+ imports: `import { registerCacheDriver } from '@geekmidas/cache';\nimport { upstashCacheDriver, upstashInsecureCacheDriver } from '@geekmidas/cache/upstash';`,
49
+ setup: "registerCacheDriver(upstashCacheDriver);\nregisterCacheDriver(upstashInsecureCacheDriver);"
50
+ },
51
+ elasticache: REDIS,
52
+ redis: REDIS,
53
+ db: {
54
+ imports: `import { registerCacheDriver } from '@geekmidas/cache';\nimport { postgresCacheDriver } from '@geekmidas/cache/postgres';`,
55
+ setup: "registerCacheDriver(postgresCacheDriver);"
56
+ }
57
+ };
58
+ /**
59
+ * The events drivers for a broker — only its own, so a bundle never resolves
60
+ * another broker's client library.
61
+ *
62
+ * Keyed off the backend, as the cache is: the target already says which broker
63
+ * carries the app's topics and queues. SNS registers SQS beside it, because on
64
+ * AWS a topic is `sns://` and a queue is `sqs://`, and an entry registering one
65
+ * would publish to topics and fail on the first queue.
66
+ */
67
+ const EVENTS_DRIVERS = {
68
+ pgboss: {
69
+ imports: `import { registerEventsDriver } from '@geekmidas/events';\nimport { pgbossEventsDriver } from '@geekmidas/events/pgboss';`,
70
+ setup: "registerEventsDriver(pgbossEventsDriver);"
71
+ },
72
+ sns: {
73
+ imports: `import { registerEventsDriver } from '@geekmidas/events';\nimport { snsEventsDriver } from '@geekmidas/events/sns';\nimport { sqsEventsDriver } from '@geekmidas/events/sqs';`,
74
+ setup: "registerEventsDriver(snsEventsDriver);\nregisterEventsDriver(sqsEventsDriver);"
75
+ },
76
+ rabbitmq: {
77
+ imports: `import { registerEventsDriver } from '@geekmidas/events';\nimport { rabbitmqEventsDriver } from '@geekmidas/events/rabbitmq';`,
78
+ setup: "registerEventsDriver(rabbitmqEventsDriver);"
79
+ }
80
+ };
81
+ /**
82
+ * Everything a generated entry should register: object storage, the cache,
83
+ * and the events broker.
84
+ *
85
+ * Merged here rather than threaded separately, because a generated file has one
86
+ * import block and one setup block whatever fills them.
87
+ */
88
+ function driversFor(options) {
89
+ const backends = listOf(options.cache);
90
+ const brokers = listOf(options.events);
91
+ const parts = [
92
+ storageDriversFor(options.appRoot),
93
+ ...[...new Set(backends)].map((backend) => CACHE_DRIVERS[backend] ?? CACHE_DRIVERS[DEFAULT_CACHE.aws]),
94
+ ...[...new Set(brokers)].map((broker) => EVENTS_DRIVERS[broker])
95
+ ].filter((part) => part.imports || part.setup);
96
+ if (parts.length === 0) return NONE;
97
+ return {
98
+ imports: parts.map((part) => part.imports).join("\n"),
99
+ setup: parts.map((part) => part.setup).join("\n")
100
+ };
101
+ }
102
+ function listOf(value) {
103
+ if (value === false || value === void 0) return [];
104
+ return typeof value === "string" ? [value] : value;
105
+ }
106
+ /**
107
+ * Which events brokers an app's entry has to speak: the target's, when the app
108
+ * declares a `Topic` or a `Queue` — and none at all when it declares neither,
109
+ * so a project without events never resolves `@geekmidas/events`.
110
+ *
111
+ * A worker on pg-boss counts too: a server schedules its crons through the
112
+ * pg-boss broker, which the plan provisions for a declared worker beside a
113
+ * declared database (`workerBroker` in `reconcile/plan.ts`) whether or not
114
+ * anything publishes.
115
+ */
116
+ function eventsBackendsIn(manifest, configured) {
117
+ const kinds = new Set(Object.values(manifest).map((d) => d.kind));
118
+ return kinds.has("topic") || kinds.has("queue") || configured === "pgboss" && kinds.has("worker") && kinds.has("database") ? [configured] : [];
119
+ }
120
+ /**
121
+ * Which cache backends an app's entry actually has to speak.
122
+ *
123
+ * The config answers this for a cache that named nowhere, and the *declaration*
124
+ * answers it for one that named a database — `orders.cache('Sessions')` is a
125
+ * statement about the application, so a deployment cannot move that cache and
126
+ * the entry cannot register a driver for somewhere else.
127
+ *
128
+ * Reading only the config is how an entry ends up registering the Upstash
129
+ * driver for a URL the target composed as `postgres://`, which fails at the
130
+ * first request with `UnregisteredCacheScheme` and a stack that points at the
131
+ * cache rather than at the config that disagreed.
132
+ */
133
+ function cacheBackendsIn(manifest, configured) {
134
+ const caches = Object.values(manifest).filter((d) => d.kind === "cache");
135
+ return [...new Set(caches.map((c) => c.of && !holdsEveryCache(configured) ? "db" : configured))];
136
+ }
137
+ /**
138
+ * The drivers an app's entry points should register.
139
+ *
140
+ * Keyed off the dependency rather than off a declared bucket, because the entry
141
+ * is generated before discovery has run on the watcher's rebuild path — and an
142
+ * app that installed `@geekmidas/storage` has already paid for the SDK it would
143
+ * otherwise resolve lazily.
144
+ *
145
+ * "Installed" the way Node resolves it: the app's own `package.json`, or any
146
+ * directory above it. A workspace that lists `@geekmidas/storage` once, at its
147
+ * root, is the common case — and reading only the app's file registered no S3
148
+ * driver there, so the first service to inject a bucket threw
149
+ * `UnregisteredStorageScheme`. A queue whose consumer depended on one logged
150
+ * that once and was never polled.
151
+ */
152
+ function storageDriversFor(appRoot) {
153
+ return installedFrom(appRoot, "@geekmidas/storage") ? S3 : NONE;
154
+ }
155
+ /**
156
+ * Whether `pkg` is a dependency of the app at `appRoot` the way Node resolves
157
+ * it: listed in the app's own `package.json`, or in any directory above it.
158
+ */
159
+ function installedFrom(appRoot, pkg) {
160
+ for (let dir = resolve(appRoot);; dir = dirname(dir)) {
161
+ if (lists(join(dir, "package.json"), pkg)) return true;
162
+ if (dirname(dir) === dir) return false;
163
+ }
164
+ }
165
+ function lists(manifest, dependency) {
166
+ if (!existsSync(manifest)) return false;
167
+ try {
168
+ const pkg = JSON.parse(readFileSync(manifest, "utf8"));
169
+ return Boolean(pkg.dependencies?.[dependency] ?? pkg.devDependencies?.[dependency]);
170
+ } catch {
171
+ return false;
172
+ }
173
+ }
174
+ //#endregion
175
+ export { installedFrom as i, driversFor as n, eventsBackendsIn as r, cacheBackendsIn as t };
176
+
177
+ //# sourceMappingURL=drivers-Ch1rJitn.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drivers-Ch1rJitn.mjs","names":[],"sources":["../src/generators/drivers.ts"],"sourcesContent":["/**\n * Driver registration for generated entry points.\n *\n * A construct declares that it needs object storage and is handed one URL; the\n * scheme in that URL picks the driver that builds the client. Which drivers\n * exist is therefore the *entry point's* decision, not the construct's and not\n * the application's — an app that never talks to S3 should never resolve the AWS\n * SDK, and application code that registers a driver has named a provider in the\n * one layer this design keeps provider-free.\n *\n * So the generated entries register: `gkm dev`'s server for local development,\n * and each Lambda handler for its own target. Registration is an explicit call\n * rather than an import side effect, because a side-effecting module is exactly\n * what a bundler is entitled to drop.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport {\n\ttype CacheBackend,\n\tDEFAULT_CACHE,\n\ttype EventsBackend,\n\tholdsEveryCache,\n} from '../types.js';\n\n/** The `s3://` driver, which serves MinIO locally and S3 deployed. */\nconst S3 = {\n\timports: `import { registerStorageDriver } from '@geekmidas/storage';\\nimport { s3Driver } from '@geekmidas/storage/aws';`,\n\tsetup: 'registerStorageDriver(s3Driver);',\n};\n\n/** What a generated entry needs in order to register the drivers it uses. */\nexport interface RuntimeDrivers {\n\t/** Import lines, or `''` when there is nothing to register. */\n\timports: string;\n\t/** The registration calls, or `''`. */\n\tsetup: string;\n}\n\n/** @deprecated The same shape, from when only storage had drivers. */\nexport type StorageDrivers = RuntimeDrivers;\n\nconst NONE: RuntimeDrivers = { imports: '', setup: '' };\n\n/** The Redis wire protocol's drivers: both schemes, plain and TLS. */\nconst REDIS: RuntimeDrivers = {\n\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { redisCacheDriver, redissCacheDriver } from '@geekmidas/cache/redis';`,\n\tsetup:\n\t\t'registerCacheDriver(redisCacheDriver);\\nregisterCacheDriver(redissCacheDriver);',\n};\n\n/**\n * The cache driver for a backend — exactly one, never all three.\n *\n * Keyed off the *backend* rather than off a dependency, because unlike storage\n * the backend already says which driver is right: a project caching in Postgres\n * resolves `pg` and never `ioredis`, and one caching in Upstash resolves\n * neither. That is the whole reason the drivers live behind separate subpaths.\n *\n * Both HTTP schemes are registered for Upstash, because the local proxy speaks\n * `http://` and Upstash speaks `https://`, and an entry that registered one\n * would work in exactly one of the two places.\n */\nconst CACHE_DRIVERS: Record<CacheBackend, RuntimeDrivers> = {\n\tupstash: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { upstashCacheDriver, upstashInsecureCacheDriver } from '@geekmidas/cache/upstash';`,\n\t\tsetup:\n\t\t\t'registerCacheDriver(upstashCacheDriver);\\nregisterCacheDriver(upstashInsecureCacheDriver);',\n\t},\n\telasticache: REDIS,\n\t// The stack's own Redis speaks the same wire protocol — and a managed\n\t// Redis set in its place may be `rediss://`.\n\tredis: REDIS,\n\tdb: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { postgresCacheDriver } from '@geekmidas/cache/postgres';`,\n\t\tsetup: 'registerCacheDriver(postgresCacheDriver);',\n\t},\n};\n\n/**\n * The events drivers for a broker — only its own, so a bundle never resolves\n * another broker's client library.\n *\n * Keyed off the backend, as the cache is: the target already says which broker\n * carries the app's topics and queues. SNS registers SQS beside it, because on\n * AWS a topic is `sns://` and a queue is `sqs://`, and an entry registering one\n * would publish to topics and fail on the first queue.\n */\nconst EVENTS_DRIVERS: Record<EventsBackend, RuntimeDrivers> = {\n\tpgboss: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { pgbossEventsDriver } from '@geekmidas/events/pgboss';`,\n\t\tsetup: 'registerEventsDriver(pgbossEventsDriver);',\n\t},\n\tsns: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { snsEventsDriver } from '@geekmidas/events/sns';\\nimport { sqsEventsDriver } from '@geekmidas/events/sqs';`,\n\t\tsetup:\n\t\t\t'registerEventsDriver(snsEventsDriver);\\nregisterEventsDriver(sqsEventsDriver);',\n\t},\n\trabbitmq: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { rabbitmqEventsDriver } from '@geekmidas/events/rabbitmq';`,\n\t\tsetup: 'registerEventsDriver(rabbitmqEventsDriver);',\n\t},\n};\n\n/**\n * Everything a generated entry should register: object storage, the cache,\n * and the events broker.\n *\n * Merged here rather than threaded separately, because a generated file has one\n * import block and one setup block whatever fills them.\n */\nexport function driversFor(options: {\n\tappRoot: string;\n\t/**\n\t * The cache backends actually in play — normally one, and more than one only\n\t * where an app declares two caches that live in different places.\n\t *\n\t * Absent or empty means nothing declared a cache, so no cache driver is\n\t * registered at all.\n\t */\n\tcache?: CacheBackend | readonly CacheBackend[] | false;\n\t/**\n\t * The events brokers in play — see {@link eventsBackendsIn}. Absent or\n\t * empty means nothing declared a topic or a queue, so no events driver is\n\t * registered and `@geekmidas/events` is never resolved.\n\t */\n\tevents?: EventsBackend | readonly EventsBackend[] | false;\n}): RuntimeDrivers {\n\tconst backends = listOf(options.cache);\n\tconst brokers = listOf(options.events);\n\n\tconst parts = [\n\t\tstorageDriversFor(options.appRoot),\n\t\t...[...new Set(backends)].map(\n\t\t\t(backend) => CACHE_DRIVERS[backend] ?? CACHE_DRIVERS[DEFAULT_CACHE.aws],\n\t\t),\n\t\t...[...new Set(brokers)].map((broker) => EVENTS_DRIVERS[broker]),\n\t].filter((part) => part.imports || part.setup);\n\n\tif (parts.length === 0) return NONE;\n\n\treturn {\n\t\timports: parts.map((part) => part.imports).join('\\n'),\n\t\tsetup: parts.map((part) => part.setup).join('\\n'),\n\t};\n}\n\nfunction listOf<T extends string>(\n\tvalue: T | readonly T[] | false | undefined,\n): readonly T[] {\n\tif (value === false || value === undefined) return [];\n\treturn typeof value === 'string' ? [value] : (value as readonly T[]);\n}\n\n/**\n * Which events brokers an app's entry has to speak: the target's, when the app\n * declares a `Topic` or a `Queue` — and none at all when it declares neither,\n * so a project without events never resolves `@geekmidas/events`.\n *\n * A worker on pg-boss counts too: a server schedules its crons through the\n * pg-boss broker, which the plan provisions for a declared worker beside a\n * declared database (`workerBroker` in `reconcile/plan.ts`) whether or not\n * anything publishes.\n */\nexport function eventsBackendsIn(\n\tmanifest: Record<string, { kind: string }>,\n\tconfigured: EventsBackend,\n): EventsBackend[] {\n\tconst kinds = new Set(Object.values(manifest).map((d) => d.kind));\n\tconst declared =\n\t\tkinds.has('topic') ||\n\t\tkinds.has('queue') ||\n\t\t(configured === 'pgboss' && kinds.has('worker') && kinds.has('database'));\n\n\treturn declared ? [configured] : [];\n}\n\n/**\n * Which cache backends an app's entry actually has to speak.\n *\n * The config answers this for a cache that named nowhere, and the *declaration*\n * answers it for one that named a database — `orders.cache('Sessions')` is a\n * statement about the application, so a deployment cannot move that cache and\n * the entry cannot register a driver for somewhere else.\n *\n * Reading only the config is how an entry ends up registering the Upstash\n * driver for a URL the target composed as `postgres://`, which fails at the\n * first request with `UnregisteredCacheScheme` and a stack that points at the\n * cache rather than at the config that disagreed.\n */\nexport function cacheBackendsIn(\n\tmanifest: Record<string, { kind: string; of?: string }>,\n\tconfigured: CacheBackend,\n): CacheBackend[] {\n\tconst caches = Object.values(manifest).filter((d) => d.kind === 'cache');\n\n\t// The stack's own Redis holds every cache, one that named a database too.\n\treturn [\n\t\t...new Set(\n\t\t\tcaches.map((c) =>\n\t\t\t\tc.of && !holdsEveryCache(configured) ? 'db' : configured,\n\t\t\t),\n\t\t),\n\t];\n}\n\n/**\n * The drivers an app's entry points should register.\n *\n * Keyed off the dependency rather than off a declared bucket, because the entry\n * is generated before discovery has run on the watcher's rebuild path — and an\n * app that installed `@geekmidas/storage` has already paid for the SDK it would\n * otherwise resolve lazily.\n *\n * \"Installed\" the way Node resolves it: the app's own `package.json`, or any\n * directory above it. A workspace that lists `@geekmidas/storage` once, at its\n * root, is the common case — and reading only the app's file registered no S3\n * driver there, so the first service to inject a bucket threw\n * `UnregisteredStorageScheme`. A queue whose consumer depended on one logged\n * that once and was never polled.\n */\nexport function storageDriversFor(appRoot: string): RuntimeDrivers {\n\treturn installedFrom(appRoot, '@geekmidas/storage') ? S3 : NONE;\n}\n\n/**\n * Whether `pkg` is a dependency of the app at `appRoot` the way Node resolves\n * it: listed in the app's own `package.json`, or in any directory above it.\n */\nexport function installedFrom(appRoot: string, pkg: string): boolean {\n\tfor (let dir = resolve(appRoot); ; dir = dirname(dir)) {\n\t\tif (lists(join(dir, 'package.json'), pkg)) return true;\n\t\tif (dirname(dir) === dir) return false;\n\t}\n}\n\nfunction lists(manifest: string, dependency: string): boolean {\n\tif (!existsSync(manifest)) return false;\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(manifest, 'utf8')) as {\n\t\t\tdependencies?: Record<string, string>;\n\t\t\tdevDependencies?: Record<string, string>;\n\t\t};\n\n\t\treturn Boolean(\n\t\t\tpkg.dependencies?.[dependency] ?? pkg.devDependencies?.[dependency],\n\t\t);\n\t} catch {\n\t\t// An unreadable package.json is the build's problem, not this function's.\n\t\treturn false;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA0BA,MAAM,KAAK;CACV,SAAS;CACT,OAAO;AACR;AAaA,MAAM,OAAuB;CAAE,SAAS;CAAI,OAAO;AAAG;;AAGtD,MAAM,QAAwB;CAC7B,SAAS;CACT,OACC;AACF;;;;;;;;;;;;;AAcA,MAAM,gBAAsD;CAC3D,SAAS;EACR,SAAS;EACT,OACC;CACF;CACA,aAAa;CAGb,OAAO;CACP,IAAI;EACH,SAAS;EACT,OAAO;CACR;AACD;;;;;;;;;;AAWA,MAAM,iBAAwD;CAC7D,QAAQ;EACP,SAAS;EACT,OAAO;CACR;CACA,KAAK;EACJ,SAAS;EACT,OACC;CACF;CACA,UAAU;EACT,SAAS;EACT,OAAO;CACR;AACD;;;;;;;;AASA,SAAgB,WAAW,SAgBR;CAClB,MAAM,WAAW,OAAO,QAAQ,KAAK;CACrC,MAAM,UAAU,OAAO,QAAQ,MAAM;CAErC,MAAM,QAAQ;EACb,kBAAkB,QAAQ,OAAO;EACjC,GAAG,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,KACxB,YAAY,cAAc,YAAY,cAAc,cAAc,IACpE;EACA,GAAG,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,WAAW,eAAe,OAAO;CAChE,CAAC,CAAC,QAAQ,SAAS,KAAK,WAAW,KAAK,KAAK;CAE7C,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,OAAO;EACN,SAAS,MAAM,KAAK,SAAS,KAAK,OAAO,CAAC,CAAC,KAAK,IAAI;EACpD,OAAO,MAAM,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI;CACjD;AACD;AAEA,SAAS,OACR,OACe;CACf,IAAI,UAAU,SAAS,UAAU,KAAA,GAAW,OAAO,CAAC;CACpD,OAAO,OAAO,UAAU,WAAW,CAAC,KAAK,IAAK;AAC/C;;;;;;;;;;;AAYA,SAAgB,iBACf,UACA,YACkB;CAClB,MAAM,QAAQ,IAAI,IAAI,OAAO,OAAO,QAAQ,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI,CAAC;CAMhE,OAJC,MAAM,IAAI,OAAO,KACjB,MAAM,IAAI,OAAO,KAChB,eAAe,YAAY,MAAM,IAAI,QAAQ,KAAK,MAAM,IAAI,UAAU,IAEtD,CAAC,UAAU,IAAI,CAAC;AACnC;;;;;;;;;;;;;;AAeA,SAAgB,gBACf,UACA,YACiB;CACjB,MAAM,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC,QAAQ,MAAM,EAAE,SAAS,OAAO;CAGvE,OAAO,CACN,GAAG,IAAI,IACN,OAAO,KAAK,MACX,EAAE,MAAM,CAAC,gBAAgB,UAAU,IAAI,OAAO,UAC/C,CACD,CACD;AACD;;;;;;;;;;;;;;;;AAiBA,SAAgB,kBAAkB,SAAiC;CAClE,OAAO,cAAc,SAAS,oBAAoB,IAAI,KAAK;AAC5D;;;;;AAMA,SAAgB,cAAc,SAAiB,KAAsB;CACpE,KAAK,IAAI,MAAM,QAAQ,OAAO,IAAK,MAAM,QAAQ,GAAG,GAAG;EACtD,IAAI,MAAM,KAAK,KAAK,cAAc,GAAG,GAAG,GAAG,OAAO;EAClD,IAAI,QAAQ,GAAG,MAAM,KAAK,OAAO;CAClC;AACD;AAEA,SAAS,MAAM,UAAkB,YAA6B;CAC7D,IAAI,CAAC,WAAW,QAAQ,GAAG,OAAO;CAElC,IAAI;EACH,MAAM,MAAM,KAAK,MAAM,aAAa,UAAU,MAAM,CAAC;EAKrD,OAAO,QACN,IAAI,eAAe,eAAe,IAAI,kBAAkB,WACzD;CACD,QAAQ;EAEP,OAAO;CACR;AACD"}
@@ -1,6 +1,6 @@
1
1
  const require_workspace = require("./workspace-vdrcRI-m.cjs");
2
2
  const require_config = require("./config.cjs");
3
- const require_state = require("./state-DMjFPOhf.cjs");
3
+ const require_state = require("./state-DCwRWyh-.cjs");
4
4
  let _geekmidas_manifest = require("@geekmidas/manifest");
5
5
  let _geekmidas_constructs_encryption = require("@geekmidas/constructs/encryption");
6
6
  //#region src/encryption/index.ts
@@ -86,4 +86,4 @@ async function updateKeyring(construct, { stage }, context, update) {
86
86
  exports.encryptionRetireCommand = encryptionRetireCommand;
87
87
  exports.encryptionRotateCommand = encryptionRotateCommand;
88
88
 
89
- //# sourceMappingURL=encryption-CDGRdcMr.cjs.map
89
+ //# sourceMappingURL=encryption-DSkJdoyk.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"encryption-CDGRdcMr.cjs","names":["rotateKeyring","parseKeyring","retireKey","loadWorkspaceSettings","canonicalId","providerOf","secretsStoreFor","provideKey","withCustomSecret"],"sources":["../src/encryption/index.ts"],"sourcesContent":["/**\n * `gkm encryption:rotate` and `gkm encryption:retire` — the keyring of an\n * `Encryption` construct on a server stage.\n *\n * Rotating adds a key in front and keeps every older one, so what they wrote\n * still opens. Retiring removes one, and is the half that makes rotating worth\n * doing: until a key is gone, it still opens everything it ever wrote. It is\n * never automatic, because the only thing that knows nothing is left under a\n * key is the sweep that re-encrypted the column — so it is done after one,\n * deliberately, by name.\n *\n * Neither applies everywhere, and each says so rather than doing something\n * that looks right: a local stage's keyring is derived and would be replaced on\n * the next `gkm dev`, and on AWS the key is KMS's, which rotates it itself and\n * never strands a value.\n */\n\nimport {\n\tparseKeyring,\n\tretireKey,\n\trotateKeyring,\n} from '@geekmidas/constructs/encryption';\nimport { canonicalId, provideKey } from '@geekmidas/manifest';\nimport { loadWorkspaceSettings } from '../config';\nimport { withCustomSecret } from '../secrets/storage.js';\nimport { type SecretsStore, secretsStoreFor } from '../secrets/store.js';\nimport { providerOf } from '../workspace/backends.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\nexport class LocalKeyringIsDerived extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' is the local stage, where ${construct}'s keyring is derived from the project and stage — nothing stored there would be read. Rotate a deployed stage: gkm encryption:rotate ${construct} --stage <stage>.`,\n\t\t);\n\t\tthis.name = 'LocalKeyringIsDerived';\n\t}\n}\n\nexport class KmsRotatesItself extends Error {\n\tconstructor(readonly construct: string) {\n\t\tsuper(\n\t\t\t`${construct} is a KMS key on AWS: KMS rotates it yearly and keeps every version, so no value is ever stranded and there is no key to retire. Nothing to do here.`,\n\t\t);\n\t\tthis.name = 'KmsRotatesItself';\n\t}\n}\n\nexport class NoKeyringYet extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t\treadonly key: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${key} yet. The first deploy generates ${construct}'s keyring: gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoKeyringYet';\n\t}\n}\n\nexport interface EncryptionCommandOptions {\n\tstage: string;\n}\n\n/** Where a command reads and writes; the real ones unless a test says. */\nexport interface EncryptionCommandContext {\n\tworkspace?: NormalizedWorkspace;\n\tstore?: SecretsStore;\n}\n\nexport async function encryptionRotateCommand(\n\tconstruct: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = rotateKeyring(id, url);\n\t\tconst [current, ...older] = parseKeyring(id, next).keys.map(({ id }) => id);\n\n\t\tlogger.log(`\\n✓ ${id} on '${options.stage}' now writes with '${current}'.`);\n\t\tlogger.log(\n\t\t\t` Still opening: ${older.join(', ')}. Redeploy to start writing with '${current}', reencrypt what is stored, then: gkm encryption:retire ${id} <key> --stage ${options.stage}`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nexport async function encryptionRetireCommand(\n\tconstruct: string,\n\tkeyId: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = retireKey(id, url, keyId);\n\n\t\tlogger.log(\n\t\t\t`\\n✓ ${id} on '${options.stage}' no longer holds '${keyId}'. Anything still under it cannot be read after the next deploy.`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nasync function updateKeyring(\n\tconstruct: string,\n\t{ stage }: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext,\n\tupdate: (id: string, url: string) => string,\n): Promise<string> {\n\tconst workspace = context.workspace ?? (await loadWorkspaceSettings());\n\tconst id = canonicalId(construct);\n\n\tif (stage === workspace.stages.local) {\n\t\tthrow new LocalKeyringIsDerived(id, stage);\n\t}\n\tassertDeployedStage(workspace.stages, stage);\n\tif (providerOf(workspace) === 'aws') throw new KmsRotatesItself(id);\n\n\tconst store = context.store ?? (await secretsStoreFor(workspace, stage));\n\tconst key = provideKey(id, 'url');\n\tconst secrets = await store.read(stage);\n\tconst url = secrets?.custom[key];\n\tif (!secrets || !url) throw new NoKeyringYet(id, stage, key);\n\n\tconst next = update(id, url);\n\tawait store.write(stage, withCustomSecret(secrets, key, next));\n\treturn next;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAM,SAAS;AAEf,IAAa,wBAAb,cAA2C,MAAM;CAEtC;CACA;CAFV,YACC,WACA,OACC;EACD,MACC,IAAI,MAAM,8BAA8B,UAAU,wIAAwI,UAAU,kBACrM;EALS,KAAA,YAAA;EACA,KAAA,QAAA;EAKT,KAAK,OAAO;CACb;AACD;AAEA,IAAa,mBAAb,cAAsC,MAAM;CACtB;CAArB,YAAY,WAA4B;EACvC,MACC,GAAG,UAAU,qJACd;EAHoB,KAAA,YAAA;EAIpB,KAAK,OAAO;CACb;AACD;AAEA,IAAa,eAAb,cAAkC,MAAM;CAE7B;CACA;CACA;CAHV,YACC,WACA,OACA,KACC;EACD,MACC,IAAI,MAAM,WAAW,IAAI,mCAAmC,UAAU,iCAAiC,MAAM,EAC9G;EANS,KAAA,YAAA;EACA,KAAA,QAAA;EACA,KAAA,MAAA;EAKT,KAAK,OAAO;CACb;AACD;AAYA,eAAsB,wBACrB,WACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,QAAA,GAAOA,iCAAAA,cAAAA,CAAc,IAAI,GAAG;EAClC,MAAM,CAAC,SAAS,GAAG,UAAA,GAASC,iCAAAA,aAAAA,CAAa,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE,SAAS,EAAE;EAE1E,OAAO,IAAI,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,QAAQ,GAAG;EAC1E,OAAO,IACN,oBAAoB,MAAM,KAAK,IAAI,EAAE,oCAAoC,QAAQ,2DAA2D,GAAG,iBAAiB,QAAQ,OACzK;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAsB,wBACrB,WACA,OACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,QAAA,GAAOC,iCAAAA,UAAAA,CAAU,IAAI,KAAK,KAAK;EAErC,OAAO,IACN,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,MAAM,iEAC3D;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAe,cACd,WACA,EAAE,SACF,SACA,QACkB;CAClB,MAAM,YAAY,QAAQ,aAAc,MAAMC,eAAAA,sBAAsB;CACpE,MAAM,MAAA,GAAKC,oBAAAA,YAAAA,CAAY,SAAS;CAEhC,IAAI,UAAU,UAAU,OAAO,OAC9B,MAAM,IAAI,sBAAsB,IAAI,KAAK;CAE1C,kBAAA,oBAAoB,UAAU,QAAQ,KAAK;CAC3C,IAAIC,cAAAA,WAAW,SAAS,MAAM,OAAO,MAAM,IAAI,iBAAiB,EAAE;CAElE,MAAM,QAAQ,QAAQ,SAAU,MAAMC,cAAAA,gBAAgB,WAAW,KAAK;CACtE,MAAM,OAAA,GAAMC,oBAAAA,WAAAA,CAAW,IAAI,KAAK;CAChC,MAAM,UAAU,MAAM,MAAM,KAAK,KAAK;CACtC,MAAM,MAAM,SAAS,OAAO;CAC5B,IAAI,CAAC,WAAW,CAAC,KAAK,MAAM,IAAI,aAAa,IAAI,OAAO,GAAG;CAE3D,MAAM,OAAO,OAAO,IAAI,GAAG;CAC3B,MAAM,MAAM,MAAM,OAAOC,cAAAA,iBAAiB,SAAS,KAAK,IAAI,CAAC;CAC7D,OAAO;AACR"}
1
+ {"version":3,"file":"encryption-DSkJdoyk.cjs","names":["rotateKeyring","parseKeyring","retireKey","loadWorkspaceSettings","canonicalId","providerOf","secretsStoreFor","provideKey","withCustomSecret"],"sources":["../src/encryption/index.ts"],"sourcesContent":["/**\n * `gkm encryption:rotate` and `gkm encryption:retire` — the keyring of an\n * `Encryption` construct on a server stage.\n *\n * Rotating adds a key in front and keeps every older one, so what they wrote\n * still opens. Retiring removes one, and is the half that makes rotating worth\n * doing: until a key is gone, it still opens everything it ever wrote. It is\n * never automatic, because the only thing that knows nothing is left under a\n * key is the sweep that re-encrypted the column — so it is done after one,\n * deliberately, by name.\n *\n * Neither applies everywhere, and each says so rather than doing something\n * that looks right: a local stage's keyring is derived and would be replaced on\n * the next `gkm dev`, and on AWS the key is KMS's, which rotates it itself and\n * never strands a value.\n */\n\nimport {\n\tparseKeyring,\n\tretireKey,\n\trotateKeyring,\n} from '@geekmidas/constructs/encryption';\nimport { canonicalId, provideKey } from '@geekmidas/manifest';\nimport { loadWorkspaceSettings } from '../config';\nimport { withCustomSecret } from '../secrets/storage.js';\nimport { type SecretsStore, secretsStoreFor } from '../secrets/store.js';\nimport { providerOf } from '../workspace/backends.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\nexport class LocalKeyringIsDerived extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' is the local stage, where ${construct}'s keyring is derived from the project and stage — nothing stored there would be read. Rotate a deployed stage: gkm encryption:rotate ${construct} --stage <stage>.`,\n\t\t);\n\t\tthis.name = 'LocalKeyringIsDerived';\n\t}\n}\n\nexport class KmsRotatesItself extends Error {\n\tconstructor(readonly construct: string) {\n\t\tsuper(\n\t\t\t`${construct} is a KMS key on AWS: KMS rotates it yearly and keeps every version, so no value is ever stranded and there is no key to retire. Nothing to do here.`,\n\t\t);\n\t\tthis.name = 'KmsRotatesItself';\n\t}\n}\n\nexport class NoKeyringYet extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t\treadonly key: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${key} yet. The first deploy generates ${construct}'s keyring: gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoKeyringYet';\n\t}\n}\n\nexport interface EncryptionCommandOptions {\n\tstage: string;\n}\n\n/** Where a command reads and writes; the real ones unless a test says. */\nexport interface EncryptionCommandContext {\n\tworkspace?: NormalizedWorkspace;\n\tstore?: SecretsStore;\n}\n\nexport async function encryptionRotateCommand(\n\tconstruct: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = rotateKeyring(id, url);\n\t\tconst [current, ...older] = parseKeyring(id, next).keys.map(({ id }) => id);\n\n\t\tlogger.log(`\\n✓ ${id} on '${options.stage}' now writes with '${current}'.`);\n\t\tlogger.log(\n\t\t\t` Still opening: ${older.join(', ')}. Redeploy to start writing with '${current}', reencrypt what is stored, then: gkm encryption:retire ${id} <key> --stage ${options.stage}`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nexport async function encryptionRetireCommand(\n\tconstruct: string,\n\tkeyId: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = retireKey(id, url, keyId);\n\n\t\tlogger.log(\n\t\t\t`\\n✓ ${id} on '${options.stage}' no longer holds '${keyId}'. Anything still under it cannot be read after the next deploy.`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nasync function updateKeyring(\n\tconstruct: string,\n\t{ stage }: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext,\n\tupdate: (id: string, url: string) => string,\n): Promise<string> {\n\tconst workspace = context.workspace ?? (await loadWorkspaceSettings());\n\tconst id = canonicalId(construct);\n\n\tif (stage === workspace.stages.local) {\n\t\tthrow new LocalKeyringIsDerived(id, stage);\n\t}\n\tassertDeployedStage(workspace.stages, stage);\n\tif (providerOf(workspace) === 'aws') throw new KmsRotatesItself(id);\n\n\tconst store = context.store ?? (await secretsStoreFor(workspace, stage));\n\tconst key = provideKey(id, 'url');\n\tconst secrets = await store.read(stage);\n\tconst url = secrets?.custom[key];\n\tif (!secrets || !url) throw new NoKeyringYet(id, stage, key);\n\n\tconst next = update(id, url);\n\tawait store.write(stage, withCustomSecret(secrets, key, next));\n\treturn next;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAM,SAAS;AAEf,IAAa,wBAAb,cAA2C,MAAM;CAEtC;CACA;CAFV,YACC,WACA,OACC;EACD,MACC,IAAI,MAAM,8BAA8B,UAAU,wIAAwI,UAAU,kBACrM;EALS,KAAA,YAAA;EACA,KAAA,QAAA;EAKT,KAAK,OAAO;CACb;AACD;AAEA,IAAa,mBAAb,cAAsC,MAAM;CACtB;CAArB,YAAY,WAA4B;EACvC,MACC,GAAG,UAAU,qJACd;EAHoB,KAAA,YAAA;EAIpB,KAAK,OAAO;CACb;AACD;AAEA,IAAa,eAAb,cAAkC,MAAM;CAE7B;CACA;CACA;CAHV,YACC,WACA,OACA,KACC;EACD,MACC,IAAI,MAAM,WAAW,IAAI,mCAAmC,UAAU,iCAAiC,MAAM,EAC9G;EANS,KAAA,YAAA;EACA,KAAA,QAAA;EACA,KAAA,MAAA;EAKT,KAAK,OAAO;CACb;AACD;AAYA,eAAsB,wBACrB,WACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,QAAA,GAAOA,iCAAAA,cAAAA,CAAc,IAAI,GAAG;EAClC,MAAM,CAAC,SAAS,GAAG,UAAA,GAASC,iCAAAA,aAAAA,CAAa,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE,SAAS,EAAE;EAE1E,OAAO,IAAI,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,QAAQ,GAAG;EAC1E,OAAO,IACN,oBAAoB,MAAM,KAAK,IAAI,EAAE,oCAAoC,QAAQ,2DAA2D,GAAG,iBAAiB,QAAQ,OACzK;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAsB,wBACrB,WACA,OACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,QAAA,GAAOC,iCAAAA,UAAAA,CAAU,IAAI,KAAK,KAAK;EAErC,OAAO,IACN,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,MAAM,iEAC3D;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAe,cACd,WACA,EAAE,SACF,SACA,QACkB;CAClB,MAAM,YAAY,QAAQ,aAAc,MAAMC,eAAAA,sBAAsB;CACpE,MAAM,MAAA,GAAKC,oBAAAA,YAAAA,CAAY,SAAS;CAEhC,IAAI,UAAU,UAAU,OAAO,OAC9B,MAAM,IAAI,sBAAsB,IAAI,KAAK;CAE1C,kBAAA,oBAAoB,UAAU,QAAQ,KAAK;CAC3C,IAAIC,cAAAA,WAAW,SAAS,MAAM,OAAO,MAAM,IAAI,iBAAiB,EAAE;CAElE,MAAM,QAAQ,QAAQ,SAAU,MAAMC,cAAAA,gBAAgB,WAAW,KAAK;CACtE,MAAM,OAAA,GAAMC,oBAAAA,WAAAA,CAAW,IAAI,KAAK;CAChC,MAAM,UAAU,MAAM,MAAM,KAAK,KAAK;CACtC,MAAM,MAAM,SAAS,OAAO;CAC5B,IAAI,CAAC,WAAW,CAAC,KAAK,MAAM,IAAI,aAAa,IAAI,OAAO,GAAG;CAE3D,MAAM,OAAO,OAAO,IAAI,GAAG;CAC3B,MAAM,MAAM,MAAM,OAAOC,cAAAA,iBAAiB,SAAS,KAAK,IAAI,CAAC;CAC7D,OAAO;AACR"}
@@ -1,6 +1,6 @@
1
1
  import { k as assertDeployedStage } from "./workspace-C7NqCsQF.mjs";
2
2
  import { loadWorkspaceSettings } from "./config.mjs";
3
- import { Kt as secretsStoreFor, dn as withCustomSecret, jt as providerOf } from "./state-DtS2HEfi.mjs";
3
+ import { Kt as secretsStoreFor, dn as withCustomSecret, jt as providerOf } from "./state-DpkxSwzI.mjs";
4
4
  import { canonicalId, provideKey } from "@geekmidas/manifest";
5
5
  import { parseKeyring, retireKey, rotateKeyring } from "@geekmidas/constructs/encryption";
6
6
  //#region src/encryption/index.ts
@@ -85,4 +85,4 @@ async function updateKeyring(construct, { stage }, context, update) {
85
85
  //#endregion
86
86
  export { encryptionRetireCommand, encryptionRotateCommand };
87
87
 
88
- //# sourceMappingURL=encryption-CmfPICSN.mjs.map
88
+ //# sourceMappingURL=encryption-sTMz3aWi.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"encryption-CmfPICSN.mjs","names":[],"sources":["../src/encryption/index.ts"],"sourcesContent":["/**\n * `gkm encryption:rotate` and `gkm encryption:retire` — the keyring of an\n * `Encryption` construct on a server stage.\n *\n * Rotating adds a key in front and keeps every older one, so what they wrote\n * still opens. Retiring removes one, and is the half that makes rotating worth\n * doing: until a key is gone, it still opens everything it ever wrote. It is\n * never automatic, because the only thing that knows nothing is left under a\n * key is the sweep that re-encrypted the column — so it is done after one,\n * deliberately, by name.\n *\n * Neither applies everywhere, and each says so rather than doing something\n * that looks right: a local stage's keyring is derived and would be replaced on\n * the next `gkm dev`, and on AWS the key is KMS's, which rotates it itself and\n * never strands a value.\n */\n\nimport {\n\tparseKeyring,\n\tretireKey,\n\trotateKeyring,\n} from '@geekmidas/constructs/encryption';\nimport { canonicalId, provideKey } from '@geekmidas/manifest';\nimport { loadWorkspaceSettings } from '../config';\nimport { withCustomSecret } from '../secrets/storage.js';\nimport { type SecretsStore, secretsStoreFor } from '../secrets/store.js';\nimport { providerOf } from '../workspace/backends.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\nexport class LocalKeyringIsDerived extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' is the local stage, where ${construct}'s keyring is derived from the project and stage — nothing stored there would be read. Rotate a deployed stage: gkm encryption:rotate ${construct} --stage <stage>.`,\n\t\t);\n\t\tthis.name = 'LocalKeyringIsDerived';\n\t}\n}\n\nexport class KmsRotatesItself extends Error {\n\tconstructor(readonly construct: string) {\n\t\tsuper(\n\t\t\t`${construct} is a KMS key on AWS: KMS rotates it yearly and keeps every version, so no value is ever stranded and there is no key to retire. Nothing to do here.`,\n\t\t);\n\t\tthis.name = 'KmsRotatesItself';\n\t}\n}\n\nexport class NoKeyringYet extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t\treadonly key: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${key} yet. The first deploy generates ${construct}'s keyring: gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoKeyringYet';\n\t}\n}\n\nexport interface EncryptionCommandOptions {\n\tstage: string;\n}\n\n/** Where a command reads and writes; the real ones unless a test says. */\nexport interface EncryptionCommandContext {\n\tworkspace?: NormalizedWorkspace;\n\tstore?: SecretsStore;\n}\n\nexport async function encryptionRotateCommand(\n\tconstruct: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = rotateKeyring(id, url);\n\t\tconst [current, ...older] = parseKeyring(id, next).keys.map(({ id }) => id);\n\n\t\tlogger.log(`\\n✓ ${id} on '${options.stage}' now writes with '${current}'.`);\n\t\tlogger.log(\n\t\t\t` Still opening: ${older.join(', ')}. Redeploy to start writing with '${current}', reencrypt what is stored, then: gkm encryption:retire ${id} <key> --stage ${options.stage}`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nexport async function encryptionRetireCommand(\n\tconstruct: string,\n\tkeyId: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = retireKey(id, url, keyId);\n\n\t\tlogger.log(\n\t\t\t`\\n✓ ${id} on '${options.stage}' no longer holds '${keyId}'. Anything still under it cannot be read after the next deploy.`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nasync function updateKeyring(\n\tconstruct: string,\n\t{ stage }: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext,\n\tupdate: (id: string, url: string) => string,\n): Promise<string> {\n\tconst workspace = context.workspace ?? (await loadWorkspaceSettings());\n\tconst id = canonicalId(construct);\n\n\tif (stage === workspace.stages.local) {\n\t\tthrow new LocalKeyringIsDerived(id, stage);\n\t}\n\tassertDeployedStage(workspace.stages, stage);\n\tif (providerOf(workspace) === 'aws') throw new KmsRotatesItself(id);\n\n\tconst store = context.store ?? (await secretsStoreFor(workspace, stage));\n\tconst key = provideKey(id, 'url');\n\tconst secrets = await store.read(stage);\n\tconst url = secrets?.custom[key];\n\tif (!secrets || !url) throw new NoKeyringYet(id, stage, key);\n\n\tconst next = update(id, url);\n\tawait store.write(stage, withCustomSecret(secrets, key, next));\n\treturn next;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAM,SAAS;AAEf,IAAa,wBAAb,cAA2C,MAAM;CAEtC;CACA;CAFV,YACC,WACA,OACC;EACD,MACC,IAAI,MAAM,8BAA8B,UAAU,wIAAwI,UAAU,kBACrM;EALS,KAAA,YAAA;EACA,KAAA,QAAA;EAKT,KAAK,OAAO;CACb;AACD;AAEA,IAAa,mBAAb,cAAsC,MAAM;CACtB;CAArB,YAAY,WAA4B;EACvC,MACC,GAAG,UAAU,qJACd;EAHoB,KAAA,YAAA;EAIpB,KAAK,OAAO;CACb;AACD;AAEA,IAAa,eAAb,cAAkC,MAAM;CAE7B;CACA;CACA;CAHV,YACC,WACA,OACA,KACC;EACD,MACC,IAAI,MAAM,WAAW,IAAI,mCAAmC,UAAU,iCAAiC,MAAM,EAC9G;EANS,KAAA,YAAA;EACA,KAAA,QAAA;EACA,KAAA,MAAA;EAKT,KAAK,OAAO;CACb;AACD;AAYA,eAAsB,wBACrB,WACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,OAAO,cAAc,IAAI,GAAG;EAClC,MAAM,CAAC,SAAS,GAAG,SAAS,aAAa,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE,SAAS,EAAE;EAE1E,OAAO,IAAI,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,QAAQ,GAAG;EAC1E,OAAO,IACN,oBAAoB,MAAM,KAAK,IAAI,EAAE,oCAAoC,QAAQ,2DAA2D,GAAG,iBAAiB,QAAQ,OACzK;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAsB,wBACrB,WACA,OACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,OAAO,UAAU,IAAI,KAAK,KAAK;EAErC,OAAO,IACN,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,MAAM,iEAC3D;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAe,cACd,WACA,EAAE,SACF,SACA,QACkB;CAClB,MAAM,YAAY,QAAQ,aAAc,MAAM,sBAAsB;CACpE,MAAM,KAAK,YAAY,SAAS;CAEhC,IAAI,UAAU,UAAU,OAAO,OAC9B,MAAM,IAAI,sBAAsB,IAAI,KAAK;CAE1C,oBAAoB,UAAU,QAAQ,KAAK;CAC3C,IAAI,WAAW,SAAS,MAAM,OAAO,MAAM,IAAI,iBAAiB,EAAE;CAElE,MAAM,QAAQ,QAAQ,SAAU,MAAM,gBAAgB,WAAW,KAAK;CACtE,MAAM,MAAM,WAAW,IAAI,KAAK;CAChC,MAAM,UAAU,MAAM,MAAM,KAAK,KAAK;CACtC,MAAM,MAAM,SAAS,OAAO;CAC5B,IAAI,CAAC,WAAW,CAAC,KAAK,MAAM,IAAI,aAAa,IAAI,OAAO,GAAG;CAE3D,MAAM,OAAO,OAAO,IAAI,GAAG;CAC3B,MAAM,MAAM,MAAM,OAAO,iBAAiB,SAAS,KAAK,IAAI,CAAC;CAC7D,OAAO;AACR"}
1
+ {"version":3,"file":"encryption-sTMz3aWi.mjs","names":[],"sources":["../src/encryption/index.ts"],"sourcesContent":["/**\n * `gkm encryption:rotate` and `gkm encryption:retire` — the keyring of an\n * `Encryption` construct on a server stage.\n *\n * Rotating adds a key in front and keeps every older one, so what they wrote\n * still opens. Retiring removes one, and is the half that makes rotating worth\n * doing: until a key is gone, it still opens everything it ever wrote. It is\n * never automatic, because the only thing that knows nothing is left under a\n * key is the sweep that re-encrypted the column — so it is done after one,\n * deliberately, by name.\n *\n * Neither applies everywhere, and each says so rather than doing something\n * that looks right: a local stage's keyring is derived and would be replaced on\n * the next `gkm dev`, and on AWS the key is KMS's, which rotates it itself and\n * never strands a value.\n */\n\nimport {\n\tparseKeyring,\n\tretireKey,\n\trotateKeyring,\n} from '@geekmidas/constructs/encryption';\nimport { canonicalId, provideKey } from '@geekmidas/manifest';\nimport { loadWorkspaceSettings } from '../config';\nimport { withCustomSecret } from '../secrets/storage.js';\nimport { type SecretsStore, secretsStoreFor } from '../secrets/store.js';\nimport { providerOf } from '../workspace/backends.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\nexport class LocalKeyringIsDerived extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' is the local stage, where ${construct}'s keyring is derived from the project and stage — nothing stored there would be read. Rotate a deployed stage: gkm encryption:rotate ${construct} --stage <stage>.`,\n\t\t);\n\t\tthis.name = 'LocalKeyringIsDerived';\n\t}\n}\n\nexport class KmsRotatesItself extends Error {\n\tconstructor(readonly construct: string) {\n\t\tsuper(\n\t\t\t`${construct} is a KMS key on AWS: KMS rotates it yearly and keeps every version, so no value is ever stranded and there is no key to retire. Nothing to do here.`,\n\t\t);\n\t\tthis.name = 'KmsRotatesItself';\n\t}\n}\n\nexport class NoKeyringYet extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t\treadonly key: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${key} yet. The first deploy generates ${construct}'s keyring: gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoKeyringYet';\n\t}\n}\n\nexport interface EncryptionCommandOptions {\n\tstage: string;\n}\n\n/** Where a command reads and writes; the real ones unless a test says. */\nexport interface EncryptionCommandContext {\n\tworkspace?: NormalizedWorkspace;\n\tstore?: SecretsStore;\n}\n\nexport async function encryptionRotateCommand(\n\tconstruct: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = rotateKeyring(id, url);\n\t\tconst [current, ...older] = parseKeyring(id, next).keys.map(({ id }) => id);\n\n\t\tlogger.log(`\\n✓ ${id} on '${options.stage}' now writes with '${current}'.`);\n\t\tlogger.log(\n\t\t\t` Still opening: ${older.join(', ')}. Redeploy to start writing with '${current}', reencrypt what is stored, then: gkm encryption:retire ${id} <key> --stage ${options.stage}`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nexport async function encryptionRetireCommand(\n\tconstruct: string,\n\tkeyId: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = retireKey(id, url, keyId);\n\n\t\tlogger.log(\n\t\t\t`\\n✓ ${id} on '${options.stage}' no longer holds '${keyId}'. Anything still under it cannot be read after the next deploy.`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nasync function updateKeyring(\n\tconstruct: string,\n\t{ stage }: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext,\n\tupdate: (id: string, url: string) => string,\n): Promise<string> {\n\tconst workspace = context.workspace ?? (await loadWorkspaceSettings());\n\tconst id = canonicalId(construct);\n\n\tif (stage === workspace.stages.local) {\n\t\tthrow new LocalKeyringIsDerived(id, stage);\n\t}\n\tassertDeployedStage(workspace.stages, stage);\n\tif (providerOf(workspace) === 'aws') throw new KmsRotatesItself(id);\n\n\tconst store = context.store ?? (await secretsStoreFor(workspace, stage));\n\tconst key = provideKey(id, 'url');\n\tconst secrets = await store.read(stage);\n\tconst url = secrets?.custom[key];\n\tif (!secrets || !url) throw new NoKeyringYet(id, stage, key);\n\n\tconst next = update(id, url);\n\tawait store.write(stage, withCustomSecret(secrets, key, next));\n\treturn next;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAM,SAAS;AAEf,IAAa,wBAAb,cAA2C,MAAM;CAEtC;CACA;CAFV,YACC,WACA,OACC;EACD,MACC,IAAI,MAAM,8BAA8B,UAAU,wIAAwI,UAAU,kBACrM;EALS,KAAA,YAAA;EACA,KAAA,QAAA;EAKT,KAAK,OAAO;CACb;AACD;AAEA,IAAa,mBAAb,cAAsC,MAAM;CACtB;CAArB,YAAY,WAA4B;EACvC,MACC,GAAG,UAAU,qJACd;EAHoB,KAAA,YAAA;EAIpB,KAAK,OAAO;CACb;AACD;AAEA,IAAa,eAAb,cAAkC,MAAM;CAE7B;CACA;CACA;CAHV,YACC,WACA,OACA,KACC;EACD,MACC,IAAI,MAAM,WAAW,IAAI,mCAAmC,UAAU,iCAAiC,MAAM,EAC9G;EANS,KAAA,YAAA;EACA,KAAA,QAAA;EACA,KAAA,MAAA;EAKT,KAAK,OAAO;CACb;AACD;AAYA,eAAsB,wBACrB,WACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,OAAO,cAAc,IAAI,GAAG;EAClC,MAAM,CAAC,SAAS,GAAG,SAAS,aAAa,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE,SAAS,EAAE;EAE1E,OAAO,IAAI,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,QAAQ,GAAG;EAC1E,OAAO,IACN,oBAAoB,MAAM,KAAK,IAAI,EAAE,oCAAoC,QAAQ,2DAA2D,GAAG,iBAAiB,QAAQ,OACzK;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAsB,wBACrB,WACA,OACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,OAAO,UAAU,IAAI,KAAK,KAAK;EAErC,OAAO,IACN,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,MAAM,iEAC3D;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAe,cACd,WACA,EAAE,SACF,SACA,QACkB;CAClB,MAAM,YAAY,QAAQ,aAAc,MAAM,sBAAsB;CACpE,MAAM,KAAK,YAAY,SAAS;CAEhC,IAAI,UAAU,UAAU,OAAO,OAC9B,MAAM,IAAI,sBAAsB,IAAI,KAAK;CAE1C,oBAAoB,UAAU,QAAQ,KAAK;CAC3C,IAAI,WAAW,SAAS,MAAM,OAAO,MAAM,IAAI,iBAAiB,EAAE;CAElE,MAAM,QAAQ,QAAQ,SAAU,MAAM,gBAAgB,WAAW,KAAK;CACtE,MAAM,MAAM,WAAW,IAAI,KAAK;CAChC,MAAM,UAAU,MAAM,MAAM,KAAK,KAAK;CACtC,MAAM,MAAM,SAAS,OAAO;CAC5B,IAAI,CAAC,WAAW,CAAC,KAAK,MAAM,IAAI,aAAa,IAAI,OAAO,GAAG;CAE3D,MAAM,OAAO,OAAO,IAAI,GAAG;CAC3B,MAAM,MAAM,MAAM,OAAO,iBAAiB,SAAS,KAAK,IAAI,CAAC;CAC7D,OAAO;AACR"}
@@ -4,7 +4,7 @@ import { c as runWorker, d as output, r as discover } from "./discover-BPelzeGW.
4
4
  import { i as activeSandbox } from "./sandbox-Dm247NGx.mjs";
5
5
  import { t as appKey } from "./derive-B4vDpzgP.mjs";
6
6
  import { g as stripPublicPrefix, h as getPublicEnvPrefix, s as getAppBuildOrder } from "./workspace-C7NqCsQF.mjs";
7
- import { $ as constructGlobs, $t as applicationName, At as eventsBackendFor, E as resolveHost, Et as WORKER_PORT, F as devServicesUsed, H as workerUnits, I as externalServices, L as manifestServiceDeclarations, M as STORAGE_KEY_PAIR, Mt as storageBackendFor, O as otelEnv, P as assertExternalServices, S as workerDockerfileOf, T as isMainFrontendApp, U as encryptSecrets, _ as deployDocker, _t as workerEnvKeys, a as recordRelease, an as ownershipMarker, c as setBackupState, d as setGeneratedSecret, f as setPostgresBackupId, i as isDnsVerified, in as markerOf, j as ExternalServicesNotConfigured, jt as providerOf, kt as cacheBackendFor, l as setDnsRecord, m as withGeneratedSecrets, n as getBackupState, nn as imageRef, o as recordRollback, on as projectName, p as DeployJournal, r as getGeneratedSecret, rn as isOwnedBy, s as setApplicationId, sn as initStageSecrets, st as applyPostgres, t as createEmptyState, tn as imageName, u as setDnsVerification, un as toEmbeddableSecrets, xt as resourceName, z as reportDevServices } from "./state-DtS2HEfi.mjs";
7
+ import { $ as constructGlobs, $t as applicationName, At as eventsBackendFor, E as resolveHost, Et as WORKER_PORT, F as devServicesUsed, H as workerUnits, I as externalServices, L as manifestServiceDeclarations, M as STORAGE_KEY_PAIR, Mt as storageBackendFor, O as otelEnv, P as assertExternalServices, S as workerDockerfileOf, T as isMainFrontendApp, U as encryptSecrets, _ as deployDocker, _t as workerEnvKeys, a as recordRelease, an as ownershipMarker, c as setBackupState, d as setGeneratedSecret, f as setPostgresBackupId, i as isDnsVerified, in as markerOf, j as ExternalServicesNotConfigured, jt as providerOf, kt as cacheBackendFor, l as setDnsRecord, m as withGeneratedSecrets, n as getBackupState, nn as imageRef, o as recordRollback, on as projectName, p as DeployJournal, r as getGeneratedSecret, rn as isOwnedBy, s as setApplicationId, sn as initStageSecrets, st as applyPostgres, t as createEmptyState, tn as imageName, u as setDnsVerification, un as toEmbeddableSecrets, xt as resourceName, z as reportDevServices } from "./state-DpkxSwzI.mjs";
8
8
  import { f as StateStoreBusy, m as StateVersionConflict, o as DokployApi, s as DokployApiError, t as sniffAllApps, v as eventError } from "./sniffer-BFmdZpZ2.mjs";
9
9
  import { o as migrationFiles } from "./provider-D7rQzzzf.mjs";
10
10
  import { n as assertStageCredentials } from "./credentialSchemas-BJqRJNK4.mjs";
@@ -3204,4 +3204,4 @@ const MOBILE_SKIP_REASON = "deploys via its framework toolchain";
3204
3204
  //#endregion
3205
3205
  export { RegistryNotConfigured as _, dokployResult as a, releaseDokploy as c, validateDokploy as d, verifyDokploy as f, RegistryAmbiguous as g, HealthCheckTimedOut as h, dokployApi as i, restoreImage as l, DeployMigrationsFailed as m, FrontendDeployFailed as n, planDokploy as o, verifySettings as p, MissingEnvVars as r, provisionDokploy as s, BackendDeployFailed as t, rollbackDokploy as u, RegistryNotFound as v, ProjectNotOwned as y };
3206
3206
 
3207
- //# sourceMappingURL=engine-B37qBO5W.mjs.map
3207
+ //# sourceMappingURL=engine-B1b4I34Z.mjs.map