alchemy 0.48.2 → 0.49.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 (93) hide show
  1. package/bin/alchemy.mjs +13 -13
  2. package/bin/commands/create.ts +0 -15
  3. package/bin/commands/deploy.ts +2 -0
  4. package/bin/commands/dev.ts +10 -1
  5. package/bin/services/execute-alchemy.ts +9 -0
  6. package/lib/alchemy.d.ts +6 -0
  7. package/lib/alchemy.d.ts.map +1 -1
  8. package/lib/alchemy.js +1 -0
  9. package/lib/alchemy.js.map +1 -1
  10. package/lib/apply.js +13 -12
  11. package/lib/apply.js.map +1 -1
  12. package/lib/cloudflare/bindings.d.ts +21 -2
  13. package/lib/cloudflare/bindings.d.ts.map +1 -1
  14. package/lib/cloudflare/bindings.js.map +1 -1
  15. package/lib/cloudflare/bound.d.ts +8 -1
  16. package/lib/cloudflare/bound.d.ts.map +1 -1
  17. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  18. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  19. package/lib/cloudflare/index.d.ts +1 -0
  20. package/lib/cloudflare/index.d.ts.map +1 -1
  21. package/lib/cloudflare/index.js +1 -0
  22. package/lib/cloudflare/index.js.map +1 -1
  23. package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts.map +1 -1
  24. package/lib/cloudflare/miniflare/miniflare-worker-options.js +4 -0
  25. package/lib/cloudflare/miniflare/miniflare-worker-options.js.map +1 -1
  26. package/lib/cloudflare/rate-limit.d.ts +63 -0
  27. package/lib/cloudflare/rate-limit.d.ts.map +1 -0
  28. package/lib/cloudflare/rate-limit.js +41 -0
  29. package/lib/cloudflare/rate-limit.js.map +1 -0
  30. package/lib/cloudflare/tanstack-start.d.ts.map +1 -1
  31. package/lib/cloudflare/tanstack-start.js +10 -1
  32. package/lib/cloudflare/tanstack-start.js.map +1 -1
  33. package/lib/cloudflare/website.d.ts +4 -0
  34. package/lib/cloudflare/website.d.ts.map +1 -1
  35. package/lib/cloudflare/website.js +14 -1
  36. package/lib/cloudflare/website.js.map +1 -1
  37. package/lib/cloudflare/worker-metadata.d.ts +3 -0
  38. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  39. package/lib/cloudflare/worker-metadata.js +9 -0
  40. package/lib/cloudflare/worker-metadata.js.map +1 -1
  41. package/lib/cloudflare/worker.d.ts +9 -0
  42. package/lib/cloudflare/worker.d.ts.map +1 -1
  43. package/lib/cloudflare/worker.js +5 -1
  44. package/lib/cloudflare/worker.js.map +1 -1
  45. package/lib/cloudflare/wrangler.json.d.ts +23 -0
  46. package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
  47. package/lib/cloudflare/wrangler.json.js +17 -1
  48. package/lib/cloudflare/wrangler.json.js.map +1 -1
  49. package/lib/context.d.ts +2 -2
  50. package/lib/context.d.ts.map +1 -1
  51. package/lib/planetscale/password.js +1 -1
  52. package/lib/planetscale/password.js.map +1 -1
  53. package/lib/scope.d.ts +7 -0
  54. package/lib/scope.d.ts.map +1 -1
  55. package/lib/scope.js +3 -1
  56. package/lib/scope.js.map +1 -1
  57. package/lib/test/bun.d.ts +1 -24
  58. package/lib/test/bun.d.ts.map +1 -1
  59. package/lib/test/bun.js.map +1 -1
  60. package/lib/test/options.d.ts +30 -0
  61. package/lib/test/options.d.ts.map +1 -0
  62. package/lib/test/options.js +1 -0
  63. package/lib/test/options.js.map +1 -0
  64. package/lib/test/vitest.d.ts +1 -24
  65. package/lib/test/vitest.d.ts.map +1 -1
  66. package/lib/test/vitest.js.map +1 -1
  67. package/lib/util/ignore-matcher.d.ts.map +1 -1
  68. package/lib/util/ignore-matcher.js +1 -3
  69. package/lib/util/ignore-matcher.js.map +1 -1
  70. package/package.json +1 -1
  71. package/src/alchemy.ts +7 -0
  72. package/src/apply.ts +14 -13
  73. package/src/cloudflare/bindings.ts +22 -0
  74. package/src/cloudflare/bound.ts +29 -22
  75. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  76. package/src/cloudflare/index.ts +1 -0
  77. package/src/cloudflare/miniflare/miniflare-worker-options.ts +4 -0
  78. package/src/cloudflare/rate-limit.ts +70 -0
  79. package/src/cloudflare/tanstack-start.ts +10 -1
  80. package/src/cloudflare/website.ts +20 -1
  81. package/src/cloudflare/worker-metadata.ts +11 -0
  82. package/src/cloudflare/worker.ts +15 -1
  83. package/src/cloudflare/wrangler.json.ts +46 -1
  84. package/src/context.ts +2 -2
  85. package/src/planetscale/password.ts +1 -1
  86. package/src/scope.ts +9 -1
  87. package/src/test/bun.ts +1 -28
  88. package/src/test/options.ts +34 -0
  89. package/src/test/vitest.ts +1 -28
  90. package/src/util/ignore-matcher.ts +0 -1
  91. package/templates/tanstack-start/alchemy.run.ts +1 -3
  92. package/templates/tanstack-start/package.json +3 -3
  93. package/templates/tanstack-start/vite.config.ts +26 -25
@@ -4,13 +4,21 @@ import type { Context } from "../context.ts";
4
4
  import { formatJson } from "../fs/static-json-file.ts";
5
5
  import { Resource } from "../resource.ts";
6
6
  import { assertNever } from "../util/assert-never.ts";
7
- import { Self, type Bindings } from "./bindings.ts";
7
+ import {
8
+ Self,
9
+ type Bindings,
10
+ type WorkerBindingRateLimit,
11
+ } from "./bindings.ts";
8
12
  import type { DurableObjectNamespace } from "./durable-object-namespace.ts";
9
13
  import type { EventSource } from "./event-source.ts";
10
14
  import { isQueueEventSource } from "./event-source.ts";
11
15
  import { isQueue } from "./queue.ts";
12
16
  import type { Worker, WorkerProps } from "./worker.ts";
13
17
 
18
+ type WranglerJsonRateLimit = Omit<WorkerBindingRateLimit, "type"> & {
19
+ type: "rate_limit";
20
+ };
21
+
14
22
  /**
15
23
  * Properties for wrangler.json configuration file
16
24
  */
@@ -149,6 +157,8 @@ export const WranglerJson = Resource(
149
157
  binding: props.assets.binding,
150
158
  }
151
159
  : undefined,
160
+ placement: worker.placement,
161
+ limits: worker.limits,
152
162
  };
153
163
 
154
164
  // Process bindings if they exist
@@ -218,6 +228,20 @@ export interface WranglerJsonSpec {
218
228
  */
219
229
  compatibility_flags?: string[];
220
230
 
231
+ /**
232
+ * The placement mode for the worker
233
+ */
234
+ placement?: {
235
+ mode: "smart";
236
+ };
237
+
238
+ /**
239
+ * The CPU time limit for the worker
240
+ */
241
+ limits?: {
242
+ cpu_ms?: number;
243
+ };
244
+
221
245
  /**
222
246
  * Whether to enable a workers.dev URL for this worker
223
247
  */
@@ -431,6 +455,13 @@ export interface WranglerJsonSpec {
431
455
  namespace: string;
432
456
  experimental_remote?: boolean;
433
457
  }[];
458
+
459
+ /**
460
+ * Unsafe bindings section for experimental features
461
+ */
462
+ unsafe?: {
463
+ bindings: WranglerJsonRateLimit[];
464
+ };
434
465
  }
435
466
 
436
467
  /**
@@ -518,6 +549,7 @@ function processBindings(
518
549
  namespace: string;
519
550
  experimental_remote?: boolean;
520
551
  }[] = [];
552
+ const unsafeBindings: WranglerJsonRateLimit[] = [];
521
553
  const containers: {
522
554
  class_name: string;
523
555
  }[] = [];
@@ -705,6 +737,13 @@ function processBindings(
705
737
  namespace: binding.namespaceName,
706
738
  experimental_remote: true,
707
739
  });
740
+ } else if (binding.type === "ratelimit") {
741
+ unsafeBindings.push({
742
+ name: bindingName,
743
+ type: "rate_limit",
744
+ namespace_id: binding.namespace_id.toString(),
745
+ simple: binding.simple,
746
+ });
708
747
  } else if (binding.type === "secret_key") {
709
748
  // no-op
710
749
  } else if (binding.type === "container") {
@@ -786,4 +825,10 @@ function processBindings(
786
825
  if (dispatchNamespaces.length > 0) {
787
826
  spec.dispatch_namespaces = dispatchNamespaces;
788
827
  }
828
+
829
+ if (unsafeBindings.length > 0) {
830
+ spec.unsafe = {
831
+ bindings: unsafeBindings,
832
+ };
833
+ }
789
834
  }
package/src/context.ts CHANGED
@@ -57,7 +57,7 @@ export interface BaseContext<Out extends Resource> {
57
57
  * Indicate that this resource is being replaced.
58
58
  * This will cause the resource to be deleted at the end of the stack's CREATE phase.
59
59
  */
60
- replace(force?: boolean): Promise<never>;
60
+ replace(force?: boolean): never;
61
61
  /**
62
62
  * Terminate the resource lifecycle handler and destroy the resource.
63
63
  *
@@ -102,7 +102,7 @@ export function context<
102
102
  seq: number;
103
103
  props: Props;
104
104
  state: State<Kind, Props, Out>;
105
- replace: (force?: boolean) => Promise<never>;
105
+ replace: (force?: boolean) => never;
106
106
  isReplacement?: boolean;
107
107
  }): Context<Out> {
108
108
  type InternalSymbols =
@@ -306,7 +306,7 @@ export const Password = Resource(
306
306
  this.output.cidrs.length === props.cidrs.length &&
307
307
  this.output.cidrs.every((cidr, i) => cidr === props.cidrs![i])))
308
308
  ) {
309
- return await this.replace();
309
+ return this.replace();
310
310
  }
311
311
  const updateResponse = await api.patch(
312
312
  `/organizations/${props.organizationId}/databases/${databaseName}/branches/${branchName}/passwords/${this.output.id}`,
package/src/scope.ts CHANGED
@@ -50,6 +50,12 @@ export interface ScopeOptions {
50
50
  * @default - `true` if ran with `alchemy dev`, `alchemy watch`, `bun --watch ./alchemy.run.ts`
51
51
  */
52
52
  watch?: boolean;
53
+ /**
54
+ * Apply updates to resources even if there are no changes.
55
+ *
56
+ * @default false
57
+ */
58
+ force?: boolean;
53
59
  telemetryClient?: ITelemetryClient;
54
60
  logger?: LoggerApi;
55
61
  }
@@ -120,6 +126,7 @@ export class Scope {
120
126
  public readonly phase: Phase;
121
127
  public readonly local: boolean;
122
128
  public readonly watch: boolean;
129
+ public readonly force: boolean;
123
130
  public readonly logger: LoggerApi;
124
131
  public readonly telemetryClient: ITelemetryClient;
125
132
  public readonly dataMutex: AsyncMutex;
@@ -177,6 +184,7 @@ export class Scope {
177
184
 
178
185
  this.local = options.local ?? this.parent?.local ?? false;
179
186
  this.watch = options.watch ?? this.parent?.watch ?? false;
187
+ this.force = options.force ?? this.parent?.force ?? false;
180
188
 
181
189
  if (this.local) {
182
190
  this.logger.warnOnce(
@@ -432,7 +440,7 @@ export class Scope {
432
440
  event: "app.success",
433
441
  elapsed: performance.now() - this.startedAt,
434
442
  });
435
- } else {
443
+ } else if (this.isErrored) {
436
444
  this.logger.warn("Scope is in error, skipping finalize");
437
445
  this.rootTelemetryClient?.record({
438
446
  event: "app.error",
package/src/test/bun.ts CHANGED
@@ -4,8 +4,8 @@ import { afterAll, beforeAll, it } from "bun:test";
4
4
  import path from "node:path";
5
5
  import { alchemy } from "../alchemy.ts";
6
6
  import { Scope } from "../scope.ts";
7
- import type { StateStoreType } from "../state.ts";
8
7
  import { NoopTelemetryClient } from "../util/telemetry/index.ts";
8
+ import type { TestOptions } from "./options.ts";
9
9
 
10
10
  /**
11
11
  * Extend the Alchemy interface to include test functionality
@@ -21,33 +21,6 @@ declare module "../alchemy.ts" {
21
21
  */
22
22
  alchemy.test = test;
23
23
 
24
- /**
25
- * Options for configuring test behavior
26
- */
27
- export interface TestOptions {
28
- /**
29
- * Whether to suppress logging output.
30
- * @default false.
31
- */
32
- quiet?: boolean;
33
-
34
- /**
35
- * Password to use for test resources.
36
- * @default "test-password".
37
- */
38
- password?: string;
39
-
40
- /**
41
- * Override the default state store for the test.
42
- */
43
- stateStore?: StateStoreType;
44
-
45
- /**
46
- * Prefix to use for the scope to isolate tests and environments.
47
- */
48
- prefix?: string;
49
- }
50
-
51
24
  /**
52
25
  * Test function type definition with overloads
53
26
  */
@@ -0,0 +1,34 @@
1
+ import type { StateStoreType } from "../state.ts";
2
+
3
+ /**
4
+ * Options for configuring test behavior
5
+ */
6
+ export interface TestOptions {
7
+ /**
8
+ * Whether to suppress logging output.
9
+ * @default false.
10
+ */
11
+ quiet?: boolean;
12
+
13
+ /**
14
+ * Apply updates to resources even if there are no changes.
15
+ * @default false
16
+ */
17
+ force?: boolean;
18
+
19
+ /**
20
+ * Password to use for test resources.
21
+ * @default "test-password".
22
+ */
23
+ password?: string;
24
+
25
+ /**
26
+ * Override the default state store for the test.
27
+ */
28
+ stateStore?: StateStoreType;
29
+
30
+ /**
31
+ * Prefix to use for the scope to isolate tests and environments.
32
+ */
33
+ prefix?: string;
34
+ }
@@ -2,7 +2,6 @@ import path from "node:path";
2
2
  import { afterAll, beforeAll, it } from "vitest";
3
3
  import { alchemy } from "../alchemy.ts";
4
4
  import { Scope } from "../scope.ts";
5
- import type { StateStoreType } from "../state.ts";
6
5
  import {
7
6
  CloudflareStateStore,
8
7
  D1StateStore,
@@ -10,6 +9,7 @@ import {
10
9
  SQLiteStateStore,
11
10
  } from "../state/index.ts";
12
11
  import { NoopTelemetryClient } from "../util/telemetry/client.ts";
12
+ import type { TestOptions } from "./options.ts";
13
13
 
14
14
  /**
15
15
  * Extend the Alchemy interface to include test functionality
@@ -25,33 +25,6 @@ declare module "../alchemy.ts" {
25
25
  */
26
26
  alchemy.test = test;
27
27
 
28
- /**
29
- * Options for configuring test behavior
30
- */
31
- export interface TestOptions {
32
- /**
33
- * Whether to suppress logging output.
34
- * @default false.
35
- */
36
- quiet?: boolean;
37
-
38
- /**
39
- * Password to use for test resources.
40
- * @default "test-password".
41
- */
42
- password?: string;
43
-
44
- /**
45
- * Override the default state store for the test.
46
- */
47
- stateStore?: StateStoreType;
48
-
49
- /**
50
- * Prefix to use for the scope to isolate tests and environments.
51
- */
52
- prefix?: string;
53
- }
54
-
55
28
  /**
56
29
  * Test function type definition with overloads
57
30
  */
@@ -835,7 +835,6 @@ const isPathValid = (path: string): boolean =>
835
835
  const setupWindows = (): void => {
836
836
  /* eslint no-control-regex: "off" */
837
837
  const makePosix = (str: string): string =>
838
- // biome-ignore lint/suspicious/noControlCharactersInRegex: adapted from node-ignore
839
838
  /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str)
840
839
  ? str
841
840
  : str.replace(/\\/g, "/");
@@ -5,9 +5,7 @@ import { TanStackStart } from "alchemy/cloudflare";
5
5
 
6
6
  const app = await alchemy("my-alchemy-app");
7
7
 
8
- export const worker = await TanStackStart("website", {
9
- command: "bun run build",
10
- });
8
+ export const worker = await TanStackStart("website");
11
9
 
12
10
  console.log({
13
11
  url: worker.url,
@@ -11,9 +11,9 @@
11
11
  "start": "node .output/server/index.mjs"
12
12
  },
13
13
  "dependencies": {
14
- "@tanstack/react-router": "^1.121.27",
15
- "@tanstack/react-router-devtools": "^1.121.27",
16
- "@tanstack/react-start": "^1.121.32",
14
+ "@tanstack/react-router": "^1.128.3",
15
+ "@tanstack/react-router-devtools": "^1.128.3",
16
+ "@tanstack/react-start": "^1.128.3",
17
17
  "react": "^19.0.0",
18
18
  "react-dom": "^19.0.0",
19
19
  "tailwind-merge": "3",
@@ -5,29 +5,30 @@ import { defineConfig, PluginOption } from "vite";
5
5
  import tsConfigPaths from "vite-tsconfig-paths";
6
6
 
7
7
  export default defineConfig({
8
- server: {
9
- port: 3000,
10
- },
11
- build: {
12
- target: "esnext",
13
- rollupOptions: {
14
- external: ["node:async_hooks", "cloudflare:workers"],
15
- },
16
- },
17
- plugins: [
18
- tailwindcss() as PluginOption,
19
- cloudflareWorkersDevEnvironmentShim(),
20
- tsConfigPaths({
21
- projects: ["./tsconfig.json"],
22
- }),
23
- tanstackStart({
24
- target: "cloudflare-module",
25
- tsr: {
26
- routeTreeFileHeader: [
27
- "/** biome-ignore-all lint/suspicious/noExplicitAny: code generated by @tanstack/react-start */",
28
- ],
29
- quoteStyle: "double",
30
- },
31
- }),
32
- ],
8
+ server: {
9
+ port: 3000,
10
+ },
11
+ build: {
12
+ target: "esnext",
13
+ rollupOptions: {
14
+ external: ["node:async_hooks", "cloudflare:workers"],
15
+ },
16
+ },
17
+ plugins: [
18
+ tailwindcss() as PluginOption,
19
+ cloudflareWorkersDevEnvironmentShim(),
20
+ tsConfigPaths({
21
+ projects: ["./tsconfig.json"],
22
+ }),
23
+ tanstackStart({
24
+ target: "cloudflare-module",
25
+ customViteReactPlugin: true,
26
+ tsr: {
27
+ routeTreeFileHeader: [
28
+ "/** biome-ignore-all lint/suspicious/noExplicitAny: code generated by @tanstack/react-start */",
29
+ ],
30
+ quoteStyle: "double",
31
+ },
32
+ }),
33
+ ],
33
34
  });