alchemy 0.87.0 → 0.89.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 (65) hide show
  1. package/bin/alchemy.js +483 -541
  2. package/lib/cloudflare/compatibility-date.d.ts +1 -1
  3. package/lib/cloudflare/compatibility-date.d.ts.map +1 -1
  4. package/lib/cloudflare/index.d.ts +1 -0
  5. package/lib/cloudflare/index.d.ts.map +1 -1
  6. package/lib/cloudflare/index.js +1 -0
  7. package/lib/cloudflare/index.js.map +1 -1
  8. package/lib/cloudflare/r2-bucket-notification.d.ts +312 -0
  9. package/lib/cloudflare/r2-bucket-notification.d.ts.map +1 -0
  10. package/lib/cloudflare/r2-bucket-notification.js +387 -0
  11. package/lib/cloudflare/r2-bucket-notification.js.map +1 -0
  12. package/lib/cloudflare/workflow.d.ts +17 -0
  13. package/lib/cloudflare/workflow.d.ts.map +1 -1
  14. package/lib/cloudflare/workflow.js +1 -0
  15. package/lib/cloudflare/workflow.js.map +1 -1
  16. package/lib/cloudflare/wrangler.json.js +1 -0
  17. package/lib/cloudflare/wrangler.json.js.map +1 -1
  18. package/lib/docker/api.d.ts +40 -0
  19. package/lib/docker/api.d.ts.map +1 -1
  20. package/lib/docker/api.js +30 -0
  21. package/lib/docker/api.js.map +1 -1
  22. package/lib/docker/network.d.ts.map +1 -1
  23. package/lib/docker/network.js +24 -7
  24. package/lib/docker/network.js.map +1 -1
  25. package/lib/planetscale/api.d.ts +1 -0
  26. package/lib/planetscale/api.d.ts.map +1 -1
  27. package/lib/planetscale/api.js +1 -1
  28. package/lib/planetscale/api.js.map +1 -1
  29. package/lib/planetscale/branch.d.ts +28 -0
  30. package/lib/planetscale/branch.d.ts.map +1 -1
  31. package/lib/planetscale/branch.js +12 -0
  32. package/lib/planetscale/branch.js.map +1 -1
  33. package/lib/planetscale/database-extensions.d.ts +515 -0
  34. package/lib/planetscale/database-extensions.d.ts.map +1 -0
  35. package/lib/planetscale/database-extensions.js +389 -0
  36. package/lib/planetscale/database-extensions.js.map +1 -0
  37. package/lib/planetscale/database.d.ts +40 -2
  38. package/lib/planetscale/database.d.ts.map +1 -1
  39. package/lib/planetscale/database.js +76 -1
  40. package/lib/planetscale/database.js.map +1 -1
  41. package/lib/scope.d.ts.map +1 -1
  42. package/lib/scope.js +1 -2
  43. package/lib/scope.js.map +1 -1
  44. package/lib/util/telemetry.d.ts +1 -6
  45. package/lib/util/telemetry.d.ts.map +1 -1
  46. package/lib/util/telemetry.js.map +1 -1
  47. package/package.json +4 -4
  48. package/src/cloudflare/index.ts +1 -0
  49. package/src/cloudflare/r2-bucket-notification.ts +809 -0
  50. package/src/cloudflare/workflow.ts +18 -0
  51. package/src/cloudflare/wrangler.json.ts +1 -0
  52. package/src/docker/api.ts +61 -0
  53. package/src/docker/network.ts +27 -7
  54. package/src/planetscale/api.ts +1 -1
  55. package/src/planetscale/branch.ts +45 -0
  56. package/src/planetscale/database-extensions.ts +947 -0
  57. package/src/planetscale/database.ts +141 -2
  58. package/src/scope.ts +1 -2
  59. package/src/util/telemetry.ts +1 -20
  60. package/workers/tunnel-proxy.js +1 -1
  61. package/lib/state/instrumented-state-store.d.ts +0 -19
  62. package/lib/state/instrumented-state-store.d.ts.map +0 -1
  63. package/lib/state/instrumented-state-store.js +0 -68
  64. package/lib/state/instrumented-state-store.js.map +0 -1
  65. package/src/state/instrumented-state-store.ts +0 -108
@@ -2,10 +2,16 @@ import type { Context } from "../context.ts";
2
2
  import { Resource } from "../resource.ts";
3
3
  import type { PlanetScaleProps } from "./api.ts";
4
4
  import { createPlanetScaleClient } from "./api.ts";
5
+ import {
6
+ type DatabaseExtensions,
7
+ diffExtensions,
8
+ updateExtensions,
9
+ } from "./database-extensions.ts";
5
10
  import {
6
11
  ensureProductionBranchClusterSize,
7
12
  type PlanetScaleClusterSize,
8
13
  sanitizeClusterSize,
14
+ waitForBranchReady,
9
15
  waitForDatabaseReady,
10
16
  } from "./utils.ts";
11
17
 
@@ -36,11 +42,15 @@ interface BaseDatabaseProps extends PlanetScaleProps {
36
42
  delete?: boolean;
37
43
 
38
44
  /**
39
- * The region where the database will be created (create only)
45
+ * The region where the database will be created (create only).
46
+ *
47
+ * @see https://planetscale.com/docs/concepts/regions
40
48
  */
41
49
  region?: {
42
50
  /**
43
- * The slug identifier of the region
51
+ * The slug identifier of the region (e.g. "us-east", "eu-west", "gcp-us-central1")
52
+ *
53
+ * @see https://planetscale.com/docs/concepts/regions#available-regions
44
54
  */
45
55
  slug: string;
46
56
  };
@@ -135,6 +145,27 @@ interface PostgreSQLDatabaseProps extends BaseDatabaseProps {
135
145
  * The CPU architecture for the database (PostgreSQL only)
136
146
  */
137
147
  arch?: "x86" | "arm";
148
+
149
+ /**
150
+ * PostgreSQL extensions to enable on the default branch.
151
+ * Each key represents an extension — if present (even as `{}`), it is enabled with the given config or defaults.
152
+ * If absent, the extension is disabled. (PostgreSQL only)
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * const db = await Database("my-pg-db", {
157
+ * kind: "postgresql",
158
+ * organization: "my-org",
159
+ * clusterSize: "PS_10",
160
+ * extensions: {
161
+ * vector: { hnswEfSearch: 100 },
162
+ * pgCron: {},
163
+ * pgStatStatements: { max: 10000 },
164
+ * },
165
+ * });
166
+ * ```
167
+ */
168
+ extensions?: DatabaseExtensions;
138
169
  }
139
170
 
140
171
  /**
@@ -190,6 +221,20 @@ export type Database = DatabaseProps & {
190
221
  * The organization of the database
191
222
  */
192
223
  organization: string;
224
+
225
+ /**
226
+ * The region of the database as reported by PlanetScale.
227
+ *
228
+ * @see https://planetscale.com/docs/concepts/regions
229
+ */
230
+ region: {
231
+ /**
232
+ * The slug identifier of the region (e.g. "us-east", "eu-west", "gcp-us-central1")
233
+ *
234
+ * @see https://planetscale.com/docs/concepts/regions#available-regions
235
+ */
236
+ slug: string;
237
+ };
193
238
  };
194
239
 
195
240
  /**
@@ -304,6 +349,49 @@ export const Database = Resource(
304
349
  cause: getResponse.error,
305
350
  });
306
351
  }
352
+
353
+ // Validate immutable properties match if specified
354
+ const actualKind = getResponse.data.kind;
355
+ if (props.kind && actualKind !== props.kind) {
356
+ throw new Error(
357
+ `Database "${databaseName}" has kind "${actualKind}" but expected "${props.kind}". ` +
358
+ `Database kind cannot be changed after creation.`,
359
+ );
360
+ }
361
+
362
+ if (props.region) {
363
+ const actualSlug = getResponse.data.region.slug;
364
+ if (actualSlug !== props.region.slug) {
365
+ throw new Error(
366
+ `Database "${databaseName}" is in region "${actualSlug}" but expected "${props.region.slug}". ` +
367
+ `PlanetScale database regions cannot be changed after creation. ` +
368
+ `Either update the region in your configuration to match, or create a new database in the correct region.`,
369
+ );
370
+ }
371
+ }
372
+
373
+ if (props.kind === "postgresql" && props.arch) {
374
+ const defaultBranch = props.defaultBranch || "main";
375
+ const branchInfo = await api.getBranch({
376
+ path: {
377
+ organization,
378
+ database: databaseName,
379
+ branch: defaultBranch,
380
+ },
381
+ throwOnError: false,
382
+ });
383
+ if (branchInfo.data?.cluster_architecture) {
384
+ const actualArch =
385
+ branchInfo.data.cluster_architecture === "aarch64" ? "arm" : "x86";
386
+ if (actualArch !== props.arch) {
387
+ throw new Error(
388
+ `Database "${databaseName}" has architecture "${actualArch}" but expected "${props.arch}". ` +
389
+ `Database architecture cannot be changed after creation.`,
390
+ );
391
+ }
392
+ }
393
+ }
394
+
307
395
  // Update database settings
308
396
  // If updating to a non-'main' default branch, create it first
309
397
  if (props.defaultBranch && props.defaultBranch !== "main") {
@@ -365,6 +453,26 @@ export const Database = Resource(
365
453
  clusterSize,
366
454
  );
367
455
 
456
+ // Sync PostgreSQL extensions on update
457
+ if (props.kind === "postgresql") {
458
+ const previousExtensions =
459
+ this.output?.kind === "postgresql"
460
+ ? this.output.extensions
461
+ : undefined;
462
+ const desiredExtensions = props.extensions;
463
+ const diff = diffExtensions(previousExtensions, desiredExtensions);
464
+ if (diff.hasChanges) {
465
+ const branch = props.defaultBranch || "main";
466
+ await waitForBranchReady(api, organization, databaseName, branch);
467
+ await updateExtensions(desiredExtensions ?? {}, {
468
+ ...props,
469
+ organization,
470
+ database: databaseName,
471
+ branch,
472
+ });
473
+ }
474
+ }
475
+
368
476
  return {
369
477
  ...props,
370
478
  id: data.id,
@@ -376,6 +484,7 @@ export const Database = Resource(
376
484
  updatedAt: data.updated_at,
377
485
  htmlUrl: data.html_url,
378
486
  organization,
487
+ region: { slug: data.region.slug },
379
488
  };
380
489
  }
381
490
 
@@ -472,6 +581,22 @@ export const Database = Resource(
472
581
  },
473
582
  });
474
583
 
584
+ // Enable PostgreSQL extensions on the new default branch
585
+ if (props.kind === "postgresql" && props.extensions) {
586
+ await waitForBranchReady(
587
+ api,
588
+ organization,
589
+ databaseName,
590
+ props.defaultBranch,
591
+ );
592
+ await updateExtensions(props.extensions, {
593
+ ...props,
594
+ organization,
595
+ database: databaseName,
596
+ branch: props.defaultBranch,
597
+ });
598
+ }
599
+
475
600
  return {
476
601
  ...props,
477
602
  id: data.id,
@@ -483,10 +608,23 @@ export const Database = Resource(
483
608
  updatedAt: updatedData.updated_at,
484
609
  htmlUrl: updatedData.html_url,
485
610
  organization,
611
+ region: { slug: updatedData.region.slug },
486
612
  };
487
613
  }
488
614
  }
489
615
 
616
+ // Enable PostgreSQL extensions on the default branch (main)
617
+ if (props.kind === "postgresql" && props.extensions) {
618
+ const branch = props.defaultBranch || "main";
619
+ await waitForBranchReady(api, organization, databaseName, branch);
620
+ await updateExtensions(props.extensions, {
621
+ ...props,
622
+ organization,
623
+ database: databaseName,
624
+ branch,
625
+ });
626
+ }
627
+
490
628
  return {
491
629
  ...props,
492
630
  id: data.id,
@@ -498,6 +636,7 @@ export const Database = Resource(
498
636
  updatedAt: data.updated_at,
499
637
  htmlUrl: data.html_url,
500
638
  organization,
639
+ region: { slug: data.region.slug },
501
640
  };
502
641
  },
503
642
  );
package/src/scope.ts CHANGED
@@ -19,7 +19,6 @@ import {
19
19
  } from "./resource.ts";
20
20
  import type { State, StateStore, StateStoreType } from "./state.ts";
21
21
  import { FileSystemStateStore } from "./state/file-system-state-store.ts";
22
- import { InstrumentedStateStore } from "./state/instrumented-state-store.ts";
23
22
  import {
24
23
  createDummyLogger,
25
24
  createLoggerInstance,
@@ -333,7 +332,7 @@ export class Scope {
333
332
  stateStore ??
334
333
  this.parent?.stateStore ??
335
334
  ((scope) => new FileSystemStateStore(scope));
336
- this.state = new InstrumentedStateStore(this.stateStore(this));
335
+ this.state = this.stateStore(this);
337
336
  this.dataMutex = new AsyncMutex();
338
337
  }
339
338
 
@@ -261,21 +261,6 @@ export type ResourceTelemetryData = {
261
261
  replaced: boolean;
262
262
  };
263
263
 
264
- export type StateStoreTelemetryData = {
265
- event:
266
- | "statestore.init"
267
- | "statestore.deinit"
268
- | "statestore.list"
269
- | "statestore.count"
270
- | "statestore.get"
271
- | "statestore.getBatch"
272
- | "statestore.all"
273
- | "statestore.set"
274
- | "statestore.delete";
275
- stateStore: string;
276
- duration: number;
277
- };
278
-
279
264
  export type AlchemyTelemetryData = {
280
265
  event: "alchemy.start" | "alchemy.success" | "alchemy.error";
281
266
  duration: number;
@@ -290,11 +275,7 @@ async function isTelemetryDisabled() {
290
275
  }
291
276
 
292
277
  export async function createAndSendEvent(
293
- data:
294
- | CliTelemetryData
295
- | ResourceTelemetryData
296
- | StateStoreTelemetryData
297
- | AlchemyTelemetryData,
278
+ data: CliTelemetryData | ResourceTelemetryData | AlchemyTelemetryData,
298
279
  error?: Error,
299
280
  ) {
300
281
  if (await isTelemetryDisabled()) {
@@ -83,7 +83,7 @@ const renderErrorHtml = (props) => `
83
83
  Alchemy</a>.</p>
84
84
  </div>
85
85
  <div class="bg-slate-200 px-5 py-3 flex flex-col w-full max-w-lg">
86
- <p class="text-sm text-slate-500">Alchemy 0.87.0</p>
86
+ <p class="text-sm text-slate-500">Alchemy 0.89.0</p>
87
87
  </div>
88
88
  </div>
89
89
  </body>
@@ -1,19 +0,0 @@
1
- import type { State, StateStore } from "../state.ts";
2
- export declare class InstrumentedStateStore<T extends StateStore> implements StateStore {
3
- /** @internal */
4
- __phantom?: T;
5
- private readonly stateStore;
6
- private readonly stateStoreClass;
7
- constructor(stateStore: StateStore);
8
- private callWithTelemetry;
9
- init(): Promise<void>;
10
- deinit(): Promise<void>;
11
- list(): Promise<string[]>;
12
- count(): Promise<number>;
13
- get(key: string): Promise<State<string, import("../resource.ts").ResourceProps | undefined, import("../resource.ts").Resource<string>> | undefined>;
14
- getBatch(ids: string[]): Promise<Record<string, State<string, import("../resource.ts").ResourceProps | undefined, import("../resource.ts").Resource<string>>>>;
15
- all(): Promise<Record<string, State<string, import("../resource.ts").ResourceProps | undefined, import("../resource.ts").Resource<string>>>>;
16
- set(key: string, value: State): Promise<void>;
17
- delete(key: string): Promise<void>;
18
- }
19
- //# sourceMappingURL=instrumented-state-store.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"instrumented-state-store.d.ts","sourceRoot":"","sources":["../../src/state/instrumented-state-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAOrD,qBAAa,sBAAsB,CACjC,CAAC,SAAS,UAAU,CACpB,YAAW,UAAU;IACrB,gBAAgB;IAChB,SAAS,CAAC,EAAE,CAAC,CAAC;IACd,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAE7B,UAAU,EAAE,UAAU;YAKpB,iBAAiB;IA2BzB,IAAI;IASJ,MAAM;IASN,IAAI;IAMJ,KAAK;IAML,GAAG,CAAC,GAAG,EAAE,MAAM;IAMf,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE;IAMtB,GAAG;IAMH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;IAM7B,MAAM,CAAC,GAAG,EAAE,MAAM;CAMzB"}
@@ -1,68 +0,0 @@
1
- import { createAndSendEvent, } from "../util/telemetry.js";
2
- //todo(michael): we should also handle serde here
3
- export class InstrumentedStateStore {
4
- /** @internal */
5
- __phantom;
6
- stateStore;
7
- stateStoreClass;
8
- constructor(stateStore) {
9
- this.stateStore = stateStore;
10
- this.stateStoreClass = stateStore.constructor.name;
11
- }
12
- async callWithTelemetry(event, fn) {
13
- const start = performance.now();
14
- let error;
15
- try {
16
- return await fn();
17
- }
18
- catch (err) {
19
- error = err;
20
- throw err;
21
- }
22
- finally {
23
- await createAndSendEvent({
24
- event,
25
- stateStore: this.stateStoreClass,
26
- duration: performance.now() - start,
27
- }, error instanceof Error
28
- ? error
29
- : error
30
- ? new Error(String(error))
31
- : undefined);
32
- }
33
- }
34
- async init() {
35
- if (this.stateStore.init == null) {
36
- return;
37
- }
38
- await this.callWithTelemetry("statestore.init", this.stateStore.init.bind(this.stateStore));
39
- }
40
- async deinit() {
41
- if (this.stateStore.deinit == null) {
42
- return;
43
- }
44
- await this.callWithTelemetry("statestore.deinit", this.stateStore.deinit.bind(this.stateStore));
45
- }
46
- async list() {
47
- return await this.callWithTelemetry("statestore.list", this.stateStore.list.bind(this.stateStore));
48
- }
49
- async count() {
50
- return await this.callWithTelemetry("statestore.count", this.stateStore.count.bind(this.stateStore));
51
- }
52
- async get(key) {
53
- return await this.callWithTelemetry("statestore.get", this.stateStore.get.bind(this.stateStore, key));
54
- }
55
- async getBatch(ids) {
56
- return await this.callWithTelemetry("statestore.getBatch", this.stateStore.getBatch.bind(this.stateStore, ids));
57
- }
58
- async all() {
59
- return await this.callWithTelemetry("statestore.all", this.stateStore.all.bind(this.stateStore));
60
- }
61
- async set(key, value) {
62
- await this.callWithTelemetry("statestore.set", this.stateStore.set.bind(this.stateStore, key, value));
63
- }
64
- async delete(key) {
65
- await this.callWithTelemetry("statestore.delete", this.stateStore.delete.bind(this.stateStore, key));
66
- }
67
- }
68
- //# sourceMappingURL=instrumented-state-store.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"instrumented-state-store.js","sourceRoot":"","sources":["../../src/state/instrumented-state-store.ts"],"names":[],"mappings":"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"}
@@ -1,108 +0,0 @@
1
- import type { State, StateStore } from "../state.ts";
2
- import {
3
- createAndSendEvent,
4
- type StateStoreTelemetryData,
5
- } from "../util/telemetry.ts";
6
-
7
- //todo(michael): we should also handle serde here
8
- export class InstrumentedStateStore<
9
- T extends StateStore,
10
- > implements StateStore {
11
- /** @internal */
12
- __phantom?: T;
13
- private readonly stateStore: StateStore;
14
- private readonly stateStoreClass: string;
15
-
16
- constructor(stateStore: StateStore) {
17
- this.stateStore = stateStore;
18
- this.stateStoreClass = stateStore.constructor.name;
19
- }
20
-
21
- private async callWithTelemetry<T>(
22
- event: StateStoreTelemetryData["event"],
23
- fn: () => Promise<T>,
24
- ): Promise<T> {
25
- const start = performance.now();
26
- let error: unknown;
27
- try {
28
- return await fn();
29
- } catch (err) {
30
- error = err;
31
- throw err;
32
- } finally {
33
- await createAndSendEvent(
34
- {
35
- event,
36
- stateStore: this.stateStoreClass,
37
- duration: performance.now() - start,
38
- },
39
- error instanceof Error
40
- ? error
41
- : error
42
- ? new Error(String(error))
43
- : undefined,
44
- );
45
- }
46
- }
47
-
48
- async init() {
49
- if (this.stateStore.init == null) {
50
- return;
51
- }
52
- await this.callWithTelemetry(
53
- "statestore.init",
54
- this.stateStore.init.bind(this.stateStore),
55
- );
56
- }
57
- async deinit() {
58
- if (this.stateStore.deinit == null) {
59
- return;
60
- }
61
- await this.callWithTelemetry(
62
- "statestore.deinit",
63
- this.stateStore.deinit.bind(this.stateStore),
64
- );
65
- }
66
- async list() {
67
- return await this.callWithTelemetry(
68
- "statestore.list",
69
- this.stateStore.list.bind(this.stateStore),
70
- );
71
- }
72
- async count() {
73
- return await this.callWithTelemetry(
74
- "statestore.count",
75
- this.stateStore.count.bind(this.stateStore),
76
- );
77
- }
78
- async get(key: string) {
79
- return await this.callWithTelemetry(
80
- "statestore.get",
81
- this.stateStore.get.bind(this.stateStore, key),
82
- );
83
- }
84
- async getBatch(ids: string[]) {
85
- return await this.callWithTelemetry(
86
- "statestore.getBatch",
87
- this.stateStore.getBatch.bind(this.stateStore, ids),
88
- );
89
- }
90
- async all() {
91
- return await this.callWithTelemetry(
92
- "statestore.all",
93
- this.stateStore.all.bind(this.stateStore),
94
- );
95
- }
96
- async set(key: string, value: State) {
97
- await this.callWithTelemetry(
98
- "statestore.set",
99
- this.stateStore.set.bind(this.stateStore, key, value),
100
- );
101
- }
102
- async delete(key: string) {
103
- await this.callWithTelemetry(
104
- "statestore.delete",
105
- this.stateStore.delete.bind(this.stateStore, key),
106
- );
107
- }
108
- }