@geekmidas/cli 10.0.0-alpha.92 → 10.0.0-alpha.94

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 (206) hide show
  1. package/dist/{GoDaddyProvider-BqYGk9XT.mjs → GoDaddyProvider-BRZUWD8z.mjs} +3 -3
  2. package/dist/{GoDaddyProvider-BqYGk9XT.mjs.map → GoDaddyProvider-BRZUWD8z.mjs.map} +1 -1
  3. package/dist/{GoDaddyProvider-CJwg2r_9.cjs → GoDaddyProvider-DzguxU_a.cjs} +3 -3
  4. package/dist/{GoDaddyProvider-CJwg2r_9.cjs.map → GoDaddyProvider-DzguxU_a.cjs.map} +1 -1
  5. package/dist/{HostingerProvider-1mqD6aG8.mjs → HostingerProvider-Cjh_2Ml6.mjs} +9 -4
  6. package/dist/{HostingerProvider-1mqD6aG8.mjs.map → HostingerProvider-Cjh_2Ml6.mjs.map} +1 -1
  7. package/dist/{HostingerProvider-C6t-PSNe.cjs → HostingerProvider-OdQmKXE3.cjs} +9 -4
  8. package/dist/HostingerProvider-OdQmKXE3.cjs.map +1 -0
  9. package/dist/LegacyStateStore-BQZb6-j2.mjs +124 -0
  10. package/dist/LegacyStateStore-BQZb6-j2.mjs.map +1 -0
  11. package/dist/LegacyStateStore-D50dgUPc.cjs +124 -0
  12. package/dist/LegacyStateStore-D50dgUPc.cjs.map +1 -0
  13. package/dist/{LocalStateStore-Y8XHdoCR.mjs → LocalStateStore-BSaS2535.mjs} +2 -2
  14. package/dist/{LocalStateStore-Y8XHdoCR.mjs.map → LocalStateStore-BSaS2535.mjs.map} +1 -1
  15. package/dist/{LocalStateStore-BMfseje2.cjs → LocalStateStore-EFBUjuMX.cjs} +8 -8
  16. package/dist/{LocalStateStore-BMfseje2.cjs.map → LocalStateStore-EFBUjuMX.cjs.map} +1 -1
  17. package/dist/{S3StateStore-BmMt2HhW.cjs → S3StateStore-C5mv8To3.cjs} +5 -5
  18. package/dist/{S3StateStore-BmMt2HhW.cjs.map → S3StateStore-C5mv8To3.cjs.map} +1 -1
  19. package/dist/{S3StateStore-BfoY4MvD.mjs → S3StateStore-CrFq4UZU.mjs} +2 -2
  20. package/dist/{S3StateStore-BfoY4MvD.mjs.map → S3StateStore-CrFq4UZU.mjs.map} +1 -1
  21. package/dist/{SSMStateStore-Bdyw_0Jj.mjs → SSMStateStore-D3Z6mZnA.mjs} +2 -2
  22. package/dist/{SSMStateStore-Bdyw_0Jj.mjs.map → SSMStateStore-D3Z6mZnA.mjs.map} +1 -1
  23. package/dist/{SSMStateStore-D9K62wM4.cjs → SSMStateStore-DuZCH7_v.cjs} +9 -9
  24. package/dist/{SSMStateStore-D9K62wM4.cjs.map → SSMStateStore-DuZCH7_v.cjs.map} +1 -1
  25. package/dist/{add-2Opn2VVE.mjs → add-C85isXFl.mjs} +4 -5
  26. package/dist/add-C85isXFl.mjs.map +1 -0
  27. package/dist/{add-oN0b18HN.cjs → add-DsVDdvon.cjs} +5 -6
  28. package/dist/add-DsVDdvon.cjs.map +1 -0
  29. package/dist/{bundler-CXQDrhzN.cjs → bundler-BEgUKc4j.cjs} +5 -5
  30. package/dist/{bundler-CXQDrhzN.cjs.map → bundler-BEgUKc4j.cjs.map} +1 -1
  31. package/dist/{bundler-C-AdesaG.mjs → bundler-BSHog8CH.mjs} +5 -5
  32. package/dist/{bundler-C-AdesaG.mjs.map → bundler-BSHog8CH.mjs.map} +1 -1
  33. package/dist/{cli-BjhaVObS.mjs → cli-BsFenyaf.mjs} +10 -6
  34. package/dist/cli-BsFenyaf.mjs.map +1 -0
  35. package/dist/{cli-BRZFYvdb.cjs → cli-BvBdv660.cjs} +9 -5
  36. package/dist/cli-BvBdv660.cjs.map +1 -0
  37. package/dist/compose-Cd09gW-n.cjs +1562 -0
  38. package/dist/compose-Cd09gW-n.cjs.map +1 -0
  39. package/dist/compose-D2RFYd_f.mjs +1515 -0
  40. package/dist/compose-D2RFYd_f.mjs.map +1 -0
  41. package/dist/{config-C9zx7KsX.d.cts → config-DvHNMoOp.d.cts} +4 -4
  42. package/dist/{config-BJo8Wggp.d.mts.map → config-DvHNMoOp.d.cts.map} +1 -1
  43. package/dist/{config-BJo8Wggp.d.mts → config-Ngesg4O8.d.mts} +4 -4
  44. package/dist/{config-C9zx7KsX.d.cts.map → config-Ngesg4O8.d.mts.map} +1 -1
  45. package/dist/config.cjs +1 -1
  46. package/dist/config.d.cts +3 -3
  47. package/dist/config.d.mts +3 -3
  48. package/dist/config.mjs +1 -1
  49. package/dist/{credentials-QBu7VgaQ.cjs → credentials-D7XJypNd.cjs} +9 -3
  50. package/dist/{credentials-QBu7VgaQ.cjs.map → credentials-D7XJypNd.cjs.map} +1 -1
  51. package/dist/{credentials-C4KII3PD.mjs → credentials-DpfnUvIR.mjs} +9 -3
  52. package/dist/{credentials-C4KII3PD.mjs.map → credentials-DpfnUvIR.mjs.map} +1 -1
  53. package/dist/deploy/public.cjs +13 -12
  54. package/dist/deploy/public.d.cts +25 -10
  55. package/dist/deploy/public.d.cts.map +1 -1
  56. package/dist/deploy/public.d.mts +25 -10
  57. package/dist/deploy/public.d.mts.map +1 -1
  58. package/dist/deploy/public.mjs +11 -11
  59. package/dist/{deploy-6ToUHJmM.cjs → deploy-C911Akkn.cjs} +125 -10
  60. package/dist/deploy-C911Akkn.cjs.map +1 -0
  61. package/dist/{deploy-yuJHlkY0.mjs → deploy-CeJ5D0xn.mjs} +124 -9
  62. package/dist/deploy-CeJ5D0xn.mjs.map +1 -0
  63. package/dist/{discover-G76EQ4vY.d.mts → discover-CTzOSGUF.d.cts} +2 -2
  64. package/dist/{discover-CRMhvHG9.d.cts.map → discover-CTzOSGUF.d.cts.map} +1 -1
  65. package/dist/{discover-CRMhvHG9.d.cts → discover-CihAtaoj.d.mts} +2 -2
  66. package/dist/{discover-G76EQ4vY.d.mts.map → discover-CihAtaoj.d.mts.map} +1 -1
  67. package/dist/{dokploy-Bvvfj2qe.cjs → dokploy-CV7GcobZ.cjs} +2 -2
  68. package/dist/{dokploy-Bvvfj2qe.cjs.map → dokploy-CV7GcobZ.cjs.map} +1 -1
  69. package/dist/{dokploy-BvbJ_A4Q.mjs → dokploy-D_aat2Xj.mjs} +2 -2
  70. package/dist/{dokploy-BvbJ_A4Q.mjs.map → dokploy-D_aat2Xj.mjs.map} +1 -1
  71. package/dist/{encryption-CgW167LO.cjs → encryption-DFcivJaO.cjs} +3 -3
  72. package/dist/{encryption-CgW167LO.cjs.map → encryption-DFcivJaO.cjs.map} +1 -1
  73. package/dist/{encryption-C9Jm9dfn.mjs → encryption-_4qKqtTx.mjs} +3 -3
  74. package/dist/{encryption-C9Jm9dfn.mjs.map → encryption-_4qKqtTx.mjs.map} +1 -1
  75. package/dist/{engine-Dj7ZKO8H.cjs → engine-Bv-LZpmj.cjs} +13 -11
  76. package/dist/engine-Bv-LZpmj.cjs.map +1 -0
  77. package/dist/{engine-C8Ye9MUm.mjs → engine-DPMHThkg.mjs} +11 -9
  78. package/dist/engine-DPMHThkg.mjs.map +1 -0
  79. package/dist/{github-BD051KZZ.cjs → github-Br42NMrW.cjs} +94 -6
  80. package/dist/github-Br42NMrW.cjs.map +1 -0
  81. package/dist/{github-B1k42hIt.mjs → github-xsDT19im.mjs} +94 -6
  82. package/dist/github-xsDT19im.mjs.map +1 -0
  83. package/dist/{index-D1pHwqkK.d.mts → index-BlpcClwM.d.mts} +2 -2
  84. package/dist/{index-B5uuwx-Y.d.cts.map → index-BlpcClwM.d.mts.map} +1 -1
  85. package/dist/{index-B5uuwx-Y.d.cts → index-CwLvFqtH.d.cts} +2 -2
  86. package/dist/{index-D1pHwqkK.d.mts.map → index-CwLvFqtH.d.cts.map} +1 -1
  87. package/dist/index.cjs +240 -250
  88. package/dist/index.cjs.map +1 -1
  89. package/dist/index.mjs +234 -243
  90. package/dist/index.mjs.map +1 -1
  91. package/dist/{local-Otp74ak0.mjs → local-B7Zv1NoA.mjs} +2 -2
  92. package/dist/{local-Otp74ak0.mjs.map → local-B7Zv1NoA.mjs.map} +1 -1
  93. package/dist/{local-B61kUh0w.cjs → local-OPC9BCZt.cjs} +2 -2
  94. package/dist/{local-B61kUh0w.cjs.map → local-OPC9BCZt.cjs.map} +1 -1
  95. package/dist/{migrate-IZsYyZ5F.mjs → migrate-B7evHv7O.mjs} +3 -3
  96. package/dist/{migrate-IZsYyZ5F.mjs.map → migrate-B7evHv7O.mjs.map} +1 -1
  97. package/dist/{migrate-D3VVsiHp.cjs → migrate-D5jUz20_.cjs} +3 -3
  98. package/dist/{migrate-D3VVsiHp.cjs.map → migrate-D5jUz20_.cjs.map} +1 -1
  99. package/dist/{migrate-BSbZ7-9n.mjs → migrate-DhvFPGLi.mjs} +4 -4
  100. package/dist/{migrate-BSbZ7-9n.mjs.map → migrate-DhvFPGLi.mjs.map} +1 -1
  101. package/dist/{migrate-f6JDjyCp.cjs → migrate-SkWJotn5.cjs} +3 -3
  102. package/dist/{migrate-f6JDjyCp.cjs.map → migrate-SkWJotn5.cjs.map} +1 -1
  103. package/dist/openapi.cjs +1 -1
  104. package/dist/openapi.d.cts +1 -1
  105. package/dist/openapi.d.mts +1 -1
  106. package/dist/openapi.mjs +1 -1
  107. package/dist/providers-BFeP_mYw.cjs +1098 -0
  108. package/dist/providers-BFeP_mYw.cjs.map +1 -0
  109. package/dist/providers-sTj-BG0S.mjs +1079 -0
  110. package/dist/providers-sTj-BG0S.mjs.map +1 -0
  111. package/dist/reconcile/public.d.cts +2 -2
  112. package/dist/reconcile/public.d.mts +2 -2
  113. package/dist/{reconcile-Dh1Jk7WE.mjs → reconcile--I7V4Klj.mjs} +2 -2
  114. package/dist/{reconcile-Dh1Jk7WE.mjs.map → reconcile--I7V4Klj.mjs.map} +1 -1
  115. package/dist/{resolve-DUp65SnJ.cjs → resolve-CFEOtRsl.cjs} +4 -4
  116. package/dist/{resolve-DUp65SnJ.cjs.map → resolve-CFEOtRsl.cjs.map} +1 -1
  117. package/dist/{resolve-Be4bF1w4.mjs → resolve-Dyd1S2wn.mjs} +4 -4
  118. package/dist/{resolve-Be4bF1w4.mjs.map → resolve-Dyd1S2wn.mjs.map} +1 -1
  119. package/dist/{rollback-DDHtd0GJ.cjs → rollback-D720SFhV.cjs} +11 -8
  120. package/dist/rollback-D720SFhV.cjs.map +1 -0
  121. package/dist/{rollback-D1d-fW0r.mjs → rollback-NroNbGC0.mjs} +10 -7
  122. package/dist/rollback-NroNbGC0.mjs.map +1 -0
  123. package/dist/{runtime-Es1V4e9M.d.mts → runtime-BRCq6dQ7.d.mts} +2 -2
  124. package/dist/{runtime-DDrHlYe5.d.cts.map → runtime-BRCq6dQ7.d.mts.map} +1 -1
  125. package/dist/{runtime-DDrHlYe5.d.cts → runtime-VktAo_8B.d.cts} +2 -2
  126. package/dist/{runtime-Es1V4e9M.d.mts.map → runtime-VktAo_8B.d.cts.map} +1 -1
  127. package/dist/{sandbox-MaxSeU92.d.cts → sandbox-D1gd0iDz.d.cts} +2 -2
  128. package/dist/{sandbox-C3WsTa5m.d.mts.map → sandbox-D1gd0iDz.d.cts.map} +1 -1
  129. package/dist/{sandbox-C3WsTa5m.d.mts → sandbox-zHnRLCj0.d.mts} +2 -2
  130. package/dist/{sandbox-MaxSeU92.d.cts.map → sandbox-zHnRLCj0.d.mts.map} +1 -1
  131. package/dist/secrets-CwLauUB_.mjs +36 -0
  132. package/dist/secrets-CwLauUB_.mjs.map +1 -0
  133. package/dist/secrets-DAvi_O9g.cjs +47 -0
  134. package/dist/secrets-DAvi_O9g.cjs.map +1 -0
  135. package/dist/{seeds-BKoJYsE7.cjs → seeds-BCg8X36S.cjs} +1732 -2300
  136. package/dist/seeds-BCg8X36S.cjs.map +1 -0
  137. package/dist/{seeds-CvGQJnPO.mjs → seeds-CTxTZa-R.mjs} +1637 -2270
  138. package/dist/seeds-CTxTZa-R.mjs.map +1 -0
  139. package/dist/{sniffer-Dq5BS98f.cjs → sniffer-C0z3NB2A.cjs} +572 -3
  140. package/dist/sniffer-C0z3NB2A.cjs.map +1 -0
  141. package/dist/{sniffer-BTtGAtm3.mjs → sniffer-DCcuzHz_.mjs} +496 -5
  142. package/dist/sniffer-DCcuzHz_.mjs.map +1 -0
  143. package/dist/{sst-CnKY-CSh.cjs → sst-DKNH243z.cjs} +3 -3
  144. package/dist/{sst-CnKY-CSh.cjs.map → sst-DKNH243z.cjs.map} +1 -1
  145. package/dist/{sst-BaRXp2Dd.mjs → sst-DRDSDwOk.mjs} +3 -3
  146. package/dist/{sst-BaRXp2Dd.mjs.map → sst-DRDSDwOk.mjs.map} +1 -1
  147. package/dist/stack-B3fPDTBg.cjs +1346 -0
  148. package/dist/stack-B3fPDTBg.cjs.map +1 -0
  149. package/dist/stack-NrdH5ngX.mjs +1239 -0
  150. package/dist/stack-NrdH5ngX.mjs.map +1 -0
  151. package/dist/{stages-BI8JvPzI.cjs → stages-CkqP1N4S.cjs} +16 -3
  152. package/dist/stages-CkqP1N4S.cjs.map +1 -0
  153. package/dist/{stages-DY-fQKmo.mjs → stages-veC4TcQe.mjs} +16 -3
  154. package/dist/stages-veC4TcQe.mjs.map +1 -0
  155. package/dist/target/public.cjs +1 -1
  156. package/dist/target/public.d.cts +2 -2
  157. package/dist/target/public.d.mts +2 -2
  158. package/dist/target/public.mjs +1 -1
  159. package/dist/{terminal-8XngdSTo.cjs → terminal-CkUYuAPd.cjs} +2 -2
  160. package/dist/{terminal-8XngdSTo.cjs.map → terminal-CkUYuAPd.cjs.map} +1 -1
  161. package/dist/{terminal-Dtd6BZX9.mjs → terminal-DAHs2Zko.mjs} +3 -3
  162. package/dist/{terminal-Dtd6BZX9.mjs.map → terminal-DAHs2Zko.mjs.map} +1 -1
  163. package/dist/{types-Bk9st2cd.d.cts → types-DMoaFrjg.d.mts} +108 -33
  164. package/dist/types-DMoaFrjg.d.mts.map +1 -0
  165. package/dist/{types-B3Gqs175.d.mts → types-DXyaSmqu.d.cts} +109 -32
  166. package/dist/types-DXyaSmqu.d.cts.map +1 -0
  167. package/dist/workspace/index.cjs +1 -1
  168. package/dist/workspace/index.d.cts +2 -2
  169. package/dist/workspace/index.d.mts +2 -2
  170. package/dist/workspace/index.mjs +1 -1
  171. package/dist/{workspace-Bzw7Zs82.mjs → workspace-DWK1PTXP.mjs} +4 -4
  172. package/dist/workspace-DWK1PTXP.mjs.map +1 -0
  173. package/dist/{workspace-CHOB2Xg3.cjs → workspace-cSyi5nb1.cjs} +4 -4
  174. package/dist/workspace-cSyi5nb1.cjs.map +1 -0
  175. package/package.json +15 -15
  176. package/dist/HostingerProvider-C6t-PSNe.cjs.map +0 -1
  177. package/dist/LegacyStateStore-CeEwayMB.cjs +0 -89
  178. package/dist/LegacyStateStore-CeEwayMB.cjs.map +0 -1
  179. package/dist/LegacyStateStore-DdJ1bCRV.mjs +0 -89
  180. package/dist/LegacyStateStore-DdJ1bCRV.mjs.map +0 -1
  181. package/dist/add-2Opn2VVE.mjs.map +0 -1
  182. package/dist/add-oN0b18HN.cjs.map +0 -1
  183. package/dist/cli-BRZFYvdb.cjs.map +0 -1
  184. package/dist/cli-BjhaVObS.mjs.map +0 -1
  185. package/dist/compose-CTdVeeVk.mjs +0 -3151
  186. package/dist/compose-CTdVeeVk.mjs.map +0 -1
  187. package/dist/compose-fnYmPk_c.cjs +0 -3276
  188. package/dist/compose-fnYmPk_c.cjs.map +0 -1
  189. package/dist/deploy-6ToUHJmM.cjs.map +0 -1
  190. package/dist/deploy-yuJHlkY0.mjs.map +0 -1
  191. package/dist/engine-C8Ye9MUm.mjs.map +0 -1
  192. package/dist/engine-Dj7ZKO8H.cjs.map +0 -1
  193. package/dist/github-B1k42hIt.mjs.map +0 -1
  194. package/dist/github-BD051KZZ.cjs.map +0 -1
  195. package/dist/rollback-D1d-fW0r.mjs.map +0 -1
  196. package/dist/rollback-DDHtd0GJ.cjs.map +0 -1
  197. package/dist/seeds-BKoJYsE7.cjs.map +0 -1
  198. package/dist/seeds-CvGQJnPO.mjs.map +0 -1
  199. package/dist/sniffer-BTtGAtm3.mjs.map +0 -1
  200. package/dist/sniffer-Dq5BS98f.cjs.map +0 -1
  201. package/dist/stages-BI8JvPzI.cjs.map +0 -1
  202. package/dist/stages-DY-fQKmo.mjs.map +0 -1
  203. package/dist/types-B3Gqs175.d.mts.map +0 -1
  204. package/dist/types-Bk9st2cd.d.cts.map +0 -1
  205. package/dist/workspace-Bzw7Zs82.mjs.map +0 -1
  206. package/dist/workspace-CHOB2Xg3.cjs.map +0 -1
@@ -0,0 +1,124 @@
1
+ import { g as currentActor } from "./seeds-CTxTZa-R.mjs";
2
+ import { b as newLockHolder, d as StageStateMissing, g as appendHistory, h as StateVersionConflict, m as StateStoreWithoutLocking, v as contentVersion, x as upgradeV2State } from "./sniffer-DCcuzHz_.mjs";
3
+ //#region src/deploy/LegacyStateStore.ts
4
+ /**
5
+ * A custom `StateProvider` as a `StateStore`.
6
+ *
7
+ * A provider only reads and writes whole states, so this adapter can check a
8
+ * version before it writes but cannot make the check and the write one step,
9
+ * and cannot lock. It warns once (`StateStoreWithoutLocking`) the first time
10
+ * a caller relies on either, and otherwise behaves as a store. Resource
11
+ * records travel inside the state the provider stores, under `resources`.
12
+ */
13
+ var LegacyStateStore = class {
14
+ provider;
15
+ warned = false;
16
+ /** The operation of the lock each stage is held for. */
17
+ held = /* @__PURE__ */ new Map();
18
+ logged = /* @__PURE__ */ new Map();
19
+ warn;
20
+ constructor(provider, options = {}) {
21
+ this.provider = provider;
22
+ this.warn = options.warn ?? ((warning) => process.emitWarning(warning));
23
+ }
24
+ async lock(stage, options) {
25
+ this.warnOnce();
26
+ const holder = newLockHolder(options);
27
+ this.held.set(stage, holder);
28
+ return {
29
+ stage,
30
+ holder,
31
+ release: async () => {
32
+ if (this.held.get(stage)?.id === holder.id) this.held.delete(stage);
33
+ }
34
+ };
35
+ }
36
+ async forceUnlock(_stage) {
37
+ return null;
38
+ }
39
+ async read(stage) {
40
+ const stored = await this.provider.read(stage);
41
+ if (!stored) return null;
42
+ const { resources: storedResources = {}, history = [], ...storedState } = stored;
43
+ const { state, resources } = storedState.provider === "dokploy" ? upgradeV2State(storedState, storedResources) : {
44
+ state: storedState,
45
+ resources: storedResources
46
+ };
47
+ return {
48
+ state,
49
+ resources,
50
+ history,
51
+ version: versionOf(stored)
52
+ };
53
+ }
54
+ async write(stage, state, options) {
55
+ const current = await this.read(stage);
56
+ this.check(stage, options.expectedVersion, current);
57
+ return this.put(stage, current, {
58
+ ...state,
59
+ resources: current?.resources ?? {}
60
+ });
61
+ }
62
+ async putResource(stage, record, options) {
63
+ const current = await this.current(stage, options);
64
+ return this.put(stage, current, {
65
+ ...current.state,
66
+ resources: {
67
+ ...current.resources,
68
+ [record.key]: {
69
+ ...record,
70
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
71
+ updatedBy: currentActor()
72
+ }
73
+ }
74
+ });
75
+ }
76
+ async deleteResource(stage, key, options) {
77
+ const current = await this.current(stage, options);
78
+ const { [key]: _removed, ...resources } = current.resources;
79
+ return this.put(stage, current, {
80
+ ...current.state,
81
+ resources
82
+ });
83
+ }
84
+ async current(stage, options) {
85
+ const current = await this.read(stage);
86
+ if (options?.expectedVersion !== void 0) this.check(stage, options.expectedVersion, current);
87
+ if (!current) throw new StageStateMissing(stage);
88
+ return current;
89
+ }
90
+ check(stage, expected, current) {
91
+ const actual = current?.version ?? null;
92
+ if (actual !== expected) throw new StateVersionConflict(stage, expected, actual);
93
+ }
94
+ async put(stage, current, state) {
95
+ this.warnOnce();
96
+ const holder = this.held.get(stage);
97
+ const operation = holder?.operation ?? "write";
98
+ const history = current?.history ?? [];
99
+ const sameRun = holder !== void 0 && this.logged.get(stage) === holder.id && history[0]?.operation === operation;
100
+ if (holder) this.logged.set(stage, holder.id);
101
+ const serial = (history[0]?.serial ?? 0) + 1;
102
+ state.history = appendHistory(history, {
103
+ serial,
104
+ at: (/* @__PURE__ */ new Date()).toISOString(),
105
+ by: currentActor(),
106
+ operation
107
+ }, sameRun);
108
+ await this.provider.write(stage, state);
109
+ return versionOf(state);
110
+ }
111
+ warnOnce() {
112
+ if (this.warned) return;
113
+ this.warned = true;
114
+ this.warn(new StateStoreWithoutLocking(this.provider.constructor?.name || "anonymous"));
115
+ }
116
+ };
117
+ /** Key order is the provider's, so this is stable for a given stored state. */
118
+ function versionOf(state) {
119
+ return contentVersion(JSON.stringify(state));
120
+ }
121
+ //#endregion
122
+ export { LegacyStateStore };
123
+
124
+ //# sourceMappingURL=LegacyStateStore-BQZb6-j2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LegacyStateStore-BQZb6-j2.mjs","names":[],"sources":["../src/deploy/LegacyStateStore.ts"],"sourcesContent":["/**\n * A custom `StateProvider` as a `StateStore`.\n *\n * A provider only reads and writes whole states, so this adapter can check a\n * version before it writes but cannot make the check and the write one step,\n * and cannot lock. It warns once (`StateStoreWithoutLocking`) the first time\n * a caller relies on either, and otherwise behaves as a store. Resource\n * records travel inside the state the provider stores, under `resources`.\n */\n\nimport { currentActor } from './actor';\nimport type { StateProvider } from './StateProvider';\nimport {\n\tappendHistory,\n\tcontentVersion,\n\ttype HistoryEntry,\n\ttype LockHolder,\n\ttype LockOptions,\n\tnewLockHolder,\n\ttype ResourceInput,\n\ttype ResourceRecord,\n\ttype ResourceWriteOptions,\n\tStageStateMissing,\n\ttype StateLock,\n\ttype StateStore,\n\tStateStoreWithoutLocking,\n\ttype StateVersion,\n\tStateVersionConflict,\n\ttype StoredStageState,\n\tupgradeV2State,\n\ttype WriteOptions,\n} from './StateStore';\nimport type { StageState } from './state';\n\ntype ProviderState = StageState & {\n\tresources?: Record<string, ResourceRecord>;\n\thistory?: HistoryEntry[];\n};\n\nexport interface LegacyStateStoreOptions {\n\t/** Where the warning goes; `process.emitWarning` by default. */\n\twarn?: (warning: StateStoreWithoutLocking) => void;\n}\n\nexport class LegacyStateStore implements StateStore {\n\tprivate warned = false;\n\t/** The operation of the lock each stage is held for. */\n\tprivate readonly held = new Map<string, LockHolder>();\n\tprivate readonly logged = new Map<string, string>();\n\tprivate readonly warn: (warning: StateStoreWithoutLocking) => void;\n\n\tconstructor(\n\t\treadonly provider: StateProvider,\n\t\toptions: LegacyStateStoreOptions = {},\n\t) {\n\t\tthis.warn = options.warn ?? ((warning) => process.emitWarning(warning));\n\t}\n\n\tasync lock(stage: string, options?: LockOptions): Promise<StateLock> {\n\t\tthis.warnOnce();\n\t\tconst holder = newLockHolder(options);\n\t\tthis.held.set(stage, holder);\n\t\treturn {\n\t\t\tstage,\n\t\t\tholder,\n\t\t\trelease: async () => {\n\t\t\t\tif (this.held.get(stage)?.id === holder.id) this.held.delete(stage);\n\t\t\t},\n\t\t};\n\t}\n\n\tasync forceUnlock(_stage: string): Promise<LockHolder | null> {\n\t\treturn null;\n\t}\n\n\tasync read(stage: string): Promise<StoredStageState | null> {\n\t\tconst stored = (await this.provider.read(stage)) as ProviderState | null;\n\t\tif (!stored) return null;\n\t\tconst {\n\t\t\tresources: storedResources = {},\n\t\t\thistory = [],\n\t\t\t...storedState\n\t\t} = stored;\n\t\tconst { state, resources } =\n\t\t\tstoredState.provider === 'dokploy'\n\t\t\t\t? upgradeV2State(storedState, storedResources)\n\t\t\t\t: { state: storedState as StageState, resources: storedResources };\n\t\treturn { state, resources, history, version: versionOf(stored) };\n\t}\n\n\tasync write(\n\t\tstage: string,\n\t\tstate: StageState,\n\t\toptions: WriteOptions,\n\t): Promise<StateVersion> {\n\t\tconst current = await this.read(stage);\n\t\tthis.check(stage, options.expectedVersion, current);\n\t\treturn this.put(stage, current, {\n\t\t\t...state,\n\t\t\tresources: current?.resources ?? {},\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\tconst current = await this.current(stage, options);\n\t\treturn this.put(stage, current, {\n\t\t\t...current.state,\n\t\t\tresources: {\n\t\t\t\t...current.resources,\n\t\t\t\t[record.key]: {\n\t\t\t\t\t...record,\n\t\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\t\tupdatedBy: currentActor(),\n\t\t\t\t},\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\tconst current = await this.current(stage, options);\n\t\tconst { [key]: _removed, ...resources } = current.resources;\n\t\treturn this.put(stage, current, { ...current.state, resources });\n\t}\n\n\tprivate async current(\n\t\tstage: string,\n\t\toptions?: ResourceWriteOptions,\n\t): Promise<StoredStageState> {\n\t\tconst current = await this.read(stage);\n\t\tif (options?.expectedVersion !== undefined) {\n\t\t\tthis.check(stage, options.expectedVersion, current);\n\t\t}\n\t\tif (!current) throw new StageStateMissing(stage);\n\t\treturn current;\n\t}\n\n\tprivate check(\n\t\tstage: string,\n\t\texpected: StateVersion | null,\n\t\tcurrent: StoredStageState | null,\n\t): void {\n\t\tconst actual = current?.version ?? null;\n\t\tif (actual !== expected) {\n\t\t\tthrow new StateVersionConflict(stage, expected, actual);\n\t\t}\n\t}\n\n\tprivate async put(\n\t\tstage: string,\n\t\tcurrent: StoredStageState | null,\n\t\tstate: ProviderState,\n\t): Promise<StateVersion> {\n\t\tthis.warnOnce();\n\t\tconst holder = this.held.get(stage);\n\t\tconst operation = holder?.operation ?? 'write';\n\t\tconst history = current?.history ?? [];\n\t\tconst sameRun =\n\t\t\tholder !== undefined &&\n\t\t\tthis.logged.get(stage) === holder.id &&\n\t\t\thistory[0]?.operation === operation;\n\t\tif (holder) this.logged.set(stage, holder.id);\n\t\tconst serial = (history[0]?.serial ?? 0) + 1;\n\t\tstate.history = appendHistory(\n\t\t\thistory,\n\t\t\t{ serial, at: new Date().toISOString(), by: currentActor(), operation },\n\t\t\tsameRun,\n\t\t);\n\t\t// Providers stamp `lastDeployedAt` on the object they are given, so the\n\t\t// version is taken after the write, from what was actually stored.\n\t\tawait this.provider.write(stage, state);\n\t\treturn versionOf(state);\n\t}\n\n\tprivate warnOnce(): void {\n\t\tif (this.warned) return;\n\t\tthis.warned = true;\n\t\tthis.warn(\n\t\t\tnew StateStoreWithoutLocking(\n\t\t\t\tthis.provider.constructor?.name || 'anonymous',\n\t\t\t),\n\t\t);\n\t}\n}\n\n/** Key order is the provider's, so this is stable for a given stored state. */\nfunction versionOf(state: ProviderState): StateVersion {\n\treturn contentVersion(JSON.stringify(state));\n}\n"],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ const require_seeds = require("./seeds-BCg8X36S.cjs");
2
+ const require_sniffer = require("./sniffer-C0z3NB2A.cjs");
3
+ //#region src/deploy/LegacyStateStore.ts
4
+ /**
5
+ * A custom `StateProvider` as a `StateStore`.
6
+ *
7
+ * A provider only reads and writes whole states, so this adapter can check a
8
+ * version before it writes but cannot make the check and the write one step,
9
+ * and cannot lock. It warns once (`StateStoreWithoutLocking`) the first time
10
+ * a caller relies on either, and otherwise behaves as a store. Resource
11
+ * records travel inside the state the provider stores, under `resources`.
12
+ */
13
+ var LegacyStateStore = class {
14
+ provider;
15
+ warned = false;
16
+ /** The operation of the lock each stage is held for. */
17
+ held = /* @__PURE__ */ new Map();
18
+ logged = /* @__PURE__ */ new Map();
19
+ warn;
20
+ constructor(provider, options = {}) {
21
+ this.provider = provider;
22
+ this.warn = options.warn ?? ((warning) => process.emitWarning(warning));
23
+ }
24
+ async lock(stage, options) {
25
+ this.warnOnce();
26
+ const holder = require_sniffer.newLockHolder(options);
27
+ this.held.set(stage, holder);
28
+ return {
29
+ stage,
30
+ holder,
31
+ release: async () => {
32
+ if (this.held.get(stage)?.id === holder.id) this.held.delete(stage);
33
+ }
34
+ };
35
+ }
36
+ async forceUnlock(_stage) {
37
+ return null;
38
+ }
39
+ async read(stage) {
40
+ const stored = await this.provider.read(stage);
41
+ if (!stored) return null;
42
+ const { resources: storedResources = {}, history = [], ...storedState } = stored;
43
+ const { state, resources } = storedState.provider === "dokploy" ? require_sniffer.upgradeV2State(storedState, storedResources) : {
44
+ state: storedState,
45
+ resources: storedResources
46
+ };
47
+ return {
48
+ state,
49
+ resources,
50
+ history,
51
+ version: versionOf(stored)
52
+ };
53
+ }
54
+ async write(stage, state, options) {
55
+ const current = await this.read(stage);
56
+ this.check(stage, options.expectedVersion, current);
57
+ return this.put(stage, current, {
58
+ ...state,
59
+ resources: current?.resources ?? {}
60
+ });
61
+ }
62
+ async putResource(stage, record, options) {
63
+ const current = await this.current(stage, options);
64
+ return this.put(stage, current, {
65
+ ...current.state,
66
+ resources: {
67
+ ...current.resources,
68
+ [record.key]: {
69
+ ...record,
70
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
71
+ updatedBy: require_seeds.currentActor()
72
+ }
73
+ }
74
+ });
75
+ }
76
+ async deleteResource(stage, key, options) {
77
+ const current = await this.current(stage, options);
78
+ const { [key]: _removed, ...resources } = current.resources;
79
+ return this.put(stage, current, {
80
+ ...current.state,
81
+ resources
82
+ });
83
+ }
84
+ async current(stage, options) {
85
+ const current = await this.read(stage);
86
+ if (options?.expectedVersion !== void 0) this.check(stage, options.expectedVersion, current);
87
+ if (!current) throw new require_sniffer.StageStateMissing(stage);
88
+ return current;
89
+ }
90
+ check(stage, expected, current) {
91
+ const actual = current?.version ?? null;
92
+ if (actual !== expected) throw new require_sniffer.StateVersionConflict(stage, expected, actual);
93
+ }
94
+ async put(stage, current, state) {
95
+ this.warnOnce();
96
+ const holder = this.held.get(stage);
97
+ const operation = holder?.operation ?? "write";
98
+ const history = current?.history ?? [];
99
+ const sameRun = holder !== void 0 && this.logged.get(stage) === holder.id && history[0]?.operation === operation;
100
+ if (holder) this.logged.set(stage, holder.id);
101
+ const serial = (history[0]?.serial ?? 0) + 1;
102
+ state.history = require_sniffer.appendHistory(history, {
103
+ serial,
104
+ at: (/* @__PURE__ */ new Date()).toISOString(),
105
+ by: require_seeds.currentActor(),
106
+ operation
107
+ }, sameRun);
108
+ await this.provider.write(stage, state);
109
+ return versionOf(state);
110
+ }
111
+ warnOnce() {
112
+ if (this.warned) return;
113
+ this.warned = true;
114
+ this.warn(new require_sniffer.StateStoreWithoutLocking(this.provider.constructor?.name || "anonymous"));
115
+ }
116
+ };
117
+ /** Key order is the provider's, so this is stable for a given stored state. */
118
+ function versionOf(state) {
119
+ return require_sniffer.contentVersion(JSON.stringify(state));
120
+ }
121
+ //#endregion
122
+ exports.LegacyStateStore = LegacyStateStore;
123
+
124
+ //# sourceMappingURL=LegacyStateStore-D50dgUPc.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LegacyStateStore-D50dgUPc.cjs","names":["newLockHolder","upgradeV2State","currentActor","StageStateMissing","StateVersionConflict","appendHistory","StateStoreWithoutLocking","contentVersion"],"sources":["../src/deploy/LegacyStateStore.ts"],"sourcesContent":["/**\n * A custom `StateProvider` as a `StateStore`.\n *\n * A provider only reads and writes whole states, so this adapter can check a\n * version before it writes but cannot make the check and the write one step,\n * and cannot lock. It warns once (`StateStoreWithoutLocking`) the first time\n * a caller relies on either, and otherwise behaves as a store. Resource\n * records travel inside the state the provider stores, under `resources`.\n */\n\nimport { currentActor } from './actor';\nimport type { StateProvider } from './StateProvider';\nimport {\n\tappendHistory,\n\tcontentVersion,\n\ttype HistoryEntry,\n\ttype LockHolder,\n\ttype LockOptions,\n\tnewLockHolder,\n\ttype ResourceInput,\n\ttype ResourceRecord,\n\ttype ResourceWriteOptions,\n\tStageStateMissing,\n\ttype StateLock,\n\ttype StateStore,\n\tStateStoreWithoutLocking,\n\ttype StateVersion,\n\tStateVersionConflict,\n\ttype StoredStageState,\n\tupgradeV2State,\n\ttype WriteOptions,\n} from './StateStore';\nimport type { StageState } from './state';\n\ntype ProviderState = StageState & {\n\tresources?: Record<string, ResourceRecord>;\n\thistory?: HistoryEntry[];\n};\n\nexport interface LegacyStateStoreOptions {\n\t/** Where the warning goes; `process.emitWarning` by default. */\n\twarn?: (warning: StateStoreWithoutLocking) => void;\n}\n\nexport class LegacyStateStore implements StateStore {\n\tprivate warned = false;\n\t/** The operation of the lock each stage is held for. */\n\tprivate readonly held = new Map<string, LockHolder>();\n\tprivate readonly logged = new Map<string, string>();\n\tprivate readonly warn: (warning: StateStoreWithoutLocking) => void;\n\n\tconstructor(\n\t\treadonly provider: StateProvider,\n\t\toptions: LegacyStateStoreOptions = {},\n\t) {\n\t\tthis.warn = options.warn ?? ((warning) => process.emitWarning(warning));\n\t}\n\n\tasync lock(stage: string, options?: LockOptions): Promise<StateLock> {\n\t\tthis.warnOnce();\n\t\tconst holder = newLockHolder(options);\n\t\tthis.held.set(stage, holder);\n\t\treturn {\n\t\t\tstage,\n\t\t\tholder,\n\t\t\trelease: async () => {\n\t\t\t\tif (this.held.get(stage)?.id === holder.id) this.held.delete(stage);\n\t\t\t},\n\t\t};\n\t}\n\n\tasync forceUnlock(_stage: string): Promise<LockHolder | null> {\n\t\treturn null;\n\t}\n\n\tasync read(stage: string): Promise<StoredStageState | null> {\n\t\tconst stored = (await this.provider.read(stage)) as ProviderState | null;\n\t\tif (!stored) return null;\n\t\tconst {\n\t\t\tresources: storedResources = {},\n\t\t\thistory = [],\n\t\t\t...storedState\n\t\t} = stored;\n\t\tconst { state, resources } =\n\t\t\tstoredState.provider === 'dokploy'\n\t\t\t\t? upgradeV2State(storedState, storedResources)\n\t\t\t\t: { state: storedState as StageState, resources: storedResources };\n\t\treturn { state, resources, history, version: versionOf(stored) };\n\t}\n\n\tasync write(\n\t\tstage: string,\n\t\tstate: StageState,\n\t\toptions: WriteOptions,\n\t): Promise<StateVersion> {\n\t\tconst current = await this.read(stage);\n\t\tthis.check(stage, options.expectedVersion, current);\n\t\treturn this.put(stage, current, {\n\t\t\t...state,\n\t\t\tresources: current?.resources ?? {},\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\tconst current = await this.current(stage, options);\n\t\treturn this.put(stage, current, {\n\t\t\t...current.state,\n\t\t\tresources: {\n\t\t\t\t...current.resources,\n\t\t\t\t[record.key]: {\n\t\t\t\t\t...record,\n\t\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\t\tupdatedBy: currentActor(),\n\t\t\t\t},\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\tconst current = await this.current(stage, options);\n\t\tconst { [key]: _removed, ...resources } = current.resources;\n\t\treturn this.put(stage, current, { ...current.state, resources });\n\t}\n\n\tprivate async current(\n\t\tstage: string,\n\t\toptions?: ResourceWriteOptions,\n\t): Promise<StoredStageState> {\n\t\tconst current = await this.read(stage);\n\t\tif (options?.expectedVersion !== undefined) {\n\t\t\tthis.check(stage, options.expectedVersion, current);\n\t\t}\n\t\tif (!current) throw new StageStateMissing(stage);\n\t\treturn current;\n\t}\n\n\tprivate check(\n\t\tstage: string,\n\t\texpected: StateVersion | null,\n\t\tcurrent: StoredStageState | null,\n\t): void {\n\t\tconst actual = current?.version ?? null;\n\t\tif (actual !== expected) {\n\t\t\tthrow new StateVersionConflict(stage, expected, actual);\n\t\t}\n\t}\n\n\tprivate async put(\n\t\tstage: string,\n\t\tcurrent: StoredStageState | null,\n\t\tstate: ProviderState,\n\t): Promise<StateVersion> {\n\t\tthis.warnOnce();\n\t\tconst holder = this.held.get(stage);\n\t\tconst operation = holder?.operation ?? 'write';\n\t\tconst history = current?.history ?? [];\n\t\tconst sameRun =\n\t\t\tholder !== undefined &&\n\t\t\tthis.logged.get(stage) === holder.id &&\n\t\t\thistory[0]?.operation === operation;\n\t\tif (holder) this.logged.set(stage, holder.id);\n\t\tconst serial = (history[0]?.serial ?? 0) + 1;\n\t\tstate.history = appendHistory(\n\t\t\thistory,\n\t\t\t{ serial, at: new Date().toISOString(), by: currentActor(), operation },\n\t\t\tsameRun,\n\t\t);\n\t\t// Providers stamp `lastDeployedAt` on the object they are given, so the\n\t\t// version is taken after the write, from what was actually stored.\n\t\tawait this.provider.write(stage, state);\n\t\treturn versionOf(state);\n\t}\n\n\tprivate warnOnce(): void {\n\t\tif (this.warned) return;\n\t\tthis.warned = true;\n\t\tthis.warn(\n\t\t\tnew StateStoreWithoutLocking(\n\t\t\t\tthis.provider.constructor?.name || 'anonymous',\n\t\t\t),\n\t\t);\n\t}\n}\n\n/** Key order is the provider's, so this is stable for a given stored state. */\nfunction versionOf(state: ProviderState): StateVersion {\n\treturn contentVersion(JSON.stringify(state));\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
- import { H as contentVersion, I as DocumentStateStore, R as StateLocked, V as StateVersionConflict, z as StateStoreBusy } from "./seeds-CvGQJnPO.mjs";
2
+ import { f as StateLocked, h as StateVersionConflict, l as DocumentStateStore, p as StateStoreBusy, v as contentVersion } from "./sniffer-DCcuzHz_.mjs";
3
3
  import { join } from "node:path";
4
4
  import { link, mkdir, open, readFile, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
5
5
  import { randomUUID } from "node:crypto";
@@ -164,4 +164,4 @@ var LocalStateStore = class extends DocumentStateStore {
164
164
  //#endregion
165
165
  export { LocalStateStore_exports as n, LocalStateStore as t };
166
166
 
167
- //# sourceMappingURL=LocalStateStore-Y8XHdoCR.mjs.map
167
+ //# sourceMappingURL=LocalStateStore-BSaS2535.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"LocalStateStore-Y8XHdoCR.mjs","names":[],"sources":["../src/deploy/LocalStateStore.ts"],"sourcesContent":["/**\n * Local filesystem state store.\n *\n * `.gkm/deploy-<stage>.json` holds the state, `.gkm/deploy-<stage>.lock` the\n * lock. Every file is mode 0600: state holds database passwords and IAM keys.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport {\n\tlink,\n\tmkdir,\n\topen,\n\treadFile,\n\trename,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcontentVersion,\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\tStateStoreBusy,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nconst FILE_MODE = 0o600;\n\n/** How long a write waits for another process's write to finish. */\nconst MUTEX_TIMEOUT_MS = 5_000;\n/**\n * A write holds the mutex for milliseconds, so one this old was left by a\n * process that died mid-write and is safe to take over.\n */\nconst MUTEX_STALE_MS = 30_000;\n\nfunction isCode(error: unknown, code: string): boolean {\n\treturn (error as NodeJS.ErrnoException)?.code === code;\n}\n\nexport class LocalStateStore extends DocumentStateStore {\n\tconstructor(readonly workspaceRoot: string) {\n\t\tsuper();\n\t}\n\n\tprivate get dir(): string {\n\t\treturn join(this.workspaceRoot, '.gkm');\n\t}\n\n\tstatePath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json`);\n\t}\n\n\tlockPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.lock`);\n\t}\n\n\tbackupPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.v1.json`);\n\t}\n\n\tprivate mutexPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json.mutex`);\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn this.statePath(stage);\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\ttry {\n\t\t\tconst body = await readFile(this.statePath(stage), 'utf-8');\n\t\t\treturn { body, version: contentVersion(body) };\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'ENOENT')) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tawait this.ensureDir();\n\t\treturn this.withMutex(stage, async () => {\n\t\t\tconst current = await this.readRaw(stage);\n\t\t\tconst actual = current?.version ?? null;\n\t\t\tif (actual !== expectedVersion) {\n\t\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t\t}\n\n\t\t\t// Written beside the target and renamed over it: a reader sees the\n\t\t\t// old file or the new one, never half of either.\n\t\t\tconst target = this.statePath(stage);\n\t\t\tconst temp = `${target}.${randomUUID()}.tmp`;\n\t\t\ttry {\n\t\t\t\tconst handle = await open(temp, 'wx', FILE_MODE);\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.writeFile(body);\n\t\t\t\t\tawait handle.sync();\n\t\t\t\t} finally {\n\t\t\t\t\tawait handle.close();\n\t\t\t\t}\n\t\t\t\tawait rename(temp, target);\n\t\t\t} catch (error) {\n\t\t\t\tawait rm(temp, { force: true });\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn contentVersion(body);\n\t\t});\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait writeFile(this.backupPath(stage), body, {\n\t\t\t\tflag: 'wx',\n\t\t\t\tmode: FILE_MODE,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tawait this.ensureDir();\n\t\t// Written whole, then linked into place: `link` fails if the lock\n\t\t// exists, so taking it is atomic — and a racing runner that loses never\n\t\t// reads a lock its winner has created but not yet written to.\n\t\tconst lock = this.lockPath(stage);\n\t\tconst temp = `${lock}.${randomUUID()}.tmp`;\n\t\tawait writeFile(temp, JSON.stringify(holder, null, 2), {\n\t\t\tflag: 'wx',\n\t\t\tmode: FILE_MODE,\n\t\t});\n\t\ttry {\n\t\t\tawait link(temp, lock);\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'EEXIST')) {\n\t\t\t\tthrow new StateLocked(stage, await this.readLock(stage), lock);\n\t\t\t}\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tawait unlink(temp).catch(() => {});\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\ttry {\n\t\t\treturn JSON.parse(\n\t\t\t\tawait readFile(this.lockPath(stage), 'utf-8'),\n\t\t\t) as LockHolder;\n\t\t} catch {\n\t\t\t// Missing, or created by a process that died before writing to it.\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\tawait rm(this.lockPath(stage), { force: true });\n\t}\n\n\tprivate async ensureDir(): Promise<void> {\n\t\tawait mkdir(this.dir, { recursive: true, mode: 0o700 });\n\t}\n\n\t/**\n\t * Serialises the check-then-rename of a conditional write across\n\t * processes. The stage lock is for whole runs; this is for the instant of\n\t * a single write, so even writers that skip the lock never interleave.\n\t */\n\tprivate async withMutex<T>(stage: string, fn: () => Promise<T>): Promise<T> {\n\t\tconst path = this.mutexPath(stage);\n\t\tconst deadline = Date.now() + MUTEX_TIMEOUT_MS;\n\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tconst handle = await open(path, 'wx', FILE_MODE);\n\t\t\t\tawait handle.close();\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t\t\tif (await this.isStale(path)) {\n\t\t\t\t\tawait unlink(path).catch(() => {});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() > deadline) throw new StateStoreBusy(stage, path);\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10));\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tawait rm(path, { force: true });\n\t\t}\n\t}\n\n\tprivate async isStale(path: string): Promise<boolean> {\n\t\ttry {\n\t\t\treturn Date.now() - (await stat(path)).mtimeMs > MUTEX_STALE_MS;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"LocalStateStore-BSaS2535.mjs","names":[],"sources":["../src/deploy/LocalStateStore.ts"],"sourcesContent":["/**\n * Local filesystem state store.\n *\n * `.gkm/deploy-<stage>.json` holds the state, `.gkm/deploy-<stage>.lock` the\n * lock. Every file is mode 0600: state holds database passwords and IAM keys.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport {\n\tlink,\n\tmkdir,\n\topen,\n\treadFile,\n\trename,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcontentVersion,\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\tStateStoreBusy,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nconst FILE_MODE = 0o600;\n\n/** How long a write waits for another process's write to finish. */\nconst MUTEX_TIMEOUT_MS = 5_000;\n/**\n * A write holds the mutex for milliseconds, so one this old was left by a\n * process that died mid-write and is safe to take over.\n */\nconst MUTEX_STALE_MS = 30_000;\n\nfunction isCode(error: unknown, code: string): boolean {\n\treturn (error as NodeJS.ErrnoException)?.code === code;\n}\n\nexport class LocalStateStore extends DocumentStateStore {\n\tconstructor(readonly workspaceRoot: string) {\n\t\tsuper();\n\t}\n\n\tprivate get dir(): string {\n\t\treturn join(this.workspaceRoot, '.gkm');\n\t}\n\n\tstatePath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json`);\n\t}\n\n\tlockPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.lock`);\n\t}\n\n\tbackupPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.v1.json`);\n\t}\n\n\tprivate mutexPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json.mutex`);\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn this.statePath(stage);\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\ttry {\n\t\t\tconst body = await readFile(this.statePath(stage), 'utf-8');\n\t\t\treturn { body, version: contentVersion(body) };\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'ENOENT')) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tawait this.ensureDir();\n\t\treturn this.withMutex(stage, async () => {\n\t\t\tconst current = await this.readRaw(stage);\n\t\t\tconst actual = current?.version ?? null;\n\t\t\tif (actual !== expectedVersion) {\n\t\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t\t}\n\n\t\t\t// Written beside the target and renamed over it: a reader sees the\n\t\t\t// old file or the new one, never half of either.\n\t\t\tconst target = this.statePath(stage);\n\t\t\tconst temp = `${target}.${randomUUID()}.tmp`;\n\t\t\ttry {\n\t\t\t\tconst handle = await open(temp, 'wx', FILE_MODE);\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.writeFile(body);\n\t\t\t\t\tawait handle.sync();\n\t\t\t\t} finally {\n\t\t\t\t\tawait handle.close();\n\t\t\t\t}\n\t\t\t\tawait rename(temp, target);\n\t\t\t} catch (error) {\n\t\t\t\tawait rm(temp, { force: true });\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn contentVersion(body);\n\t\t});\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait writeFile(this.backupPath(stage), body, {\n\t\t\t\tflag: 'wx',\n\t\t\t\tmode: FILE_MODE,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tawait this.ensureDir();\n\t\t// Written whole, then linked into place: `link` fails if the lock\n\t\t// exists, so taking it is atomic — and a racing runner that loses never\n\t\t// reads a lock its winner has created but not yet written to.\n\t\tconst lock = this.lockPath(stage);\n\t\tconst temp = `${lock}.${randomUUID()}.tmp`;\n\t\tawait writeFile(temp, JSON.stringify(holder, null, 2), {\n\t\t\tflag: 'wx',\n\t\t\tmode: FILE_MODE,\n\t\t});\n\t\ttry {\n\t\t\tawait link(temp, lock);\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'EEXIST')) {\n\t\t\t\tthrow new StateLocked(stage, await this.readLock(stage), lock);\n\t\t\t}\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tawait unlink(temp).catch(() => {});\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\ttry {\n\t\t\treturn JSON.parse(\n\t\t\t\tawait readFile(this.lockPath(stage), 'utf-8'),\n\t\t\t) as LockHolder;\n\t\t} catch {\n\t\t\t// Missing, or created by a process that died before writing to it.\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\tawait rm(this.lockPath(stage), { force: true });\n\t}\n\n\tprivate async ensureDir(): Promise<void> {\n\t\tawait mkdir(this.dir, { recursive: true, mode: 0o700 });\n\t}\n\n\t/**\n\t * Serialises the check-then-rename of a conditional write across\n\t * processes. The stage lock is for whole runs; this is for the instant of\n\t * a single write, so even writers that skip the lock never interleave.\n\t */\n\tprivate async withMutex<T>(stage: string, fn: () => Promise<T>): Promise<T> {\n\t\tconst path = this.mutexPath(stage);\n\t\tconst deadline = Date.now() + MUTEX_TIMEOUT_MS;\n\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tconst handle = await open(path, 'wx', FILE_MODE);\n\t\t\t\tawait handle.close();\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t\t\tif (await this.isStale(path)) {\n\t\t\t\t\tawait unlink(path).catch(() => {});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() > deadline) throw new StateStoreBusy(stage, path);\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10));\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tawait rm(path, { force: true });\n\t\t}\n\t}\n\n\tprivate async isStale(path: string): Promise<boolean> {\n\t\ttry {\n\t\t\treturn Date.now() - (await stat(path)).mtimeMs > MUTEX_STALE_MS;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
- const require_seeds = require("./seeds-BKoJYsE7.cjs");
2
+ const require_sniffer = require("./sniffer-C0z3NB2A.cjs");
3
3
  let node_path = require("node:path");
4
4
  let node_fs_promises = require("node:fs/promises");
5
5
  let node_crypto = require("node:crypto");
@@ -22,7 +22,7 @@ const MUTEX_STALE_MS = 3e4;
22
22
  function isCode(error, code) {
23
23
  return error?.code === code;
24
24
  }
25
- var LocalStateStore = class extends require_seeds.DocumentStateStore {
25
+ var LocalStateStore = class extends require_sniffer.DocumentStateStore {
26
26
  workspaceRoot;
27
27
  constructor(workspaceRoot) {
28
28
  super();
@@ -51,7 +51,7 @@ var LocalStateStore = class extends require_seeds.DocumentStateStore {
51
51
  const body = await (0, node_fs_promises.readFile)(this.statePath(stage), "utf-8");
52
52
  return {
53
53
  body,
54
- version: require_seeds.contentVersion(body)
54
+ version: require_sniffer.contentVersion(body)
55
55
  };
56
56
  } catch (error) {
57
57
  if (isCode(error, "ENOENT")) return null;
@@ -62,7 +62,7 @@ var LocalStateStore = class extends require_seeds.DocumentStateStore {
62
62
  await this.ensureDir();
63
63
  return this.withMutex(stage, async () => {
64
64
  const actual = (await this.readRaw(stage))?.version ?? null;
65
- if (actual !== expectedVersion) throw new require_seeds.StateVersionConflict(stage, expectedVersion, actual);
65
+ if (actual !== expectedVersion) throw new require_sniffer.StateVersionConflict(stage, expectedVersion, actual);
66
66
  const target = this.statePath(stage);
67
67
  const temp = `${target}.${(0, node_crypto.randomUUID)()}.tmp`;
68
68
  try {
@@ -78,7 +78,7 @@ var LocalStateStore = class extends require_seeds.DocumentStateStore {
78
78
  await (0, node_fs_promises.rm)(temp, { force: true });
79
79
  throw error;
80
80
  }
81
- return require_seeds.contentVersion(body);
81
+ return require_sniffer.contentVersion(body);
82
82
  });
83
83
  }
84
84
  async writeV1Backup(stage, body) {
@@ -102,7 +102,7 @@ var LocalStateStore = class extends require_seeds.DocumentStateStore {
102
102
  try {
103
103
  await (0, node_fs_promises.link)(temp, lock);
104
104
  } catch (error) {
105
- if (isCode(error, "EEXIST")) throw new require_seeds.StateLocked(stage, await this.readLock(stage), lock);
105
+ if (isCode(error, "EEXIST")) throw new require_sniffer.StateLocked(stage, await this.readLock(stage), lock);
106
106
  throw error;
107
107
  } finally {
108
108
  await (0, node_fs_promises.unlink)(temp).catch(() => {});
@@ -144,7 +144,7 @@ var LocalStateStore = class extends require_seeds.DocumentStateStore {
144
144
  await (0, node_fs_promises.unlink)(path).catch(() => {});
145
145
  continue;
146
146
  }
147
- if (Date.now() > deadline) throw new require_seeds.StateStoreBusy(stage, path);
147
+ if (Date.now() > deadline) throw new require_sniffer.StateStoreBusy(stage, path);
148
148
  await new Promise((resolve) => setTimeout(resolve, 10));
149
149
  }
150
150
  try {
@@ -175,4 +175,4 @@ Object.defineProperty(exports, "LocalStateStore_exports", {
175
175
  }
176
176
  });
177
177
 
178
- //# sourceMappingURL=LocalStateStore-BMfseje2.cjs.map
178
+ //# sourceMappingURL=LocalStateStore-EFBUjuMX.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"LocalStateStore-BMfseje2.cjs","names":["DocumentStateStore","join","readFile","contentVersion","StateVersionConflict","randomUUID","open","rename","rm","writeFile","link","StateLocked","unlink","mkdir","StateStoreBusy","stat"],"sources":["../src/deploy/LocalStateStore.ts"],"sourcesContent":["/**\n * Local filesystem state store.\n *\n * `.gkm/deploy-<stage>.json` holds the state, `.gkm/deploy-<stage>.lock` the\n * lock. Every file is mode 0600: state holds database passwords and IAM keys.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport {\n\tlink,\n\tmkdir,\n\topen,\n\treadFile,\n\trename,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcontentVersion,\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\tStateStoreBusy,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nconst FILE_MODE = 0o600;\n\n/** How long a write waits for another process's write to finish. */\nconst MUTEX_TIMEOUT_MS = 5_000;\n/**\n * A write holds the mutex for milliseconds, so one this old was left by a\n * process that died mid-write and is safe to take over.\n */\nconst MUTEX_STALE_MS = 30_000;\n\nfunction isCode(error: unknown, code: string): boolean {\n\treturn (error as NodeJS.ErrnoException)?.code === code;\n}\n\nexport class LocalStateStore extends DocumentStateStore {\n\tconstructor(readonly workspaceRoot: string) {\n\t\tsuper();\n\t}\n\n\tprivate get dir(): string {\n\t\treturn join(this.workspaceRoot, '.gkm');\n\t}\n\n\tstatePath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json`);\n\t}\n\n\tlockPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.lock`);\n\t}\n\n\tbackupPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.v1.json`);\n\t}\n\n\tprivate mutexPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json.mutex`);\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn this.statePath(stage);\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\ttry {\n\t\t\tconst body = await readFile(this.statePath(stage), 'utf-8');\n\t\t\treturn { body, version: contentVersion(body) };\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'ENOENT')) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tawait this.ensureDir();\n\t\treturn this.withMutex(stage, async () => {\n\t\t\tconst current = await this.readRaw(stage);\n\t\t\tconst actual = current?.version ?? null;\n\t\t\tif (actual !== expectedVersion) {\n\t\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t\t}\n\n\t\t\t// Written beside the target and renamed over it: a reader sees the\n\t\t\t// old file or the new one, never half of either.\n\t\t\tconst target = this.statePath(stage);\n\t\t\tconst temp = `${target}.${randomUUID()}.tmp`;\n\t\t\ttry {\n\t\t\t\tconst handle = await open(temp, 'wx', FILE_MODE);\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.writeFile(body);\n\t\t\t\t\tawait handle.sync();\n\t\t\t\t} finally {\n\t\t\t\t\tawait handle.close();\n\t\t\t\t}\n\t\t\t\tawait rename(temp, target);\n\t\t\t} catch (error) {\n\t\t\t\tawait rm(temp, { force: true });\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn contentVersion(body);\n\t\t});\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait writeFile(this.backupPath(stage), body, {\n\t\t\t\tflag: 'wx',\n\t\t\t\tmode: FILE_MODE,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tawait this.ensureDir();\n\t\t// Written whole, then linked into place: `link` fails if the lock\n\t\t// exists, so taking it is atomic — and a racing runner that loses never\n\t\t// reads a lock its winner has created but not yet written to.\n\t\tconst lock = this.lockPath(stage);\n\t\tconst temp = `${lock}.${randomUUID()}.tmp`;\n\t\tawait writeFile(temp, JSON.stringify(holder, null, 2), {\n\t\t\tflag: 'wx',\n\t\t\tmode: FILE_MODE,\n\t\t});\n\t\ttry {\n\t\t\tawait link(temp, lock);\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'EEXIST')) {\n\t\t\t\tthrow new StateLocked(stage, await this.readLock(stage), lock);\n\t\t\t}\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tawait unlink(temp).catch(() => {});\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\ttry {\n\t\t\treturn JSON.parse(\n\t\t\t\tawait readFile(this.lockPath(stage), 'utf-8'),\n\t\t\t) as LockHolder;\n\t\t} catch {\n\t\t\t// Missing, or created by a process that died before writing to it.\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\tawait rm(this.lockPath(stage), { force: true });\n\t}\n\n\tprivate async ensureDir(): Promise<void> {\n\t\tawait mkdir(this.dir, { recursive: true, mode: 0o700 });\n\t}\n\n\t/**\n\t * Serialises the check-then-rename of a conditional write across\n\t * processes. The stage lock is for whole runs; this is for the instant of\n\t * a single write, so even writers that skip the lock never interleave.\n\t */\n\tprivate async withMutex<T>(stage: string, fn: () => Promise<T>): Promise<T> {\n\t\tconst path = this.mutexPath(stage);\n\t\tconst deadline = Date.now() + MUTEX_TIMEOUT_MS;\n\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tconst handle = await open(path, 'wx', FILE_MODE);\n\t\t\t\tawait handle.close();\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t\t\tif (await this.isStale(path)) {\n\t\t\t\t\tawait unlink(path).catch(() => {});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() > deadline) throw new StateStoreBusy(stage, path);\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10));\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tawait rm(path, { force: true });\n\t\t}\n\t}\n\n\tprivate async isStale(path: string): Promise<boolean> {\n\t\ttry {\n\t\t\treturn Date.now() - (await stat(path)).mtimeMs > MUTEX_STALE_MS;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"LocalStateStore-EFBUjuMX.cjs","names":["DocumentStateStore","join","readFile","contentVersion","StateVersionConflict","randomUUID","open","rename","rm","writeFile","link","StateLocked","unlink","mkdir","StateStoreBusy","stat"],"sources":["../src/deploy/LocalStateStore.ts"],"sourcesContent":["/**\n * Local filesystem state store.\n *\n * `.gkm/deploy-<stage>.json` holds the state, `.gkm/deploy-<stage>.lock` the\n * lock. Every file is mode 0600: state holds database passwords and IAM keys.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport {\n\tlink,\n\tmkdir,\n\topen,\n\treadFile,\n\trename,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcontentVersion,\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\tStateStoreBusy,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nconst FILE_MODE = 0o600;\n\n/** How long a write waits for another process's write to finish. */\nconst MUTEX_TIMEOUT_MS = 5_000;\n/**\n * A write holds the mutex for milliseconds, so one this old was left by a\n * process that died mid-write and is safe to take over.\n */\nconst MUTEX_STALE_MS = 30_000;\n\nfunction isCode(error: unknown, code: string): boolean {\n\treturn (error as NodeJS.ErrnoException)?.code === code;\n}\n\nexport class LocalStateStore extends DocumentStateStore {\n\tconstructor(readonly workspaceRoot: string) {\n\t\tsuper();\n\t}\n\n\tprivate get dir(): string {\n\t\treturn join(this.workspaceRoot, '.gkm');\n\t}\n\n\tstatePath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json`);\n\t}\n\n\tlockPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.lock`);\n\t}\n\n\tbackupPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.v1.json`);\n\t}\n\n\tprivate mutexPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json.mutex`);\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn this.statePath(stage);\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\ttry {\n\t\t\tconst body = await readFile(this.statePath(stage), 'utf-8');\n\t\t\treturn { body, version: contentVersion(body) };\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'ENOENT')) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tawait this.ensureDir();\n\t\treturn this.withMutex(stage, async () => {\n\t\t\tconst current = await this.readRaw(stage);\n\t\t\tconst actual = current?.version ?? null;\n\t\t\tif (actual !== expectedVersion) {\n\t\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t\t}\n\n\t\t\t// Written beside the target and renamed over it: a reader sees the\n\t\t\t// old file or the new one, never half of either.\n\t\t\tconst target = this.statePath(stage);\n\t\t\tconst temp = `${target}.${randomUUID()}.tmp`;\n\t\t\ttry {\n\t\t\t\tconst handle = await open(temp, 'wx', FILE_MODE);\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.writeFile(body);\n\t\t\t\t\tawait handle.sync();\n\t\t\t\t} finally {\n\t\t\t\t\tawait handle.close();\n\t\t\t\t}\n\t\t\t\tawait rename(temp, target);\n\t\t\t} catch (error) {\n\t\t\t\tawait rm(temp, { force: true });\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn contentVersion(body);\n\t\t});\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait writeFile(this.backupPath(stage), body, {\n\t\t\t\tflag: 'wx',\n\t\t\t\tmode: FILE_MODE,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tawait this.ensureDir();\n\t\t// Written whole, then linked into place: `link` fails if the lock\n\t\t// exists, so taking it is atomic — and a racing runner that loses never\n\t\t// reads a lock its winner has created but not yet written to.\n\t\tconst lock = this.lockPath(stage);\n\t\tconst temp = `${lock}.${randomUUID()}.tmp`;\n\t\tawait writeFile(temp, JSON.stringify(holder, null, 2), {\n\t\t\tflag: 'wx',\n\t\t\tmode: FILE_MODE,\n\t\t});\n\t\ttry {\n\t\t\tawait link(temp, lock);\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'EEXIST')) {\n\t\t\t\tthrow new StateLocked(stage, await this.readLock(stage), lock);\n\t\t\t}\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tawait unlink(temp).catch(() => {});\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\ttry {\n\t\t\treturn JSON.parse(\n\t\t\t\tawait readFile(this.lockPath(stage), 'utf-8'),\n\t\t\t) as LockHolder;\n\t\t} catch {\n\t\t\t// Missing, or created by a process that died before writing to it.\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\tawait rm(this.lockPath(stage), { force: true });\n\t}\n\n\tprivate async ensureDir(): Promise<void> {\n\t\tawait mkdir(this.dir, { recursive: true, mode: 0o700 });\n\t}\n\n\t/**\n\t * Serialises the check-then-rename of a conditional write across\n\t * processes. The stage lock is for whole runs; this is for the instant of\n\t * a single write, so even writers that skip the lock never interleave.\n\t */\n\tprivate async withMutex<T>(stage: string, fn: () => Promise<T>): Promise<T> {\n\t\tconst path = this.mutexPath(stage);\n\t\tconst deadline = Date.now() + MUTEX_TIMEOUT_MS;\n\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tconst handle = await open(path, 'wx', FILE_MODE);\n\t\t\t\tawait handle.close();\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t\t\tif (await this.isStale(path)) {\n\t\t\t\t\tawait unlink(path).catch(() => {});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() > deadline) throw new StateStoreBusy(stage, path);\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10));\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tawait rm(path, { force: true });\n\t\t}\n\t}\n\n\tprivate async isStale(path: string): Promise<boolean> {\n\t\ttry {\n\t\t\treturn Date.now() - (await stat(path)).mtimeMs > MUTEX_STALE_MS;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AA+BA,MAAM,YAAY;;AAGlB,MAAM,mBAAmB;;;;;AAKzB,MAAM,iBAAiB;AAEvB,SAAS,OAAO,OAAgB,MAAuB;CACtD,OAAQ,OAAiC,SAAS;AACnD;AAEA,IAAa,kBAAb,cAAqCA,gBAAAA,mBAAmB;CAClC;CAArB,YAAY,eAAgC;EAC3C,MAAM;EADc,KAAA,gBAAA;CAErB;CAEA,IAAY,MAAc;EACzB,QAAA,GAAOC,UAAAA,KAAAA,CAAK,KAAK,eAAe,MAAM;CACvC;CAEA,UAAU,OAAuB;EAChC,QAAA,GAAOA,UAAAA,KAAAA,CAAK,KAAK,KAAK,UAAU,MAAM,MAAM;CAC7C;CAEA,SAAS,OAAuB;EAC/B,QAAA,GAAOA,UAAAA,KAAAA,CAAK,KAAK,KAAK,UAAU,MAAM,MAAM;CAC7C;CAEA,WAAW,OAAuB;EACjC,QAAA,GAAOA,UAAAA,KAAAA,CAAK,KAAK,KAAK,UAAU,MAAM,SAAS;CAChD;CAEA,UAAkB,OAAuB;EACxC,QAAA,GAAOA,UAAAA,KAAAA,CAAK,KAAK,KAAK,UAAU,MAAM,YAAY;CACnD;CAEA,SAAmB,OAAuB;EACzC,OAAO,KAAK,UAAU,KAAK;CAC5B;CAEA,MAAgB,QAAQ,OAAyC;EAChE,IAAI;GACH,MAAM,OAAO,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,KAAK,UAAU,KAAK,GAAG,OAAO;GAC1D,OAAO;IAAE;IAAM,SAASC,gBAAAA,eAAe,IAAI;GAAE;EAC9C,SAAS,OAAO;GACf,IAAI,OAAO,OAAO,QAAQ,GAAG,OAAO;GACpC,MAAM;EACP;CACD;CAEA,MAAgB,SACf,OACA,MACA,iBACwB;EACxB,MAAM,KAAK,UAAU;EACrB,OAAO,KAAK,UAAU,OAAO,YAAY;GAExC,MAAM,UAAS,MADO,KAAK,QAAQ,KAAK,EAAA,EAChB,WAAW;GACnC,IAAI,WAAW,iBACd,MAAM,IAAIC,gBAAAA,qBAAqB,OAAO,iBAAiB,MAAM;GAK9D,MAAM,SAAS,KAAK,UAAU,KAAK;GACnC,MAAM,OAAO,GAAG,OAAO,IAAA,GAAGC,YAAAA,WAAAA,CAAW,EAAE;GACvC,IAAI;IACH,MAAM,SAAS,OAAA,GAAMC,iBAAAA,KAAAA,CAAK,MAAM,MAAM,SAAS;IAC/C,IAAI;KACH,MAAM,OAAO,UAAU,IAAI;KAC3B,MAAM,OAAO,KAAK;IACnB,UAAU;KACT,MAAM,OAAO,MAAM;IACpB;IACA,OAAA,GAAMC,iBAAAA,OAAAA,CAAO,MAAM,MAAM;GAC1B,SAAS,OAAO;IACf,OAAA,GAAMC,iBAAAA,GAAAA,CAAG,MAAM,EAAE,OAAO,KAAK,CAAC;IAC9B,MAAM;GACP;GACA,OAAOL,gBAAAA,eAAe,IAAI;EAC3B,CAAC;CACF;CAEA,MAAgB,cAAc,OAAe,MAA6B;EACzE,IAAI;GACH,OAAA,GAAMM,iBAAAA,UAAAA,CAAU,KAAK,WAAW,KAAK,GAAG,MAAM;IAC7C,MAAM;IACN,MAAM;GACP,CAAC;EACF,SAAS,OAAO;GAEf,IAAI,CAAC,OAAO,OAAO,QAAQ,GAAG,MAAM;EACrC;CACD;CAEA,MAAgB,WAAW,OAAe,QAAmC;EAC5E,MAAM,KAAK,UAAU;EAIrB,MAAM,OAAO,KAAK,SAAS,KAAK;EAChC,MAAM,OAAO,GAAG,KAAK,IAAA,GAAGJ,YAAAA,WAAAA,CAAW,EAAE;EACrC,OAAA,GAAMI,iBAAAA,UAAAA,CAAU,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG;GACtD,MAAM;GACN,MAAM;EACP,CAAC;EACD,IAAI;GACH,OAAA,GAAMC,iBAAAA,KAAAA,CAAK,MAAM,IAAI;EACtB,SAAS,OAAO;GACf,IAAI,OAAO,OAAO,QAAQ,GACzB,MAAM,IAAIC,gBAAAA,YAAY,OAAO,MAAM,KAAK,SAAS,KAAK,GAAG,IAAI;GAE9D,MAAM;EACP,UAAU;GACT,OAAA,GAAMC,iBAAAA,OAAAA,CAAO,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC;EAClC;CACD;CAEA,MAAgB,SAAS,OAA2C;EACnE,IAAI;GACH,OAAO,KAAK,MACX,OAAA,GAAMV,iBAAAA,SAAAA,CAAS,KAAK,SAAS,KAAK,GAAG,OAAO,CAC7C;EACD,QAAQ;GAEP,OAAO;EACR;CACD;CAEA,MAAgB,WACf,OACA,QACgB;EAChB,IAAI,QAEC;QAAA,MADkB,KAAK,SAAS,KAAK,EAAA,EAC5B,OAAO,OAAO,IAAI;EAAA;EAEhC,OAAA,GAAMM,iBAAAA,GAAAA,CAAG,KAAK,SAAS,KAAK,GAAG,EAAE,OAAO,KAAK,CAAC;CAC/C;CAEA,MAAc,YAA2B;EACxC,OAAA,GAAMK,iBAAAA,MAAAA,CAAM,KAAK,KAAK;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC;CACvD;;;;;;CAOA,MAAc,UAAa,OAAe,IAAkC;EAC3E,MAAM,OAAO,KAAK,UAAU,KAAK;EACjC,MAAM,WAAW,KAAK,IAAI,IAAI;EAE9B,SACC,IAAI;GAEH,OAAM,OAAA,GADeP,iBAAAA,KAAAA,CAAK,MAAM,MAAM,SAAS,EAAA,CAClC,MAAM;GACnB;EACD,SAAS,OAAO;GACf,IAAI,CAAC,OAAO,OAAO,QAAQ,GAAG,MAAM;GACpC,IAAI,MAAM,KAAK,QAAQ,IAAI,GAAG;IAC7B,OAAA,GAAMM,iBAAAA,OAAAA,CAAO,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC;IACjC;GACD;GACA,IAAI,KAAK,IAAI,IAAI,UAAU,MAAM,IAAIE,gBAAAA,eAAe,OAAO,IAAI;GAC/D,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;EACvD;EAGD,IAAI;GACH,OAAO,MAAM,GAAG;EACjB,UAAU;GACT,OAAA,GAAMN,iBAAAA,GAAAA,CAAG,MAAM,EAAE,OAAO,KAAK,CAAC;EAC/B;CACD;CAEA,MAAc,QAAQ,MAAgC;EACrD,IAAI;GACH,OAAO,KAAK,IAAI,KAAK,OAAA,GAAMO,iBAAAA,KAAAA,CAAK,IAAI,EAAA,CAAG,UAAU;EAClD,QAAQ;GACP,OAAO;EACR;CACD;AACD"}
@@ -1,4 +1,4 @@
1
- const require_seeds = require("./seeds-BKoJYsE7.cjs");
1
+ const require_sniffer = require("./sniffer-C0z3NB2A.cjs");
2
2
  let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
3
3
  //#region src/deploy/S3StateStore.ts
4
4
  /**
@@ -23,7 +23,7 @@ function notFound(error) {
23
23
  const { name, $metadata } = error ?? {};
24
24
  return name === "NoSuchKey" || name === "NotFound" || $metadata?.httpStatusCode === 404;
25
25
  }
26
- var S3StateStore = class S3StateStore extends require_seeds.DocumentStateStore {
26
+ var S3StateStore = class S3StateStore extends require_sniffer.DocumentStateStore {
27
27
  workspaceName;
28
28
  bucket;
29
29
  client;
@@ -77,7 +77,7 @@ var S3StateStore = class S3StateStore extends require_seeds.DocumentStateStore {
77
77
  } catch (error) {
78
78
  if (lostRace(error)) {
79
79
  const current = await this.get(Key);
80
- throw new require_seeds.StateVersionConflict(stage, expectedVersion, current?.etag ?? null);
80
+ throw new require_sniffer.StateVersionConflict(stage, expectedVersion, current?.etag ?? null);
81
81
  }
82
82
  throw error;
83
83
  }
@@ -106,7 +106,7 @@ var S3StateStore = class S3StateStore extends require_seeds.DocumentStateStore {
106
106
  IfNoneMatch: "*"
107
107
  }));
108
108
  } catch (error) {
109
- if (lostRace(error)) throw new require_seeds.StateLocked(stage, await this.readLock(stage), `s3://${this.bucket}/${Key}`);
109
+ if (lostRace(error)) throw new require_sniffer.StateLocked(stage, await this.readLock(stage), `s3://${this.bucket}/${Key}`);
110
110
  throw error;
111
111
  }
112
112
  }
@@ -157,4 +157,4 @@ var S3StateStore = class S3StateStore extends require_seeds.DocumentStateStore {
157
157
  //#endregion
158
158
  exports.S3StateStore = S3StateStore;
159
159
 
160
- //# sourceMappingURL=S3StateStore-BmMt2HhW.cjs.map
160
+ //# sourceMappingURL=S3StateStore-C5mv8To3.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"S3StateStore-BmMt2HhW.cjs","names":["DocumentStateStore","S3Client","PutObjectCommand","StateVersionConflict","StateLocked","DeleteObjectCommand","GetObjectCommand"],"sources":["../src/deploy/S3StateStore.ts"],"sourcesContent":["/**\n * Amazon S3 state store.\n *\n * - `<prefix>/<workspace>/<stage>/state.json`: the state.\n * - `<prefix>/<workspace>/<stage>/lock.json`: the lock.\n * - `<prefix>/<workspace>/<stage>/state.v1.json`: the v1 state, kept on\n * migration.\n *\n * Every write is conditional on the server: `If-None-Match: *` to create,\n * `If-Match: <etag>` to replace. S3 answers a lost race with 412 (or 409 when\n * two conditional writes are in flight at once), so there is no window\n * between a check and a write for another run to slip into.\n */\n\nimport {\n\tDeleteObjectCommand,\n\tGetObjectCommand,\n\tPutObjectCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface S3StateStoreOptions {\n\tworkspaceName: string;\n\tbucket: string;\n\t/** Key prefix inside the bucket; `gkm` by default. */\n\tprefix?: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: S3ClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\ninterface S3Failure {\n\tname?: string;\n\t$metadata?: { httpStatusCode?: number };\n}\n\n/** A conditional write that lost: the object is not what the write assumed. */\nfunction lostRace(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'PreconditionFailed' ||\n\t\tname === 'ConditionalRequestConflict' ||\n\t\t$metadata?.httpStatusCode === 412 ||\n\t\t$metadata?.httpStatusCode === 409\n\t);\n}\n\nfunction notFound(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'NoSuchKey' ||\n\t\tname === 'NotFound' ||\n\t\t$metadata?.httpStatusCode === 404\n\t);\n}\n\nexport class S3StateStore extends DocumentStateStore {\n\tprivate readonly prefix: string;\n\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\treadonly bucket: string,\n\t\tprivate readonly client: S3Client,\n\t\tprefix = 'gkm',\n\t) {\n\t\tsuper();\n\t\tthis.prefix = prefix.replace(/\\/+$/, '');\n\t}\n\n\tstatic create(options: S3StateStoreOptions): S3StateStore {\n\t\tconst config: S3ClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t\t// The emulator serves buckets by path, not by subdomain.\n\t\t\tforcePathStyle: options.endpoint ? true : undefined,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new S3StateStore(\n\t\t\toptions.workspaceName,\n\t\t\toptions.bucket,\n\t\t\tnew S3Client(config),\n\t\t\toptions.prefix,\n\t\t);\n\t}\n\n\tprivate key(stage: string, leaf: string): string {\n\t\tconst path = `${this.workspaceName}/${stage}/${leaf}`;\n\t\treturn this.prefix ? `${this.prefix}/${path}` : path;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `s3://${this.bucket}/${this.key(stage, 'state.json')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst object = await this.get(this.key(stage, 'state.json'));\n\t\tif (!object) return null;\n\t\treturn { body: object.body, version: object.etag };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Key = this.key(stage, 'state.json');\n\t\ttry {\n\t\t\tconst { ETag } = await this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\t...(expectedVersion === null\n\t\t\t\t\t\t? { IfNoneMatch: '*' }\n\t\t\t\t\t\t: { IfMatch: expectedVersion }),\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (ETag) return ETag;\n\t\t\t// Some S3-compatible servers omit the ETag on a conditional put.\n\t\t\treturn (await this.get(Key))?.etag ?? '';\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tconst current = await this.get(Key);\n\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\tstage,\n\t\t\t\t\texpectedVersion,\n\t\t\t\t\tcurrent?.etag ?? null,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey: this.key(stage, 'state.v1.json'),\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!lostRace(error)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: JSON.stringify(holder),\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\t`s3://${this.bucket}/${Key}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst object = await this.get(this.key(stage, 'lock.json'));\n\t\tif (!object) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(object.body) as LockHolder;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\tconst object = await this.get(Key);\n\t\tif (!object) return;\n\t\tif (holder) {\n\t\t\ttry {\n\t\t\t\tif ((JSON.parse(object.body) as LockHolder).id !== holder.id) return;\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\t// Conditional on the lock read above, so a lock taken by another\n\t\t\t// run in the meantime survives.\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tIfMatch: object.etag,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!lostRace(error) && !notFound(error)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tKey: string,\n\t): Promise<{ body: string; etag: string } | null> {\n\t\ttry {\n\t\t\tconst response = await this.client.send(\n\t\t\t\tnew GetObjectCommand({ Bucket: this.bucket, Key }),\n\t\t\t);\n\t\t\tconst body = (await response.Body?.transformToString('utf-8')) ?? '';\n\t\t\treturn { body, etag: response.ETag ?? '' };\n\t\t} catch (error) {\n\t\t\tif (notFound(error)) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"S3StateStore-C5mv8To3.cjs","names":["DocumentStateStore","S3Client","PutObjectCommand","StateVersionConflict","StateLocked","DeleteObjectCommand","GetObjectCommand"],"sources":["../src/deploy/S3StateStore.ts"],"sourcesContent":["/**\n * Amazon S3 state store.\n *\n * - `<prefix>/<workspace>/<stage>/state.json`: the state.\n * - `<prefix>/<workspace>/<stage>/lock.json`: the lock.\n * - `<prefix>/<workspace>/<stage>/state.v1.json`: the v1 state, kept on\n * migration.\n *\n * Every write is conditional on the server: `If-None-Match: *` to create,\n * `If-Match: <etag>` to replace. S3 answers a lost race with 412 (or 409 when\n * two conditional writes are in flight at once), so there is no window\n * between a check and a write for another run to slip into.\n */\n\nimport {\n\tDeleteObjectCommand,\n\tGetObjectCommand,\n\tPutObjectCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface S3StateStoreOptions {\n\tworkspaceName: string;\n\tbucket: string;\n\t/** Key prefix inside the bucket; `gkm` by default. */\n\tprefix?: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: S3ClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\ninterface S3Failure {\n\tname?: string;\n\t$metadata?: { httpStatusCode?: number };\n}\n\n/** A conditional write that lost: the object is not what the write assumed. */\nfunction lostRace(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'PreconditionFailed' ||\n\t\tname === 'ConditionalRequestConflict' ||\n\t\t$metadata?.httpStatusCode === 412 ||\n\t\t$metadata?.httpStatusCode === 409\n\t);\n}\n\nfunction notFound(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'NoSuchKey' ||\n\t\tname === 'NotFound' ||\n\t\t$metadata?.httpStatusCode === 404\n\t);\n}\n\nexport class S3StateStore extends DocumentStateStore {\n\tprivate readonly prefix: string;\n\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\treadonly bucket: string,\n\t\tprivate readonly client: S3Client,\n\t\tprefix = 'gkm',\n\t) {\n\t\tsuper();\n\t\tthis.prefix = prefix.replace(/\\/+$/, '');\n\t}\n\n\tstatic create(options: S3StateStoreOptions): S3StateStore {\n\t\tconst config: S3ClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t\t// The emulator serves buckets by path, not by subdomain.\n\t\t\tforcePathStyle: options.endpoint ? true : undefined,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new S3StateStore(\n\t\t\toptions.workspaceName,\n\t\t\toptions.bucket,\n\t\t\tnew S3Client(config),\n\t\t\toptions.prefix,\n\t\t);\n\t}\n\n\tprivate key(stage: string, leaf: string): string {\n\t\tconst path = `${this.workspaceName}/${stage}/${leaf}`;\n\t\treturn this.prefix ? `${this.prefix}/${path}` : path;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `s3://${this.bucket}/${this.key(stage, 'state.json')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst object = await this.get(this.key(stage, 'state.json'));\n\t\tif (!object) return null;\n\t\treturn { body: object.body, version: object.etag };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Key = this.key(stage, 'state.json');\n\t\ttry {\n\t\t\tconst { ETag } = await this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\t...(expectedVersion === null\n\t\t\t\t\t\t? { IfNoneMatch: '*' }\n\t\t\t\t\t\t: { IfMatch: expectedVersion }),\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (ETag) return ETag;\n\t\t\t// Some S3-compatible servers omit the ETag on a conditional put.\n\t\t\treturn (await this.get(Key))?.etag ?? '';\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tconst current = await this.get(Key);\n\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\tstage,\n\t\t\t\t\texpectedVersion,\n\t\t\t\t\tcurrent?.etag ?? null,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey: this.key(stage, 'state.v1.json'),\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!lostRace(error)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: JSON.stringify(holder),\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\t`s3://${this.bucket}/${Key}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst object = await this.get(this.key(stage, 'lock.json'));\n\t\tif (!object) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(object.body) as LockHolder;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\tconst object = await this.get(Key);\n\t\tif (!object) return;\n\t\tif (holder) {\n\t\t\ttry {\n\t\t\t\tif ((JSON.parse(object.body) as LockHolder).id !== holder.id) return;\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\t// Conditional on the lock read above, so a lock taken by another\n\t\t\t// run in the meantime survives.\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tIfMatch: object.etag,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!lostRace(error) && !notFound(error)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tKey: string,\n\t): Promise<{ body: string; etag: string } | null> {\n\t\ttry {\n\t\t\tconst response = await this.client.send(\n\t\t\t\tnew GetObjectCommand({ Bucket: this.bucket, Key }),\n\t\t\t);\n\t\t\tconst body = (await response.Body?.transformToString('utf-8')) ?? '';\n\t\t\treturn { body, etag: response.ETag ?? '' };\n\t\t} catch (error) {\n\t\t\tif (notFound(error)) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAkDA,SAAS,SAAS,OAAyB;CAC1C,MAAM,EAAE,MAAM,cAAe,SAAS,CAAC;CACvC,OACC,SAAS,wBACT,SAAS,gCACT,WAAW,mBAAmB,OAC9B,WAAW,mBAAmB;AAEhC;AAEA,SAAS,SAAS,OAAyB;CAC1C,MAAM,EAAE,MAAM,cAAe,SAAS,CAAC;CACvC,OACC,SAAS,eACT,SAAS,cACT,WAAW,mBAAmB;AAEhC;AAEA,IAAa,eAAb,MAAa,qBAAqBA,gBAAAA,mBAAmB;CAI1C;CACA;CACQ;CALlB;CAEA,YACC,eACA,QACA,QACA,SAAS,OACR;EACD,MAAM;EALG,KAAA,gBAAA;EACA,KAAA,SAAA;EACQ,KAAA,SAAA;EAIjB,KAAK,SAAS,OAAO,QAAQ,QAAQ,EAAE;CACxC;CAEA,OAAO,OAAO,SAA4C;EACzD,MAAM,SAAyB;GAC9B,QAAQ,QAAQ;GAChB,UAAU,QAAQ;GAElB,gBAAgB,QAAQ,WAAW,OAAO,KAAA;EAC3C;EACA,IAAI,QAAQ,SAAS;GAEpB,MAAM,EAAE,YAAY,QAAQ,+BAA+B;GAC3D,OAAO,cAAc,QAAQ,EAAE,SAAS,QAAQ,QAAQ,CAAC;EAC1D,OAAO,IAAI,QAAQ,aAClB,OAAO,cAAc,QAAQ;EAE9B,OAAO,IAAI,aACV,QAAQ,eACR,QAAQ,QACR,IAAIC,mBAAAA,SAAS,MAAM,GACnB,QAAQ,MACT;CACD;CAEA,IAAY,OAAe,MAAsB;EAChD,MAAM,OAAO,GAAG,KAAK,cAAc,GAAG,MAAM,GAAG;EAC/C,OAAO,KAAK,SAAS,GAAG,KAAK,OAAO,GAAG,SAAS;CACjD;CAEA,SAAmB,OAAuB;EACzC,OAAO,QAAQ,KAAK,OAAO,GAAG,KAAK,IAAI,OAAO,YAAY;CAC3D;CAEA,MAAgB,QAAQ,OAAyC;EAChE,MAAM,SAAS,MAAM,KAAK,IAAI,KAAK,IAAI,OAAO,YAAY,CAAC;EAC3D,IAAI,CAAC,QAAQ,OAAO;EACpB,OAAO;GAAE,MAAM,OAAO;GAAM,SAAS,OAAO;EAAK;CAClD;CAEA,MAAgB,SACf,OACA,MACA,iBACwB;EACxB,MAAM,MAAM,KAAK,IAAI,OAAO,YAAY;EACxC,IAAI;GACH,MAAM,EAAE,SAAS,MAAM,KAAK,OAAO,KAClC,IAAIC,mBAAAA,iBAAiB;IACpB,QAAQ,KAAK;IACb;IACA,MAAM;IACN,aAAa;IACb,GAAI,oBAAoB,OACrB,EAAE,aAAa,IAAI,IACnB,EAAE,SAAS,gBAAgB;GAC/B,CAAC,CACF;GACA,IAAI,MAAM,OAAO;GAEjB,QAAQ,MAAM,KAAK,IAAI,GAAG,EAAA,EAAI,QAAQ;EACvC,SAAS,OAAO;GACf,IAAI,SAAS,KAAK,GAAG;IACpB,MAAM,UAAU,MAAM,KAAK,IAAI,GAAG;IAClC,MAAM,IAAIC,gBAAAA,qBACT,OACA,iBACA,SAAS,QAAQ,IAClB;GACD;GACA,MAAM;EACP;CACD;CAEA,MAAgB,cAAc,OAAe,MAA6B;EACzE,IAAI;GACH,MAAM,KAAK,OAAO,KACjB,IAAID,mBAAAA,iBAAiB;IACpB,QAAQ,KAAK;IACb,KAAK,KAAK,IAAI,OAAO,eAAe;IACpC,MAAM;IACN,aAAa;IACb,aAAa;GACd,CAAC,CACF;EACD,SAAS,OAAO;GAEf,IAAI,CAAC,SAAS,KAAK,GAAG,MAAM;EAC7B;CACD;CAEA,MAAgB,WAAW,OAAe,QAAmC;EAC5E,MAAM,MAAM,KAAK,IAAI,OAAO,WAAW;EACvC,IAAI;GACH,MAAM,KAAK,OAAO,KACjB,IAAIA,mBAAAA,iBAAiB;IACpB,QAAQ,KAAK;IACb;IACA,MAAM,KAAK,UAAU,MAAM;IAC3B,aAAa;IACb,aAAa;GACd,CAAC,CACF;EACD,SAAS,OAAO;GACf,IAAI,SAAS,KAAK,GACjB,MAAM,IAAIE,gBAAAA,YACT,OACA,MAAM,KAAK,SAAS,KAAK,GACzB,QAAQ,KAAK,OAAO,GAAG,KACxB;GAED,MAAM;EACP;CACD;CAEA,MAAgB,SAAS,OAA2C;EACnE,MAAM,SAAS,MAAM,KAAK,IAAI,KAAK,IAAI,OAAO,WAAW,CAAC;EAC1D,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI;GACH,OAAO,KAAK,MAAM,OAAO,IAAI;EAC9B,QAAQ;GACP,OAAO;EACR;CACD;CAEA,MAAgB,WACf,OACA,QACgB;EAChB,MAAM,MAAM,KAAK,IAAI,OAAO,WAAW;EACvC,MAAM,SAAS,MAAM,KAAK,IAAI,GAAG;EACjC,IAAI,CAAC,QAAQ;EACb,IAAI,QACH,IAAI;GACH,IAAK,KAAK,MAAM,OAAO,IAAI,CAAC,CAAgB,OAAO,OAAO,IAAI;EAC/D,QAAQ;GACP;EACD;EAED,IAAI;GAGH,MAAM,KAAK,OAAO,KACjB,IAAIC,mBAAAA,oBAAoB;IACvB,QAAQ,KAAK;IACb;IACA,SAAS,OAAO;GACjB,CAAC,CACF;EACD,SAAS,OAAO;GACf,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,KAAK,GAAG,MAAM;EACjD;CACD;CAEA,MAAc,IACb,KACiD;EACjD,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,OAAO,KAClC,IAAIC,mBAAAA,iBAAiB;IAAE,QAAQ,KAAK;IAAQ;GAAI,CAAC,CAClD;GAEA,OAAO;IAAE,MADK,MAAM,SAAS,MAAM,kBAAkB,OAAO,KAAM;IACnD,MAAM,SAAS,QAAQ;GAAG;EAC1C,SAAS,OAAO;GACf,IAAI,SAAS,KAAK,GAAG,OAAO;GAC5B,MAAM;EACP;CACD;AACD"}
@@ -1,5 +1,5 @@
1
1
  import { n as __require } from "./rolldown-runtime-BhDjJH2R.mjs";
2
- import { I as DocumentStateStore, R as StateLocked, V as StateVersionConflict } from "./seeds-CvGQJnPO.mjs";
2
+ import { f as StateLocked, h as StateVersionConflict, l as DocumentStateStore } from "./sniffer-DCcuzHz_.mjs";
3
3
  import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
4
4
  //#region src/deploy/S3StateStore.ts
5
5
  /**
@@ -158,4 +158,4 @@ var S3StateStore = class S3StateStore extends DocumentStateStore {
158
158
  //#endregion
159
159
  export { S3StateStore };
160
160
 
161
- //# sourceMappingURL=S3StateStore-BfoY4MvD.mjs.map
161
+ //# sourceMappingURL=S3StateStore-CrFq4UZU.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"S3StateStore-BfoY4MvD.mjs","names":[],"sources":["../src/deploy/S3StateStore.ts"],"sourcesContent":["/**\n * Amazon S3 state store.\n *\n * - `<prefix>/<workspace>/<stage>/state.json`: the state.\n * - `<prefix>/<workspace>/<stage>/lock.json`: the lock.\n * - `<prefix>/<workspace>/<stage>/state.v1.json`: the v1 state, kept on\n * migration.\n *\n * Every write is conditional on the server: `If-None-Match: *` to create,\n * `If-Match: <etag>` to replace. S3 answers a lost race with 412 (or 409 when\n * two conditional writes are in flight at once), so there is no window\n * between a check and a write for another run to slip into.\n */\n\nimport {\n\tDeleteObjectCommand,\n\tGetObjectCommand,\n\tPutObjectCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface S3StateStoreOptions {\n\tworkspaceName: string;\n\tbucket: string;\n\t/** Key prefix inside the bucket; `gkm` by default. */\n\tprefix?: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: S3ClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\ninterface S3Failure {\n\tname?: string;\n\t$metadata?: { httpStatusCode?: number };\n}\n\n/** A conditional write that lost: the object is not what the write assumed. */\nfunction lostRace(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'PreconditionFailed' ||\n\t\tname === 'ConditionalRequestConflict' ||\n\t\t$metadata?.httpStatusCode === 412 ||\n\t\t$metadata?.httpStatusCode === 409\n\t);\n}\n\nfunction notFound(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'NoSuchKey' ||\n\t\tname === 'NotFound' ||\n\t\t$metadata?.httpStatusCode === 404\n\t);\n}\n\nexport class S3StateStore extends DocumentStateStore {\n\tprivate readonly prefix: string;\n\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\treadonly bucket: string,\n\t\tprivate readonly client: S3Client,\n\t\tprefix = 'gkm',\n\t) {\n\t\tsuper();\n\t\tthis.prefix = prefix.replace(/\\/+$/, '');\n\t}\n\n\tstatic create(options: S3StateStoreOptions): S3StateStore {\n\t\tconst config: S3ClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t\t// The emulator serves buckets by path, not by subdomain.\n\t\t\tforcePathStyle: options.endpoint ? true : undefined,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new S3StateStore(\n\t\t\toptions.workspaceName,\n\t\t\toptions.bucket,\n\t\t\tnew S3Client(config),\n\t\t\toptions.prefix,\n\t\t);\n\t}\n\n\tprivate key(stage: string, leaf: string): string {\n\t\tconst path = `${this.workspaceName}/${stage}/${leaf}`;\n\t\treturn this.prefix ? `${this.prefix}/${path}` : path;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `s3://${this.bucket}/${this.key(stage, 'state.json')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst object = await this.get(this.key(stage, 'state.json'));\n\t\tif (!object) return null;\n\t\treturn { body: object.body, version: object.etag };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Key = this.key(stage, 'state.json');\n\t\ttry {\n\t\t\tconst { ETag } = await this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\t...(expectedVersion === null\n\t\t\t\t\t\t? { IfNoneMatch: '*' }\n\t\t\t\t\t\t: { IfMatch: expectedVersion }),\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (ETag) return ETag;\n\t\t\t// Some S3-compatible servers omit the ETag on a conditional put.\n\t\t\treturn (await this.get(Key))?.etag ?? '';\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tconst current = await this.get(Key);\n\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\tstage,\n\t\t\t\t\texpectedVersion,\n\t\t\t\t\tcurrent?.etag ?? null,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey: this.key(stage, 'state.v1.json'),\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!lostRace(error)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: JSON.stringify(holder),\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\t`s3://${this.bucket}/${Key}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst object = await this.get(this.key(stage, 'lock.json'));\n\t\tif (!object) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(object.body) as LockHolder;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\tconst object = await this.get(Key);\n\t\tif (!object) return;\n\t\tif (holder) {\n\t\t\ttry {\n\t\t\t\tif ((JSON.parse(object.body) as LockHolder).id !== holder.id) return;\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\t// Conditional on the lock read above, so a lock taken by another\n\t\t\t// run in the meantime survives.\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tIfMatch: object.etag,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!lostRace(error) && !notFound(error)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tKey: string,\n\t): Promise<{ body: string; etag: string } | null> {\n\t\ttry {\n\t\t\tconst response = await this.client.send(\n\t\t\t\tnew GetObjectCommand({ Bucket: this.bucket, Key }),\n\t\t\t);\n\t\t\tconst body = (await response.Body?.transformToString('utf-8')) ?? '';\n\t\t\treturn { body, etag: response.ETag ?? '' };\n\t\t} catch (error) {\n\t\t\tif (notFound(error)) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"S3StateStore-CrFq4UZU.mjs","names":[],"sources":["../src/deploy/S3StateStore.ts"],"sourcesContent":["/**\n * Amazon S3 state store.\n *\n * - `<prefix>/<workspace>/<stage>/state.json`: the state.\n * - `<prefix>/<workspace>/<stage>/lock.json`: the lock.\n * - `<prefix>/<workspace>/<stage>/state.v1.json`: the v1 state, kept on\n * migration.\n *\n * Every write is conditional on the server: `If-None-Match: *` to create,\n * `If-Match: <etag>` to replace. S3 answers a lost race with 412 (or 409 when\n * two conditional writes are in flight at once), so there is no window\n * between a check and a write for another run to slip into.\n */\n\nimport {\n\tDeleteObjectCommand,\n\tGetObjectCommand,\n\tPutObjectCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface S3StateStoreOptions {\n\tworkspaceName: string;\n\tbucket: string;\n\t/** Key prefix inside the bucket; `gkm` by default. */\n\tprefix?: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: S3ClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\ninterface S3Failure {\n\tname?: string;\n\t$metadata?: { httpStatusCode?: number };\n}\n\n/** A conditional write that lost: the object is not what the write assumed. */\nfunction lostRace(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'PreconditionFailed' ||\n\t\tname === 'ConditionalRequestConflict' ||\n\t\t$metadata?.httpStatusCode === 412 ||\n\t\t$metadata?.httpStatusCode === 409\n\t);\n}\n\nfunction notFound(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'NoSuchKey' ||\n\t\tname === 'NotFound' ||\n\t\t$metadata?.httpStatusCode === 404\n\t);\n}\n\nexport class S3StateStore extends DocumentStateStore {\n\tprivate readonly prefix: string;\n\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\treadonly bucket: string,\n\t\tprivate readonly client: S3Client,\n\t\tprefix = 'gkm',\n\t) {\n\t\tsuper();\n\t\tthis.prefix = prefix.replace(/\\/+$/, '');\n\t}\n\n\tstatic create(options: S3StateStoreOptions): S3StateStore {\n\t\tconst config: S3ClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t\t// The emulator serves buckets by path, not by subdomain.\n\t\t\tforcePathStyle: options.endpoint ? true : undefined,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new S3StateStore(\n\t\t\toptions.workspaceName,\n\t\t\toptions.bucket,\n\t\t\tnew S3Client(config),\n\t\t\toptions.prefix,\n\t\t);\n\t}\n\n\tprivate key(stage: string, leaf: string): string {\n\t\tconst path = `${this.workspaceName}/${stage}/${leaf}`;\n\t\treturn this.prefix ? `${this.prefix}/${path}` : path;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `s3://${this.bucket}/${this.key(stage, 'state.json')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst object = await this.get(this.key(stage, 'state.json'));\n\t\tif (!object) return null;\n\t\treturn { body: object.body, version: object.etag };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Key = this.key(stage, 'state.json');\n\t\ttry {\n\t\t\tconst { ETag } = await this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\t...(expectedVersion === null\n\t\t\t\t\t\t? { IfNoneMatch: '*' }\n\t\t\t\t\t\t: { IfMatch: expectedVersion }),\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (ETag) return ETag;\n\t\t\t// Some S3-compatible servers omit the ETag on a conditional put.\n\t\t\treturn (await this.get(Key))?.etag ?? '';\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tconst current = await this.get(Key);\n\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\tstage,\n\t\t\t\t\texpectedVersion,\n\t\t\t\t\tcurrent?.etag ?? null,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey: this.key(stage, 'state.v1.json'),\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!lostRace(error)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: JSON.stringify(holder),\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\t`s3://${this.bucket}/${Key}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst object = await this.get(this.key(stage, 'lock.json'));\n\t\tif (!object) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(object.body) as LockHolder;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\tconst object = await this.get(Key);\n\t\tif (!object) return;\n\t\tif (holder) {\n\t\t\ttry {\n\t\t\t\tif ((JSON.parse(object.body) as LockHolder).id !== holder.id) return;\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\t// Conditional on the lock read above, so a lock taken by another\n\t\t\t// run in the meantime survives.\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tIfMatch: object.etag,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!lostRace(error) && !notFound(error)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tKey: string,\n\t): Promise<{ body: string; etag: string } | null> {\n\t\ttry {\n\t\t\tconst response = await this.client.send(\n\t\t\t\tnew GetObjectCommand({ Bucket: this.bucket, Key }),\n\t\t\t);\n\t\t\tconst body = (await response.Body?.transformToString('utf-8')) ?? '';\n\t\t\treturn { body, etag: response.ETag ?? '' };\n\t\t} catch (error) {\n\t\t\tif (notFound(error)) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n"],"mappings":"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"}