alchemy 0.63.1 → 0.65.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 (235) hide show
  1. package/bin/alchemy.js +7664 -5857
  2. package/bin/commands/init.ts +130 -9
  3. package/bin/constants.ts +4 -0
  4. package/bin/services/cdp-manager/cdp-proxy.ts +69 -0
  5. package/bin/services/cdp-manager/server.ts +173 -0
  6. package/bin/services/execute-alchemy.ts +83 -4
  7. package/bin/types.ts +1 -0
  8. package/lib/alchemy.d.ts +4 -0
  9. package/lib/alchemy.d.ts.map +1 -1
  10. package/lib/alchemy.js +10 -4
  11. package/lib/alchemy.js.map +1 -1
  12. package/lib/apply.d.ts +3 -0
  13. package/lib/apply.d.ts.map +1 -1
  14. package/lib/apply.js +11 -1
  15. package/lib/apply.js.map +1 -1
  16. package/lib/cloudflare/bindings.d.ts +3 -1
  17. package/lib/cloudflare/bindings.d.ts.map +1 -1
  18. package/lib/cloudflare/bucket.d.ts +2 -1
  19. package/lib/cloudflare/bucket.d.ts.map +1 -1
  20. package/lib/cloudflare/bucket.js.map +1 -1
  21. package/lib/cloudflare/bundle/cloudflare-internal.d.ts +6 -0
  22. package/lib/cloudflare/bundle/cloudflare-internal.d.ts.map +1 -0
  23. package/lib/cloudflare/bundle/cloudflare-internal.js +39 -0
  24. package/lib/cloudflare/bundle/cloudflare-internal.js.map +1 -0
  25. package/lib/cloudflare/bundle/nodejs-builtin-modules.d.ts +2 -0
  26. package/lib/cloudflare/bundle/nodejs-builtin-modules.d.ts.map +1 -0
  27. package/lib/cloudflare/bundle/nodejs-builtin-modules.js +9 -0
  28. package/lib/cloudflare/bundle/nodejs-builtin-modules.js.map +1 -0
  29. package/lib/cloudflare/bundle/plugin-als-external.d.ts +6 -0
  30. package/lib/cloudflare/bundle/plugin-als-external.d.ts.map +1 -0
  31. package/lib/cloudflare/bundle/plugin-als-external.js +12 -0
  32. package/lib/cloudflare/bundle/plugin-als-external.js.map +1 -0
  33. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.d.ts +4 -1
  34. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.d.ts.map +1 -1
  35. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js +23 -23
  36. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js.map +1 -1
  37. package/lib/cloudflare/bundle/plugin-node-compat.d.ts +11 -0
  38. package/lib/cloudflare/bundle/plugin-node-compat.d.ts.map +1 -0
  39. package/lib/cloudflare/bundle/plugin-node-compat.js +93 -0
  40. package/lib/cloudflare/bundle/plugin-node-compat.js.map +1 -0
  41. package/lib/cloudflare/bundle/plugin-wasm.d.ts.map +1 -1
  42. package/lib/cloudflare/bundle/plugin-wasm.js +20 -11
  43. package/lib/cloudflare/bundle/plugin-wasm.js.map +1 -1
  44. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  45. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  46. package/lib/cloudflare/d1-local-migrations.js.map +1 -1
  47. package/lib/cloudflare/durable-object-namespace.d.ts +1 -0
  48. package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -1
  49. package/lib/cloudflare/durable-object-namespace.js.map +1 -1
  50. package/lib/cloudflare/hyperdrive.d.ts +7 -2
  51. package/lib/cloudflare/hyperdrive.d.ts.map +1 -1
  52. package/lib/cloudflare/hyperdrive.js +39 -57
  53. package/lib/cloudflare/hyperdrive.js.map +1 -1
  54. package/lib/cloudflare/index.d.ts +1 -0
  55. package/lib/cloudflare/index.d.ts.map +1 -1
  56. package/lib/cloudflare/index.js +1 -0
  57. package/lib/cloudflare/index.js.map +1 -1
  58. package/lib/cloudflare/next.d.ts +13 -0
  59. package/lib/cloudflare/next.d.ts.map +1 -0
  60. package/lib/cloudflare/next.js +91 -0
  61. package/lib/cloudflare/next.js.map +1 -0
  62. package/lib/cloudflare/website.d.ts.map +1 -1
  63. package/lib/cloudflare/website.js +80 -82
  64. package/lib/cloudflare/website.js.map +1 -1
  65. package/lib/cloudflare/worker-assets.d.ts +3 -3
  66. package/lib/cloudflare/worker-assets.d.ts.map +1 -1
  67. package/lib/cloudflare/worker-assets.js +22 -47
  68. package/lib/cloudflare/worker-assets.js.map +1 -1
  69. package/lib/cloudflare/worker-bundle.d.ts +6 -0
  70. package/lib/cloudflare/worker-bundle.d.ts.map +1 -1
  71. package/lib/cloudflare/worker-bundle.js +34 -10
  72. package/lib/cloudflare/worker-bundle.js.map +1 -1
  73. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  74. package/lib/cloudflare/worker-metadata.js +1 -0
  75. package/lib/cloudflare/worker-metadata.js.map +1 -1
  76. package/lib/cloudflare/worker.d.ts.map +1 -1
  77. package/lib/cloudflare/worker.js +24 -6
  78. package/lib/cloudflare/worker.js.map +1 -1
  79. package/lib/cloudflare/wrangler.json.d.ts +4 -6
  80. package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
  81. package/lib/cloudflare/wrangler.json.js +20 -15
  82. package/lib/cloudflare/wrangler.json.js.map +1 -1
  83. package/lib/context.d.ts +3 -1
  84. package/lib/context.d.ts.map +1 -1
  85. package/lib/context.js +2 -2
  86. package/lib/context.js.map +1 -1
  87. package/lib/destroy.d.ts +8 -0
  88. package/lib/destroy.d.ts.map +1 -1
  89. package/lib/destroy.js +20 -4
  90. package/lib/destroy.js.map +1 -1
  91. package/lib/planetscale/api/client.gen.d.ts +1750 -0
  92. package/lib/planetscale/api/client.gen.d.ts.map +1 -0
  93. package/lib/planetscale/api/client.gen.js +2401 -0
  94. package/lib/planetscale/api/client.gen.js.map +1 -0
  95. package/lib/planetscale/api/index.d.ts +3 -0
  96. package/lib/planetscale/api/index.d.ts.map +1 -0
  97. package/lib/planetscale/api/index.js +4 -0
  98. package/lib/planetscale/api/index.js.map +1 -0
  99. package/lib/planetscale/api/types.gen.d.ts +12156 -0
  100. package/lib/planetscale/api/types.gen.d.ts.map +1 -0
  101. package/lib/planetscale/api/types.gen.js +2 -0
  102. package/lib/planetscale/api/types.gen.js.map +1 -0
  103. package/lib/planetscale/branch.d.ts +5 -8
  104. package/lib/planetscale/branch.d.ts.map +1 -1
  105. package/lib/planetscale/branch.js +122 -97
  106. package/lib/planetscale/branch.js.map +1 -1
  107. package/lib/planetscale/database.d.ts +24 -11
  108. package/lib/planetscale/database.d.ts.map +1 -1
  109. package/lib/planetscale/database.js +163 -118
  110. package/lib/planetscale/database.js.map +1 -1
  111. package/lib/planetscale/index.d.ts +2 -1
  112. package/lib/planetscale/index.d.ts.map +1 -1
  113. package/lib/planetscale/index.js +2 -1
  114. package/lib/planetscale/index.js.map +1 -1
  115. package/lib/planetscale/password.d.ts +7 -8
  116. package/lib/planetscale/password.d.ts.map +1 -1
  117. package/lib/planetscale/password.js +74 -57
  118. package/lib/planetscale/password.js.map +1 -1
  119. package/lib/planetscale/role.d.ts +126 -0
  120. package/lib/planetscale/role.d.ts.map +1 -0
  121. package/lib/planetscale/role.js +144 -0
  122. package/lib/planetscale/role.js.map +1 -0
  123. package/lib/planetscale/utils.d.ts +23 -7
  124. package/lib/planetscale/utils.d.ts.map +1 -1
  125. package/lib/planetscale/utils.js +169 -75
  126. package/lib/planetscale/utils.js.map +1 -1
  127. package/lib/scope.d.ts +5 -1
  128. package/lib/scope.d.ts.map +1 -1
  129. package/lib/scope.js +11 -3
  130. package/lib/scope.js.map +1 -1
  131. package/lib/secret.d.ts +0 -3
  132. package/lib/secret.d.ts.map +1 -1
  133. package/lib/secret.js +0 -2
  134. package/lib/secret.js.map +1 -1
  135. package/lib/state/cloudflare-state-store.js +3 -3
  136. package/lib/state/cloudflare-state-store.js.map +1 -1
  137. package/lib/test/bun.d.ts +2 -2
  138. package/lib/test/bun.d.ts.map +1 -1
  139. package/lib/test/vitest.d.ts +2 -2
  140. package/lib/test/vitest.d.ts.map +1 -1
  141. package/lib/test/vitest.js +4 -4
  142. package/lib/test/vitest.js.map +1 -1
  143. package/lib/util/content-type.js +2 -2
  144. package/lib/util/content-type.js.map +1 -1
  145. package/lib/util/dedent.d.ts +3 -14
  146. package/lib/util/dedent.d.ts.map +1 -1
  147. package/lib/util/dedent.js +33 -45
  148. package/lib/util/dedent.js.map +1 -1
  149. package/lib/util/memoize.d.ts.map +1 -1
  150. package/lib/util/memoize.js +11 -1
  151. package/lib/util/memoize.js.map +1 -1
  152. package/package.json +8 -3
  153. package/src/alchemy.ts +14 -6
  154. package/src/apply.ts +13 -1
  155. package/src/cloudflare/bindings.ts +3 -1
  156. package/src/cloudflare/bucket.ts +3 -1
  157. package/src/cloudflare/bundle/cloudflare-internal.ts +42 -0
  158. package/src/cloudflare/bundle/nodejs-builtin-modules.ts +14 -0
  159. package/src/cloudflare/bundle/plugin-als-external.ts +13 -0
  160. package/src/cloudflare/bundle/plugin-hybrid-node-compat.ts +29 -28
  161. package/src/cloudflare/bundle/plugin-node-compat.ts +114 -0
  162. package/src/cloudflare/bundle/plugin-wasm.ts +34 -14
  163. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  164. package/src/cloudflare/d1-local-migrations.ts +2 -2
  165. package/src/cloudflare/durable-object-namespace.ts +1 -0
  166. package/src/cloudflare/hyperdrive.ts +136 -95
  167. package/src/cloudflare/index.ts +1 -0
  168. package/src/cloudflare/next.ts +123 -0
  169. package/src/cloudflare/website.ts +88 -89
  170. package/src/cloudflare/worker-assets.ts +43 -105
  171. package/src/cloudflare/worker-bundle.ts +46 -9
  172. package/src/cloudflare/worker-metadata.ts +2 -0
  173. package/src/cloudflare/worker.ts +35 -7
  174. package/src/cloudflare/wrangler.json.ts +114 -113
  175. package/src/context.ts +5 -3
  176. package/src/destroy.ts +26 -8
  177. package/src/planetscale/api/client.gen.ts +2882 -0
  178. package/src/planetscale/api/index.ts +4 -0
  179. package/src/planetscale/api/types.gen.ts +13041 -0
  180. package/src/planetscale/branch.ts +170 -162
  181. package/src/planetscale/database.ts +263 -243
  182. package/src/planetscale/index.ts +2 -1
  183. package/src/planetscale/password.ts +89 -86
  184. package/src/planetscale/role.ts +278 -0
  185. package/src/planetscale/utils.ts +289 -128
  186. package/src/scope.ts +12 -3
  187. package/src/secret.ts +0 -4
  188. package/src/state/cloudflare-state-store.ts +4 -4
  189. package/src/test/bun.ts +2 -2
  190. package/src/test/vitest.ts +6 -6
  191. package/src/util/content-type.ts +2 -2
  192. package/src/util/dedent.ts +34 -50
  193. package/src/util/memoize.ts +13 -1
  194. package/templates/astro/alchemy.run.ts +1 -3
  195. package/templates/hono/alchemy.run.ts +0 -1
  196. package/templates/nextjs/README.md +85 -0
  197. package/templates/nextjs/_env +1 -0
  198. package/templates/nextjs/_env.example +1 -0
  199. package/templates/nextjs/_gitignore +46 -0
  200. package/templates/nextjs/alchemy.run.ts +16 -0
  201. package/templates/nextjs/eslint.config.mjs +16 -0
  202. package/templates/nextjs/next-env.d.ts +5 -0
  203. package/templates/nextjs/next.config.ts +10 -0
  204. package/templates/nextjs/open-next.config.ts +5 -0
  205. package/templates/nextjs/package.json +31 -0
  206. package/templates/nextjs/src/app/api/hello/route.ts +19 -0
  207. package/templates/nextjs/src/app/favicon.ico +0 -0
  208. package/templates/nextjs/src/app/globals.css +72 -0
  209. package/templates/nextjs/src/app/layout.tsx +19 -0
  210. package/templates/nextjs/src/app/page.tsx +55 -0
  211. package/templates/nextjs/tsconfig.json +33 -0
  212. package/templates/nextjs/types/env.d.ts +14 -0
  213. package/templates/nuxt/alchemy.run.ts +1 -3
  214. package/templates/react-router/alchemy.run.ts +1 -3
  215. package/templates/rwsdk/alchemy.run.ts +0 -2
  216. package/templates/sveltekit/alchemy.run.ts +1 -3
  217. package/templates/tanstack-start/alchemy.run.ts +1 -3
  218. package/templates/typescript/alchemy.run.ts +0 -1
  219. package/templates/vite/alchemy.run.ts +0 -1
  220. package/workers/cloudflare-state-store.js +58 -58
  221. package/lib/cloudflare/bundle/externals.d.ts +0 -3
  222. package/lib/cloudflare/bundle/externals.d.ts.map +0 -1
  223. package/lib/cloudflare/bundle/externals.js +0 -35
  224. package/lib/cloudflare/bundle/externals.js.map +0 -1
  225. package/lib/cloudflare/bundle/plugin-compat-warning.d.ts +0 -10
  226. package/lib/cloudflare/bundle/plugin-compat-warning.d.ts.map +0 -1
  227. package/lib/cloudflare/bundle/plugin-compat-warning.js +0 -67
  228. package/lib/cloudflare/bundle/plugin-compat-warning.js.map +0 -1
  229. package/lib/planetscale/api.d.ts +0 -53
  230. package/lib/planetscale/api.d.ts.map +0 -1
  231. package/lib/planetscale/api.js +0 -84
  232. package/lib/planetscale/api.js.map +0 -1
  233. package/src/cloudflare/bundle/externals.ts +0 -36
  234. package/src/cloudflare/bundle/plugin-compat-warning.ts +0 -94
  235. package/src/planetscale/api.ts +0 -115
@@ -1,17 +1,18 @@
1
1
  import type { Context } from "../context.ts";
2
2
  import { Resource } from "../resource.ts";
3
- import type { Secret } from "../secret.ts";
4
- import { PlanetScaleApi } from "./api.ts";
3
+ import type { PlanetScaleProps } from "./api/client.gen.ts";
4
+ import { PlanetScaleClient } from "./api/client.gen.ts";
5
5
  import {
6
- fixClusterSize,
7
- waitForDatabaseReady,
6
+ ensureProductionBranchClusterSize,
7
+ sanitizeClusterSize,
8
+ waitForBranchReady,
8
9
  type PlanetScaleClusterSize,
9
10
  } from "./utils.ts";
10
11
 
11
12
  /**
12
13
  * Properties for creating or updating a PlanetScale Branch
13
14
  */
14
- export interface BranchProps {
15
+ export interface BranchProps extends PlanetScaleProps {
15
16
  /**
16
17
  * The name of the branch
17
18
  *
@@ -29,11 +30,6 @@ export interface BranchProps {
29
30
  */
30
31
  databaseName: string;
31
32
 
32
- /**
33
- * PlanetScale API token (overrides environment variable)
34
- */
35
- apiKey?: Secret;
36
-
37
33
  /**
38
34
  * Whether or not the branch should be set to a production branch or not.
39
35
  */
@@ -49,6 +45,7 @@ export interface BranchProps {
49
45
 
50
46
  /**
51
47
  * The parent branch name or Branch object
48
+ * @default "main"
52
49
  */
53
50
  parentBranch?: string | Branch;
54
51
 
@@ -64,11 +61,11 @@ export interface BranchProps {
64
61
  * If provided, restores the last successful backup's schema and data to the new branch.
65
62
  * Must have restore_production_branch_backup(s) or restore_backup(s) access,
66
63
  * in addition to Data Branching being enabled for the branch.
67
- * Use 'last_successful_backup' or empty string.
64
+ * Use 'last_successful_backup' or undefined.
68
65
  *
69
66
  * Ignored if the branch already exists.
70
67
  */
71
- seedData?: string;
68
+ seedData?: "last_successful_backup";
72
69
 
73
70
  /**
74
71
  * The database cluster size is required if a backup_id is provided. If the branch is not a production branch, the cluster size MUST be "PS_DEV".
@@ -158,16 +155,16 @@ export const Branch = Resource(
158
155
  props: BranchProps,
159
156
  ): Promise<Branch> {
160
157
  const adopt = props.adopt ?? this.scope.adopt;
161
- const apiKey =
162
- props.apiKey?.unencrypted || process.env.PLANETSCALE_API_TOKEN;
163
- if (!apiKey) {
164
- throw new Error("PLANETSCALE_API_TOKEN environment variable is required");
165
- }
166
158
 
167
- const api = new PlanetScaleApi({ apiKey });
159
+ const api = new PlanetScaleClient(props);
168
160
 
169
161
  const branchName =
170
162
  props.name ?? this.output?.name ?? this.scope.createPhysicalName(id);
163
+ const parentBranchName = !props.parentBranch
164
+ ? "main"
165
+ : typeof props.parentBranch === "string"
166
+ ? props.parentBranch
167
+ : props.parentBranch.name;
171
168
 
172
169
  if (this.phase === "update" && this.output.name !== branchName) {
173
170
  // TODO(sam): maybe we don't need to replace? just branch again? or rename?
@@ -175,189 +172,200 @@ export const Branch = Resource(
175
172
  }
176
173
 
177
174
  if (this.phase === "delete") {
178
- try {
179
- if (this.output?.name) {
180
- const response = await api.delete(
181
- `/organizations/${props.organizationId}/databases/${props.databaseName}/branches/${this.output.name}`,
182
- );
175
+ if (this.output?.name) {
176
+ const response = await api.organizations.databases.branches.delete({
177
+ path: {
178
+ organization: props.organizationId,
179
+ database: props.databaseName,
180
+ name: this.output.name,
181
+ },
182
+ result: "full",
183
+ });
183
184
 
184
- if (!response.ok && response.status !== 404) {
185
- throw new Error(
186
- `Failed to delete branch: ${response.statusText} ${await response.text()}`,
187
- );
188
- }
185
+ if (response.error && response.error.status !== 404) {
186
+ throw new Error(
187
+ `Failed to delete branch: ${response.error.message}`,
188
+ {
189
+ cause: response.error,
190
+ },
191
+ );
189
192
  }
190
- } catch (error) {
191
- console.error("Error deleting branch:", error);
192
- throw error;
193
193
  }
194
194
  return this.destroy();
195
195
  }
196
196
 
197
- try {
198
- const parentBranchName = !props.parentBranch
199
- ? "main"
200
- : typeof props.parentBranch === "string"
201
- ? props.parentBranch
202
- : props.parentBranch.name;
197
+ if (typeof props.parentBranch !== "string" && props.parentBranch) {
198
+ await waitForBranchReady(
199
+ api,
200
+ props.organizationId,
201
+ props.databaseName,
202
+ props.parentBranch.name,
203
+ );
204
+ }
203
205
 
204
- if (typeof props.parentBranch !== "string" && props.parentBranch) {
205
- await waitForDatabaseReady(
206
- api,
207
- props.organizationId,
208
- props.databaseName,
209
- props.parentBranch.name,
206
+ // Check if branch exists
207
+ const getResponse = await api.organizations.databases.branches.get({
208
+ path: {
209
+ organization: props.organizationId,
210
+ database: props.databaseName,
211
+ name: branchName,
212
+ },
213
+ result: "full",
214
+ });
215
+
216
+ if (getResponse.error && getResponse.error.status !== 404) {
217
+ // Error getting branch
218
+ throw new Error(`Failed to get branch: ${getResponse.error.message}`, {
219
+ cause: getResponse.error,
220
+ });
221
+ }
222
+
223
+ if (getResponse.data) {
224
+ // Branch exists
225
+ if (!adopt) {
226
+ throw new Error(
227
+ `Branch ${branchName} already exists and adopt is false`,
210
228
  );
211
229
  }
212
230
 
213
- // Check if branch exists
214
- const getResponse = await api.get(
215
- `/organizations/${props.organizationId}/databases/${props.databaseName}/branches/${branchName}`,
216
- );
231
+ const data = getResponse.data;
232
+ const currentParentBranch = data.parent_branch || "main";
217
233
 
218
- if (!getResponse.ok && getResponse.status !== 404) {
219
- // Error getting branch
234
+ // Check immutable properties
235
+ if (props.parentBranch && parentBranchName !== currentParentBranch) {
220
236
  throw new Error(
221
- `Failed to get branch: ${getResponse.statusText} ${await getResponse.text()}`,
237
+ `Cannot change parent branch from ${currentParentBranch} to ${parentBranchName}`,
222
238
  );
223
239
  }
224
-
225
- if (getResponse.ok) {
226
- // Branch exists
227
- if (!adopt) {
228
- throw new Error(
229
- `Branch ${branchName} already exists and adopt is false`,
230
- );
231
- }
232
-
233
- const data = await getResponse.json<any>();
234
- const currentParentBranch = data.parent_branch || "main";
235
-
236
- // Check immutable properties
237
- if (props.parentBranch && parentBranchName !== currentParentBranch) {
238
- throw new Error(
239
- `Cannot change parent branch from ${currentParentBranch} to ${parentBranchName}`,
240
- );
241
- }
242
- if (this.output?.backupId) {
243
- console.warn(
244
- "BackupID is set, but branch already exists, so it will be ignored",
245
- );
246
- }
247
-
248
- if (this.output?.seedData) {
249
- console.warn(
250
- "SeedData is set, but branch already exists, so it will be ignored",
251
- );
252
- }
253
-
254
- // Update mutable properties if they've changed
255
- if (props.safeMigrations !== undefined) {
256
- const safeMigrationsResponse = await api[
257
- props.safeMigrations ? "post" : "delete"
258
- ](
259
- `/organizations/${props.organizationId}/databases/${props.databaseName}/branches/${branchName}/safe-migrations`,
260
- );
261
-
262
- if (!safeMigrationsResponse.ok) {
263
- throw new Error(
264
- `Failed to ${props.safeMigrations ? "enable" : "disable"} safe migrations: ${safeMigrationsResponse.statusText}`,
265
- );
266
- }
267
- }
268
-
269
- if (props.clusterSize && data.cluster_size !== props.clusterSize) {
270
- const clusterResponse = await api.patch(
271
- `/organizations/${props.organizationId}/databases/${props.databaseName}/branches/${branchName}/cluster`,
272
- {
273
- cluster_size: props.clusterSize,
274
- },
275
- );
276
-
277
- if (!clusterResponse.ok) {
278
- throw new Error(
279
- `Failed to update cluster size: ${clusterResponse.statusText}`,
280
- );
281
- }
282
- }
283
-
284
- return this({
285
- ...props,
286
- name: branchName,
287
- parentBranch: currentParentBranch,
288
- createdAt: data.created_at,
289
- updatedAt: data.updated_at,
290
- htmlUrl: data.html_url,
291
- });
240
+ if (this.output?.backupId) {
241
+ console.warn(
242
+ "BackupID is set, but branch already exists, so it will be ignored",
243
+ );
292
244
  }
293
- await waitForDatabaseReady(api, props.organizationId, props.databaseName);
294
245
 
295
- // Branch doesn't exist, create it
296
- const createResponse = await api.post(
297
- `/organizations/${props.organizationId}/databases/${props.databaseName}/branches`,
298
- {
299
- name: branchName,
300
- parent_branch: parentBranchName,
301
- backup_id: props.backupId,
302
- seed_data: props.seedData,
303
- cluster_size: props.clusterSize,
304
- },
305
- );
306
-
307
- if (!createResponse.ok) {
308
- throw new Error(
309
- `Failed to create branch: ${createResponse.statusText} ${await createResponse.text()}`,
246
+ if (this.output?.seedData) {
247
+ console.warn(
248
+ "SeedData is set, but branch already exists, so it will be ignored",
310
249
  );
311
250
  }
312
251
 
313
- const data = await createResponse.json<any>();
314
-
315
- // Handle safe migrations if specified
252
+ // Update mutable properties if they've changed
316
253
  if (props.safeMigrations !== undefined) {
317
- // We can't change the migrations mode if the database is not ready
318
- await waitForDatabaseReady(
319
- api,
320
- props.organizationId,
321
- props.databaseName,
322
- branchName,
323
- );
324
- const safeMigrationsResponse = await api[
254
+ await api.organizations.databases.branches.safeMigrations[
325
255
  props.safeMigrations ? "post" : "delete"
326
- ](
327
- `/organizations/${props.organizationId}/databases/${props.databaseName}/branches/${branchName}/safe-migrations`,
328
- );
256
+ ]({
257
+ path: {
258
+ organization: props.organizationId,
259
+ database: props.databaseName,
260
+ name: branchName,
261
+ },
262
+ });
263
+ }
329
264
 
330
- if (!safeMigrationsResponse.ok) {
265
+ const clusterSize = props.clusterSize
266
+ ? sanitizeClusterSize({
267
+ size: props.clusterSize,
268
+ kind: data.kind,
269
+ arch: data.cluster_architecture === "aarch64" ? "arm" : "x86",
270
+ region: data.region.slug,
271
+ })
272
+ : undefined;
273
+ if (clusterSize && data.cluster_name !== clusterSize) {
274
+ if (!props.isProduction) {
331
275
  throw new Error(
332
- `Failed to ${props.safeMigrations ? "enable" : "disable"} safe migrations: ${safeMigrationsResponse.statusText} : ${await safeMigrationsResponse.text()}`,
276
+ `Cannot change cluster size of non-production branch ${branchName}`,
333
277
  );
334
278
  }
335
- }
336
-
337
- // Handle cluster size update if specified
338
- if (props.clusterSize) {
339
- // Wait for database to be ready before modifying cluster size
340
- await fixClusterSize(
279
+ await ensureProductionBranchClusterSize(
341
280
  api,
342
281
  props.organizationId,
343
282
  props.databaseName,
344
283
  branchName,
345
- props.clusterSize,
346
- true,
284
+ data.kind,
285
+ clusterSize,
347
286
  );
348
287
  }
349
288
 
350
289
  return this({
351
290
  ...props,
352
291
  name: branchName,
353
- parentBranch: data.parent_branch,
292
+ parentBranch: currentParentBranch,
354
293
  createdAt: data.created_at,
355
294
  updatedAt: data.updated_at,
356
295
  htmlUrl: data.html_url,
357
296
  });
358
- } catch (error) {
359
- console.error("Error managing branch:", error);
360
- throw error;
361
297
  }
298
+ let clusterSize: string | undefined;
299
+ const parent = await waitForBranchReady(
300
+ api,
301
+ props.organizationId,
302
+ props.databaseName,
303
+ parentBranchName,
304
+ );
305
+ if (props.clusterSize) {
306
+ clusterSize = sanitizeClusterSize({
307
+ size: props.clusterSize,
308
+ kind: parent.kind,
309
+ arch: parent.cluster_architecture === "aarch64" ? "arm" : "x86",
310
+ region: parent.region.slug,
311
+ });
312
+ }
313
+
314
+ // Branch doesn't exist, create it
315
+ const data = await api.organizations.databases.branches.post({
316
+ path: {
317
+ organization: props.organizationId,
318
+ database: props.databaseName,
319
+ },
320
+ body: {
321
+ name: branchName,
322
+ parent_branch: parentBranchName,
323
+ backup_id: props.backupId,
324
+ seed_data: props.seedData,
325
+ // This is ignored unless props.backupId is provided
326
+ cluster_size: clusterSize,
327
+ },
328
+ });
329
+
330
+ // Handle safe migrations if specified
331
+ if (props.safeMigrations !== undefined) {
332
+ // We can't change the migrations mode if the branch is not ready
333
+ await waitForBranchReady(
334
+ api,
335
+ props.organizationId,
336
+ props.databaseName,
337
+ branchName,
338
+ );
339
+ await api.organizations.databases.branches.safeMigrations[
340
+ props.safeMigrations ? "post" : "delete"
341
+ ]({
342
+ path: {
343
+ organization: props.organizationId,
344
+ database: props.databaseName,
345
+ name: branchName,
346
+ },
347
+ });
348
+ }
349
+
350
+ // Handle cluster size update if specified
351
+ if (clusterSize) {
352
+ await ensureProductionBranchClusterSize(
353
+ api,
354
+ props.organizationId,
355
+ props.databaseName,
356
+ branchName,
357
+ data.kind,
358
+ clusterSize,
359
+ );
360
+ }
361
+
362
+ return this({
363
+ ...props,
364
+ name: branchName,
365
+ parentBranch: data.parent_branch,
366
+ createdAt: data.created_at,
367
+ updatedAt: data.updated_at,
368
+ htmlUrl: data.html_url,
369
+ });
362
370
  },
363
371
  );