alchemy 0.48.3 → 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.
- package/bin/alchemy.mjs +13 -13
- package/bin/commands/create.ts +0 -15
- package/bin/commands/deploy.ts +2 -0
- package/bin/commands/dev.ts +10 -1
- package/bin/services/execute-alchemy.ts +9 -0
- package/lib/alchemy.d.ts +6 -0
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +1 -0
- package/lib/alchemy.js.map +1 -1
- package/lib/apply.js +13 -12
- package/lib/apply.js.map +1 -1
- package/lib/cloudflare/bindings.d.ts +21 -2
- package/lib/cloudflare/bindings.d.ts.map +1 -1
- package/lib/cloudflare/bindings.js.map +1 -1
- package/lib/cloudflare/bound.d.ts +8 -1
- package/lib/cloudflare/bound.d.ts.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-options.js +4 -0
- package/lib/cloudflare/miniflare/miniflare-worker-options.js.map +1 -1
- package/lib/cloudflare/rate-limit.d.ts +63 -0
- package/lib/cloudflare/rate-limit.d.ts.map +1 -0
- package/lib/cloudflare/rate-limit.js +41 -0
- package/lib/cloudflare/rate-limit.js.map +1 -0
- package/lib/cloudflare/tanstack-start.d.ts.map +1 -1
- package/lib/cloudflare/tanstack-start.js +10 -1
- package/lib/cloudflare/tanstack-start.js.map +1 -1
- package/lib/cloudflare/website.d.ts +4 -0
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +14 -1
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +8 -0
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.js +1 -1
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +11 -0
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +15 -1
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/context.d.ts +2 -2
- package/lib/context.d.ts.map +1 -1
- package/lib/planetscale/password.js +1 -1
- package/lib/planetscale/password.js.map +1 -1
- package/lib/scope.d.ts +7 -0
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +3 -1
- package/lib/scope.js.map +1 -1
- package/lib/test/bun.d.ts +1 -24
- package/lib/test/bun.d.ts.map +1 -1
- package/lib/test/bun.js.map +1 -1
- package/lib/test/options.d.ts +30 -0
- package/lib/test/options.d.ts.map +1 -0
- package/lib/test/options.js +1 -0
- package/lib/test/options.js.map +1 -0
- package/lib/test/vitest.d.ts +1 -24
- package/lib/test/vitest.d.ts.map +1 -1
- package/lib/test/vitest.js.map +1 -1
- package/lib/util/ignore-matcher.d.ts.map +1 -1
- package/lib/util/ignore-matcher.js +1 -3
- package/lib/util/ignore-matcher.js.map +1 -1
- package/package.json +1 -1
- package/src/alchemy.ts +7 -0
- package/src/apply.ts +14 -13
- package/src/cloudflare/bindings.ts +22 -0
- package/src/cloudflare/bound.ts +29 -22
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/miniflare/miniflare-worker-options.ts +4 -0
- package/src/cloudflare/rate-limit.ts +70 -0
- package/src/cloudflare/tanstack-start.ts +10 -1
- package/src/cloudflare/website.ts +20 -1
- package/src/cloudflare/worker-metadata.ts +7 -0
- package/src/cloudflare/worker.ts +1 -1
- package/src/cloudflare/wrangler.json.ts +30 -1
- package/src/context.ts +2 -2
- package/src/planetscale/password.ts +1 -1
- package/src/scope.ts +9 -1
- package/src/test/bun.ts +1 -28
- package/src/test/options.ts +34 -0
- package/src/test/vitest.ts +1 -28
- package/src/util/ignore-matcher.ts +0 -1
- package/templates/tanstack-start/alchemy.run.ts +1 -3
- package/templates/tanstack-start/package.json +3 -3
- package/templates/tanstack-start/vite.config.ts +26 -25
package/src/apply.ts
CHANGED
|
@@ -113,7 +113,8 @@ async function _apply<Out extends Resource>(
|
|
|
113
113
|
});
|
|
114
114
|
if (
|
|
115
115
|
JSON.stringify(oldProps) === JSON.stringify(newProps) &&
|
|
116
|
-
alwaysUpdate !== true
|
|
116
|
+
alwaysUpdate !== true &&
|
|
117
|
+
!scope.force
|
|
117
118
|
) {
|
|
118
119
|
scope.skip();
|
|
119
120
|
if (!quiet) {
|
|
@@ -174,7 +175,7 @@ async function _apply<Out extends Resource>(
|
|
|
174
175
|
props: state.oldProps,
|
|
175
176
|
state,
|
|
176
177
|
isReplacement: false,
|
|
177
|
-
replace:
|
|
178
|
+
replace: (force?: boolean) => {
|
|
178
179
|
if (phase === "create") {
|
|
179
180
|
throw new Error(
|
|
180
181
|
`Resource ${resource[ResourceKind]} ${resource[ResourceFQN]} cannot be replaced in create phase.`,
|
|
@@ -186,17 +187,6 @@ async function _apply<Out extends Resource>(
|
|
|
186
187
|
);
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
if (force) {
|
|
190
|
-
await destroy(resource, {
|
|
191
|
-
quiet: scope.quiet,
|
|
192
|
-
strategy: "sequential",
|
|
193
|
-
replace: {
|
|
194
|
-
props: state.oldProps,
|
|
195
|
-
output: resource,
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
190
|
isReplaced = true;
|
|
201
191
|
throw new ReplacedSignal(force);
|
|
202
192
|
},
|
|
@@ -223,6 +213,17 @@ async function _apply<Out extends Resource>(
|
|
|
223
213
|
);
|
|
224
214
|
}
|
|
225
215
|
|
|
216
|
+
if (error.force) {
|
|
217
|
+
await destroy(resource, {
|
|
218
|
+
quiet: scope.quiet,
|
|
219
|
+
strategy: "sequential",
|
|
220
|
+
replace: {
|
|
221
|
+
props: state.oldProps,
|
|
222
|
+
output: resource,
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
226
227
|
output = await alchemy.run(
|
|
227
228
|
resource[ResourceID],
|
|
228
229
|
{ isResource: true, parent: scope },
|
|
@@ -20,6 +20,7 @@ import type { Images } from "./images.ts";
|
|
|
20
20
|
import type { KVNamespaceResource } from "./kv-namespace.ts";
|
|
21
21
|
import type { PipelineResource } from "./pipeline.ts";
|
|
22
22
|
import type { QueueResource } from "./queue.ts";
|
|
23
|
+
import type { RateLimit } from "./rate-limit.ts";
|
|
23
24
|
import type { SecretKey } from "./secret-key.ts";
|
|
24
25
|
import type { Secret as CloudflareSecret } from "./secret.ts";
|
|
25
26
|
import type { VectorizeIndexResource } from "./vectorize-index.ts";
|
|
@@ -56,6 +57,7 @@ export type Binding =
|
|
|
56
57
|
| KVNamespaceResource
|
|
57
58
|
| PipelineResource
|
|
58
59
|
| QueueResource
|
|
60
|
+
| RateLimit
|
|
59
61
|
| R2BucketResource
|
|
60
62
|
| {
|
|
61
63
|
type: "kv_namespace";
|
|
@@ -108,6 +110,7 @@ export type WorkerBindingSpec =
|
|
|
108
110
|
| WorkerBindingPipeline
|
|
109
111
|
| WorkerBindingPlainText
|
|
110
112
|
| WorkerBindingQueue
|
|
113
|
+
| WorkerBindingRateLimit
|
|
111
114
|
| WorkerBindingR2Bucket
|
|
112
115
|
| WorkerBindingSecretKey
|
|
113
116
|
| WorkerBindingSecretText
|
|
@@ -284,6 +287,25 @@ export interface WorkerBindingQueue {
|
|
|
284
287
|
queue_name: string;
|
|
285
288
|
}
|
|
286
289
|
|
|
290
|
+
/**
|
|
291
|
+
* Rate Limit binding type
|
|
292
|
+
*/
|
|
293
|
+
export interface WorkerBindingRateLimit {
|
|
294
|
+
/** The name of the binding */
|
|
295
|
+
name: string;
|
|
296
|
+
/** Type identifier for Rate Limit binding */
|
|
297
|
+
type: "ratelimit";
|
|
298
|
+
/** Namespace ID for the rate limit */
|
|
299
|
+
namespace_id: string;
|
|
300
|
+
/** Simple rate limiting configuration */
|
|
301
|
+
simple: {
|
|
302
|
+
/** Maximum number of requests */
|
|
303
|
+
limit: number;
|
|
304
|
+
/** Time period in seconds */
|
|
305
|
+
period: 60 | 10;
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
287
309
|
/**
|
|
288
310
|
* R2 Bucket binding type
|
|
289
311
|
*/
|
package/src/cloudflare/bound.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { HyperdriveResource as _Hyperdrive } from "./hyperdrive.ts";
|
|
|
15
15
|
import type { Images as _Images } from "./images.ts";
|
|
16
16
|
import type { PipelineResource as _Pipeline } from "./pipeline.ts";
|
|
17
17
|
import type { QueueResource as _Queue } from "./queue.ts";
|
|
18
|
+
import type { RateLimit } from "./rate-limit.ts";
|
|
18
19
|
import type { SecretKey } from "./secret-key.ts";
|
|
19
20
|
import type { Secret as CloudflareSecret } from "./secret.ts";
|
|
20
21
|
import type { VectorizeIndexResource as _VectorizeIndex } from "./vectorize-index.ts";
|
|
@@ -74,25 +75,31 @@ export type Bound<T extends Binding> = T extends _DurableObjectNamespace<
|
|
|
74
75
|
? AnalyticsEngineDataset
|
|
75
76
|
: T extends _Pipeline<infer R>
|
|
76
77
|
? Pipeline<R>
|
|
77
|
-
: T extends
|
|
78
|
-
?
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
78
|
+
: T extends RateLimit
|
|
79
|
+
? {
|
|
80
|
+
limit(options: {
|
|
81
|
+
key: string;
|
|
82
|
+
}): Promise<{ success: boolean }>;
|
|
83
|
+
}
|
|
84
|
+
: T extends string
|
|
85
|
+
? string
|
|
86
|
+
: T extends BrowserRendering
|
|
87
|
+
? Fetcher
|
|
88
|
+
: T extends _Ai<infer M>
|
|
89
|
+
? Ai<M>
|
|
90
|
+
: T extends _Images
|
|
91
|
+
? ImagesBinding
|
|
92
|
+
: T extends _VersionMetadata
|
|
93
|
+
? WorkerVersionMetadata
|
|
94
|
+
: T extends Self
|
|
95
|
+
? Service
|
|
96
|
+
: T extends Json<infer T>
|
|
97
|
+
? T
|
|
98
|
+
: T extends _Container<
|
|
99
|
+
infer Obj
|
|
100
|
+
>
|
|
101
|
+
? DurableObjectNamespace<
|
|
102
|
+
Obj &
|
|
103
|
+
Rpc.DurableObjectBranded
|
|
104
|
+
>
|
|
105
|
+
: Service;
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -36,6 +36,7 @@ export * from "./permission-groups.ts";
|
|
|
36
36
|
export * from "./pipeline.ts";
|
|
37
37
|
export * from "./queue-consumer.ts";
|
|
38
38
|
export * from "./queue.ts";
|
|
39
|
+
export * from "./rate-limit.ts";
|
|
39
40
|
export * from "./react-router.ts";
|
|
40
41
|
export * from "./redirect-rule.ts";
|
|
41
42
|
export * from "./redwood.ts";
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base interface for rate limiting configuration.
|
|
3
|
+
*/
|
|
4
|
+
export interface RateLimitProps {
|
|
5
|
+
/**
|
|
6
|
+
* A positive integer that uniquely defines this rate limiting configuration (e.g., namespace_id = 999).
|
|
7
|
+
*/
|
|
8
|
+
namespace_id: number;
|
|
9
|
+
/**
|
|
10
|
+
* Simple rate limiting configuration.
|
|
11
|
+
*/
|
|
12
|
+
simple: {
|
|
13
|
+
/**
|
|
14
|
+
* The limit (number of requests or API calls) to be applied. This is incremented when you call the limit() function in your Worker.
|
|
15
|
+
*/
|
|
16
|
+
limit: number;
|
|
17
|
+
/**
|
|
18
|
+
* The period, in seconds, to measure increments to the limit over. Must be either 10 or 60.
|
|
19
|
+
*/
|
|
20
|
+
period: 60 | 10;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* RateLimit binding for Cloudflare Workers.
|
|
26
|
+
*/
|
|
27
|
+
export interface RateLimit extends RateLimitProps {
|
|
28
|
+
type: "ratelimit";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Rate limiting binding for Cloudflare Workers.
|
|
33
|
+
*
|
|
34
|
+
* The RateLimit binding provides access to Cloudflare's rate limiting functionality,
|
|
35
|
+
* allowing you to implement rate limiting directly in your Workers without needing
|
|
36
|
+
* to rely on external services.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { Worker, RateLimit } from "alchemy/cloudflare";
|
|
41
|
+
*
|
|
42
|
+
* const rateLimit = RateLimit({
|
|
43
|
+
* namespace_id: 1001,
|
|
44
|
+
* simple: {
|
|
45
|
+
* limit: 1500,
|
|
46
|
+
* period: 60
|
|
47
|
+
* }
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* await Worker("my-worker", {
|
|
51
|
+
* name: "my-worker",
|
|
52
|
+
* entrypoint: "./src/worker.ts",
|
|
53
|
+
* bindings: {
|
|
54
|
+
* MY_RATE_LIMIT: rateLimit
|
|
55
|
+
* }
|
|
56
|
+
* });
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @see https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/
|
|
60
|
+
*
|
|
61
|
+
* Creates a rate limiting binding for Cloudflare Workers.
|
|
62
|
+
* @returns A RateLimit binding object.
|
|
63
|
+
*/
|
|
64
|
+
export function RateLimit(options: RateLimitProps): RateLimit {
|
|
65
|
+
return {
|
|
66
|
+
type: "ratelimit",
|
|
67
|
+
namespace_id: options.namespace_id,
|
|
68
|
+
simple: options.simple,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -17,7 +17,16 @@ export async function TanStackStart<B extends Bindings>(
|
|
|
17
17
|
): Promise<TanStackStart<B>> {
|
|
18
18
|
return Website(id, {
|
|
19
19
|
...props,
|
|
20
|
-
command: props?.command ?? "
|
|
20
|
+
command: props?.command ?? "vite build",
|
|
21
|
+
commandEnv: {
|
|
22
|
+
// TODO(sam): should probably be set for all Websites, or at least all Vite Websites
|
|
23
|
+
// needs to be set or else jsxDev is included in the bundle and breaks in production
|
|
24
|
+
NODE_ENV: process.env.NODE_ENV ?? "production",
|
|
25
|
+
},
|
|
26
|
+
noBundle: true,
|
|
27
|
+
dev: props?.dev ?? {
|
|
28
|
+
command: "vite dev",
|
|
29
|
+
},
|
|
21
30
|
wrangler: props?.wrangler ?? true,
|
|
22
31
|
main: props?.main ?? ".output/server/index.mjs",
|
|
23
32
|
compatibilityFlags: ["nodejs_compat", ...(props?.compatibilityFlags ?? [])],
|
|
@@ -2,6 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { alchemy } from "../alchemy.ts";
|
|
3
3
|
import { Exec } from "../os/exec.ts";
|
|
4
4
|
import { Scope } from "../scope.ts";
|
|
5
|
+
import { isSecret } from "../secret.ts";
|
|
5
6
|
import { detectPackageManager } from "../util/detect-package-manager.ts";
|
|
6
7
|
import { Assets } from "./assets.ts";
|
|
7
8
|
import type { Bindings } from "./bindings.ts";
|
|
@@ -18,6 +19,11 @@ export interface WebsiteProps<B extends Bindings>
|
|
|
18
19
|
*/
|
|
19
20
|
command?: string;
|
|
20
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Additional environment variables to set when running the command
|
|
24
|
+
*/
|
|
25
|
+
commandEnv?: Record<string, string>;
|
|
26
|
+
|
|
21
27
|
/**
|
|
22
28
|
* Whether to memoize the command (only re-run if the command changes)
|
|
23
29
|
*
|
|
@@ -224,7 +230,20 @@ export default {
|
|
|
224
230
|
await Exec("build", {
|
|
225
231
|
cwd,
|
|
226
232
|
command: props.command,
|
|
227
|
-
env:
|
|
233
|
+
env: {
|
|
234
|
+
...(props.env ?? {}),
|
|
235
|
+
...(props.commandEnv ?? {}),
|
|
236
|
+
...Object.fromEntries(
|
|
237
|
+
Object.entries(props.bindings ?? {}).flatMap(([key, value]) => {
|
|
238
|
+
if (isSecret(value)) {
|
|
239
|
+
return [[key, value.unencrypted]];
|
|
240
|
+
} else if (typeof value === "string") {
|
|
241
|
+
return [[key, value]];
|
|
242
|
+
}
|
|
243
|
+
return [];
|
|
244
|
+
}),
|
|
245
|
+
),
|
|
246
|
+
},
|
|
228
247
|
memoize: props.memoize,
|
|
229
248
|
});
|
|
230
249
|
}
|
|
@@ -565,6 +565,13 @@ export async function prepareWorkerMetadata(
|
|
|
565
565
|
configureClassMigration(bindingName, binding);
|
|
566
566
|
}
|
|
567
567
|
(meta.containers ??= []).push({ class_name: binding.className });
|
|
568
|
+
} else if (binding.type === "ratelimit") {
|
|
569
|
+
meta.bindings.push({
|
|
570
|
+
type: "ratelimit",
|
|
571
|
+
name: bindingName,
|
|
572
|
+
namespace_id: binding.namespace_id.toString(),
|
|
573
|
+
simple: binding.simple,
|
|
574
|
+
});
|
|
568
575
|
} else {
|
|
569
576
|
assertNever(
|
|
570
577
|
binding,
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -1166,7 +1166,7 @@ export const _Worker = Resource(
|
|
|
1166
1166
|
|
|
1167
1167
|
if (oldName !== newName) {
|
|
1168
1168
|
if (dispatchNamespace) {
|
|
1169
|
-
|
|
1169
|
+
this.replace(true);
|
|
1170
1170
|
} else {
|
|
1171
1171
|
const renameResponse = await api.patch(
|
|
1172
1172
|
`/accounts/${api.accountId}/workers/services/${oldName}`,
|
|
@@ -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 {
|
|
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
|
*/
|
|
@@ -447,6 +455,13 @@ export interface WranglerJsonSpec {
|
|
|
447
455
|
namespace: string;
|
|
448
456
|
experimental_remote?: boolean;
|
|
449
457
|
}[];
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Unsafe bindings section for experimental features
|
|
461
|
+
*/
|
|
462
|
+
unsafe?: {
|
|
463
|
+
bindings: WranglerJsonRateLimit[];
|
|
464
|
+
};
|
|
450
465
|
}
|
|
451
466
|
|
|
452
467
|
/**
|
|
@@ -534,6 +549,7 @@ function processBindings(
|
|
|
534
549
|
namespace: string;
|
|
535
550
|
experimental_remote?: boolean;
|
|
536
551
|
}[] = [];
|
|
552
|
+
const unsafeBindings: WranglerJsonRateLimit[] = [];
|
|
537
553
|
const containers: {
|
|
538
554
|
class_name: string;
|
|
539
555
|
}[] = [];
|
|
@@ -721,6 +737,13 @@ function processBindings(
|
|
|
721
737
|
namespace: binding.namespaceName,
|
|
722
738
|
experimental_remote: true,
|
|
723
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
|
+
});
|
|
724
747
|
} else if (binding.type === "secret_key") {
|
|
725
748
|
// no-op
|
|
726
749
|
} else if (binding.type === "container") {
|
|
@@ -802,4 +825,10 @@ function processBindings(
|
|
|
802
825
|
if (dispatchNamespaces.length > 0) {
|
|
803
826
|
spec.dispatch_namespaces = dispatchNamespaces;
|
|
804
827
|
}
|
|
828
|
+
|
|
829
|
+
if (unsafeBindings.length > 0) {
|
|
830
|
+
spec.unsafe = {
|
|
831
|
+
bindings: unsafeBindings,
|
|
832
|
+
};
|
|
833
|
+
}
|
|
805
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):
|
|
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) =>
|
|
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
|
|
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
|
+
}
|
package/src/test/vitest.ts
CHANGED
|
@@ -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.
|
|
15
|
-
"@tanstack/react-router-devtools": "^1.
|
|
16
|
-
"@tanstack/react-start": "^1.
|
|
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",
|