@vendure/cloud 0.3.1 → 0.4.0

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 (259) hide show
  1. package/README.md +6 -1
  2. package/dist/cli/commands/deployment/build-logs.js +2 -9
  3. package/dist/cli/commands/deployment/build-logs.js.map +1 -1
  4. package/dist/cli/commands/deployment/deploy.d.ts.map +1 -1
  5. package/dist/cli/commands/deployment/deploy.js +34 -64
  6. package/dist/cli/commands/deployment/deploy.js.map +1 -1
  7. package/dist/cli/commands/deployment/index.d.ts.map +1 -1
  8. package/dist/cli/commands/deployment/index.js +52 -27
  9. package/dist/cli/commands/deployment/index.js.map +1 -1
  10. package/dist/cli/commands/deployment/log-export.d.ts +13 -0
  11. package/dist/cli/commands/deployment/log-export.d.ts.map +1 -0
  12. package/dist/cli/commands/deployment/log-export.js +133 -0
  13. package/dist/cli/commands/deployment/log-export.js.map +1 -0
  14. package/dist/cli/commands/deployment/log-query-output.d.ts +13 -0
  15. package/dist/cli/commands/deployment/log-query-output.d.ts.map +1 -0
  16. package/dist/cli/commands/deployment/log-query-output.js +106 -0
  17. package/dist/cli/commands/deployment/log-query-output.js.map +1 -0
  18. package/dist/cli/commands/deployment/logs.d.ts +1 -0
  19. package/dist/cli/commands/deployment/logs.d.ts.map +1 -1
  20. package/dist/cli/commands/deployment/logs.js +347 -69
  21. package/dist/cli/commands/deployment/logs.js.map +1 -1
  22. package/dist/cli/commands/deployment/options.d.ts +2 -6
  23. package/dist/cli/commands/deployment/options.d.ts.map +1 -1
  24. package/dist/cli/commands/deployment/options.js +21 -9
  25. package/dist/cli/commands/deployment/options.js.map +1 -1
  26. package/dist/cli/commands/deployment/plan.d.ts +1 -1
  27. package/dist/cli/commands/deployment/plan.d.ts.map +1 -1
  28. package/dist/cli/commands/deployment/plan.js +4 -6
  29. package/dist/cli/commands/deployment/plan.js.map +1 -1
  30. package/dist/cli/commands/deployment/recovery.d.ts +9 -0
  31. package/dist/cli/commands/deployment/recovery.d.ts.map +1 -0
  32. package/dist/cli/commands/deployment/recovery.js +86 -0
  33. package/dist/cli/commands/deployment/recovery.js.map +1 -0
  34. package/dist/cli/commands/deployment/rollback.d.ts.map +1 -1
  35. package/dist/cli/commands/deployment/rollback.js +8 -3
  36. package/dist/cli/commands/deployment/rollback.js.map +1 -1
  37. package/dist/cli/commands/deployment/show.d.ts.map +1 -1
  38. package/dist/cli/commands/deployment/show.js +16 -5
  39. package/dist/cli/commands/deployment/show.js.map +1 -1
  40. package/dist/cli/commands/deployment/source.d.ts +1 -12
  41. package/dist/cli/commands/deployment/source.d.ts.map +1 -1
  42. package/dist/cli/commands/deployment/source.js +5 -13
  43. package/dist/cli/commands/deployment/source.js.map +1 -1
  44. package/dist/cli/commands/deployment/status.d.ts.map +1 -1
  45. package/dist/cli/commands/deployment/status.js +22 -20
  46. package/dist/cli/commands/deployment/status.js.map +1 -1
  47. package/dist/cli/commands/deployment/teardown.d.ts.map +1 -1
  48. package/dist/cli/commands/deployment/teardown.js +12 -1
  49. package/dist/cli/commands/deployment/teardown.js.map +1 -1
  50. package/dist/cli/commands/identity-context/keys.d.ts.map +1 -1
  51. package/dist/cli/commands/identity-context/keys.js +6 -5
  52. package/dist/cli/commands/identity-context/keys.js.map +1 -1
  53. package/dist/cli/commands/identity-context/login.d.ts.map +1 -1
  54. package/dist/cli/commands/identity-context/login.js +2 -2
  55. package/dist/cli/commands/identity-context/login.js.map +1 -1
  56. package/dist/cli/commands/identity-context/logout.d.ts.map +1 -1
  57. package/dist/cli/commands/identity-context/logout.js +3 -4
  58. package/dist/cli/commands/identity-context/logout.js.map +1 -1
  59. package/dist/cli/commands/identity-context/org-switch.d.ts.map +1 -1
  60. package/dist/cli/commands/identity-context/org-switch.js +19 -7
  61. package/dist/cli/commands/identity-context/org-switch.js.map +1 -1
  62. package/dist/cli/commands/identity-context/use.d.ts.map +1 -1
  63. package/dist/cli/commands/identity-context/use.js +2 -3
  64. package/dist/cli/commands/identity-context/use.js.map +1 -1
  65. package/dist/cli/commands/identity-context/whoami.d.ts.map +1 -1
  66. package/dist/cli/commands/identity-context/whoami.js +33 -12
  67. package/dist/cli/commands/identity-context/whoami.js.map +1 -1
  68. package/dist/cli/commands/index.d.ts.map +1 -1
  69. package/dist/cli/commands/index.js +0 -1
  70. package/dist/cli/commands/index.js.map +1 -1
  71. package/dist/cli/commands/project-config/config/desired-state.d.ts +1 -1
  72. package/dist/cli/commands/project-config/config/desired-state.d.ts.map +1 -1
  73. package/dist/cli/commands/project-config/config/desired-state.js +4 -3
  74. package/dist/cli/commands/project-config/config/desired-state.js.map +1 -1
  75. package/dist/cli/commands/project-config/config/email/domain.d.ts +5 -0
  76. package/dist/cli/commands/project-config/config/email/domain.d.ts.map +1 -0
  77. package/dist/cli/commands/project-config/config/email/domain.js +80 -0
  78. package/dist/cli/commands/project-config/config/email/domain.js.map +1 -0
  79. package/dist/cli/commands/project-config/config/email/events.d.ts +4 -0
  80. package/dist/cli/commands/project-config/config/email/events.d.ts.map +1 -0
  81. package/dist/cli/commands/project-config/config/email/events.js +55 -0
  82. package/dist/cli/commands/project-config/config/email/events.js.map +1 -0
  83. package/dist/cli/commands/project-config/config/email/index.d.ts +2 -0
  84. package/dist/cli/commands/project-config/config/email/index.d.ts.map +1 -0
  85. package/dist/cli/commands/project-config/config/email/index.js +108 -0
  86. package/dist/cli/commands/project-config/config/email/index.js.map +1 -0
  87. package/dist/cli/commands/project-config/config/email/test-sending.d.ts +4 -0
  88. package/dist/cli/commands/project-config/config/email/test-sending.d.ts.map +1 -0
  89. package/dist/cli/commands/project-config/config/email/test-sending.js +68 -0
  90. package/dist/cli/commands/project-config/config/email/test-sending.js.map +1 -0
  91. package/dist/cli/commands/project-config/config/index.d.ts.map +1 -1
  92. package/dist/cli/commands/project-config/config/index.js +12 -7
  93. package/dist/cli/commands/project-config/config/index.js.map +1 -1
  94. package/dist/cli/commands/project-config/config/runtime.d.ts +2 -1
  95. package/dist/cli/commands/project-config/config/runtime.d.ts.map +1 -1
  96. package/dist/cli/commands/project-config/config/runtime.js +43 -29
  97. package/dist/cli/commands/project-config/config/runtime.js.map +1 -1
  98. package/dist/cli/commands/project-config/env/index.d.ts.map +1 -1
  99. package/dist/cli/commands/project-config/env/index.js +8 -2
  100. package/dist/cli/commands/project-config/env/index.js.map +1 -1
  101. package/dist/cli/commands/project-config/environment/index.d.ts.map +1 -1
  102. package/dist/cli/commands/project-config/environment/index.js +15 -1
  103. package/dist/cli/commands/project-config/environment/index.js.map +1 -1
  104. package/dist/cli/commands/project-config/environment/regions.d.ts +6 -0
  105. package/dist/cli/commands/project-config/environment/regions.d.ts.map +1 -0
  106. package/dist/cli/commands/project-config/environment/regions.js +22 -0
  107. package/dist/cli/commands/project-config/environment/regions.js.map +1 -0
  108. package/dist/cli/commands/project-config/init/init.d.ts.map +1 -1
  109. package/dist/cli/commands/project-config/init/init.js +28 -5
  110. package/dist/cli/commands/project-config/init/init.js.map +1 -1
  111. package/dist/cli/commands/project-config/project/list.d.ts.map +1 -1
  112. package/dist/cli/commands/project-config/project/list.js +8 -1
  113. package/dist/cli/commands/project-config/project/list.js.map +1 -1
  114. package/dist/cli/commands/project-config/project/show.d.ts.map +1 -1
  115. package/dist/cli/commands/project-config/project/show.js +12 -0
  116. package/dist/cli/commands/project-config/project/show.js.map +1 -1
  117. package/dist/cli/commands/project-config/project/subscription-state.d.ts +5 -0
  118. package/dist/cli/commands/project-config/project/subscription-state.d.ts.map +1 -0
  119. package/dist/cli/commands/project-config/project/subscription-state.js +57 -0
  120. package/dist/cli/commands/project-config/project/subscription-state.js.map +1 -0
  121. package/dist/cli/commands/project-config/source/connect.d.ts.map +1 -1
  122. package/dist/cli/commands/project-config/source/connect.js +5 -5
  123. package/dist/cli/commands/project-config/source/connect.js.map +1 -1
  124. package/dist/cli/commands/project-config/source/map.d.ts.map +1 -1
  125. package/dist/cli/commands/project-config/source/map.js +4 -4
  126. package/dist/cli/commands/project-config/source/map.js.map +1 -1
  127. package/dist/cli/commands/project-config/source/status.d.ts.map +1 -1
  128. package/dist/cli/commands/project-config/source/status.js +71 -17
  129. package/dist/cli/commands/project-config/source/status.js.map +1 -1
  130. package/dist/cli/commands/runtime-data/backup.d.ts +1 -0
  131. package/dist/cli/commands/runtime-data/backup.d.ts.map +1 -1
  132. package/dist/cli/commands/runtime-data/backup.js +36 -0
  133. package/dist/cli/commands/runtime-data/backup.js.map +1 -1
  134. package/dist/cli/commands/runtime-data/index.d.ts +1 -0
  135. package/dist/cli/commands/runtime-data/index.d.ts.map +1 -1
  136. package/dist/cli/commands/runtime-data/index.js +26 -4
  137. package/dist/cli/commands/runtime-data/index.js.map +1 -1
  138. package/dist/cli/commands/runtime-data/restore.d.ts.map +1 -1
  139. package/dist/cli/commands/runtime-data/restore.js +33 -5
  140. package/dist/cli/commands/runtime-data/restore.js.map +1 -1
  141. package/dist/cli/index.d.ts +13 -3
  142. package/dist/cli/index.d.ts.map +1 -1
  143. package/dist/cli/index.js +12 -2
  144. package/dist/cli/index.js.map +1 -1
  145. package/dist/cli/lib/auth.d.ts +21 -2
  146. package/dist/cli/lib/auth.d.ts.map +1 -1
  147. package/dist/cli/lib/auth.js +22 -2
  148. package/dist/cli/lib/auth.js.map +1 -1
  149. package/dist/cli/lib/commit.d.ts +4 -0
  150. package/dist/cli/lib/commit.d.ts.map +1 -0
  151. package/dist/cli/lib/commit.js +22 -0
  152. package/dist/cli/lib/commit.js.map +1 -0
  153. package/dist/cli/lib/console-credential.d.ts +55 -0
  154. package/dist/cli/lib/console-credential.d.ts.map +1 -0
  155. package/dist/cli/lib/console-credential.js +164 -0
  156. package/dist/cli/lib/console-credential.js.map +1 -0
  157. package/dist/cli/lib/console-origins.d.ts +30 -0
  158. package/dist/cli/lib/console-origins.d.ts.map +1 -0
  159. package/dist/cli/lib/console-origins.js +87 -0
  160. package/dist/cli/lib/console-origins.js.map +1 -0
  161. package/dist/cli/lib/console-pairing.d.ts +15 -0
  162. package/dist/cli/lib/console-pairing.d.ts.map +1 -0
  163. package/dist/cli/lib/console-pairing.js +70 -0
  164. package/dist/cli/lib/console-pairing.js.map +1 -0
  165. package/dist/cli/lib/console-session-lock.d.ts +39 -0
  166. package/dist/cli/lib/console-session-lock.d.ts.map +1 -0
  167. package/dist/cli/lib/console-session-lock.js +235 -0
  168. package/dist/cli/lib/console-session-lock.js.map +1 -0
  169. package/dist/cli/lib/console-session.d.ts +47 -0
  170. package/dist/cli/lib/console-session.d.ts.map +1 -0
  171. package/dist/cli/lib/console-session.js +235 -0
  172. package/dist/cli/lib/console-session.js.map +1 -0
  173. package/dist/cli/lib/contract/backup-schedule-reasons.d.ts +4 -0
  174. package/dist/cli/lib/contract/backup-schedule-reasons.d.ts.map +1 -0
  175. package/dist/cli/lib/contract/backup-schedule-reasons.js +17 -0
  176. package/dist/cli/lib/contract/backup-schedule-reasons.js.map +1 -0
  177. package/dist/cli/lib/contract/build-settings.d.ts +1 -1
  178. package/dist/cli/lib/contract/database.d.ts +19 -0
  179. package/dist/cli/lib/contract/database.d.ts.map +1 -1
  180. package/dist/cli/lib/contract/database.js.map +1 -1
  181. package/dist/cli/lib/contract/deployments.d.ts +13 -0
  182. package/dist/cli/lib/contract/deployments.d.ts.map +1 -1
  183. package/dist/cli/lib/contract/deployments.js +38 -0
  184. package/dist/cli/lib/contract/deployments.js.map +1 -1
  185. package/dist/cli/lib/contract/desired-state.d.ts +3 -5
  186. package/dist/cli/lib/contract/desired-state.d.ts.map +1 -1
  187. package/dist/cli/lib/contract/desired-state.js +42 -28
  188. package/dist/cli/lib/contract/desired-state.js.map +1 -1
  189. package/dist/cli/lib/contract/foreign-credentials.d.ts +6 -4
  190. package/dist/cli/lib/contract/foreign-credentials.d.ts.map +1 -1
  191. package/dist/cli/lib/contract/foreign-credentials.js +7 -5
  192. package/dist/cli/lib/contract/foreign-credentials.js.map +1 -1
  193. package/dist/cli/lib/contract/logs.d.ts +120 -0
  194. package/dist/cli/lib/contract/logs.d.ts.map +1 -1
  195. package/dist/cli/lib/contract/logs.js +106 -1
  196. package/dist/cli/lib/contract/logs.js.map +1 -1
  197. package/dist/cli/lib/contract/managed-email.d.ts +57 -0
  198. package/dist/cli/lib/contract/managed-email.d.ts.map +1 -0
  199. package/dist/cli/lib/contract/managed-email.js +21 -0
  200. package/dist/cli/lib/contract/managed-email.js.map +1 -0
  201. package/dist/cli/lib/contract/projects.d.ts +6 -0
  202. package/dist/cli/lib/contract/projects.d.ts.map +1 -1
  203. package/dist/cli/lib/contract/regions.d.ts +11 -0
  204. package/dist/cli/lib/contract/regions.d.ts.map +1 -0
  205. package/dist/cli/lib/contract/regions.js +7 -0
  206. package/dist/cli/lib/contract/regions.js.map +1 -0
  207. package/dist/cli/lib/contract/runtime-operations.d.ts +0 -1
  208. package/dist/cli/lib/contract/runtime-operations.d.ts.map +1 -1
  209. package/dist/cli/lib/contract/source-disconnect-reasons.d.ts +4 -0
  210. package/dist/cli/lib/contract/source-disconnect-reasons.d.ts.map +1 -0
  211. package/dist/cli/lib/contract/source-disconnect-reasons.js +31 -0
  212. package/dist/cli/lib/contract/source-disconnect-reasons.js.map +1 -0
  213. package/dist/cli/lib/contract/source-repository.d.ts +1 -1
  214. package/dist/cli/lib/contract/source-repository.d.ts.map +1 -1
  215. package/dist/cli/lib/contract/source-repository.js +1 -0
  216. package/dist/cli/lib/contract/source-repository.js.map +1 -1
  217. package/dist/cli/lib/errors.d.ts +30 -0
  218. package/dist/cli/lib/errors.d.ts.map +1 -1
  219. package/dist/cli/lib/errors.js +46 -0
  220. package/dist/cli/lib/errors.js.map +1 -1
  221. package/dist/cli/lib/platform-address.d.ts +3 -0
  222. package/dist/cli/lib/platform-address.d.ts.map +1 -0
  223. package/dist/cli/lib/platform-address.js +7 -0
  224. package/dist/cli/lib/platform-address.js.map +1 -0
  225. package/dist/cli/lib/resolve.d.ts +1 -0
  226. package/dist/cli/lib/resolve.d.ts.map +1 -1
  227. package/dist/cli/lib/resolve.js +26 -3
  228. package/dist/cli/lib/resolve.js.map +1 -1
  229. package/dist/cli/lib/terminal-output.d.ts +8 -0
  230. package/dist/cli/lib/terminal-output.d.ts.map +1 -0
  231. package/dist/cli/lib/terminal-output.js +52 -0
  232. package/dist/cli/lib/terminal-output.js.map +1 -0
  233. package/dist/dev/dashboard-vite-config.d.ts +1 -0
  234. package/dist/dev/dashboard-vite-config.d.ts.map +1 -1
  235. package/dist/dev/dashboard-vite-config.js +26 -3
  236. package/dist/dev/dashboard-vite-config.js.map +1 -1
  237. package/dist/dev/dev-command.d.ts.map +1 -1
  238. package/dist/dev/dev-command.js +21 -1
  239. package/dist/dev/dev-command.js.map +1 -1
  240. package/dist/dev/dev-migrate.js +2 -0
  241. package/dist/dev/dev-migrate.js.map +1 -1
  242. package/dist/dev/dev-server.js +3 -0
  243. package/dist/dev/dev-server.js.map +1 -1
  244. package/dist/dev/dev-worker.js +2 -1
  245. package/dist/dev/dev-worker.js.map +1 -1
  246. package/dist/local-core.d.ts +2 -0
  247. package/dist/local-core.d.ts.map +1 -0
  248. package/dist/local-core.js +15 -0
  249. package/dist/local-core.js.map +1 -0
  250. package/dist/local-durable-execution-plugin.d.ts +3 -0
  251. package/dist/local-durable-execution-plugin.d.ts.map +1 -0
  252. package/dist/local-durable-execution-plugin.js +33 -0
  253. package/dist/local-durable-execution-plugin.js.map +1 -0
  254. package/dist/local-runtime.d.ts.map +1 -1
  255. package/dist/local-runtime.js +7 -0
  256. package/dist/local-runtime.js.map +1 -1
  257. package/dist/vite.js +3 -4
  258. package/dist/vite.js.map +1 -1
  259. package/package.json +6 -6
@@ -0,0 +1,235 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { link, mkdir, open, readFile, rename, stat, unlink, utimes } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { setTimeout as sleep } from "node:timers/promises";
5
+ import { consoleConfigDir } from "./console-credential.js";
6
+ import { ConsoleSessionLockUnavailableError } from "./errors.js";
7
+ import { logger } from "./logger.js";
8
+ // The cross-process lock guarding `~/.vendure/console-credentials.json`.
9
+ //
10
+ // Console rotates the refresh token on use and refuses the spent one, so two processes that refresh
11
+ // together leave one of them with nothing, and a stale pair written back over a live one ends a
12
+ // session that was working. Two processes are ordinary here: `vendure dev` runs for hours while
13
+ // Cloud commands run in another terminal.
14
+ //
15
+ // **This is a vendored fork, not a second implementation.** It is
16
+ // `test/fixtures/console-auth/upstream/session-lock.ts` from
17
+ // `vendurehq/vendure-platform@0d09dfd2`, adapted only in what this repository requires: double
18
+ // quotes, this project's formatting, `ConsoleSessionLockUnavailableError` in place of a bare
19
+ // `Error`, and a debug line on the release path. The algorithm, the constants and the reasoning are
20
+ // upstream's. Forking rather than importing is what upstream's README asks for, because the two
21
+ // tools ship on separate release trains, and there is no published build to depend on: nothing has
22
+ // been released to that repository's `major` dist-tag.
23
+ //
24
+ // The fork is what makes the drift dangerous, so two things guard it. `cli-console-session-lock.integration.test.ts`
25
+ // pins the values both processes must agree on against the vendored copy, and runs that copy in a
26
+ // second process against this one over a single credential file. That proves the fork still
27
+ // cooperates with its original. It does not prove two independent implementations agree, and
28
+ // nothing here should be read as claiming it does.
29
+ //
30
+ // This is not `file-lock.ts`. That lock guards `~/.vendure-cloud/refresh.lock`, is best-effort by
31
+ // design, and runs the guarded work anyway when it cannot be taken. Neither property is allowed
32
+ // here: a participant that runs unlocked is a participant that is not in the protocol.
33
+ /**
34
+ * How long a lock file may sit untouched before another participant may take it over.
35
+ *
36
+ * A holder touches its own lock while it works, so this measures a holder that has stopped rather
37
+ * than one that is slow.
38
+ */
39
+ export const CONSOLE_SESSION_LOCK_STALE_MS = 30_000;
40
+ /**
41
+ * How long to wait for a lock somebody else holds.
42
+ *
43
+ * Never shorter than the stale budget. A waiter that gave up sooner could give up while the holder
44
+ * is dead but not yet stale, and then "I gave up waiting" would stop meaning "the holder is gone".
45
+ */
46
+ export const CONSOLE_SESSION_LOCK_WAIT_MS = 30_000;
47
+ const CONSOLE_SESSION_LOCK_POLL_MS = 50;
48
+ /** The lock file, beside the credential it guards. */
49
+ export function consoleSessionLockPath(home) {
50
+ return join(consoleConfigDir(home), "console-credentials.lock");
51
+ }
52
+ /**
53
+ * Run `work` while holding the Console session lock.
54
+ *
55
+ * Every step that spends a refresh token or rewrites the credential file goes through here,
56
+ * including the ones in the other tool. Guarding only some of the call sites leaves the double
57
+ * spend reachable through the rest.
58
+ *
59
+ * The lock is not reentrant.
60
+ */
61
+ export async function withConsoleSessionLock(options, work) {
62
+ const lock = await acquire(options);
63
+ const beat = heartbeat(lock, options.staleMs ?? CONSOLE_SESSION_LOCK_STALE_MS);
64
+ try {
65
+ return await work();
66
+ }
67
+ finally {
68
+ clearInterval(beat);
69
+ await release(lock);
70
+ }
71
+ }
72
+ /**
73
+ * Say the holder is still here, by touching the lock while the work runs.
74
+ *
75
+ * Without it the stale budget measures how long the work takes rather than whether the holder is
76
+ * alive, so a hold that runs long on a loaded machine is broken while it is still working and two
77
+ * processes refresh at once. A third of the budget leaves two missed beats before anyone may act
78
+ * on the silence.
79
+ */
80
+ function heartbeat(lock, staleMs) {
81
+ const beat = setInterval(() => {
82
+ void (async () => {
83
+ // Only ours. A lock broken and taken over while this holder was working belongs to
84
+ // somebody else, and beating on their behalf would keep their lock alive after they let
85
+ // it go.
86
+ const current = await readLockFile(lock.path);
87
+ if (current?.token !== lock.token)
88
+ return;
89
+ const when = new Date();
90
+ await utimes(lock.path, when, when);
91
+ })().catch(() => undefined);
92
+ }, Math.max(1, Math.round(staleMs / 3)));
93
+ // A lock is never a reason to keep the process running.
94
+ beat.unref();
95
+ return beat;
96
+ }
97
+ async function acquire(options) {
98
+ const path = consoleSessionLockPath(options.home);
99
+ const staleMs = options.staleMs ?? CONSOLE_SESSION_LOCK_STALE_MS;
100
+ const waitMs = Math.max(options.waitMs ?? CONSOLE_SESSION_LOCK_WAIT_MS, staleMs);
101
+ const pollMs = options.pollMs ?? CONSOLE_SESSION_LOCK_POLL_MS;
102
+ const token = randomUUID();
103
+ const deadline = Date.now() + waitMs;
104
+ await mkdir(consoleConfigDir(options.home), { recursive: true, mode: 0o700 });
105
+ const takeOver = async () => {
106
+ if (!(await breakIfStale(path, staleMs)))
107
+ return undefined;
108
+ return (await create(path, token)) ? { path, token } : undefined;
109
+ };
110
+ for (;;) {
111
+ if (await create(path, token))
112
+ return { path, token };
113
+ const taken = await takeOver();
114
+ if (taken)
115
+ return taken;
116
+ // Checked on every pass, including one that broke a lock. A state where `create` reports
117
+ // `EEXIST` while the lock itself cannot be read, such as a dangling symlink on the path, is
118
+ // otherwise a loop with no exit.
119
+ if (Date.now() >= deadline) {
120
+ // The lock can cross the stale boundary while the checks above are reading it, so it gets
121
+ // one last look. Without it a waiter that spent its whole budget can still walk away from a
122
+ // dead holder.
123
+ const last = await takeOver();
124
+ if (last)
125
+ return last;
126
+ throw new ConsoleSessionLockUnavailableError();
127
+ }
128
+ await sleep(pollMs);
129
+ }
130
+ }
131
+ /**
132
+ * Create the lock file, or report that somebody else holds it.
133
+ *
134
+ * `wx` is the whole mechanism: the create either wins or fails with `EEXIST`, with no window in
135
+ * between. The body records who took it, which is what makes releasing and breaking safe, and
136
+ * gives a person looking at a stuck lock a pid to check.
137
+ */
138
+ async function create(path, token) {
139
+ let handle;
140
+ try {
141
+ handle = await open(path, "wx", 0o600);
142
+ }
143
+ catch (error) {
144
+ const { code } = error;
145
+ if (code === "EEXIST")
146
+ return false;
147
+ throw new ConsoleSessionLockUnavailableError({ cause: error });
148
+ }
149
+ try {
150
+ const body = { token, pid: process.pid, acquiredAt: new Date().toISOString() };
151
+ await handle.writeFile(`${JSON.stringify(body)}\n`, "utf8");
152
+ return true;
153
+ }
154
+ catch (error) {
155
+ await unlink(path).catch(() => undefined);
156
+ throw new ConsoleSessionLockUnavailableError({ cause: error });
157
+ }
158
+ finally {
159
+ await handle.close().catch(() => undefined);
160
+ }
161
+ }
162
+ /**
163
+ * Take a lock whose holder has stopped touching it out of the way. Reports whether the path is
164
+ * worth another create attempt.
165
+ */
166
+ async function breakIfStale(path, staleMs) {
167
+ const before = await readLockFile(path);
168
+ if (!before)
169
+ return true;
170
+ // `Date.now()` counts whole milliseconds while `mtimeMs` carries a fraction, so an untouched
171
+ // lock written in the same millisecond a waiter started reads as younger than that waiter's own
172
+ // clock says it is. Floor it: both sides then measure in the same units, and a waiter that has
173
+ // spent its whole budget can always break what it spent the budget on.
174
+ if (Date.now() - Math.floor(before.mtimeMs) < staleMs)
175
+ return false;
176
+ // Move the lock aside rather than unlink it. `rename` is one operation, so one waiter moves this
177
+ // file and the rest get `ENOENT` and go back to creating. Unlinking by path would let a second
178
+ // waiter, still holding its reading of a file the first waiter has already removed, delete the
179
+ // fresh lock the first waiter created in its place and leave two holders.
180
+ const aside = `${path}.${randomUUID()}`;
181
+ try {
182
+ await rename(path, aside);
183
+ }
184
+ catch {
185
+ return true;
186
+ }
187
+ const moved = await readLockFile(aside);
188
+ if (moved && moved.token !== before.token) {
189
+ // Somebody took the lock between the read above and the rename, so what moved is their live
190
+ // lock and not the dead one. Put it back. `link` fails rather than overwrite, and a failure
191
+ // means a third lock is already there, at which point the holder whose lock was moved has lost
192
+ // it whatever happens next.
193
+ await link(aside, path).catch(() => undefined);
194
+ }
195
+ await unlink(aside).catch(() => undefined);
196
+ return true;
197
+ }
198
+ /**
199
+ * Give up the lock.
200
+ *
201
+ * A lock carrying somebody else's token is not ours to remove: ours was already broken as stale
202
+ * and the file belongs to whoever took it. Releasing it would let a third process in while the
203
+ * second one writes.
204
+ */
205
+ async function release(lock) {
206
+ const current = await readLockFile(lock.path);
207
+ if (current?.token !== lock.token) {
208
+ logger.debug("Console session lock was taken over while held; leaving it alone");
209
+ return;
210
+ }
211
+ await unlink(lock.path).catch(() => undefined);
212
+ }
213
+ async function readLockFile(path) {
214
+ try {
215
+ const info = await stat(path);
216
+ let token;
217
+ try {
218
+ const value = JSON.parse(await readFile(path, "utf8"));
219
+ if (value && typeof value === "object" && "token" in value) {
220
+ const { token: candidate } = value;
221
+ if (typeof candidate === "string")
222
+ token = candidate;
223
+ }
224
+ }
225
+ catch {
226
+ // A lock written by something older, or caught mid-write. It still holds the lock; it just
227
+ // cannot prove ownership, so it can only ever be removed by the stale path.
228
+ }
229
+ return { token, mtimeMs: info.mtimeMs };
230
+ }
231
+ catch {
232
+ return undefined;
233
+ }
234
+ }
235
+ //# sourceMappingURL=console-session-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-session-lock.js","sourceRoot":"","sources":["../../../src/cli/lib/console-session-lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { type ConsoleCredentialLocation } from "./console-credential.js";
2
+ export interface ConsoleSessionOptions extends ConsoleCredentialLocation {
3
+ fetch?: typeof globalThis.fetch;
4
+ now?: () => Date;
5
+ }
6
+ /**
7
+ * Whether this machine may reuse the shared Vendure Console CLI Session for Cloud commands.
8
+ *
9
+ * Off unless the variable is set, and the control plane refuses a Console session unless it has
10
+ * been configured to reach Console, so both ends of CLO-641 default to the behaviour that shipped
11
+ * before it. The switch is one a person sets rather than something inferred from the presence of a
12
+ * file another tool wrote: finding a credential is not the same as being told to use it, and a
13
+ * machine that turned this on by itself would change which account a command runs under.
14
+ *
15
+ * It lives here rather than in `config.ts` because a machine with the feature off must not even
16
+ * read the Console credential, and keeping the switch beside the reader is what makes that
17
+ * obvious at the one call site that checks it.
18
+ */
19
+ export declare function isSharedConsoleSessionEnabled(): boolean;
20
+ export interface ResolvedConsoleSession {
21
+ accessToken: string;
22
+ /** Whether the access token is past its expiry, counting the skew. */
23
+ expired: boolean;
24
+ /** Whether an expired session can be renewed without a browser. */
25
+ refreshable: boolean;
26
+ }
27
+ /**
28
+ * The stored Console session, or `undefined` when this machine has none to reuse.
29
+ *
30
+ * Synchronous, because it sits on the credential-resolution path that every command runs through
31
+ * before it does any work. An expired session is still returned: it may carry a refresh token, and
32
+ * the renewal happens on the first 401 through the same retry the stored Cloud login uses.
33
+ */
34
+ export declare function resolveConsoleSession(options?: ConsoleCredentialLocation & {
35
+ now?: () => Date;
36
+ }): ResolvedConsoleSession | undefined;
37
+ /**
38
+ * Renew the shared session and return the fresh access token.
39
+ *
40
+ * Deduplicated twice, for the same reason the stored Cloud login is. This promise collapses the
41
+ * concurrent calls of one process, and the session lock underneath it serializes the token request
42
+ * against every other process on the machine, Cloud and Platform alike. Neither is enough alone:
43
+ * this one knows nothing about the other tool, and the lock cannot see a second client inside this
44
+ * process that is already waiting on it.
45
+ */
46
+ export declare function refreshConsoleSession(failedAccessToken: string | undefined, options?: ConsoleSessionOptions): Promise<string>;
47
+ //# sourceMappingURL=console-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-session.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/console-session.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,yBAAyB,EAK/B,MAAM,yBAAyB,CAAC;AAgCjC,MAAM,WAAW,qBAAsB,SAAQ,yBAAyB;IACtE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,IAAI,OAAO,CAGvD;AAED,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,yBAAyB,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAAO,GAC7D,sBAAsB,GAAG,SAAS,CAUpC;AAID;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,iBAAiB,EAAE,MAAM,GAAG,SAAS,EACrC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,CAAC,CAUjB"}
@@ -0,0 +1,235 @@
1
+ import { discardConsoleCredential, isConsoleCredentialExpired, readConsoleCredential, writeConsoleCredential, } from "./console-credential.js";
2
+ import { consoleOrigins } from "./console-origins.js";
3
+ import { withConsoleSessionLock } from "./console-session-lock.js";
4
+ import { ConsoleSessionNotAcceptedError, ConsoleSessionRejectedError, ConsoleSessionUnavailableError, } from "./errors.js";
5
+ import { logger } from "./logger.js";
6
+ // Reusing the Console CLI Session that Vendure Platform already obtained for this person, so a
7
+ // developer who signed in during `vendure console link` does not approve a second login for Cloud.
8
+ //
9
+ // Cloud never mints one of these. It reads the stored session, refreshes it when it has expired,
10
+ // and presents the access token to the control plane, which resolves the identity behind it
11
+ // server-side against Console. Nothing here decodes the token or trusts what the file claims about
12
+ // who the person is.
13
+ const TOKEN_PATH = "/v1/auth/cli/token";
14
+ /**
15
+ * The deadline covers reading the answer as well as getting it. A server that sends headers and
16
+ * then stops sending would otherwise hold the session lock for as long as it liked, and the lock's
17
+ * stale budget is set from this bound.
18
+ */
19
+ const TOKEN_REQUEST_TIMEOUT_MS = 10_000;
20
+ /** What Console names a grant it will not honour again. */
21
+ const GRANT_REFUSED_CODE = "cli_session.invalid_grant";
22
+ const MAX_TOKEN_LIFETIME_SECONDS = 365 * 24 * 60 * 60;
23
+ /**
24
+ * Whether this machine may reuse the shared Vendure Console CLI Session for Cloud commands.
25
+ *
26
+ * Off unless the variable is set, and the control plane refuses a Console session unless it has
27
+ * been configured to reach Console, so both ends of CLO-641 default to the behaviour that shipped
28
+ * before it. The switch is one a person sets rather than something inferred from the presence of a
29
+ * file another tool wrote: finding a credential is not the same as being told to use it, and a
30
+ * machine that turned this on by itself would change which account a command runs under.
31
+ *
32
+ * It lives here rather than in `config.ts` because a machine with the feature off must not even
33
+ * read the Console credential, and keeping the switch beside the reader is what makes that
34
+ * obvious at the one call site that checks it.
35
+ */
36
+ export function isSharedConsoleSessionEnabled() {
37
+ const value = process.env.VENDURE_CLOUD_SHARED_CONSOLE_SESSION;
38
+ return value === "1" || value === "true";
39
+ }
40
+ /**
41
+ * The stored Console session, or `undefined` when this machine has none to reuse.
42
+ *
43
+ * Synchronous, because it sits on the credential-resolution path that every command runs through
44
+ * before it does any work. An expired session is still returned: it may carry a refresh token, and
45
+ * the renewal happens on the first 401 through the same retry the stored Cloud login uses.
46
+ */
47
+ export function resolveConsoleSession(options = {}) {
48
+ const credential = readConsoleCredential(options);
49
+ if (!credential) {
50
+ return undefined;
51
+ }
52
+ return {
53
+ accessToken: credential.accessToken,
54
+ expired: isConsoleCredentialExpired(credential, options.now?.() ?? new Date()),
55
+ refreshable: credential.refreshToken !== undefined,
56
+ };
57
+ }
58
+ let activeRefresh = null;
59
+ /**
60
+ * Renew the shared session and return the fresh access token.
61
+ *
62
+ * Deduplicated twice, for the same reason the stored Cloud login is. This promise collapses the
63
+ * concurrent calls of one process, and the session lock underneath it serializes the token request
64
+ * against every other process on the machine, Cloud and Platform alike. Neither is enough alone:
65
+ * this one knows nothing about the other tool, and the lock cannot see a second client inside this
66
+ * process that is already waiting on it.
67
+ */
68
+ export function refreshConsoleSession(failedAccessToken, options = {}) {
69
+ if (activeRefresh) {
70
+ return activeRefresh;
71
+ }
72
+ activeRefresh = withConsoleSessionLock({ home: options.home }, () => refreshUnderLock(failedAccessToken, options)).finally(() => {
73
+ activeRefresh = null;
74
+ });
75
+ return activeRefresh;
76
+ }
77
+ async function refreshUnderLock(failedAccessToken, options) {
78
+ // Read again now that the lock is held. Waiting for it is the usual way to find out that another
79
+ // process has already refreshed, and by then the pair read before the wait is the stale copy.
80
+ let credential = readConsoleCredential(options);
81
+ if (!credential) {
82
+ throw new ConsoleSessionRejectedError();
83
+ }
84
+ if (credential.accessToken !== failedAccessToken) {
85
+ if (!isConsoleCredentialExpired(credential, currentTime(options))) {
86
+ logger.debug("Another process already refreshed the Console session; reusing its access token");
87
+ return credential.accessToken;
88
+ }
89
+ }
90
+ else if (!isConsoleCredentialExpired(credential, currentTime(options))) {
91
+ // The token that was refused is still the stored one and it has not expired, so whatever the
92
+ // control plane objected to, it was not the age of this token. Spending the grant would rotate
93
+ // a credential another Vendure product owns, on every failing command, and buy a retry that is
94
+ // refused for the same reason. A 401 is not by itself evidence that a session needs renewing.
95
+ logger.debug("Console session was refused while still valid; not spending the refresh grant");
96
+ throw new ConsoleSessionNotAcceptedError();
97
+ }
98
+ for (let attempt = 0;; attempt++) {
99
+ const spent = credential.refreshToken;
100
+ if (!spent) {
101
+ throw new ConsoleSessionRejectedError();
102
+ }
103
+ try {
104
+ const renewed = await requestToken(credential.apiOrigin, spent, options);
105
+ writeConsoleCredential({
106
+ schemaVersion: 2,
107
+ origin: credential.origin,
108
+ apiOrigin: credential.apiOrigin,
109
+ accessToken: renewed.accessToken,
110
+ expiresAt: renewed.expiresAt,
111
+ ...(renewed.refreshToken ? { refreshToken: renewed.refreshToken } : {}),
112
+ }, { home: options.home });
113
+ return renewed.accessToken;
114
+ }
115
+ catch (error) {
116
+ // A transient failure says nothing about the refresh token, so the credential survives it
117
+ // untouched. One retry, and only against a token somebody else rotated in, is enough to hand
118
+ // over the race. A second refusal is reported rather than acted on: it refuses a token this
119
+ // process took from a store another participant was still moving, which is not the proof the
120
+ // first refusal is.
121
+ if (attempt > 0 || !(error instanceof ConsoleSessionRejectedError)) {
122
+ throw error;
123
+ }
124
+ const latest = readConsoleCredential(options);
125
+ if (latest?.refreshToken && latest.refreshToken !== spent) {
126
+ logger.debug("Console refresh token was spent by another process; using its replacement");
127
+ if (!isConsoleCredentialExpired(latest, currentTime(options))) {
128
+ return latest.accessToken;
129
+ }
130
+ credential = latest;
131
+ continue;
132
+ }
133
+ // The store still holds the token that was just refused, and the lock was held across both
134
+ // the refusal and this read, so nothing can have rotated it in between. The session is over.
135
+ discardConsoleCredential({ home: options.home });
136
+ throw error;
137
+ }
138
+ }
139
+ }
140
+ async function requestToken(storedApiOrigin, refreshToken, options) {
141
+ // Re-checked rather than taken from the file. The origin was validated when the credential was
142
+ // read, and validating it again here means no path reaches `fetch` with a URL that skipped the
143
+ // check.
144
+ const { apiOrigin } = consoleOrigins({ apiOrigin: storedApiOrigin });
145
+ const fetchImpl = options.fetch ?? globalThis.fetch;
146
+ let response;
147
+ try {
148
+ response = await fetchImpl(`${apiOrigin}${TOKEN_PATH}`, {
149
+ method: "POST",
150
+ redirect: "error",
151
+ signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS),
152
+ headers: { accept: "application/json", "content-type": "application/json" },
153
+ body: JSON.stringify({ grant_type: "refresh_token", refresh_token: refreshToken }),
154
+ });
155
+ }
156
+ catch (error) {
157
+ throw new ConsoleSessionUnavailableError({ cause: error });
158
+ }
159
+ if (!response.ok) {
160
+ throw (await refusesTheGrant(response))
161
+ ? new ConsoleSessionRejectedError()
162
+ : new ConsoleSessionUnavailableError({
163
+ cause: new Error(`Vendure Console answered the token request with ${response.status}.`),
164
+ });
165
+ }
166
+ let body;
167
+ try {
168
+ body = await response.json();
169
+ }
170
+ catch (error) {
171
+ throw new ConsoleSessionUnavailableError({ cause: error });
172
+ }
173
+ return toRenewedSession(body, refreshToken, currentTime(options));
174
+ }
175
+ /**
176
+ * Whether Console judged the grant itself and refused it.
177
+ *
178
+ * The status alone does not say. A 404 from an API origin pointed at the wrong host, a 403 from a
179
+ * proxy in front of Console, and a 401 from a gateway that wants a credential of its own are all
180
+ * 4xx, and none of them is Console reporting a spent refresh token. Console names that case in the
181
+ * body, so the body decides, and everything else leaves the stored credential alone.
182
+ */
183
+ async function refusesTheGrant(response) {
184
+ if (response.status < 400 || response.status >= 500) {
185
+ return false;
186
+ }
187
+ try {
188
+ const value = JSON.parse(await response.text());
189
+ if (!value || typeof value !== "object" || !("code" in value)) {
190
+ return false;
191
+ }
192
+ const { code } = value;
193
+ return code === GRANT_REFUSED_CODE;
194
+ }
195
+ catch {
196
+ return false;
197
+ }
198
+ }
199
+ /**
200
+ * Validate the token response. An unreadable expiry is a failure rather than an assumed default: a
201
+ * session treated as valid for longer than it is fails later, where the cause is no longer visible.
202
+ */
203
+ function toRenewedSession(value, presentedRefreshToken, now) {
204
+ const body = value;
205
+ if (typeof body?.access_token !== "string" || !body.access_token) {
206
+ throw new ConsoleSessionUnavailableError({
207
+ cause: new Error("Vendure Console returned no access token."),
208
+ });
209
+ }
210
+ if (body.token_type !== "Bearer") {
211
+ throw new ConsoleSessionUnavailableError({
212
+ cause: new Error("Vendure Console returned an unsupported token type."),
213
+ });
214
+ }
215
+ if (typeof body.expires_in !== "number" ||
216
+ !Number.isInteger(body.expires_in) ||
217
+ body.expires_in <= 0 ||
218
+ body.expires_in > MAX_TOKEN_LIFETIME_SECONDS) {
219
+ throw new ConsoleSessionUnavailableError({
220
+ cause: new Error("Vendure Console returned no usable token expiry."),
221
+ });
222
+ }
223
+ const refreshToken = typeof body.refresh_token === "string" && body.refresh_token
224
+ ? body.refresh_token
225
+ : presentedRefreshToken;
226
+ return {
227
+ accessToken: body.access_token,
228
+ expiresAt: new Date(now.getTime() + body.expires_in * 1000).toISOString(),
229
+ refreshToken,
230
+ };
231
+ }
232
+ function currentTime(options) {
233
+ return options.now?.() ?? new Date();
234
+ }
235
+ //# sourceMappingURL=console-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-session.js","sourceRoot":"","sources":["../../../src/cli/lib/console-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { BackupScheduleExclusion } from "./database.js";
2
+ export declare const BACKUP_SCHEDULE_EXCLUSION_DESCRIPTIONS: Record<BackupScheduleExclusion, string>;
3
+ export declare function describeBackupScheduleExclusion(reason: BackupScheduleExclusion): string;
4
+ //# sourceMappingURL=backup-schedule-reasons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-schedule-reasons.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/contract/backup-schedule-reasons.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAQ7D,eAAO,MAAM,sCAAsC,EAAE,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAK1F,CAAC;AAEF,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,uBAAuB,GAAG,MAAM,CAKvF"}
@@ -0,0 +1,17 @@
1
+ // Why a VC Environment receives no scheduled dump, in the words a customer reads. The wire carries
2
+ // the code so that a client can act on the case — the two want different next steps, and only one of
3
+ // them is a suggestion the platform will accept.
4
+ //
5
+ // Stated positively rather than left to an empty list, because the inference a customer draws from
6
+ // silence is the wrong one.
7
+ export const BACKUP_SCHEDULE_EXCLUSION_DESCRIPTIONS = {
8
+ "non-production": "This environment receives no scheduled backups. Scheduled backups run for production environments only.",
9
+ "not-deployed": "This environment has no provisioned database, so no scheduled backup runs for it yet.",
10
+ };
11
+ export function describeBackupScheduleExclusion(reason) {
12
+ // A control plane ahead of this reader can send a code it has no sentence for. Saying the platform
13
+ // gave a reason this client cannot render beats printing nothing where an explanation belongs.
14
+ const description = BACKUP_SCHEDULE_EXCLUSION_DESCRIPTIONS[reason];
15
+ return description ?? `This environment receives no scheduled backups (${reason}).`;
16
+ }
17
+ //# sourceMappingURL=backup-schedule-reasons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-schedule-reasons.js","sourceRoot":"","sources":["../../../../src/cli/lib/contract/backup-schedule-reasons.ts"],"names":[],"mappings":"AAIA,mGAAmG;AACnG,qGAAqG;AACrG,iDAAiD;AACjD,EAAE;AACF,mGAAmG;AACnG,4BAA4B;AAC5B,MAAM,CAAC,MAAM,sCAAsC,GAA4C;IAC7F,gBAAgB,EACd,yGAAyG;IAC3G,cAAc,EACZ,uFAAuF;CAC1F,CAAC;AAEF,MAAM,UAAU,+BAA+B,CAAC,MAA+B;IAC7E,mGAAmG;IACnG,+FAA+F;IAC/F,MAAM,WAAW,GAAuB,sCAAsC,CAAC,MAAM,CAAC,CAAC;IACvF,OAAO,WAAW,IAAI,mDAAmD,MAAM,IAAI,CAAC;AACtF,CAAC"}
@@ -14,7 +14,7 @@ export interface ResolvedBuildSettings {
14
14
  declare const appDirSchema: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
15
15
  export { appDirSchema };
16
16
  export declare const sourceBuildSettingsSchema: z.ZodObject<{
17
- nodeVersion: z.ZodOptional<z.ZodLiteral<20 | 22 | 24>>;
17
+ nodeVersion: z.ZodOptional<z.ZodLiteral<24 | 20 | 22>>;
18
18
  packageManager: z.ZodOptional<z.ZodEnum<{
19
19
  bun: "bun";
20
20
  pnpm: "pnpm";
@@ -1,9 +1,12 @@
1
1
  export declare const DATABASE_BACKUP_KINDS: readonly ["manual", "scheduled", "safety"];
2
2
  export type DatabaseBackupKind = (typeof DATABASE_BACKUP_KINDS)[number];
3
+ export type ScheduledDatabaseBackupTier = "daily" | "weekly";
4
+ export type DatabaseBackupTier = ScheduledDatabaseBackupTier | "recovery";
3
5
  export interface DatabaseBackup {
4
6
  id: string;
5
7
  status: string;
6
8
  kind: DatabaseBackupKind;
9
+ tier: DatabaseBackupTier;
7
10
  createdAt: string;
8
11
  completedAt: string | null;
9
12
  sizeBytes: number | null;
@@ -31,6 +34,22 @@ export interface RestoreResult {
31
34
  safetyBackupId: string;
32
35
  deadlineAt: string;
33
36
  }
37
+ export type BackupScheduleDay = "Sunday" | "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday";
38
+ export interface BackupScheduleEntry {
39
+ tier: ScheduledDatabaseBackupTier;
40
+ day: BackupScheduleDay | null;
41
+ startsAt: string;
42
+ timeZone: string;
43
+ retentionDays: number;
44
+ }
45
+ export type BackupScheduleExclusion = "non-production" | "not-deployed";
46
+ export type BackupSchedule = {
47
+ scheduled: true;
48
+ entries: BackupScheduleEntry[];
49
+ } | {
50
+ scheduled: false;
51
+ reason: BackupScheduleExclusion;
52
+ };
34
53
  export interface TunnelTicket {
35
54
  ticket: string;
36
55
  expiresAt: string;
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/contract/database.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,qBAAqB,4CAA6C,CAAC;AAChF,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,kBAAkB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAG9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAID,eAAO,MAAM,yBAAyB,uDAAwD,CAAC;AAC/F,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAO/E,eAAO,MAAM,uBAAuB,0EAM1B,CAAC;AACX,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,qBAAqB,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAIxB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,oBAAoB,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAG3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAG1B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IAKvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AAED,eAAO,MAAM,2BAA2B,cAAc,CAAC;AACvD,eAAO,MAAM,4BAA4B,aAAqD,CAAC"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/contract/database.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,qBAAqB,4CAA6C,CAAC;AAChF,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAKxE,MAAM,MAAM,2BAA2B,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC7D,MAAM,MAAM,kBAAkB,GAAG,2BAA2B,GAAG,UAAU,CAAC;AAE1E,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,kBAAkB,CAAC;IAGzB,IAAI,EAAE,kBAAkB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAG9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAID,eAAO,MAAM,yBAAyB,uDAAwD,CAAC;AAC/F,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAO/E,eAAO,MAAM,uBAAuB,0EAM1B,CAAC;AACX,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,qBAAqB,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAIxB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,oBAAoB,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAG3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAG1B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IAKvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,WAAW,GACX,UAAU,GACV,QAAQ,GACR,UAAU,CAAC;AAEf,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,2BAA2B,CAAC;IAClC,GAAG,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,uBAAuB,GAAG,gBAAgB,GAAG,cAAc,CAAC;AAExE,MAAM,MAAM,cAAc,GACtB;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,mBAAmB,EAAE,CAAA;CAAE,GACnD;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AAED,eAAO,MAAM,2BAA2B,cAAc,CAAC;AACvD,eAAO,MAAM,4BAA4B,aAAqD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","sourceRoot":"","sources":["../../../../src/cli/lib/contract/database.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,oGAAoG;AACpG,4BAA4B;AAC5B,EAAE;AACF,iGAAiG;AACjG,oGAAoG;AACpG,qFAAqF;AAErF,oGAAoG;AACpG,kGAAkG;AAClG,qFAAqF;AACrF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAU,CAAC;AAiBhF,kFAAkF;AAClF,oFAAoF;AACpF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAU,CAAC;AAG/F,8FAA8F;AAC9F,iGAAiG;AACjG,gGAAgG;AAChG,gGAAgG;AAChG,0BAA0B;AAC1B,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,QAAQ;IACR,eAAe;IACf,WAAW;IACX,WAAW;IACX,QAAQ;CACA,CAAC;AA4CX,MAAM,CAAC,MAAM,2BAA2B,GAAG,WAAW,CAAC;AACvD,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../../../src/cli/lib/contract/database.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,oGAAoG;AACpG,4BAA4B;AAC5B,EAAE;AACF,iGAAiG;AACjG,oGAAoG;AACpG,qFAAqF;AAErF,oGAAoG;AACpG,kGAAkG;AAClG,qFAAqF;AACrF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAU,CAAC;AA0BhF,kFAAkF;AAClF,oFAAoF;AACpF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAU,CAAC;AAG/F,8FAA8F;AAC9F,iGAAiG;AACjG,gGAAgG;AAChG,gGAAgG;AAChG,0BAA0B;AAC1B,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,QAAQ;IACR,eAAe;IACf,WAAW;IACX,WAAW;IACX,QAAQ;CACA,CAAC;AAqEX,MAAM,CAAC,MAAM,2BAA2B,GAAG,WAAW,CAAC;AACvD,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC"}