alchemy 0.63.0 → 0.64.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.js +97 -87
- package/bin/commands/init.ts +1 -1
- package/bin/services/github-workflow.ts +2 -2
- package/lib/alchemy.d.ts +4 -0
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +10 -4
- package/lib/alchemy.js.map +1 -1
- package/lib/apply.d.ts +3 -0
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +11 -1
- package/lib/apply.js.map +1 -1
- package/lib/cloudflare/bindings.d.ts +3 -1
- package/lib/cloudflare/bindings.d.ts.map +1 -1
- package/lib/cloudflare/bucket.d.ts +2 -1
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/bundle/cloudflare-internal.d.ts +6 -0
- package/lib/cloudflare/bundle/cloudflare-internal.d.ts.map +1 -0
- package/lib/cloudflare/bundle/cloudflare-internal.js +39 -0
- package/lib/cloudflare/bundle/cloudflare-internal.js.map +1 -0
- package/lib/cloudflare/bundle/nodejs-builtin-modules.d.ts +2 -0
- package/lib/cloudflare/bundle/nodejs-builtin-modules.d.ts.map +1 -0
- package/lib/cloudflare/bundle/nodejs-builtin-modules.js +9 -0
- package/lib/cloudflare/bundle/nodejs-builtin-modules.js.map +1 -0
- package/lib/cloudflare/bundle/plugin-als-external.d.ts +6 -0
- package/lib/cloudflare/bundle/plugin-als-external.d.ts.map +1 -0
- package/lib/cloudflare/bundle/plugin-als-external.js +12 -0
- package/lib/cloudflare/bundle/plugin-als-external.js.map +1 -0
- package/lib/cloudflare/bundle/plugin-hybrid-node-compat.d.ts +4 -1
- package/lib/cloudflare/bundle/plugin-hybrid-node-compat.d.ts.map +1 -1
- package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js +23 -23
- package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js.map +1 -1
- package/lib/cloudflare/bundle/plugin-node-compat.d.ts +11 -0
- package/lib/cloudflare/bundle/plugin-node-compat.d.ts.map +1 -0
- package/lib/cloudflare/bundle/plugin-node-compat.js +93 -0
- package/lib/cloudflare/bundle/plugin-node-compat.js.map +1 -0
- package/lib/cloudflare/cloudflare-env-proxy.js +1 -1
- package/lib/cloudflare/cloudflare-env-proxy.js.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/d1-local-migrations.js.map +1 -1
- package/lib/cloudflare/durable-object-namespace.d.ts +1 -0
- package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -1
- package/lib/cloudflare/hyperdrive.js +1 -1
- package/lib/cloudflare/hyperdrive.js.map +1 -1
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +80 -82
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-bundle.d.ts +6 -0
- package/lib/cloudflare/worker-bundle.d.ts.map +1 -1
- package/lib/cloudflare/worker-bundle.js +34 -10
- package/lib/cloudflare/worker-bundle.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +2 -1
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +24 -6
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +2 -4
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +16 -14
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/context.d.ts +3 -1
- package/lib/context.d.ts.map +1 -1
- package/lib/context.js +2 -2
- package/lib/context.js.map +1 -1
- package/lib/destroy.d.ts +8 -0
- package/lib/destroy.d.ts.map +1 -1
- package/lib/destroy.js +20 -4
- package/lib/destroy.js.map +1 -1
- package/lib/neon/project.d.ts.map +1 -1
- package/lib/neon/project.js +4 -5
- package/lib/neon/project.js.map +1 -1
- package/lib/planetscale/api/client.gen.d.ts +1750 -0
- package/lib/planetscale/api/client.gen.d.ts.map +1 -0
- package/lib/planetscale/api/client.gen.js +2401 -0
- package/lib/planetscale/api/client.gen.js.map +1 -0
- package/lib/planetscale/api/index.d.ts +3 -0
- package/lib/planetscale/api/index.d.ts.map +1 -0
- package/lib/planetscale/api/index.js +4 -0
- package/lib/planetscale/api/index.js.map +1 -0
- package/lib/planetscale/api/types.gen.d.ts +12156 -0
- package/lib/planetscale/api/types.gen.d.ts.map +1 -0
- package/lib/planetscale/api/types.gen.js +2 -0
- package/lib/planetscale/api/types.gen.js.map +1 -0
- package/lib/planetscale/branch.d.ts +5 -8
- package/lib/planetscale/branch.d.ts.map +1 -1
- package/lib/planetscale/branch.js +111 -97
- package/lib/planetscale/branch.js.map +1 -1
- package/lib/planetscale/database.d.ts +2 -6
- package/lib/planetscale/database.d.ts.map +1 -1
- package/lib/planetscale/database.js +157 -118
- package/lib/planetscale/database.js.map +1 -1
- package/lib/planetscale/index.d.ts +1 -1
- package/lib/planetscale/index.d.ts.map +1 -1
- package/lib/planetscale/index.js +1 -1
- package/lib/planetscale/index.js.map +1 -1
- package/lib/planetscale/password.d.ts +4 -7
- package/lib/planetscale/password.d.ts.map +1 -1
- package/lib/planetscale/password.js +71 -56
- package/lib/planetscale/password.js.map +1 -1
- package/lib/planetscale/utils.d.ts +8 -6
- package/lib/planetscale/utils.d.ts.map +1 -1
- package/lib/planetscale/utils.js +85 -70
- package/lib/planetscale/utils.js.map +1 -1
- package/lib/scope.d.ts +5 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +11 -3
- package/lib/scope.js.map +1 -1
- package/lib/upstash/redis.js +1 -1
- package/lib/upstash/redis.js.map +1 -1
- package/lib/util/dedent.d.ts +3 -14
- package/lib/util/dedent.d.ts.map +1 -1
- package/lib/util/dedent.js +28 -52
- package/lib/util/dedent.js.map +1 -1
- package/lib/util/idempotent-spawn.d.ts.map +1 -1
- package/lib/util/idempotent-spawn.js +21 -2
- package/lib/util/idempotent-spawn.js.map +1 -1
- package/lib/util/xml.js +1 -1
- package/lib/util/xml.js.map +1 -1
- package/package.json +4 -3
- package/src/alchemy.ts +14 -6
- package/src/apply.ts +13 -1
- package/src/cloudflare/bindings.ts +3 -1
- package/src/cloudflare/bucket.ts +3 -1
- package/src/cloudflare/bundle/cloudflare-internal.ts +42 -0
- package/src/cloudflare/bundle/nodejs-builtin-modules.ts +14 -0
- package/src/cloudflare/bundle/plugin-als-external.ts +13 -0
- package/src/cloudflare/bundle/plugin-hybrid-node-compat.ts +29 -28
- package/src/cloudflare/bundle/plugin-node-compat.ts +114 -0
- package/src/cloudflare/cloudflare-env-proxy.ts +1 -1
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/d1-local-migrations.ts +2 -2
- package/src/cloudflare/durable-object-namespace.ts +1 -1
- package/src/cloudflare/hyperdrive.ts +1 -1
- package/src/cloudflare/website.ts +88 -89
- package/src/cloudflare/worker-bundle.ts +46 -9
- package/src/cloudflare/worker-metadata.ts +3 -1
- package/src/cloudflare/worker.ts +35 -7
- package/src/cloudflare/wrangler.json.ts +108 -110
- package/src/context.ts +5 -3
- package/src/destroy.ts +26 -8
- package/src/neon/project.ts +4 -5
- package/src/planetscale/api/client.gen.ts +2882 -0
- package/src/planetscale/api/index.ts +4 -0
- package/src/planetscale/api/types.gen.ts +13041 -0
- package/src/planetscale/branch.ts +152 -169
- package/src/planetscale/database.ts +195 -204
- package/src/planetscale/index.ts +1 -1
- package/src/planetscale/password.ts +86 -85
- package/src/planetscale/utils.ts +141 -122
- package/src/scope.ts +12 -3
- package/src/upstash/redis.ts +1 -1
- package/src/util/dedent.ts +33 -61
- package/src/util/idempotent-spawn.ts +23 -2
- package/src/util/xml.ts +1 -1
- package/templates/astro/alchemy.run.ts +1 -3
- package/templates/hono/alchemy.run.ts +0 -1
- package/templates/nuxt/alchemy.run.ts +1 -3
- package/templates/react-router/alchemy.run.ts +1 -3
- package/templates/rwsdk/alchemy.run.ts +0 -2
- package/templates/sveltekit/alchemy.run.ts +1 -3
- package/templates/tanstack-start/alchemy.run.ts +1 -3
- package/templates/typescript/alchemy.run.ts +0 -1
- package/templates/vite/alchemy.run.ts +0 -1
- package/workers/cloudflare-state-store.js +58 -58
- package/lib/cloudflare/bundle/externals.d.ts +0 -3
- package/lib/cloudflare/bundle/externals.d.ts.map +0 -1
- package/lib/cloudflare/bundle/externals.js +0 -35
- package/lib/cloudflare/bundle/externals.js.map +0 -1
- package/lib/cloudflare/bundle/plugin-compat-warning.d.ts +0 -10
- package/lib/cloudflare/bundle/plugin-compat-warning.d.ts.map +0 -1
- package/lib/cloudflare/bundle/plugin-compat-warning.js +0 -67
- package/lib/cloudflare/bundle/plugin-compat-warning.js.map +0 -1
- package/lib/planetscale/api.d.ts +0 -53
- package/lib/planetscale/api.d.ts.map +0 -1
- package/lib/planetscale/api.js +0 -84
- package/lib/planetscale/api.js.map +0 -1
- package/src/cloudflare/bundle/externals.ts +0 -36
- package/src/cloudflare/bundle/plugin-compat-warning.ts +0 -94
- package/src/planetscale/api.ts +0 -115
package/src/cloudflare/worker.ts
CHANGED
|
@@ -709,14 +709,29 @@ const _Worker = Resource(
|
|
|
709
709
|
id: string,
|
|
710
710
|
props: WorkerProps<B>,
|
|
711
711
|
) {
|
|
712
|
-
|
|
712
|
+
let adopt = props.adopt ?? this.scope.adopt;
|
|
713
|
+
const workerName =
|
|
714
|
+
props.name ?? this.output?.name ?? this.scope.createPhysicalName(id);
|
|
715
|
+
if (this.phase === "create" && !props.adopt) {
|
|
716
|
+
// it is possible that this worker already exists and was created by the old Website wrapper with a nested scope
|
|
717
|
+
// we need to detect this and set adopt=true so that the previous version will be adopted seamlessly
|
|
718
|
+
|
|
719
|
+
// HEURISTIC: `this.scope` would previously be the nested outer scope alchemy.run
|
|
720
|
+
// so, if `this.scope` has a child of `wrangler.jsonc`, then it is likely that it was created by the old Website wrapper with a nested scope
|
|
721
|
+
if (await this.scope.has("wrangler.jsonc", "cloudflare::WranglerJson")) {
|
|
722
|
+
logger.warn(
|
|
723
|
+
`Migrating Worker '${workerName}' from the legacy Website wrapper.`,
|
|
724
|
+
);
|
|
725
|
+
props.adopt = true;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
713
729
|
const options = (() => {
|
|
714
730
|
if (props.projectRoot) {
|
|
715
731
|
logger.warn("projectRoot is deprecated, use cwd instead");
|
|
716
732
|
props.cwd = props.projectRoot;
|
|
717
733
|
}
|
|
718
|
-
|
|
719
|
-
props.name ?? this.output?.name ?? this.scope.createPhysicalName(id);
|
|
734
|
+
|
|
720
735
|
const cwd = path.resolve(props.cwd ?? process.cwd());
|
|
721
736
|
const compatibilityDate =
|
|
722
737
|
props.compatibilityDate ?? DEFAULT_COMPATIBILITY_DATE;
|
|
@@ -739,7 +754,8 @@ const _Worker = Resource(
|
|
|
739
754
|
cwd,
|
|
740
755
|
compatibilityDate,
|
|
741
756
|
compatibilityFlags,
|
|
742
|
-
outdir:
|
|
757
|
+
outdir:
|
|
758
|
+
props.bundle?.outdir ?? path.join(cwd, ".alchemy", "out", workerName),
|
|
743
759
|
sourceMap: "sourceMap" in props ? props.sourceMap : undefined,
|
|
744
760
|
});
|
|
745
761
|
|
|
@@ -756,19 +772,19 @@ const _Worker = Resource(
|
|
|
756
772
|
containers.push(binding);
|
|
757
773
|
} else if (
|
|
758
774
|
binding.type === "workflow" &&
|
|
759
|
-
(!binding.scriptName || binding.scriptName ===
|
|
775
|
+
(!binding.scriptName || binding.scriptName === workerName)
|
|
760
776
|
) {
|
|
761
777
|
workflows.push(binding);
|
|
762
778
|
} else if (
|
|
763
779
|
binding.type === "durable_object_namespace" &&
|
|
764
|
-
(!binding.scriptName || binding.scriptName ===
|
|
780
|
+
(!binding.scriptName || binding.scriptName === workerName)
|
|
765
781
|
) {
|
|
766
782
|
durableObjects.push(binding);
|
|
767
783
|
}
|
|
768
784
|
}
|
|
769
785
|
|
|
770
786
|
return {
|
|
771
|
-
name,
|
|
787
|
+
name: workerName,
|
|
772
788
|
cwd,
|
|
773
789
|
compatibilityDate,
|
|
774
790
|
compatibilityFlags,
|
|
@@ -780,7 +796,19 @@ const _Worker = Resource(
|
|
|
780
796
|
durableObjects,
|
|
781
797
|
};
|
|
782
798
|
})();
|
|
799
|
+
|
|
783
800
|
if (this.phase === "delete") {
|
|
801
|
+
// Heuristic: we must detect the case where this is the Worker wrapped in the old Website nested scope and not delete it
|
|
802
|
+
// we must not delete it because what we're actually doing is migrating to a flat worker
|
|
803
|
+
// we will achieve this by checking for a sibling resource with ID wrangler.jsonc and type cloudflare::WranglerJson
|
|
804
|
+
if (await this.scope.has("wrangler.jsonc", "cloudflare::WranglerJson")) {
|
|
805
|
+
// skip deletion
|
|
806
|
+
logger.warn(
|
|
807
|
+
`Migrating Worker '${options.name}' from the legacy Website wrapper.`,
|
|
808
|
+
);
|
|
809
|
+
return this.destroy(true);
|
|
810
|
+
}
|
|
811
|
+
|
|
784
812
|
if (options.bundle.isOk()) {
|
|
785
813
|
await options.bundle.value.delete?.();
|
|
786
814
|
}
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import type { Context } from "../context.ts";
|
|
4
4
|
import { Resource } from "../resource.ts";
|
|
5
|
+
import { Scope } from "../scope.ts";
|
|
5
6
|
import { isSecret } from "../secret.ts";
|
|
6
7
|
import { assertNever } from "../util/assert-never.ts";
|
|
7
8
|
import {
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
type Bindings,
|
|
10
11
|
type WorkerBindingRateLimit,
|
|
11
12
|
} from "./bindings.ts";
|
|
13
|
+
import type { R2BucketJurisdiction } from "./bucket.ts";
|
|
12
14
|
import type { DurableObjectNamespace } from "./durable-object-namespace.ts";
|
|
13
15
|
import type { EventSource } from "./event-source.ts";
|
|
14
16
|
import { isQueueEventSource } from "./event-source.ts";
|
|
@@ -86,9 +88,7 @@ export interface WranglerJsonProps {
|
|
|
86
88
|
/**
|
|
87
89
|
* Output returned after WranglerJson creation/update
|
|
88
90
|
*/
|
|
89
|
-
export interface WranglerJson
|
|
90
|
-
extends Resource<"cloudflare::WranglerJson">,
|
|
91
|
-
WranglerJsonProps {
|
|
91
|
+
export interface WranglerJson extends WranglerJsonProps {
|
|
92
92
|
/**
|
|
93
93
|
* Time at which the file was created
|
|
94
94
|
*/
|
|
@@ -110,128 +110,123 @@ export interface WranglerJson
|
|
|
110
110
|
spec: WranglerJsonSpec;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
// we are deprecating the WranglerJson resource (it is now just a funciton)
|
|
114
|
+
// but, a user may still have a resource that depends on it, so we register a no-op dummy resource so that it can be cleanly delted
|
|
115
|
+
Resource("cloudflare::WranglerJson", async function (this: Context<any>) {
|
|
116
|
+
if (this.phase === "delete") {
|
|
117
|
+
return this.destroy();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
throw new Error("Not implemented");
|
|
121
|
+
});
|
|
122
|
+
|
|
113
123
|
/**
|
|
114
124
|
* Resource for managing wrangler.json configuration files
|
|
115
125
|
*/
|
|
116
|
-
export
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
},
|
|
121
|
-
async function (
|
|
122
|
-
this: Context<WranglerJson>,
|
|
123
|
-
_id: string,
|
|
124
|
-
props: WranglerJsonProps,
|
|
125
|
-
): Promise<WranglerJson> {
|
|
126
|
-
if (this.phase === "delete") {
|
|
127
|
-
return this.destroy();
|
|
128
|
-
}
|
|
126
|
+
export async function WranglerJson(
|
|
127
|
+
props: WranglerJsonProps,
|
|
128
|
+
): Promise<WranglerJson> {
|
|
129
|
+
const cwd = props.worker.cwd ? path.resolve(props.worker.cwd) : process.cwd();
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const toAbsolute = <T extends string | undefined>(input: T): T => {
|
|
135
|
-
return (input ? path.resolve(cwd, input) : undefined) as T;
|
|
136
|
-
};
|
|
131
|
+
const toAbsolute = <T extends string | undefined>(input: T): T => {
|
|
132
|
+
return (input ? path.resolve(cwd, input) : undefined) as T;
|
|
133
|
+
};
|
|
137
134
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
135
|
+
const main = toAbsolute(props.main ?? props.worker.entrypoint);
|
|
136
|
+
let filePath = toAbsolute(props.path ?? cwd);
|
|
137
|
+
if (!path.basename(filePath).match(".json")) {
|
|
138
|
+
filePath = path.join(filePath, props.name ?? "wrangler.jsonc");
|
|
139
|
+
}
|
|
143
140
|
|
|
144
|
-
|
|
141
|
+
const dirname = path.dirname(filePath);
|
|
145
142
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
143
|
+
if (!main) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
"Worker must have an entrypoint to generate a wrangler.json",
|
|
146
|
+
);
|
|
147
|
+
}
|
|
151
148
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
149
|
+
const worker = props.worker;
|
|
150
|
+
|
|
151
|
+
const spec: WranglerJsonSpec = {
|
|
152
|
+
name: worker.name,
|
|
153
|
+
// Use entrypoint as main if it exists
|
|
154
|
+
main: path.relative(dirname, main),
|
|
155
|
+
// see: https://developers.cloudflare.com/workers/configuration/compatibility-dates/
|
|
156
|
+
compatibility_date: worker.compatibilityDate,
|
|
157
|
+
compatibility_flags: props.worker.compatibilityFlags,
|
|
158
|
+
assets: props.assets
|
|
159
|
+
? {
|
|
160
|
+
directory: toAbsolute(props.assets.directory),
|
|
161
|
+
binding: props.assets.binding,
|
|
162
|
+
not_found_handling: props.worker.assets?.not_found_handling,
|
|
163
|
+
html_handling: props.worker.assets?.html_handling,
|
|
164
|
+
run_worker_first: props.worker.assets?.run_worker_first,
|
|
165
|
+
}
|
|
166
|
+
: undefined,
|
|
167
|
+
placement: worker.placement,
|
|
168
|
+
limits: worker.limits,
|
|
169
|
+
};
|
|
173
170
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
171
|
+
// Process bindings if they exist
|
|
172
|
+
if (worker.bindings) {
|
|
173
|
+
processBindings(
|
|
174
|
+
spec,
|
|
175
|
+
worker.bindings,
|
|
176
|
+
worker.eventSources,
|
|
177
|
+
worker.name,
|
|
178
|
+
cwd,
|
|
179
|
+
props.secrets ?? false,
|
|
180
|
+
Scope.current.local && !props.worker.dev?.remote,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
186
183
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
// Add environment variables as vars
|
|
185
|
+
if (worker.env) {
|
|
186
|
+
spec.vars = { ...worker.env };
|
|
187
|
+
}
|
|
191
188
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
189
|
+
if (worker.crons && worker.crons.length > 0) {
|
|
190
|
+
spec.triggers = { crons: worker.crons };
|
|
191
|
+
}
|
|
195
192
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
193
|
+
if (spec.assets) {
|
|
194
|
+
spec.assets.directory = path.relative(dirname, spec.assets.directory);
|
|
195
|
+
}
|
|
199
196
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
),
|
|
197
|
+
// Apply the wrangler configuration hook as the final transformation
|
|
198
|
+
const finalSpec = props.transform?.wrangler
|
|
199
|
+
? await props.transform.wrangler(spec)
|
|
200
|
+
: spec;
|
|
201
|
+
|
|
202
|
+
await fs.mkdir(dirname, { recursive: true });
|
|
203
|
+
if (props.secrets) {
|
|
204
|
+
// If secrets are enabled, decrypt them in the wrangler.json file,
|
|
205
|
+
// but do not modify `finalSpec` so that way secrets aren't written to state unencrypted.
|
|
206
|
+
const withSecretsUnwrapped = {
|
|
207
|
+
...finalSpec,
|
|
208
|
+
vars: {
|
|
209
|
+
...finalSpec.vars,
|
|
210
|
+
...Object.fromEntries(
|
|
211
|
+
Object.entries(finalSpec.vars ?? {}).map(([key, value]) =>
|
|
212
|
+
isSecret(value) ? [key, value.unencrypted] : [key, value],
|
|
217
213
|
),
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
214
|
+
),
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
await writeJSON(filePath, withSecretsUnwrapped);
|
|
218
|
+
} else {
|
|
219
|
+
await writeJSON(filePath, finalSpec);
|
|
220
|
+
}
|
|
224
221
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
},
|
|
234
|
-
);
|
|
222
|
+
return {
|
|
223
|
+
...props,
|
|
224
|
+
path: path.relative(cwd, filePath),
|
|
225
|
+
spec: finalSpec,
|
|
226
|
+
createdAt: Date.now(),
|
|
227
|
+
updatedAt: Date.now(),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
235
230
|
|
|
236
231
|
const writeJSON = async (filePath: string, content: any) => {
|
|
237
232
|
await fs.writeFile(filePath, `${JSON.stringify(content, null, 2)}\n`);
|
|
@@ -537,6 +532,7 @@ function processBindings(
|
|
|
537
532
|
binding: string;
|
|
538
533
|
bucket_name: string;
|
|
539
534
|
preview_bucket_name: string;
|
|
535
|
+
jurisdiction?: R2BucketJurisdiction;
|
|
540
536
|
}[] = [];
|
|
541
537
|
const services: { binding: string; service: string; environment?: string }[] =
|
|
542
538
|
[];
|
|
@@ -683,6 +679,8 @@ function processBindings(
|
|
|
683
679
|
binding: bindingName,
|
|
684
680
|
bucket_name: name,
|
|
685
681
|
preview_bucket_name: name,
|
|
682
|
+
jurisdiction:
|
|
683
|
+
binding.jurisdiction === "default" ? undefined : binding.jurisdiction,
|
|
686
684
|
...(binding.dev?.remote ? { experimental_remote: true } : {}),
|
|
687
685
|
});
|
|
688
686
|
} else if (binding.type === "secret") {
|
package/src/context.ts
CHANGED
|
@@ -65,8 +65,10 @@ export interface BaseContext<Out extends Resource> {
|
|
|
65
65
|
*
|
|
66
66
|
* It is so that the resource lifecycle handler can "return never" instead of
|
|
67
67
|
* "return undefined" so that `await MyResource()` always returns a value.
|
|
68
|
+
*
|
|
69
|
+
* @param retainChildren - Whether to retain the children of the resource.
|
|
68
70
|
*/
|
|
69
|
-
destroy(): never;
|
|
71
|
+
destroy(retainChildren?: boolean): never;
|
|
70
72
|
/**
|
|
71
73
|
* Register a cleanup function that will be called when the process exits.
|
|
72
74
|
*
|
|
@@ -161,8 +163,8 @@ export function context<
|
|
|
161
163
|
return value;
|
|
162
164
|
},
|
|
163
165
|
quiet: scope.quiet,
|
|
164
|
-
destroy: () => {
|
|
165
|
-
throw new DestroyedSignal();
|
|
166
|
+
destroy: (retainChildren = false) => {
|
|
167
|
+
throw new DestroyedSignal(retainChildren);
|
|
166
168
|
},
|
|
167
169
|
onCleanup: (fn: () => void | Promise<void>) => {
|
|
168
170
|
// make the function idempotent so repeated calls don't cause the process to hang
|
package/src/destroy.ts
CHANGED
|
@@ -15,7 +15,16 @@ import type { State } from "./state.ts";
|
|
|
15
15
|
import { formatFQN } from "./util/cli.ts";
|
|
16
16
|
import { logger } from "./util/logger.ts";
|
|
17
17
|
|
|
18
|
-
export
|
|
18
|
+
export function isDestroyedSignal(error: any): error is DestroyedSignal {
|
|
19
|
+
return error instanceof Error && (error as any).kind === "DestroyedSignal";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class DestroyedSignal extends Error {
|
|
23
|
+
readonly kind = "DestroyedSignal";
|
|
24
|
+
constructor(public readonly noop: boolean) {
|
|
25
|
+
super();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
19
28
|
|
|
20
29
|
export type DestroyStrategy = "sequential" | "parallel";
|
|
21
30
|
|
|
@@ -28,6 +37,10 @@ export interface DestroyOptions {
|
|
|
28
37
|
props?: ResourceProps | undefined;
|
|
29
38
|
output?: Resource<string>;
|
|
30
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* If true, children of the resource will not be destroyed (but their state will be deleted).
|
|
42
|
+
*/
|
|
43
|
+
noop?: boolean;
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
function isScopeArgs(a: any): a is [scope: Scope, options?: DestroyOptions] {
|
|
@@ -98,7 +111,7 @@ export async function destroy<Type extends string>(
|
|
|
98
111
|
const quiet = options?.quiet ?? scope.quiet;
|
|
99
112
|
|
|
100
113
|
try {
|
|
101
|
-
if (!quiet) {
|
|
114
|
+
if (!quiet && !options?.noop) {
|
|
102
115
|
logger.task(instance[ResourceFQN], {
|
|
103
116
|
prefix: options?.replace ? "cleanup" : "deleting",
|
|
104
117
|
prefixColor: options?.replace ? "magenta" : "redBright",
|
|
@@ -149,6 +162,8 @@ export async function destroy<Type extends string>(
|
|
|
149
162
|
});
|
|
150
163
|
|
|
151
164
|
let nestedScope: Scope | undefined;
|
|
165
|
+
let noop = options?.noop ?? false;
|
|
166
|
+
|
|
152
167
|
try {
|
|
153
168
|
// BUG: this does not restore persisted scope
|
|
154
169
|
await alchemy.run(
|
|
@@ -158,17 +173,19 @@ export async function destroy<Type extends string>(
|
|
|
158
173
|
isResource: instance[ResourceKind] !== "alchemy::Scope",
|
|
159
174
|
parent: scope,
|
|
160
175
|
destroyStrategy: instance[DestroyStrategy] ?? "sequential",
|
|
176
|
+
noop,
|
|
161
177
|
},
|
|
162
178
|
async (scope) => {
|
|
163
179
|
nestedScope = options?.replace?.props == null ? scope : undefined;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
);
|
|
180
|
+
if (noop) {
|
|
181
|
+
return ctx.destroy(noop);
|
|
182
|
+
}
|
|
183
|
+
return Provider.handler.bind(ctx)(instance[ResourceID], ctx.props);
|
|
168
184
|
},
|
|
169
185
|
);
|
|
170
186
|
} catch (err) {
|
|
171
|
-
if (err
|
|
187
|
+
if (isDestroyedSignal(err)) {
|
|
188
|
+
noop = noop || err.noop;
|
|
172
189
|
// TODO: should we fail if the DestroyedSignal is not thrown?
|
|
173
190
|
} else {
|
|
174
191
|
throw err;
|
|
@@ -178,6 +195,7 @@ export async function destroy<Type extends string>(
|
|
|
178
195
|
if (nestedScope) {
|
|
179
196
|
await destroy(nestedScope, {
|
|
180
197
|
...options,
|
|
198
|
+
noop,
|
|
181
199
|
strategy: instance[DestroyStrategy] ?? "sequential",
|
|
182
200
|
});
|
|
183
201
|
}
|
|
@@ -195,7 +213,7 @@ export async function destroy<Type extends string>(
|
|
|
195
213
|
await scope.set("pendingDeletions", pendingDeletions);
|
|
196
214
|
}
|
|
197
215
|
|
|
198
|
-
if (!quiet) {
|
|
216
|
+
if (!quiet && !options?.noop) {
|
|
199
217
|
logger.task(instance[ResourceFQN], {
|
|
200
218
|
prefix: options?.replace ? "cleaned" : "deleted",
|
|
201
219
|
prefixColor: "greenBright",
|
package/src/neon/project.ts
CHANGED
|
@@ -630,15 +630,14 @@ export const NeonProject = Resource(
|
|
|
630
630
|
default_branch_name: props.default_branch_name,
|
|
631
631
|
baseUrl: props.baseUrl,
|
|
632
632
|
// Add all available data
|
|
633
|
-
// @ts-
|
|
633
|
+
// @ts-expect-error - api ensures they're non-empty
|
|
634
634
|
connection_uris: response.connection_uris,
|
|
635
|
-
// @ts-
|
|
635
|
+
// @ts-expect-error
|
|
636
636
|
roles: response.roles,
|
|
637
|
-
// @ts-
|
|
637
|
+
// @ts-expect-error
|
|
638
638
|
databases: response.databases,
|
|
639
|
-
// @ts-ignore
|
|
640
639
|
branch: response.branch,
|
|
641
|
-
// @ts-
|
|
640
|
+
// @ts-expect-error
|
|
642
641
|
endpoints: response.endpoints,
|
|
643
642
|
});
|
|
644
643
|
} catch (error) {
|