@geekmidas/cli 10.0.0-alpha.58 → 10.0.0-alpha.59

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 (91) hide show
  1. package/dist/{HostingerProvider-cL-6oQ5r.mjs → HostingerProvider-YQY3rMpV.mjs} +2 -2
  2. package/dist/{HostingerProvider-cL-6oQ5r.mjs.map → HostingerProvider-YQY3rMpV.mjs.map} +1 -1
  3. package/dist/{LegacyStateStore-ByRWQ9s1.mjs → LegacyStateStore-CB4ch3Dl.mjs} +2 -2
  4. package/dist/{LegacyStateStore-ByRWQ9s1.mjs.map → LegacyStateStore-CB4ch3Dl.mjs.map} +1 -1
  5. package/dist/{LegacyStateStore-C_OFs1gN.cjs → LegacyStateStore-CQJqvmXw.cjs} +7 -7
  6. package/dist/{LegacyStateStore-C_OFs1gN.cjs.map → LegacyStateStore-CQJqvmXw.cjs.map} +1 -1
  7. package/dist/{S3StateStore-FhzyENrN.mjs → S3StateStore-CXTenDTW.mjs} +2 -2
  8. package/dist/{S3StateStore-FhzyENrN.mjs.map → S3StateStore-CXTenDTW.mjs.map} +1 -1
  9. package/dist/{S3StateStore-C7eBae0G.cjs → S3StateStore-DLS4rEc0.cjs} +5 -5
  10. package/dist/{S3StateStore-C7eBae0G.cjs.map → S3StateStore-DLS4rEc0.cjs.map} +1 -1
  11. package/dist/{SSMStateStore-DC43R7zG.cjs → SSMStateStore-EqsTBIOs.cjs} +9 -9
  12. package/dist/{SSMStateStore-DC43R7zG.cjs.map → SSMStateStore-EqsTBIOs.cjs.map} +1 -1
  13. package/dist/{SSMStateStore-BjZZm-nV.mjs → SSMStateStore-hnP5deFJ.mjs} +2 -2
  14. package/dist/{SSMStateStore-BjZZm-nV.mjs.map → SSMStateStore-hnP5deFJ.mjs.map} +1 -1
  15. package/dist/StateStore-B4u0wx2t.cjs +412 -0
  16. package/dist/StateStore-B4u0wx2t.cjs.map +1 -0
  17. package/dist/StateStore-CYUfGYcx.mjs +359 -0
  18. package/dist/StateStore-CYUfGYcx.mjs.map +1 -0
  19. package/dist/{bundler-URImKj-A.mjs → bundler-ouDdNvk9.mjs} +2 -2
  20. package/dist/{bundler-URImKj-A.mjs.map → bundler-ouDdNvk9.mjs.map} +1 -1
  21. package/dist/{config-CSOK73pY.mjs → config-EuzFEkcH.mjs} +2 -2
  22. package/dist/config-EuzFEkcH.mjs.map +1 -0
  23. package/dist/{config-DdIu1O_I.cjs → config-WMOB9bLL.cjs} +2 -2
  24. package/dist/config-WMOB9bLL.cjs.map +1 -0
  25. package/dist/config.cjs +2 -2
  26. package/dist/config.d.cts +65 -3
  27. package/dist/config.d.cts.map +1 -1
  28. package/dist/config.d.mts +65 -3
  29. package/dist/config.d.mts.map +1 -1
  30. package/dist/config.mjs +2 -2
  31. package/dist/{encryption-BspqzZIb.cjs → encryption-BP8zfxEJ.cjs} +3 -3
  32. package/dist/{encryption-BspqzZIb.cjs.map → encryption-BP8zfxEJ.cjs.map} +1 -1
  33. package/dist/{encryption-Dlnl5XEU.mjs → encryption-BW7Gh51G.mjs} +3 -3
  34. package/dist/{encryption-Dlnl5XEU.mjs.map → encryption-BW7Gh51G.mjs.map} +1 -1
  35. package/dist/{github-mQorSS36.cjs → github-DHpDMBT7.cjs} +3 -3
  36. package/dist/{github-mQorSS36.cjs.map → github-DHpDMBT7.cjs.map} +1 -1
  37. package/dist/{github-BdF9tsvl.mjs → github-T_6sK3Rz.mjs} +3 -3
  38. package/dist/{github-BdF9tsvl.mjs.map → github-T_6sK3Rz.mjs.map} +1 -1
  39. package/dist/{index-BorYE_uO.d.cts → index-B5TJ-mB6.d.mts} +2 -2
  40. package/dist/{index-BorYE_uO.d.cts.map → index-B5TJ-mB6.d.mts.map} +1 -1
  41. package/dist/{index-C3JmluyO.d.mts → index-DV6j4Ydq.d.cts} +2 -2
  42. package/dist/{index-C3JmluyO.d.mts.map → index-DV6j4Ydq.d.cts.map} +1 -1
  43. package/dist/index.cjs +1210 -701
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.mjs +1215 -704
  46. package/dist/index.mjs.map +1 -1
  47. package/dist/{migrate-CXVnh1J3.mjs → migrate-BrFREmgQ.mjs} +4 -4
  48. package/dist/{migrate-CXVnh1J3.mjs.map → migrate-BrFREmgQ.mjs.map} +1 -1
  49. package/dist/{migrate-DzYqA-e3.cjs → migrate-CThJfVHR.cjs} +4 -4
  50. package/dist/{migrate-DzYqA-e3.cjs.map → migrate-CThJfVHR.cjs.map} +1 -1
  51. package/dist/openapi.cjs +1 -1
  52. package/dist/openapi.d.cts +1 -1
  53. package/dist/openapi.d.mts +1 -1
  54. package/dist/openapi.mjs +1 -1
  55. package/dist/ready-DbDHneLR.mjs.map +1 -1
  56. package/dist/ready-mR96orkJ.cjs.map +1 -1
  57. package/dist/reconcile/public.d.cts +1 -1
  58. package/dist/reconcile/public.d.mts +1 -1
  59. package/dist/{types-D7j6QWeX.d.mts → types-CipIIm8f.d.mts} +34 -2
  60. package/dist/{types-D7j6QWeX.d.mts.map → types-CipIIm8f.d.mts.map} +1 -1
  61. package/dist/{types-JV4XOY01.d.cts → types-D9b-IYdW.d.cts} +34 -2
  62. package/dist/{types-JV4XOY01.d.cts.map → types-D9b-IYdW.d.cts.map} +1 -1
  63. package/dist/workspace/index.cjs +1 -1
  64. package/dist/workspace/index.d.cts +2 -2
  65. package/dist/workspace/index.d.mts +2 -2
  66. package/dist/workspace/index.mjs +1 -1
  67. package/dist/{workspace-Bah36xSG.cjs → workspace-B64Xk1Mb.cjs} +14 -4
  68. package/dist/workspace-B64Xk1Mb.cjs.map +1 -0
  69. package/dist/{workspace-D-f58jsP.mjs → workspace-BRZ14o-o.mjs} +9 -6
  70. package/dist/{workspace-D-f58jsP.mjs.map → workspace-BRZ14o-o.mjs.map} +1 -1
  71. package/dist/{workspace-BvNXAXQI.mjs → workspace-Bp1xVaOz.mjs} +14 -4
  72. package/dist/workspace-Bp1xVaOz.mjs.map +1 -0
  73. package/dist/{workspace-B14L2TrY.cjs → workspace-Dbn07ILl.cjs} +7 -4
  74. package/dist/{workspace-B14L2TrY.cjs.map → workspace-Dbn07ILl.cjs.map} +1 -1
  75. package/package.json +13 -13
  76. package/dist/LocalStateProvider-7HR6SS2t.cjs +0 -48
  77. package/dist/LocalStateProvider-7HR6SS2t.cjs.map +0 -1
  78. package/dist/LocalStateProvider-D7QyJoLM.mjs +0 -48
  79. package/dist/LocalStateProvider-D7QyJoLM.mjs.map +0 -1
  80. package/dist/LocalStateStore-BupdV-VN.mjs +0 -163
  81. package/dist/LocalStateStore-BupdV-VN.mjs.map +0 -1
  82. package/dist/LocalStateStore-DDDU0HkI.cjs +0 -163
  83. package/dist/LocalStateStore-DDDU0HkI.cjs.map +0 -1
  84. package/dist/SSMStateProvider-B0Hu1MKZ.mjs +0 -75
  85. package/dist/SSMStateProvider-B0Hu1MKZ.mjs.map +0 -1
  86. package/dist/SSMStateProvider-VrQ-yeqd.cjs +0 -74
  87. package/dist/SSMStateProvider-VrQ-yeqd.cjs.map +0 -1
  88. package/dist/config-CSOK73pY.mjs.map +0 -1
  89. package/dist/config-DdIu1O_I.cjs.map +0 -1
  90. package/dist/workspace-Bah36xSG.cjs.map +0 -1
  91. package/dist/workspace-BvNXAXQI.mjs.map +0 -1
@@ -0,0 +1,412 @@
1
+ let node_os = require("node:os");
2
+ let node_crypto = require("node:crypto");
3
+ //#region src/deploy/StateProvider.ts
4
+ /**
5
+ * Check if value is a StateProvider implementation.
6
+ */
7
+ function isStateProvider(value) {
8
+ return typeof value === "object" && value !== null && typeof value.read === "function" && typeof value.write === "function";
9
+ }
10
+ //#endregion
11
+ //#region src/deploy/StateStore.ts
12
+ /**
13
+ * Deploy state store.
14
+ *
15
+ * `StateProvider` reads and writes a whole stage at once, with nothing to stop
16
+ * two runs of the same stage from overwriting each other and nowhere to record
17
+ * a resource the moment it exists. A `StateStore` adds the three things a
18
+ * deploy needs to be safe against crashes and concurrent runs:
19
+ *
20
+ * - `lock(stage)`: one run per stage. A second gets `StateLocked`, naming the
21
+ * holder, instead of racing the first.
22
+ * - Versioned `read` and conditional `write`: a write names the version it was
23
+ * based on, and raises `StateVersionConflict` when the stored state moved on
24
+ * underneath it, so nothing is lost silently even without the lock.
25
+ * - Per-resource records: `putResource` journals one resource at a time, so a
26
+ * run that dies half way leaves behind the ids of everything it created.
27
+ *
28
+ * Built-in stores: `LocalStateStore`, `SSMStateStore`, `S3StateStore`. A
29
+ * custom `StateProvider` is wrapped by `LegacyStateStore`, which keeps it
30
+ * working but cannot lock.
31
+ */
32
+ var StateLocked = class extends Error {
33
+ stage;
34
+ holder;
35
+ location;
36
+ constructor(stage, holder, location) {
37
+ const by = holder ? `${holder.owner}@${holder.host} (pid ${holder.pid}) since ${holder.acquiredAt}${holder.operation ? ` for '${holder.operation}'` : ""}` : "a run that did not record who it was";
38
+ super(`Deploy state for stage '${stage}' is locked by ${by} (${location}). Wait for that run to finish; if it crashed, release the lock with \`gkm state:unlock --stage ${stage}\`.`);
39
+ this.stage = stage;
40
+ this.holder = holder;
41
+ this.location = location;
42
+ this.name = "StateLocked";
43
+ }
44
+ };
45
+ var StateVersionConflict = class extends Error {
46
+ stage;
47
+ expectedVersion;
48
+ actualVersion;
49
+ constructor(stage, expectedVersion, actualVersion) {
50
+ super(`Deploy state for stage '${stage}' changed since it was read (expected ${expectedVersion ? `version ${expectedVersion}` : "no state"}, found ${actualVersion ? `version ${actualVersion}` : "no state"}). Another run wrote it: read the state again and re-apply the change, and hold the stage's lock so runs take turns.`);
51
+ this.stage = stage;
52
+ this.expectedVersion = expectedVersion;
53
+ this.actualVersion = actualVersion;
54
+ this.name = "StateVersionConflict";
55
+ }
56
+ };
57
+ var StageStateMissing = class extends Error {
58
+ stage;
59
+ constructor(stage) {
60
+ super(`There is no deploy state for stage '${stage}' to record a resource in. Write the stage's state first (write with expectedVersion: null).`);
61
+ this.stage = stage;
62
+ this.name = "StageStateMissing";
63
+ }
64
+ };
65
+ var StateUnreadable = class extends Error {
66
+ stage;
67
+ location;
68
+ constructor(stage, location, cause) {
69
+ super(`Deploy state for stage '${stage}' at ${location} is not valid JSON. Restore it from a backup (or the provider's history) rather than deploying without it — a deploy without state recreates every resource.`, { cause });
70
+ this.stage = stage;
71
+ this.location = location;
72
+ this.name = "StateUnreadable";
73
+ }
74
+ };
75
+ var StateSchemaTooNew = class extends Error {
76
+ stage;
77
+ schemaVersion;
78
+ constructor(stage, schemaVersion) {
79
+ super(`Deploy state for stage '${stage}' has schema version ${String(schemaVersion)}, newer than this @geekmidas/cli understands (2). Upgrade @geekmidas/cli to the version that wrote it.`);
80
+ this.stage = stage;
81
+ this.schemaVersion = schemaVersion;
82
+ this.name = "StateSchemaTooNew";
83
+ }
84
+ };
85
+ /**
86
+ * Not thrown: emitted (once per store) when a custom `StateProvider` stands
87
+ * in for a store, so a deploy that cannot be protected from a concurrent one
88
+ * says so.
89
+ */
90
+ var StateStoreWithoutLocking = class extends Error {
91
+ provider;
92
+ constructor(provider) {
93
+ super(`The custom state provider '${provider}' has no lock and no versions, so two deploys of the same stage can still overwrite each other. Implement StateStore (lock, versioned read, conditional write) or use the 'local', 'ssm' or 's3' state provider.`);
94
+ this.provider = provider;
95
+ this.name = "StateStoreWithoutLocking";
96
+ }
97
+ };
98
+ var StateStoreNeedsWorkspaceName = class extends Error {
99
+ provider;
100
+ constructor(provider) {
101
+ super(`The '${provider}' state provider keys state by workspace name. Set "name" in gkm.config.ts.`);
102
+ this.provider = provider;
103
+ this.name = "StateStoreNeedsWorkspaceName";
104
+ }
105
+ };
106
+ var UnknownStateProvider = class extends Error {
107
+ config;
108
+ constructor(config) {
109
+ super(`Unknown state provider ${JSON.stringify(config)}. Use 'local', 'ssm', 's3', or an object implementing StateStore or StateProvider.`);
110
+ this.config = config;
111
+ this.name = "UnknownStateProvider";
112
+ }
113
+ };
114
+ /** A lock or write that could not get at the state backend in time. */
115
+ var StateStoreBusy = class extends Error {
116
+ stage;
117
+ location;
118
+ constructor(stage, location) {
119
+ super(`Timed out waiting to write deploy state for stage '${stage}' (${location}). If no other gkm process is running, remove ${location}.`);
120
+ this.stage = stage;
121
+ this.location = location;
122
+ this.name = "StateStoreBusy";
123
+ }
124
+ };
125
+ function decodeDocument(stage, body, location) {
126
+ let parsed;
127
+ try {
128
+ parsed = JSON.parse(body);
129
+ } catch (error) {
130
+ throw new StateUnreadable(stage, location, error);
131
+ }
132
+ if (typeof parsed !== "object" || parsed === null) throw new StateUnreadable(stage, location, parsed);
133
+ const schemaVersion = parsed.schemaVersion;
134
+ if (schemaVersion === void 0) return {
135
+ schemaVersion: 1,
136
+ state: parsed
137
+ };
138
+ if (schemaVersion === 2) return {
139
+ schemaVersion: 2,
140
+ document: parsed
141
+ };
142
+ throw new StateSchemaTooNew(stage, schemaVersion);
143
+ }
144
+ function encodeDocument(document) {
145
+ return JSON.stringify(document, null, 2);
146
+ }
147
+ /**
148
+ * v1 state as a v2 document. The ids v1 kept are seeded as `ready` records,
149
+ * so the first journaled deploy after an upgrade adopts them rather than
150
+ * treating them as unknown.
151
+ */
152
+ function migrateV1(stage, state) {
153
+ const now = (/* @__PURE__ */ new Date()).toISOString();
154
+ const resources = {};
155
+ const seed = (type, name, id) => {
156
+ if (!id) return;
157
+ const key = name ? `${type}:${name}` : type;
158
+ resources[key] = {
159
+ key,
160
+ type,
161
+ id,
162
+ status: "ready",
163
+ updatedAt: now
164
+ };
165
+ };
166
+ seed("project", null, state.projectId);
167
+ seed("environment", null, state.environmentId);
168
+ for (const [app, id] of Object.entries(state.applications ?? {})) seed("application", app, id);
169
+ seed("postgres", null, state.services?.postgresId);
170
+ seed("redis", null, state.services?.redisId);
171
+ seed("backup-destination", null, state.backups?.destinationId);
172
+ return {
173
+ schemaVersion: 2,
174
+ stage,
175
+ serial: 1,
176
+ state,
177
+ resources,
178
+ updatedAt: now
179
+ };
180
+ }
181
+ function newLockHolder(options) {
182
+ let owner = "unknown";
183
+ try {
184
+ owner = (0, node_os.userInfo)().username;
185
+ } catch {}
186
+ return {
187
+ id: (0, node_crypto.randomUUID)(),
188
+ owner,
189
+ host: (0, node_os.hostname)(),
190
+ pid: process.pid,
191
+ acquiredAt: (/* @__PURE__ */ new Date()).toISOString(),
192
+ operation: options?.operation
193
+ };
194
+ }
195
+ function contentVersion(body) {
196
+ return (0, node_crypto.createHash)("sha256").update(body).digest("hex").slice(0, 16);
197
+ }
198
+ /**
199
+ * A store over a backend that can read and conditionally write one blob per
200
+ * stage, and create a lock object only if it does not exist. Migration and
201
+ * resource records are the same for every such backend, so they live here.
202
+ */
203
+ var DocumentStateStore = class {
204
+ async lock(stage, options) {
205
+ const holder = newLockHolder(options);
206
+ await this.createLock(stage, holder);
207
+ let released = false;
208
+ return {
209
+ stage,
210
+ holder,
211
+ release: async () => {
212
+ if (released) return;
213
+ released = true;
214
+ await this.removeLock(stage, holder);
215
+ }
216
+ };
217
+ }
218
+ async forceUnlock(stage) {
219
+ const holder = await this.readLock(stage);
220
+ await this.removeLock(stage, null);
221
+ return holder;
222
+ }
223
+ async read(stage) {
224
+ const current = await this.readDocument(stage);
225
+ if (!current) return null;
226
+ return {
227
+ state: current.document.state,
228
+ resources: current.document.resources,
229
+ version: current.version
230
+ };
231
+ }
232
+ async write(stage, state, options) {
233
+ if (options.expectedVersion === null) {
234
+ const document = {
235
+ schemaVersion: 2,
236
+ stage,
237
+ serial: 1,
238
+ state,
239
+ resources: {},
240
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
241
+ };
242
+ return this.writeRaw(stage, encodeDocument(document), null);
243
+ }
244
+ return this.mutate(stage, options.expectedVersion, (document) => ({
245
+ ...document,
246
+ state
247
+ }));
248
+ }
249
+ async putResource(stage, record, options) {
250
+ return this.mutate(stage, options?.expectedVersion, (document) => ({
251
+ ...document,
252
+ resources: {
253
+ ...document.resources,
254
+ [record.key]: {
255
+ ...record,
256
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
257
+ }
258
+ }
259
+ }));
260
+ }
261
+ async deleteResource(stage, key, options) {
262
+ return this.mutate(stage, options?.expectedVersion, (document) => {
263
+ const { [key]: _removed, ...resources } = document.resources;
264
+ return {
265
+ ...document,
266
+ resources
267
+ };
268
+ });
269
+ }
270
+ async mutate(stage, expectedVersion, change) {
271
+ const current = await this.readDocument(stage);
272
+ if (!current) {
273
+ if (expectedVersion !== void 0) throw new StateVersionConflict(stage, expectedVersion, null);
274
+ throw new StageStateMissing(stage);
275
+ }
276
+ if (expectedVersion !== void 0 && current.version !== expectedVersion) throw new StateVersionConflict(stage, expectedVersion, current.version);
277
+ const next = change(current.document);
278
+ next.serial = current.document.serial + 1;
279
+ next.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
280
+ return this.writeRaw(stage, encodeDocument(next), current.version);
281
+ }
282
+ /**
283
+ * Reads the stage's document, migrating v1 in place on the way. The
284
+ * migration is itself a conditional write: if another run migrated (or
285
+ * wrote) first, its result is read instead.
286
+ */
287
+ async readDocument(stage, attempt = 0) {
288
+ const raw = await this.readRaw(stage);
289
+ if (!raw) return null;
290
+ const decoded = decodeDocument(stage, raw.body, this.location(stage));
291
+ if (decoded.schemaVersion === 2) return {
292
+ document: decoded.document,
293
+ version: raw.version
294
+ };
295
+ await this.writeV1Backup(stage, raw.body);
296
+ const document = migrateV1(stage, decoded.state);
297
+ try {
298
+ return {
299
+ document,
300
+ version: await this.writeRaw(stage, encodeDocument(document), raw.version)
301
+ };
302
+ } catch (error) {
303
+ if (error instanceof StateVersionConflict && attempt < 2) return this.readDocument(stage, attempt + 1);
304
+ throw error;
305
+ }
306
+ }
307
+ };
308
+ function isStateStore(value) {
309
+ return typeof value === "object" && value !== null && typeof value.lock === "function" && typeof value.read === "function" && typeof value.write === "function" && typeof value.putResource === "function";
310
+ }
311
+ /**
312
+ * The store for a workspace's `state` config:
313
+ *
314
+ * - none / `'local'`: `LocalStateStore` under `.gkm/`
315
+ * - `'ssm'`: `SSMStateStore`, with no local cache — SSM is read directly, so
316
+ * a stale local copy can never win over it
317
+ * - `'s3'`: `S3StateStore`
318
+ * - a `StateStore` object: used as is
319
+ * - a `StateProvider` object: wrapped by `LegacyStateStore`
320
+ */
321
+ async function createStateStore(options) {
322
+ const { config, workspaceRoot, workspaceName } = options;
323
+ if (!config || config.provider === "local") {
324
+ const { LocalStateStore } = await Promise.resolve().then(() => require("./index.cjs")).then((n) => n.LocalStateStore_exports);
325
+ return new LocalStateStore(workspaceRoot);
326
+ }
327
+ if (isStateStore(config.provider)) return config.provider;
328
+ if (isStateProvider(config.provider)) {
329
+ const { LegacyStateStore } = await Promise.resolve().then(() => require("./LegacyStateStore-CQJqvmXw.cjs"));
330
+ return new LegacyStateStore(config.provider, { warn: options.warn });
331
+ }
332
+ if (config.provider === "ssm") {
333
+ if (!workspaceName) throw new StateStoreNeedsWorkspaceName("ssm");
334
+ const { SSMStateStore } = await Promise.resolve().then(() => require("./SSMStateStore-EqsTBIOs.cjs"));
335
+ const ssm = config;
336
+ return SSMStateStore.create({
337
+ workspaceName,
338
+ region: ssm.region,
339
+ profile: ssm.profile
340
+ });
341
+ }
342
+ if (config.provider === "s3") {
343
+ if (!workspaceName) throw new StateStoreNeedsWorkspaceName("s3");
344
+ const { S3StateStore } = await Promise.resolve().then(() => require("./S3StateStore-DLS4rEc0.cjs"));
345
+ const s3 = config;
346
+ return S3StateStore.create({
347
+ workspaceName,
348
+ bucket: s3.bucket,
349
+ prefix: s3.prefix,
350
+ region: s3.region,
351
+ profile: s3.profile
352
+ });
353
+ }
354
+ throw new UnknownStateProvider(config);
355
+ }
356
+ //#endregion
357
+ Object.defineProperty(exports, "DocumentStateStore", {
358
+ enumerable: true,
359
+ get: function() {
360
+ return DocumentStateStore;
361
+ }
362
+ });
363
+ Object.defineProperty(exports, "StageStateMissing", {
364
+ enumerable: true,
365
+ get: function() {
366
+ return StageStateMissing;
367
+ }
368
+ });
369
+ Object.defineProperty(exports, "StateLocked", {
370
+ enumerable: true,
371
+ get: function() {
372
+ return StateLocked;
373
+ }
374
+ });
375
+ Object.defineProperty(exports, "StateStoreBusy", {
376
+ enumerable: true,
377
+ get: function() {
378
+ return StateStoreBusy;
379
+ }
380
+ });
381
+ Object.defineProperty(exports, "StateStoreWithoutLocking", {
382
+ enumerable: true,
383
+ get: function() {
384
+ return StateStoreWithoutLocking;
385
+ }
386
+ });
387
+ Object.defineProperty(exports, "StateVersionConflict", {
388
+ enumerable: true,
389
+ get: function() {
390
+ return StateVersionConflict;
391
+ }
392
+ });
393
+ Object.defineProperty(exports, "contentVersion", {
394
+ enumerable: true,
395
+ get: function() {
396
+ return contentVersion;
397
+ }
398
+ });
399
+ Object.defineProperty(exports, "createStateStore", {
400
+ enumerable: true,
401
+ get: function() {
402
+ return createStateStore;
403
+ }
404
+ });
405
+ Object.defineProperty(exports, "newLockHolder", {
406
+ enumerable: true,
407
+ get: function() {
408
+ return newLockHolder;
409
+ }
410
+ });
411
+
412
+ //# sourceMappingURL=StateStore-B4u0wx2t.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StateStore-B4u0wx2t.cjs","names":["userInfo","randomUUID","hostname","createHash"],"sources":["../src/deploy/StateProvider.ts","../src/deploy/StateStore.ts"],"sourcesContent":["/**\n * State configuration, and the `StateProvider` interface a custom backend can\n * implement.\n *\n * Deploy state is read and written through a `StateStore` (`StateStore.ts`):\n * locks, versions and resource records. A custom `StateProvider` — read and\n * write a whole state, nothing else — still works behind `LegacyStateStore`,\n * which warns that it cannot lock.\n */\n\nimport type { StateStore } from './StateStore';\nimport type { DokployStageState } from './state';\n\n/**\n * Interface for deployment state storage providers.\n *\n * Implementations must handle:\n * - Reading state for a stage (returns null if not found)\n * - Writing state for a stage (creates or updates)\n */\nexport interface StateProvider {\n\t/**\n\t * Read deployment state for a stage.\n\t *\n\t * @param stage - The deployment stage (e.g., 'development', 'production')\n\t * @returns The state object or null if not found\n\t */\n\tread(stage: string): Promise<DokployStageState | null>;\n\n\t/**\n\t * Write deployment state for a stage.\n\t *\n\t * @param stage - The deployment stage\n\t * @param state - The state object to persist\n\t */\n\twrite(stage: string, state: DokployStageState): Promise<void>;\n}\n\n/**\n * Valid AWS regions.\n */\nexport type AwsRegion =\n\t| 'us-east-1'\n\t| 'us-east-2'\n\t| 'us-west-1'\n\t| 'us-west-2'\n\t| 'af-south-1'\n\t| 'ap-east-1'\n\t| 'ap-south-1'\n\t| 'ap-south-2'\n\t| 'ap-southeast-1'\n\t| 'ap-southeast-2'\n\t| 'ap-southeast-3'\n\t| 'ap-southeast-4'\n\t| 'ap-northeast-1'\n\t| 'ap-northeast-2'\n\t| 'ap-northeast-3'\n\t| 'ca-central-1'\n\t| 'eu-central-1'\n\t| 'eu-central-2'\n\t| 'eu-west-1'\n\t| 'eu-west-2'\n\t| 'eu-west-3'\n\t| 'eu-south-1'\n\t| 'eu-south-2'\n\t| 'eu-north-1'\n\t| 'me-south-1'\n\t| 'me-central-1'\n\t| 'sa-east-1';\n\n/**\n * Local state provider config.\n */\nexport interface LocalStateConfig {\n\tprovider: 'local';\n}\n\n/**\n * SSM state provider config (requires region).\n */\nexport interface SSMStateConfig {\n\tprovider: 'ssm';\n\t/** AWS region (required for SSM provider) */\n\tregion: AwsRegion;\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile?: string;\n}\n\n/**\n * S3 state config: one object per stage, written with conditional puts\n * (`If-Match` / `If-None-Match`), and a lock object beside it.\n *\n * Keys: `<prefix>/<workspace name>/<stage>/state.json` and `lock.json`.\n */\nexport interface S3StateConfig {\n\tprovider: 's3';\n\t/** Bucket the state lives in. It must already exist. */\n\tbucket: string;\n\t/** AWS region of the bucket */\n\tregion: AwsRegion;\n\t/** Key prefix inside the bucket (default: `gkm`) */\n\tprefix?: string;\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile?: string;\n}\n\n/**\n * Custom state provider config.\n */\nexport interface CustomStateConfig {\n\t/**\n\t * Custom implementation. A `StateStore` is used as is; a `StateProvider`\n\t * still works, but cannot lock — deploy warns `StateStoreWithoutLocking`.\n\t */\n\tprovider: StateProvider | StateStore;\n}\n\n/**\n * State configuration types.\n */\nexport type StateConfig =\n\t| LocalStateConfig\n\t| SSMStateConfig\n\t| S3StateConfig\n\t| CustomStateConfig;\n\n/**\n * Check if value is a StateProvider implementation.\n */\nexport function isStateProvider(value: unknown): value is StateProvider {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\ttypeof (value as StateProvider).read === 'function' &&\n\t\ttypeof (value as StateProvider).write === 'function'\n\t);\n}\n\nexport interface CreateStateStoreConfig {\n\t/** State config from workspace */\n\tconfig?: StateConfig;\n\t/** Workspace root directory (for local provider) */\n\tworkspaceRoot: string;\n\t/** Workspace name (for SSM parameter path) */\n\tworkspaceName: string;\n}\n","/**\n * Deploy state store.\n *\n * `StateProvider` reads and writes a whole stage at once, with nothing to stop\n * two runs of the same stage from overwriting each other and nowhere to record\n * a resource the moment it exists. A `StateStore` adds the three things a\n * deploy needs to be safe against crashes and concurrent runs:\n *\n * - `lock(stage)`: one run per stage. A second gets `StateLocked`, naming the\n * holder, instead of racing the first.\n * - Versioned `read` and conditional `write`: a write names the version it was\n * based on, and raises `StateVersionConflict` when the stored state moved on\n * underneath it, so nothing is lost silently even without the lock.\n * - Per-resource records: `putResource` journals one resource at a time, so a\n * run that dies half way leaves behind the ids of everything it created.\n *\n * Built-in stores: `LocalStateStore`, `SSMStateStore`, `S3StateStore`. A\n * custom `StateProvider` is wrapped by `LegacyStateStore`, which keeps it\n * working but cannot lock.\n */\n\nimport { createHash, randomUUID } from 'node:crypto';\nimport { hostname, userInfo } from 'node:os';\nimport {\n\ttype CreateStateStoreConfig,\n\tisStateProvider,\n\ttype S3StateConfig,\n\ttype SSMStateConfig,\n} from './StateProvider';\nimport type { DokployStageState } from './state';\n\n/** Opaque: a file hash, an SSM parameter version or an S3 ETag. */\nexport type StateVersion = string;\n\n/**\n * `pending` is written before the call that creates the resource, `ready`\n * once the target has returned its id. A run that finds a `pending` record\n * knows a previous run may have created the resource and should look for it\n * before creating another.\n */\nexport type ResourceStatus = 'pending' | 'ready';\n\nexport interface ResourceRecord {\n\t/** Unique within the stage, `<type>:<name>` — e.g. `application:api`. */\n\tkey: string;\n\t/** What kind of resource it is — e.g. `application`, `postgres`. */\n\ttype: string;\n\t/** The id the target gave it. Absent while `pending`. */\n\tid?: string;\n\tstatus: ResourceStatus;\n\t/** Whatever the target needs to adopt the resource on a later run. */\n\tdata?: Record<string, unknown>;\n\tupdatedAt: string;\n}\n\n/** A resource as a caller hands it to `putResource`; the store stamps it. */\nexport type ResourceInput = Omit<ResourceRecord, 'updatedAt'>;\n\nexport interface StoredStageState {\n\tstate: DokployStageState;\n\tresources: Record<string, ResourceRecord>;\n\t/** Pass back as `expectedVersion` to write on top of exactly this read. */\n\tversion: StateVersion;\n}\n\nexport interface WriteOptions {\n\t/**\n\t * The version this write is based on — `null` to create state that must\n\t * not exist yet.\n\t */\n\texpectedVersion: StateVersion | null;\n}\n\nexport interface ResourceWriteOptions {\n\t/**\n\t * Omitted, the record is applied on top of whatever is stored now (still\n\t * conditionally, so a concurrent write raises rather than being lost).\n\t */\n\texpectedVersion?: StateVersion;\n}\n\nexport interface LockHolder {\n\t/** Unique per acquisition, so a release never removes someone else's lock. */\n\tid: string;\n\towner: string;\n\thost: string;\n\tpid: number;\n\tacquiredAt: string;\n\t/** What the holder is doing, e.g. `deploy`. */\n\toperation?: string;\n}\n\nexport interface LockOptions {\n\toperation?: string;\n}\n\nexport interface StateLock {\n\treadonly stage: string;\n\treadonly holder: LockHolder;\n\t/** Removes the lock if it is still this one; safe to call twice. */\n\trelease(): Promise<void>;\n}\n\nexport interface StateStore {\n\t/** Takes the stage's lock, or raises `StateLocked` naming its holder. */\n\tlock(stage: string, options?: LockOptions): Promise<StateLock>;\n\t/**\n\t * Removes the stage's lock whoever holds it, for a run that crashed with\n\t * it held. Returns the holder it removed, or `null` if there was none.\n\t */\n\tforceUnlock(stage: string): Promise<LockHolder | null>;\n\tread(stage: string): Promise<StoredStageState | null>;\n\t/** Replaces the stage's state, keeping its resource records. */\n\twrite(\n\t\tstage: string,\n\t\tstate: DokployStageState,\n\t\toptions: WriteOptions,\n\t): Promise<StateVersion>;\n\tputResource(\n\t\tstage: string,\n\t\trecord: ResourceInput,\n\t\toptions?: ResourceWriteOptions,\n\t): Promise<StateVersion>;\n\tdeleteResource(\n\t\tstage: string,\n\t\tkey: string,\n\t\toptions?: ResourceWriteOptions,\n\t): Promise<StateVersion>;\n}\n\n// ============================================================================\n// Errors\n// ============================================================================\n\nexport class StateLocked extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly holder: LockHolder | null,\n\t\treadonly location: string,\n\t) {\n\t\tconst by = holder\n\t\t\t? `${holder.owner}@${holder.host} (pid ${holder.pid}) since ${holder.acquiredAt}${holder.operation ? ` for '${holder.operation}'` : ''}`\n\t\t\t: 'a run that did not record who it was';\n\t\tsuper(\n\t\t\t`Deploy state for stage '${stage}' is locked by ${by} (${location}). ` +\n\t\t\t\t`Wait for that run to finish; if it crashed, release the lock with ` +\n\t\t\t\t`\\`gkm state:unlock --stage ${stage}\\`.`,\n\t\t);\n\t\tthis.name = 'StateLocked';\n\t}\n}\n\nexport class StateVersionConflict extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly expectedVersion: StateVersion | null,\n\t\treadonly actualVersion: StateVersion | null,\n\t) {\n\t\tsuper(\n\t\t\t`Deploy state for stage '${stage}' changed since it was read ` +\n\t\t\t\t`(expected ${expectedVersion ? `version ${expectedVersion}` : 'no state'}, ` +\n\t\t\t\t`found ${actualVersion ? `version ${actualVersion}` : 'no state'}). ` +\n\t\t\t\t`Another run wrote it: read the state again and re-apply the change, ` +\n\t\t\t\t`and hold the stage's lock so runs take turns.`,\n\t\t);\n\t\tthis.name = 'StateVersionConflict';\n\t}\n}\n\nexport class StageStateMissing extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`There is no deploy state for stage '${stage}' to record a resource in. ` +\n\t\t\t\t`Write the stage's state first (write with expectedVersion: null).`,\n\t\t);\n\t\tthis.name = 'StageStateMissing';\n\t}\n}\n\nexport class StateUnreadable extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly location: string,\n\t\tcause: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`Deploy state for stage '${stage}' at ${location} is not valid JSON. ` +\n\t\t\t\t`Restore it from a backup (or the provider's history) rather than ` +\n\t\t\t\t`deploying without it — a deploy without state recreates every resource.`,\n\t\t\t{ cause },\n\t\t);\n\t\tthis.name = 'StateUnreadable';\n\t}\n}\n\nexport class StateSchemaTooNew extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly schemaVersion: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`Deploy state for stage '${stage}' has schema version ${String(schemaVersion)}, ` +\n\t\t\t\t`newer than this @geekmidas/cli understands (${STATE_SCHEMA_VERSION}). ` +\n\t\t\t\t`Upgrade @geekmidas/cli to the version that wrote it.`,\n\t\t);\n\t\tthis.name = 'StateSchemaTooNew';\n\t}\n}\n\n/**\n * Not thrown: emitted (once per store) when a custom `StateProvider` stands\n * in for a store, so a deploy that cannot be protected from a concurrent one\n * says so.\n */\nexport class StateStoreWithoutLocking extends Error {\n\tconstructor(readonly provider: string) {\n\t\tsuper(\n\t\t\t`The custom state provider '${provider}' has no lock and no versions, ` +\n\t\t\t\t`so two deploys of the same stage can still overwrite each other. ` +\n\t\t\t\t`Implement StateStore (lock, versioned read, conditional write) or ` +\n\t\t\t\t`use the 'local', 'ssm' or 's3' state provider.`,\n\t\t);\n\t\tthis.name = 'StateStoreWithoutLocking';\n\t}\n}\n\nexport class StateStoreNeedsWorkspaceName extends Error {\n\tconstructor(readonly provider: string) {\n\t\tsuper(\n\t\t\t`The '${provider}' state provider keys state by workspace name. ` +\n\t\t\t\t`Set \"name\" in gkm.config.ts.`,\n\t\t);\n\t\tthis.name = 'StateStoreNeedsWorkspaceName';\n\t}\n}\n\nexport class UnknownStateProvider extends Error {\n\tconstructor(readonly config: unknown) {\n\t\tsuper(\n\t\t\t`Unknown state provider ${JSON.stringify(config)}. Use 'local', 'ssm', ` +\n\t\t\t\t`'s3', or an object implementing StateStore or StateProvider.`,\n\t\t);\n\t\tthis.name = 'UnknownStateProvider';\n\t}\n}\n\n/** A lock or write that could not get at the state backend in time. */\nexport class StateStoreBusy extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly location: string,\n\t) {\n\t\tsuper(\n\t\t\t`Timed out waiting to write deploy state for stage '${stage}' (${location}). ` +\n\t\t\t\t`If no other gkm process is running, remove ${location}.`,\n\t\t);\n\t\tthis.name = 'StateStoreBusy';\n\t}\n}\n\n// ============================================================================\n// Document format\n// ============================================================================\n\nexport const STATE_SCHEMA_VERSION = 2;\n\n/**\n * What a store persists. v1 was `DokployStageState` on its own; v2 wraps it\n * so resource records and a write serial can sit beside it. The serial\n * changes every write, so content-addressed versions (a file hash, an S3\n * ETag) never repeat — a writer holding a version from two writes ago cannot\n * match by accident.\n */\nexport interface StateDocument {\n\tschemaVersion: typeof STATE_SCHEMA_VERSION;\n\tstage: string;\n\tserial: number;\n\tstate: DokployStageState;\n\tresources: Record<string, ResourceRecord>;\n\tupdatedAt: string;\n}\n\nexport type DecodedDocument =\n\t| { schemaVersion: 2; document: StateDocument }\n\t| { schemaVersion: 1; state: DokployStageState };\n\nexport function decodeDocument(\n\tstage: string,\n\tbody: string,\n\tlocation: string,\n): DecodedDocument {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(body);\n\t} catch (error) {\n\t\tthrow new StateUnreadable(stage, location, error);\n\t}\n\n\tif (typeof parsed !== 'object' || parsed === null) {\n\t\tthrow new StateUnreadable(stage, location, parsed);\n\t}\n\n\tconst schemaVersion = (parsed as { schemaVersion?: unknown }).schemaVersion;\n\tif (schemaVersion === undefined) {\n\t\treturn { schemaVersion: 1, state: parsed as DokployStageState };\n\t}\n\tif (schemaVersion === STATE_SCHEMA_VERSION) {\n\t\treturn { schemaVersion: 2, document: parsed as StateDocument };\n\t}\n\tthrow new StateSchemaTooNew(stage, schemaVersion);\n}\n\nexport function encodeDocument(document: StateDocument): string {\n\treturn JSON.stringify(document, null, 2);\n}\n\n/**\n * v1 state as a v2 document. The ids v1 kept are seeded as `ready` records,\n * so the first journaled deploy after an upgrade adopts them rather than\n * treating them as unknown.\n */\nexport function migrateV1(\n\tstage: string,\n\tstate: DokployStageState,\n): StateDocument {\n\tconst now = new Date().toISOString();\n\tconst resources: Record<string, ResourceRecord> = {};\n\tconst seed = (type: string, name: string | null, id: string | undefined) => {\n\t\tif (!id) return;\n\t\tconst key = name ? `${type}:${name}` : type;\n\t\tresources[key] = { key, type, id, status: 'ready', updatedAt: now };\n\t};\n\n\tseed('project', null, state.projectId);\n\tseed('environment', null, state.environmentId);\n\tfor (const [app, id] of Object.entries(state.applications ?? {})) {\n\t\tseed('application', app, id);\n\t}\n\tseed('postgres', null, state.services?.postgresId);\n\tseed('redis', null, state.services?.redisId);\n\tseed('backup-destination', null, state.backups?.destinationId);\n\n\treturn {\n\t\tschemaVersion: STATE_SCHEMA_VERSION,\n\t\tstage,\n\t\tserial: 1,\n\t\tstate,\n\t\tresources,\n\t\tupdatedAt: now,\n\t};\n}\n\nexport function newLockHolder(options?: LockOptions): LockHolder {\n\tlet owner = 'unknown';\n\ttry {\n\t\towner = userInfo().username;\n\t} catch {\n\t\t// No passwd entry (a container running as an arbitrary uid).\n\t}\n\treturn {\n\t\tid: randomUUID(),\n\t\towner,\n\t\thost: hostname(),\n\t\tpid: process.pid,\n\t\tacquiredAt: new Date().toISOString(),\n\t\toperation: options?.operation,\n\t};\n}\n\nexport function contentVersion(body: string): StateVersion {\n\treturn createHash('sha256').update(body).digest('hex').slice(0, 16);\n}\n\n// ============================================================================\n// Shared implementation\n// ============================================================================\n\nexport interface RawState {\n\tbody: string;\n\tversion: StateVersion;\n}\n\n/**\n * A store over a backend that can read and conditionally write one blob per\n * stage, and create a lock object only if it does not exist. Migration and\n * resource records are the same for every such backend, so they live here.\n */\nexport abstract class DocumentStateStore implements StateStore {\n\t/** Where the stage's state lives, for messages. */\n\tprotected abstract location(stage: string): string;\n\tprotected abstract readRaw(stage: string): Promise<RawState | null>;\n\t/**\n\t * Writes only if the stored version is `expectedVersion` (`null`: only if\n\t * nothing is stored), else raises `StateVersionConflict`.\n\t */\n\tprotected abstract writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion>;\n\t/** Keeps the v1 body as it was. Never replaces an existing backup. */\n\tprotected abstract writeV1Backup(stage: string, body: string): Promise<void>;\n\t/** Creates the lock only if absent, else raises `StateLocked`. */\n\tprotected abstract createLock(\n\t\tstage: string,\n\t\tholder: LockHolder,\n\t): Promise<void>;\n\tprotected abstract readLock(stage: string): Promise<LockHolder | null>;\n\t/** Removes the lock — only if it is `holder`'s, when one is given. */\n\tprotected abstract removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void>;\n\n\tasync lock(stage: string, options?: LockOptions): Promise<StateLock> {\n\t\tconst holder = newLockHolder(options);\n\t\tawait this.createLock(stage, holder);\n\t\tlet released = false;\n\t\treturn {\n\t\t\tstage,\n\t\t\tholder,\n\t\t\trelease: async () => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tawait this.removeLock(stage, holder);\n\t\t\t},\n\t\t};\n\t}\n\n\tasync forceUnlock(stage: string): Promise<LockHolder | null> {\n\t\tconst holder = await this.readLock(stage);\n\t\tawait this.removeLock(stage, null);\n\t\treturn holder;\n\t}\n\n\tasync read(stage: string): Promise<StoredStageState | null> {\n\t\tconst current = await this.readDocument(stage);\n\t\tif (!current) return null;\n\t\treturn {\n\t\t\tstate: current.document.state,\n\t\t\tresources: current.document.resources,\n\t\t\tversion: current.version,\n\t\t};\n\t}\n\n\tasync write(\n\t\tstage: string,\n\t\tstate: DokployStageState,\n\t\toptions: WriteOptions,\n\t): Promise<StateVersion> {\n\t\tif (options.expectedVersion === null) {\n\t\t\tconst document: StateDocument = {\n\t\t\t\tschemaVersion: STATE_SCHEMA_VERSION,\n\t\t\t\tstage,\n\t\t\t\tserial: 1,\n\t\t\t\tstate,\n\t\t\t\tresources: {},\n\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t};\n\t\t\treturn this.writeRaw(stage, encodeDocument(document), null);\n\t\t}\n\t\treturn this.mutate(stage, options.expectedVersion, (document) => ({\n\t\t\t...document,\n\t\t\tstate,\n\t\t}));\n\t}\n\n\tasync putResource(\n\t\tstage: string,\n\t\trecord: ResourceInput,\n\t\toptions?: ResourceWriteOptions,\n\t): Promise<StateVersion> {\n\t\treturn this.mutate(stage, options?.expectedVersion, (document) => ({\n\t\t\t...document,\n\t\t\tresources: {\n\t\t\t\t...document.resources,\n\t\t\t\t[record.key]: { ...record, updatedAt: new Date().toISOString() },\n\t\t\t},\n\t\t}));\n\t}\n\n\tasync deleteResource(\n\t\tstage: string,\n\t\tkey: string,\n\t\toptions?: ResourceWriteOptions,\n\t): Promise<StateVersion> {\n\t\treturn this.mutate(stage, options?.expectedVersion, (document) => {\n\t\t\tconst { [key]: _removed, ...resources } = document.resources;\n\t\t\treturn { ...document, resources };\n\t\t});\n\t}\n\n\tprivate async mutate(\n\t\tstage: string,\n\t\texpectedVersion: StateVersion | undefined,\n\t\tchange: (document: StateDocument) => StateDocument,\n\t): Promise<StateVersion> {\n\t\tconst current = await this.readDocument(stage);\n\t\tif (!current) {\n\t\t\tif (expectedVersion !== undefined) {\n\t\t\t\tthrow new StateVersionConflict(stage, expectedVersion, null);\n\t\t\t}\n\t\t\tthrow new StageStateMissing(stage);\n\t\t}\n\t\tif (expectedVersion !== undefined && current.version !== expectedVersion) {\n\t\t\tthrow new StateVersionConflict(stage, expectedVersion, current.version);\n\t\t}\n\n\t\tconst next = change(current.document);\n\t\tnext.serial = current.document.serial + 1;\n\t\tnext.updatedAt = new Date().toISOString();\n\t\treturn this.writeRaw(stage, encodeDocument(next), current.version);\n\t}\n\n\t/**\n\t * Reads the stage's document, migrating v1 in place on the way. The\n\t * migration is itself a conditional write: if another run migrated (or\n\t * wrote) first, its result is read instead.\n\t */\n\tprivate async readDocument(\n\t\tstage: string,\n\t\tattempt = 0,\n\t): Promise<{ document: StateDocument; version: StateVersion } | null> {\n\t\tconst raw = await this.readRaw(stage);\n\t\tif (!raw) return null;\n\n\t\tconst decoded = decodeDocument(stage, raw.body, this.location(stage));\n\t\tif (decoded.schemaVersion === 2) {\n\t\t\treturn { document: decoded.document, version: raw.version };\n\t\t}\n\n\t\tawait this.writeV1Backup(stage, raw.body);\n\t\tconst document = migrateV1(stage, decoded.state);\n\t\ttry {\n\t\t\tconst version = await this.writeRaw(\n\t\t\t\tstage,\n\t\t\t\tencodeDocument(document),\n\t\t\t\traw.version,\n\t\t\t);\n\t\t\treturn { document, version };\n\t\t} catch (error) {\n\t\t\tif (error instanceof StateVersionConflict && attempt < 2) {\n\t\t\t\treturn this.readDocument(stage, attempt + 1);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\n// ============================================================================\n// Resolution\n// ============================================================================\n\nexport function isStateStore(value: unknown): value is StateStore {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\ttypeof (value as StateStore).lock === 'function' &&\n\t\ttypeof (value as StateStore).read === 'function' &&\n\t\ttypeof (value as StateStore).write === 'function' &&\n\t\ttypeof (value as StateStore).putResource === 'function'\n\t);\n}\n\nexport interface CreateStateStoreOptions extends CreateStateStoreConfig {\n\t/** Where `StateStoreWithoutLocking` goes; `process.emitWarning` by default. */\n\twarn?: (warning: StateStoreWithoutLocking) => void;\n}\n\n/**\n * The store for a workspace's `state` config:\n *\n * - none / `'local'`: `LocalStateStore` under `.gkm/`\n * - `'ssm'`: `SSMStateStore`, with no local cache — SSM is read directly, so\n * a stale local copy can never win over it\n * - `'s3'`: `S3StateStore`\n * - a `StateStore` object: used as is\n * - a `StateProvider` object: wrapped by `LegacyStateStore`\n */\nexport async function createStateStore(\n\toptions: CreateStateStoreOptions,\n): Promise<StateStore> {\n\tconst { config, workspaceRoot, workspaceName } = options;\n\n\tif (!config || config.provider === 'local') {\n\t\tconst { LocalStateStore } = await import('./LocalStateStore');\n\t\treturn new LocalStateStore(workspaceRoot);\n\t}\n\n\tif (isStateStore(config.provider)) {\n\t\treturn config.provider;\n\t}\n\n\tif (isStateProvider(config.provider)) {\n\t\tconst { LegacyStateStore } = await import('./LegacyStateStore');\n\t\treturn new LegacyStateStore(config.provider, { warn: options.warn });\n\t}\n\n\tif (config.provider === 'ssm') {\n\t\tif (!workspaceName) throw new StateStoreNeedsWorkspaceName('ssm');\n\t\tconst { SSMStateStore } = await import('./SSMStateStore');\n\t\tconst ssm = config as SSMStateConfig;\n\t\treturn SSMStateStore.create({\n\t\t\tworkspaceName,\n\t\t\tregion: ssm.region,\n\t\t\tprofile: ssm.profile,\n\t\t});\n\t}\n\n\tif (config.provider === 's3') {\n\t\tif (!workspaceName) throw new StateStoreNeedsWorkspaceName('s3');\n\t\tconst { S3StateStore } = await import('./S3StateStore');\n\t\tconst s3 = config as S3StateConfig;\n\t\treturn S3StateStore.create({\n\t\t\tworkspaceName,\n\t\t\tbucket: s3.bucket,\n\t\t\tprefix: s3.prefix,\n\t\t\tregion: s3.region,\n\t\t\tprofile: s3.profile,\n\t\t});\n\t}\n\n\tthrow new UnknownStateProvider(config);\n}\n"],"mappings":"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"}