alchemy 0.71.0 → 0.72.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 (97) hide show
  1. package/README.md +1 -1
  2. package/bin/alchemy.js +119 -44
  3. package/bin/services/execute-alchemy.ts +12 -3
  4. package/bin/trpc.ts +12 -12
  5. package/lib/alchemy.d.ts.map +1 -1
  6. package/lib/alchemy.js +21 -6
  7. package/lib/alchemy.js.map +1 -1
  8. package/lib/apply.js +1 -1
  9. package/lib/apply.js.map +1 -1
  10. package/lib/cloudflare/d1-database.d.ts +1 -1
  11. package/lib/cloudflare/d1-database.d.ts.map +1 -1
  12. package/lib/cloudflare/d1-database.js +1 -1
  13. package/lib/cloudflare/d1-database.js.map +1 -1
  14. package/lib/cloudflare/health-check.d.ts +261 -0
  15. package/lib/cloudflare/health-check.d.ts.map +1 -0
  16. package/lib/cloudflare/health-check.js +277 -0
  17. package/lib/cloudflare/health-check.js.map +1 -0
  18. package/lib/cloudflare/index.d.ts +1 -0
  19. package/lib/cloudflare/index.d.ts.map +1 -1
  20. package/lib/cloudflare/index.js +1 -0
  21. package/lib/cloudflare/index.js.map +1 -1
  22. package/lib/cloudflare/wrangler.json.d.ts +8 -8
  23. package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
  24. package/lib/cloudflare/wrangler.json.js +9 -11
  25. package/lib/cloudflare/wrangler.json.js.map +1 -1
  26. package/lib/cloudflare/zone.js +1 -1
  27. package/lib/destroy.d.ts.map +1 -1
  28. package/lib/destroy.js +27 -0
  29. package/lib/destroy.js.map +1 -1
  30. package/lib/docker/image.js +1 -1
  31. package/lib/docker/image.js.map +1 -1
  32. package/lib/planetscale/api.d.ts +0 -4
  33. package/lib/planetscale/api.d.ts.map +1 -1
  34. package/lib/planetscale/api.js.map +1 -1
  35. package/lib/planetscale/branch.d.ts +12 -11
  36. package/lib/planetscale/branch.d.ts.map +1 -1
  37. package/lib/planetscale/branch.js +40 -23
  38. package/lib/planetscale/branch.js.map +1 -1
  39. package/lib/planetscale/database.d.ts +10 -5
  40. package/lib/planetscale/database.d.ts.map +1 -1
  41. package/lib/planetscale/database.js +30 -18
  42. package/lib/planetscale/database.js.map +1 -1
  43. package/lib/planetscale/index.d.ts +1 -0
  44. package/lib/planetscale/index.d.ts.map +1 -1
  45. package/lib/planetscale/index.js +1 -0
  46. package/lib/planetscale/index.js.map +1 -1
  47. package/lib/planetscale/organization.d.ts +5 -0
  48. package/lib/planetscale/organization.d.ts.map +1 -0
  49. package/lib/planetscale/organization.js +11 -0
  50. package/lib/planetscale/organization.js.map +1 -0
  51. package/lib/planetscale/password.d.ts +10 -9
  52. package/lib/planetscale/password.d.ts.map +1 -1
  53. package/lib/planetscale/password.js +28 -13
  54. package/lib/planetscale/password.js.map +1 -1
  55. package/lib/planetscale/role.d.ts +2 -1
  56. package/lib/planetscale/role.d.ts.map +1 -1
  57. package/lib/planetscale/role.js +11 -8
  58. package/lib/planetscale/role.js.map +1 -1
  59. package/lib/resource.js +1 -1
  60. package/lib/resource.js.map +1 -1
  61. package/lib/scope.d.ts +6 -1
  62. package/lib/scope.d.ts.map +1 -1
  63. package/lib/scope.js +16 -15
  64. package/lib/scope.js.map +1 -1
  65. package/lib/state/instrumented-state-store.js +1 -1
  66. package/lib/state/instrumented-state-store.js.map +1 -1
  67. package/lib/util/cli-args.d.ts +3 -0
  68. package/lib/util/cli-args.d.ts.map +1 -0
  69. package/lib/util/cli-args.js +6 -0
  70. package/lib/util/cli-args.js.map +1 -0
  71. package/lib/util/telemetry.d.ts +1 -0
  72. package/lib/util/telemetry.d.ts.map +1 -1
  73. package/lib/util/telemetry.js +47 -2
  74. package/lib/util/telemetry.js.map +1 -1
  75. package/package.json +10 -8
  76. package/src/alchemy.ts +25 -11
  77. package/src/apply.ts +1 -1
  78. package/src/cloudflare/d1-database.ts +2 -2
  79. package/src/cloudflare/health-check.ts +617 -0
  80. package/src/cloudflare/index.ts +1 -0
  81. package/src/cloudflare/wrangler.json.ts +21 -23
  82. package/src/cloudflare/zone.ts +1 -1
  83. package/src/destroy.ts +32 -0
  84. package/src/docker/image.ts +1 -1
  85. package/src/planetscale/api.ts +0 -4
  86. package/src/planetscale/branch.ts +51 -35
  87. package/src/planetscale/database.ts +40 -20
  88. package/src/planetscale/index.ts +1 -0
  89. package/src/planetscale/organization.ts +17 -0
  90. package/src/planetscale/password.ts +34 -16
  91. package/src/planetscale/role.ts +16 -9
  92. package/src/resource.ts +1 -1
  93. package/src/scope.ts +25 -15
  94. package/src/state/instrumented-state-store.ts +1 -1
  95. package/src/util/cli-args.ts +11 -0
  96. package/src/util/telemetry.ts +68 -2
  97. package/workers/tunnel-proxy.js +1 -1
@@ -4,7 +4,6 @@ import type { Context } from "../context.ts";
4
4
  import { Resource } from "../resource.ts";
5
5
  import { Scope } from "../scope.ts";
6
6
  import { isSecret } from "../secret.ts";
7
- import { unencryptSecrets } from "./util/filter-env-bindings.ts";
8
7
  import { assertNever } from "../util/assert-never.ts";
9
8
  import type { Bindings, WorkerBindingRateLimit } from "./bindings.ts";
10
9
  import type { R2BucketJurisdiction } from "./bucket.ts";
@@ -12,6 +11,7 @@ import type { DurableObjectNamespace } from "./durable-object-namespace.ts";
12
11
  import type { EventSource } from "./event-source.ts";
13
12
  import { isQueueEventSource } from "./event-source.ts";
14
13
  import { isQueue } from "./queue.ts";
14
+ import { unencryptSecrets } from "./util/filter-env-bindings.ts";
15
15
  import { isWorker, type Worker, type WorkerProps } from "./worker.ts";
16
16
 
17
17
  type WranglerJsonRateLimit = Omit<WorkerBindingRateLimit, "type"> & {
@@ -301,7 +301,7 @@ export interface WranglerJsonSpec {
301
301
  */
302
302
  ai?: {
303
303
  binding: string;
304
- experimental_remote?: boolean;
304
+ remote?: boolean;
305
305
  };
306
306
 
307
307
  /**
@@ -309,7 +309,7 @@ export interface WranglerJsonSpec {
309
309
  */
310
310
  browser?: {
311
311
  binding: string;
312
- experimental_remote?: boolean;
312
+ remote?: boolean;
313
313
  };
314
314
 
315
315
  /**
@@ -317,7 +317,7 @@ export interface WranglerJsonSpec {
317
317
  */
318
318
  images?: {
319
319
  binding: string;
320
- experimental_remote?: boolean;
320
+ remote?: boolean;
321
321
  };
322
322
 
323
323
  /**
@@ -330,7 +330,7 @@ export interface WranglerJsonSpec {
330
330
  * The ID of the KV namespace used during `wrangler dev`
331
331
  */
332
332
  preview_id?: string;
333
- experimental_remote?: boolean;
333
+ remote?: boolean;
334
334
  }[];
335
335
 
336
336
  /**
@@ -355,7 +355,7 @@ export interface WranglerJsonSpec {
355
355
  * The preview name of this R2 bucket at the edge.
356
356
  */
357
357
  preview_bucket_name?: string;
358
- experimental_remote?: boolean;
358
+ remote?: boolean;
359
359
  }[];
360
360
 
361
361
  /**
@@ -396,7 +396,7 @@ export interface WranglerJsonSpec {
396
396
  vectorize?: {
397
397
  binding: string;
398
398
  index_name: string;
399
- experimental_remote?: boolean;
399
+ remote?: boolean;
400
400
  }[];
401
401
 
402
402
  /**
@@ -416,7 +416,7 @@ export interface WranglerJsonSpec {
416
416
  * The ID of the D1 database used during `wrangler dev`
417
417
  */
418
418
  preview_database_id?: string;
419
- experimental_remote?: boolean;
419
+ remote?: boolean;
420
420
  }[];
421
421
 
422
422
  /**
@@ -499,7 +499,7 @@ export interface WranglerJsonSpec {
499
499
  dispatch_namespaces?: {
500
500
  binding: string;
501
501
  namespace: string;
502
- experimental_remote?: boolean;
502
+ remote?: boolean;
503
503
  }[];
504
504
 
505
505
  /**
@@ -559,7 +559,7 @@ function processBindings(
559
559
  binding: string;
560
560
  id: string;
561
561
  preview_id: string;
562
- experimental_remote?: boolean;
562
+ remote?: boolean;
563
563
  }[] = [];
564
564
  const durableObjects: {
565
565
  name: string;
@@ -592,7 +592,7 @@ function processBindings(
592
592
  database_name: string;
593
593
  migrations_dir?: string;
594
594
  preview_database_id: string;
595
- experimental_remote?: boolean;
595
+ remote?: boolean;
596
596
  }[] = [];
597
597
  const queues: {
598
598
  producers: { queue: string; binding: string }[];
@@ -608,7 +608,7 @@ function processBindings(
608
608
  const vectorizeIndexes: {
609
609
  binding: string;
610
610
  index_name: string;
611
- experimental_remote?: boolean;
611
+ remote?: boolean;
612
612
  }[] = [];
613
613
  const analyticsEngineDatasets: { binding: string; dataset: string }[] = [];
614
614
  const hyperdrive: {
@@ -625,7 +625,7 @@ function processBindings(
625
625
  const dispatchNamespaces: {
626
626
  binding: string;
627
627
  namespace: string;
628
- experimental_remote?: boolean;
628
+ remote?: boolean;
629
629
  }[] = [];
630
630
  const unsafeBindings: WranglerJsonRateLimit[] = [];
631
631
  const containers: {
@@ -693,9 +693,7 @@ function processBindings(
693
693
  binding: bindingName,
694
694
  id: id,
695
695
  preview_id: id,
696
- ...("dev" in binding && binding.dev?.remote
697
- ? { experimental_remote: true }
698
- : {}),
696
+ ...("dev" in binding && binding.dev?.remote ? { remote: true } : {}),
699
697
  });
700
698
  } else if (
701
699
  typeof binding === "object" &&
@@ -725,7 +723,7 @@ function processBindings(
725
723
  preview_bucket_name: name,
726
724
  jurisdiction:
727
725
  binding.jurisdiction === "default" ? undefined : binding.jurisdiction,
728
- ...(binding.dev?.remote ? { experimental_remote: true } : {}),
726
+ ...(binding.dev?.remote ? { remote: true } : {}),
729
727
  });
730
728
  } else if (binding.type === "secret") {
731
729
  // Secret binding
@@ -753,7 +751,7 @@ function processBindings(
753
751
  database_name: binding.name,
754
752
  migrations_dir: binding.migrationsDir,
755
753
  preview_database_id: id,
756
- ...(binding.dev?.remote ? { experimental_remote: true } : {}),
754
+ ...(binding.dev?.remote ? { remote: true } : {}),
757
755
  });
758
756
  } else if (binding.type === "queue") {
759
757
  const id =
@@ -769,7 +767,7 @@ function processBindings(
769
767
  binding: bindingName,
770
768
  index_name: binding.name,
771
769
  // https://developers.cloudflare.com/workers/development-testing/#recommended-remote-bindings
772
- experimental_remote: true,
770
+ remote: true,
773
771
  });
774
772
  } else if (binding.type === "browser") {
775
773
  if (spec.browser) {
@@ -778,7 +776,7 @@ function processBindings(
778
776
  spec.browser = {
779
777
  binding: bindingName,
780
778
  // https://developers.cloudflare.com/workers/development-testing/#recommended-remote-bindings
781
- experimental_remote: true,
779
+ remote: true,
782
780
  };
783
781
  } else if (binding.type === "ai") {
784
782
  if (spec.ai) {
@@ -787,7 +785,7 @@ function processBindings(
787
785
  spec.ai = {
788
786
  binding: bindingName,
789
787
  // https://developers.cloudflare.com/workers/development-testing/#recommended-remote-bindings
790
- experimental_remote: true,
788
+ remote: true,
791
789
  };
792
790
  } else if (binding.type === "images") {
793
791
  if (spec.images) {
@@ -796,7 +794,7 @@ function processBindings(
796
794
  spec.images = {
797
795
  binding: bindingName,
798
796
  // https://developers.cloudflare.com/workers/development-testing/#recommended-remote-bindings
799
- experimental_remote: true,
797
+ remote: true,
800
798
  };
801
799
  } else if (binding.type === "analytics_engine") {
802
800
  analyticsEngineDatasets.push({
@@ -837,7 +835,7 @@ function processBindings(
837
835
  dispatchNamespaces.push({
838
836
  binding: bindingName,
839
837
  namespace: binding.namespaceName,
840
- experimental_remote: true,
838
+ remote: true,
841
839
  });
842
840
  } else if (binding.type === "ratelimit") {
843
841
  unsafeBindings.push({
@@ -414,7 +414,7 @@ export const Zone = Resource(
414
414
 
415
415
  // Add a small delay to ensure settings are propagated
416
416
  // TODO(michael): do we need this?
417
- // https://github.com/sam-goodwin/alchemy/issues/681
417
+ // https://github.com/alchemy-run/alchemy/issues/681
418
418
  await new Promise((resolve) => setTimeout(resolve, 2000));
419
419
 
420
420
  // Update Bot Management configuration if provided
package/src/destroy.ts CHANGED
@@ -14,6 +14,7 @@ import { isScope, type PendingDeletions, Scope } from "./scope.ts";
14
14
  import type { State } from "./state.ts";
15
15
  import { formatFQN } from "./util/cli.ts";
16
16
  import { logger } from "./util/logger.ts";
17
+ import { createAndSendEvent } from "./util/telemetry.ts";
17
18
 
18
19
  export function isDestroyedSignal(error: any): error is DestroyedSignal {
19
20
  return error instanceof Error && (error as any).kind === "DestroyedSignal";
@@ -109,6 +110,7 @@ export async function destroy(
109
110
  logger.warn(`Resource "${instance[ResourceFQN]}" has no scope`);
110
111
  }
111
112
  const quiet = options?.quiet ?? scope.quiet;
113
+ const start = performance.now();
112
114
 
113
115
  try {
114
116
  if (!quiet && !options?.noop) {
@@ -122,6 +124,15 @@ export async function destroy(
122
124
  });
123
125
  }
124
126
 
127
+ await createAndSendEvent({
128
+ event: "resource.start",
129
+ resource: instance[ResourceKind],
130
+ status: "deleting",
131
+ phase: "destroy",
132
+ duration: performance.now() - start,
133
+ replaced: !!options?.replace,
134
+ });
135
+
125
136
  let state: State;
126
137
  let props: ResourceProps | undefined;
127
138
  if (options?.replace) {
@@ -224,7 +235,28 @@ export async function destroy(
224
235
  status: "success",
225
236
  });
226
237
  }
238
+
239
+ await createAndSendEvent({
240
+ event: "resource.success",
241
+ resource: instance[ResourceKind],
242
+ status: "deleted",
243
+ phase: "destroy",
244
+ duration: performance.now() - start,
245
+ replaced: !!options?.replace,
246
+ });
227
247
  } catch (error) {
248
+ let errorToSend = error instanceof Error ? error : new Error(String(error));
249
+ await createAndSendEvent(
250
+ {
251
+ event: "resource.error",
252
+ resource: instance[ResourceKind],
253
+ duration: performance.now() - start,
254
+ phase: "destroy",
255
+ status: "deleting",
256
+ replaced: !!options?.replace,
257
+ },
258
+ errorToSend,
259
+ );
228
260
  logger.error(error);
229
261
  throw error;
230
262
  }
@@ -190,7 +190,7 @@ export const Image = Resource(
190
190
 
191
191
  // Add build arguments
192
192
  for (const [key, value] of Object.entries(buildOptions)) {
193
- buildArgs.push("--build-arg", `${key}="${value}"`);
193
+ buildArgs.push("--build-arg", `${key}=${value}`);
194
194
  }
195
195
 
196
196
  buildArgs.push("-f", dockerfile);
@@ -28,10 +28,6 @@ export interface PlanetScaleProps {
28
28
  * @deprecated Use serviceTokenId and serviceToken instead.
29
29
  */
30
30
  apiKey?: Secret;
31
- /**
32
- * The organization to use for authentication. Defaults to the value of the PLANETSCALE_ORGANIZATION or PLANETSCALE_ORG_ID environment variable.
33
- */
34
- organizationId?: string;
35
31
  }
36
32
 
37
33
  export class PlanetScaleError extends Error {
@@ -1,6 +1,7 @@
1
1
  import type { Context } from "../context.ts";
2
2
  import { Resource } from "../resource.ts";
3
3
  import { createPlanetScaleClient, type PlanetScaleProps } from "./api.ts";
4
+ import type { Database } from "./database.ts";
4
5
  import {
5
6
  ensureProductionBranchClusterSize,
6
7
  sanitizeClusterSize,
@@ -20,14 +21,14 @@ export interface BranchProps extends PlanetScaleProps {
20
21
  name?: string;
21
22
 
22
23
  /**
23
- * The organization ID
24
+ * The organization name. Automatically inferred from the database if the database is provided as an object.
24
25
  */
25
- organizationId: string;
26
+ organization?: string;
26
27
 
27
28
  /**
28
29
  * The database name
29
30
  */
30
- databaseName: string;
31
+ database: string | Database;
31
32
 
32
33
  /**
33
34
  * Whether or not the branch should be set to a production branch or not.
@@ -114,8 +115,8 @@ export interface Branch extends BranchProps {
114
115
  * // Create a branch from 'main'
115
116
  * const branch = await Branch("feature-123", {
116
117
  * name: "feature-123",
117
- * organizationId: "my-org",
118
- * databaseName: "my-database",
118
+ * organization: "my-org",
119
+ * database: "my-database",
119
120
  * parentBranch: "main"
120
121
  * });
121
122
  *
@@ -123,15 +124,15 @@ export interface Branch extends BranchProps {
123
124
  * // Create a branch from another branch object
124
125
  * const parentBranch = await Branch("staging", {
125
126
  * name: "staging",
126
- * organizationId: "my-org",
127
- * databaseName: "my-database",
127
+ * organization: "my-org",
128
+ * database: "my-database",
128
129
  * parentBranch: "main"
129
130
  * });
130
131
  *
131
132
  * const featureBranch = await Branch("feature-456", {
132
133
  * name: "feature-456",
133
- * organizationId: "my-org",
134
- * databaseName: "my-database",
134
+ * organization: "my-org",
135
+ * database: "my-database",
135
136
  * parentBranch: parentBranch // Using Branch object instead of string
136
137
  * });
137
138
  *
@@ -139,8 +140,8 @@ export interface Branch extends BranchProps {
139
140
  * // Create a branch from a backup
140
141
  * const branch = await Branch("restored-branch", {
141
142
  * name: "restored-branch",
142
- * organizationId: "my-org",
143
- * databaseName: "my-database",
143
+ * organization: "my-org",
144
+ * database: "my-database",
144
145
  * parentBranch: "main",
145
146
  * backupId: "backup-123",
146
147
  * clusterSize: "PS_10"
@@ -164,6 +165,26 @@ export const Branch = Resource(
164
165
  : typeof props.parentBranch === "string"
165
166
  ? props.parentBranch
166
167
  : props.parentBranch.name;
168
+ const organization =
169
+ // @ts-expect-error - organizationId is a legacy thing, we keep this so we can destroy
170
+ this.output?.organizationId ??
171
+ props.organization ??
172
+ (typeof props.database !== "string"
173
+ ? props.database.organization
174
+ : (process.env.PLANETSCALE_ORGANIZATION ??
175
+ process.env.PLANETSCALE_ORG_ID));
176
+ const database =
177
+ // @ts-expect-error - databaseName is a legacy thing, we keep this so we can destroy
178
+ this.output?.databaseName ??
179
+ (typeof props.database === "string"
180
+ ? props.database
181
+ : props.database.name);
182
+
183
+ if (!organization) {
184
+ throw new Error(
185
+ "PlanetScale organization is required. Please set the `organization` property or the `PLANETSCALE_ORGANIZATION` environment variable.",
186
+ );
187
+ }
167
188
 
168
189
  if (this.phase === "update" && this.output.name !== branchName) {
169
190
  // TODO(sam): maybe we don't need to replace? just branch again? or rename?
@@ -174,8 +195,8 @@ export const Branch = Resource(
174
195
  if (this.output?.name) {
175
196
  const response = await api.deleteBranch({
176
197
  path: {
177
- organization: props.organizationId,
178
- database: props.databaseName,
198
+ organization,
199
+ database,
179
200
  name: this.output.name,
180
201
  },
181
202
  throwOnError: false,
@@ -196,8 +217,8 @@ export const Branch = Resource(
196
217
  if (typeof props.parentBranch !== "string" && props.parentBranch) {
197
218
  await waitForBranchReady(
198
219
  api,
199
- props.organizationId,
200
- props.databaseName,
220
+ organization,
221
+ database,
201
222
  props.parentBranch.name,
202
223
  );
203
224
  }
@@ -205,8 +226,8 @@ export const Branch = Resource(
205
226
  // Check if branch exists
206
227
  const getResponse = await api.getBranch({
207
228
  path: {
208
- organization: props.organizationId,
209
- database: props.databaseName,
229
+ organization,
230
+ database,
210
231
  name: branchName,
211
232
  },
212
233
  throwOnError: false,
@@ -256,8 +277,8 @@ export const Branch = Resource(
256
277
  : "disableSafeMigrations"
257
278
  ]({
258
279
  path: {
259
- organization: props.organizationId,
260
- database: props.databaseName,
280
+ organization,
281
+ database,
261
282
  name: branchName,
262
283
  },
263
284
  });
@@ -279,8 +300,8 @@ export const Branch = Resource(
279
300
  }
280
301
  await ensureProductionBranchClusterSize(
281
302
  api,
282
- props.organizationId,
283
- props.databaseName,
303
+ organization,
304
+ database,
284
305
  branchName,
285
306
  data.kind,
286
307
  clusterSize,
@@ -299,8 +320,8 @@ export const Branch = Resource(
299
320
  let clusterSize: string | undefined;
300
321
  const parent = await waitForBranchReady(
301
322
  api,
302
- props.organizationId,
303
- props.databaseName,
323
+ organization,
324
+ database,
304
325
  parentBranchName,
305
326
  );
306
327
  if (props.clusterSize) {
@@ -315,8 +336,8 @@ export const Branch = Resource(
315
336
  // Branch doesn't exist, create it
316
337
  const { data } = await api.createBranch({
317
338
  path: {
318
- organization: props.organizationId,
319
- database: props.databaseName,
339
+ organization,
340
+ database,
320
341
  },
321
342
  body: {
322
343
  name: branchName,
@@ -331,18 +352,13 @@ export const Branch = Resource(
331
352
  // Handle safe migrations if specified
332
353
  if (props.safeMigrations !== undefined) {
333
354
  // We can't change the migrations mode if the branch is not ready
334
- await waitForBranchReady(
335
- api,
336
- props.organizationId,
337
- props.databaseName,
338
- branchName,
339
- );
355
+ await waitForBranchReady(api, organization, database, branchName);
340
356
  await api[
341
357
  props.safeMigrations ? "enableSafeMigrations" : "disableSafeMigrations"
342
358
  ]({
343
359
  path: {
344
- organization: props.organizationId,
345
- database: props.databaseName,
360
+ organization,
361
+ database,
346
362
  name: branchName,
347
363
  },
348
364
  });
@@ -352,8 +368,8 @@ export const Branch = Resource(
352
368
  if (clusterSize) {
353
369
  await ensureProductionBranchClusterSize(
354
370
  api,
355
- props.organizationId,
356
- props.databaseName,
371
+ organization,
372
+ database,
357
373
  branchName,
358
374
  data.kind,
359
375
  clusterSize,
@@ -18,9 +18,10 @@ interface BaseDatabaseProps extends PlanetScaleProps {
18
18
  name?: string;
19
19
 
20
20
  /**
21
- * The organization ID where the database will be created
21
+ * The organization name where the database will be created
22
+ * @default process.env.PLANETSCALE_ORGANIZATION
22
23
  */
23
- organizationId: string;
24
+ organization?: string;
24
25
 
25
26
  /**
26
27
  * Whether to adopt the database if it already exists in Planetscale
@@ -157,6 +158,11 @@ export type Database = DatabaseProps & {
157
158
  * HTML URL to access the database
158
159
  */
159
160
  htmlUrl: string;
161
+
162
+ /**
163
+ * The organization of the database
164
+ */
165
+ organization: string;
160
166
  };
161
167
 
162
168
  /**
@@ -166,7 +172,7 @@ export type Database = DatabaseProps & {
166
172
  * // Create a basic database in a specific organization
167
173
  * const db = await Database("my-app-db", {
168
174
  * name: "my-app-db",
169
- * organizationId: "my-org",
175
+ * organization: "my-org",
170
176
  * clusterSize: "PS_10"
171
177
  * });
172
178
  *
@@ -174,7 +180,7 @@ export type Database = DatabaseProps & {
174
180
  * // Create a database with specific region and settings
175
181
  * const db = await Database("my-app-db", {
176
182
  * name: "my-app-db",
177
- * organizationId: "my-org",
183
+ * organization: "my-org",
178
184
  * region: {
179
185
  * slug: "us-east"
180
186
  * },
@@ -188,7 +194,7 @@ export type Database = DatabaseProps & {
188
194
  * // Create a database with custom API key
189
195
  * const db = await Database("my-app-db", {
190
196
  * name: "my-app-db",
191
- * organizationId: "my-org",
197
+ * organization: "my-org",
192
198
  * apiKey: alchemy.secret(process.env.CUSTOM_PLANETSCALE_TOKEN),
193
199
  * clusterSize: "PS_10"
194
200
  * });
@@ -210,11 +216,22 @@ export const Database = Resource(
210
216
  arch: props.arch,
211
217
  region: props.region?.slug,
212
218
  });
219
+ const organization =
220
+ // @ts-expect-error - organizationId is a legacy thing, we keep this so we can destroy
221
+ this.output?.organizationId ??
222
+ props.organization ??
223
+ process.env.PLANETSCALE_ORGANIZATION ??
224
+ process.env.PLANETSCALE_ORG_ID;
225
+ if (!organization) {
226
+ throw new Error(
227
+ "PlanetScale organization is required. Please set the `organization` property or the `PLANETSCALE_ORGANIZATION` environment variable.",
228
+ );
229
+ }
213
230
 
214
231
  if (this.phase === "update" && this.output.name !== databaseName) {
215
232
  await api.updateDatabaseSettings({
216
233
  path: {
217
- organization: props.organizationId,
234
+ organization,
218
235
  name: this.output.name,
219
236
  },
220
237
  body: { new_name: databaseName },
@@ -225,7 +242,7 @@ export const Database = Resource(
225
242
  if (this.output?.name) {
226
243
  const response = await api.deleteDatabase({
227
244
  path: {
228
- organization: props.organizationId,
245
+ organization,
229
246
  name: this.output.name,
230
247
  },
231
248
  throwOnError: false,
@@ -243,7 +260,7 @@ export const Database = Resource(
243
260
  // Check if database exists
244
261
  const getResponse = await api.getDatabase({
245
262
  path: {
246
- organization: props.organizationId,
263
+ organization,
247
264
  name: databaseName,
248
265
  },
249
266
  throwOnError: false,
@@ -259,20 +276,20 @@ export const Database = Resource(
259
276
  if (props.defaultBranch && props.defaultBranch !== "main") {
260
277
  const branchResponse = await api.getBranch({
261
278
  path: {
262
- organization: props.organizationId,
279
+ organization,
263
280
  database: databaseName,
264
281
  name: props.defaultBranch,
265
282
  },
266
283
  throwOnError: false,
267
284
  });
268
285
  if (!branchResponse.data) {
269
- await waitForDatabaseReady(api, props.organizationId, databaseName);
286
+ await waitForDatabaseReady(api, organization, databaseName);
270
287
  }
271
288
  if (branchResponse.error && branchResponse.response.status === 404) {
272
289
  // Create the branch
273
290
  await api.createBranch({
274
291
  path: {
275
- organization: props.organizationId,
292
+ organization,
276
293
  database: databaseName,
277
294
  },
278
295
  body: {
@@ -285,7 +302,7 @@ export const Database = Resource(
285
302
 
286
303
  const { data } = await api.updateDatabaseSettings({
287
304
  path: {
288
- organization: props.organizationId,
305
+ organization,
289
306
  name: databaseName,
290
307
  },
291
308
  body: {
@@ -303,7 +320,7 @@ export const Database = Resource(
303
320
 
304
321
  await ensureProductionBranchClusterSize(
305
322
  api,
306
- props.organizationId,
323
+ organization,
307
324
  databaseName,
308
325
  props.defaultBranch || "main",
309
326
  data.kind,
@@ -320,6 +337,7 @@ export const Database = Resource(
320
337
  createdAt: data.created_at,
321
338
  updatedAt: data.updated_at,
322
339
  htmlUrl: data.html_url,
340
+ organization,
323
341
  };
324
342
  }
325
343
 
@@ -330,7 +348,7 @@ export const Database = Resource(
330
348
  // Create new database
331
349
  await api.createDatabase({
332
350
  path: {
333
- organization: props.organizationId,
351
+ organization,
334
352
  },
335
353
  body: {
336
354
  name: databaseName,
@@ -343,7 +361,7 @@ export const Database = Resource(
343
361
  // These settings can't be set on creation, so we need to patch them after creation.
344
362
  const { data } = await api.updateDatabaseSettings({
345
363
  path: {
346
- organization: props.organizationId,
364
+ organization,
347
365
  name: databaseName,
348
366
  },
349
367
  body: {
@@ -360,12 +378,12 @@ export const Database = Resource(
360
378
 
361
379
  // If a non-'main' default branch is specified, create it
362
380
  if (props.defaultBranch && props.defaultBranch !== "main") {
363
- await waitForDatabaseReady(api, props.organizationId, databaseName);
381
+ await waitForDatabaseReady(api, organization, databaseName);
364
382
 
365
383
  // Check if branch exists
366
384
  const branchResponse = await api.getBranch({
367
385
  path: {
368
- organization: props.organizationId,
386
+ organization,
369
387
  database: databaseName,
370
388
  name: props.defaultBranch,
371
389
  },
@@ -376,7 +394,7 @@ export const Database = Resource(
376
394
  // Create the branch
377
395
  await api.createBranch({
378
396
  path: {
379
- organization: props.organizationId,
397
+ organization,
380
398
  database: databaseName,
381
399
  },
382
400
  body: {
@@ -387,7 +405,7 @@ export const Database = Resource(
387
405
 
388
406
  await ensureProductionBranchClusterSize(
389
407
  api,
390
- props.organizationId,
408
+ organization,
391
409
  databaseName,
392
410
  props.defaultBranch || "main",
393
411
  data.kind,
@@ -397,7 +415,7 @@ export const Database = Resource(
397
415
  // Update database to use new branch as default
398
416
  const { data: updatedData } = await api.updateDatabaseSettings({
399
417
  path: {
400
- organization: props.organizationId,
418
+ organization,
401
419
  name: databaseName,
402
420
  },
403
421
  body: {
@@ -415,6 +433,7 @@ export const Database = Resource(
415
433
  createdAt: updatedData.created_at,
416
434
  updatedAt: updatedData.updated_at,
417
435
  htmlUrl: updatedData.html_url,
436
+ organization,
418
437
  };
419
438
  }
420
439
  }
@@ -429,6 +448,7 @@ export const Database = Resource(
429
448
  createdAt: data.created_at,
430
449
  updatedAt: data.updated_at,
431
450
  htmlUrl: data.html_url,
451
+ organization,
432
452
  };
433
453
  },
434
454
  );