wrangler 2.0.6 → 2.0.9
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/README.md +1 -1
- package/bin/wrangler.js +16 -4
- package/package.json +6 -4
- package/pages/functions/buildPlugin.ts +13 -0
- package/pages/functions/buildWorker.ts +13 -0
- package/src/__tests__/configuration.test.ts +132 -60
- package/src/__tests__/dev.test.tsx +168 -67
- package/src/__tests__/helpers/mock-dialogs.ts +41 -1
- package/src/__tests__/index.test.ts +25 -10
- package/src/__tests__/init.test.ts +252 -131
- package/src/__tests__/kv.test.ts +16 -16
- package/src/__tests__/package-manager.test.ts +154 -7
- package/src/__tests__/pages.test.ts +442 -38
- package/src/__tests__/parse.test.ts +5 -1
- package/src/__tests__/publish.test.ts +377 -84
- package/src/__tests__/secret.test.ts +4 -4
- package/src/__tests__/whoami.test.tsx +34 -0
- package/src/abort.d.ts +3 -0
- package/src/cfetch/index.ts +21 -4
- package/src/cfetch/internal.ts +20 -18
- package/src/config/config.ts +1 -1
- package/src/config/index.ts +162 -0
- package/src/config/validation.ts +77 -29
- package/src/create-worker-preview.ts +32 -22
- package/src/dev/dev.tsx +6 -16
- package/src/dev/remote.tsx +40 -16
- package/src/dialogs.tsx +48 -0
- package/src/durable.ts +102 -0
- package/src/index.tsx +291 -207
- package/src/inspect.ts +39 -0
- package/src/kv.ts +74 -25
- package/src/open-in-browser.ts +5 -12
- package/src/package-manager.ts +50 -3
- package/src/pages.tsx +218 -61
- package/src/parse.ts +21 -4
- package/src/proxy.ts +38 -22
- package/src/publish.ts +166 -108
- package/src/sites.tsx +8 -8
- package/src/user.tsx +12 -1
- package/src/whoami.tsx +3 -2
- package/src/worker.ts +2 -1
- package/src/zones.ts +73 -0
- package/templates/new-worker-scheduled.js +17 -0
- package/templates/new-worker-scheduled.ts +32 -0
- package/templates/new-worker.ts +16 -1
- package/wrangler-dist/cli.js +33066 -20052
|
@@ -148,7 +148,7 @@ describe("wrangler secret", () => {
|
|
|
148
148
|
}
|
|
149
149
|
expect(std.out).toMatchInlineSnapshot(`
|
|
150
150
|
"
|
|
151
|
-
[32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new
|
|
151
|
+
[32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose[0m"
|
|
152
152
|
`);
|
|
153
153
|
expect(std.err).toMatchInlineSnapshot(`
|
|
154
154
|
"[31mX [41;31m[[41;97mERROR[41;31m][0m [1mMissing script name[0m
|
|
@@ -204,7 +204,7 @@ describe("wrangler secret", () => {
|
|
|
204
204
|
|
|
205
205
|
expect(std.out).toMatchInlineSnapshot(`
|
|
206
206
|
"
|
|
207
|
-
[32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new
|
|
207
|
+
[32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose[0m"
|
|
208
208
|
`);
|
|
209
209
|
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
210
210
|
});
|
|
@@ -372,7 +372,7 @@ describe("wrangler secret", () => {
|
|
|
372
372
|
}
|
|
373
373
|
expect(std.out).toMatchInlineSnapshot(`
|
|
374
374
|
"
|
|
375
|
-
[32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new
|
|
375
|
+
[32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose[0m"
|
|
376
376
|
`);
|
|
377
377
|
expect(std.err).toMatchInlineSnapshot(`
|
|
378
378
|
"[31mX [41;31m[[41;97mERROR[41;31m][0m [1mMissing script name[0m
|
|
@@ -468,7 +468,7 @@ describe("wrangler secret", () => {
|
|
|
468
468
|
}
|
|
469
469
|
expect(std.out).toMatchInlineSnapshot(`
|
|
470
470
|
"
|
|
471
|
-
[32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new
|
|
471
|
+
[32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose[0m"
|
|
472
472
|
`);
|
|
473
473
|
expect(std.err).toMatchInlineSnapshot(`
|
|
474
474
|
"[31mX [41;31m[[41;97mERROR[41;31m][0m [1mMissing script name[0m
|
|
@@ -9,6 +9,8 @@ import { runInTempDir } from "./helpers/run-in-tmp";
|
|
|
9
9
|
import type { UserInfo } from "../whoami";
|
|
10
10
|
|
|
11
11
|
describe("getUserInfo()", () => {
|
|
12
|
+
const ENV_COPY = process.env;
|
|
13
|
+
|
|
12
14
|
runInTempDir({ homedir: "./home" });
|
|
13
15
|
const std = mockConsoleMethods();
|
|
14
16
|
const { setIsTTY } = useMockIsTTY();
|
|
@@ -17,6 +19,10 @@ describe("getUserInfo()", () => {
|
|
|
17
19
|
setIsTTY(true);
|
|
18
20
|
});
|
|
19
21
|
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
process.env = ENV_COPY;
|
|
24
|
+
});
|
|
25
|
+
|
|
20
26
|
it("should return undefined if there is no config file", async () => {
|
|
21
27
|
const userInfo = await getUserInfo();
|
|
22
28
|
expect(userInfo).toBeUndefined();
|
|
@@ -28,6 +34,34 @@ describe("getUserInfo()", () => {
|
|
|
28
34
|
expect(userInfo).toBeUndefined();
|
|
29
35
|
});
|
|
30
36
|
|
|
37
|
+
it("should say it's using an API token when one is set", async () => {
|
|
38
|
+
process.env = {
|
|
39
|
+
CLOUDFLARE_API_TOKEN: "123456789",
|
|
40
|
+
};
|
|
41
|
+
setMockResponse("/user", () => {
|
|
42
|
+
return { email: "user@example.com" };
|
|
43
|
+
});
|
|
44
|
+
setMockResponse("/accounts", () => {
|
|
45
|
+
return [
|
|
46
|
+
{ name: "Account One", id: "account-1" },
|
|
47
|
+
{ name: "Account Two", id: "account-2" },
|
|
48
|
+
{ name: "Account Three", id: "account-3" },
|
|
49
|
+
];
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const userInfo = await getUserInfo();
|
|
53
|
+
expect(userInfo).toEqual({
|
|
54
|
+
authType: "API",
|
|
55
|
+
apiToken: "123456789",
|
|
56
|
+
email: "user@example.com",
|
|
57
|
+
accounts: [
|
|
58
|
+
{ name: "Account One", id: "account-1" },
|
|
59
|
+
{ name: "Account Two", id: "account-2" },
|
|
60
|
+
{ name: "Account Three", id: "account-3" },
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
31
65
|
it("should return the user's email and accounts if authenticated via config token", async () => {
|
|
32
66
|
writeAuthConfigFile({ oauth_token: "some-oauth-token" });
|
|
33
67
|
|
package/src/abort.d.ts
ADDED
package/src/cfetch/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { getCloudflareAPIBaseURL as getCloudflareApiBaseUrl } from "./internal";
|
|
|
10
10
|
export interface FetchError {
|
|
11
11
|
code: number;
|
|
12
12
|
message: string;
|
|
13
|
+
error_chain?: FetchError[];
|
|
13
14
|
}
|
|
14
15
|
export interface FetchResult<ResponseType = unknown> {
|
|
15
16
|
success: boolean;
|
|
@@ -27,12 +28,14 @@ export { fetchKVGetValue } from "./internal";
|
|
|
27
28
|
export async function fetchResult<ResponseType>(
|
|
28
29
|
resource: string,
|
|
29
30
|
init: RequestInit = {},
|
|
30
|
-
queryParams?: URLSearchParams
|
|
31
|
+
queryParams?: URLSearchParams,
|
|
32
|
+
abortSignal?: AbortSignal
|
|
31
33
|
): Promise<ResponseType> {
|
|
32
34
|
const json = await fetchInternal<FetchResult<ResponseType>>(
|
|
33
35
|
resource,
|
|
34
36
|
init,
|
|
35
|
-
queryParams
|
|
37
|
+
queryParams,
|
|
38
|
+
abortSignal
|
|
36
39
|
);
|
|
37
40
|
if (json.success) {
|
|
38
41
|
return json.result;
|
|
@@ -83,9 +86,9 @@ function throwFetchError(
|
|
|
83
86
|
response: FetchResult<unknown>
|
|
84
87
|
): never {
|
|
85
88
|
const error = new ParseError({
|
|
86
|
-
text:
|
|
89
|
+
text: `A request to the Cloudflare API (${resource}) failed.`,
|
|
87
90
|
notes: response.errors.map((err) => ({
|
|
88
|
-
text: err
|
|
91
|
+
text: renderError(err),
|
|
89
92
|
})),
|
|
90
93
|
});
|
|
91
94
|
// add the first error code directly to this error
|
|
@@ -102,3 +105,17 @@ function hasCursor(result_info: unknown): result_info is { cursor: string } {
|
|
|
102
105
|
const cursor = (result_info as { cursor: string } | undefined)?.cursor;
|
|
103
106
|
return cursor !== undefined && cursor !== null && cursor !== "";
|
|
104
107
|
}
|
|
108
|
+
|
|
109
|
+
function renderError(err: FetchError, level = 0): string {
|
|
110
|
+
const chainedMessages =
|
|
111
|
+
err.error_chain
|
|
112
|
+
?.map(
|
|
113
|
+
(chainedError) =>
|
|
114
|
+
`\n${" ".repeat(level)}- ${renderError(chainedError, level + 1)}`
|
|
115
|
+
)
|
|
116
|
+
.join("\n") ?? "";
|
|
117
|
+
return (
|
|
118
|
+
(err.code ? `${err.message} [code: ${err.code}]` : err.message) +
|
|
119
|
+
chainedMessages
|
|
120
|
+
);
|
|
121
|
+
}
|
package/src/cfetch/internal.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
1
2
|
import { fetch, Headers } from "undici";
|
|
3
|
+
import { version as wranglerVersion } from "../../package.json";
|
|
2
4
|
import { getEnvironmentVariableFactory } from "../environment-variables";
|
|
3
5
|
import { ParseError, parseJSON } from "../parse";
|
|
4
|
-
import {
|
|
6
|
+
import { loginOrRefreshIfRequired, requireApiToken } from "../user";
|
|
5
7
|
import type { URLSearchParams } from "node:url";
|
|
6
8
|
import type { RequestInit, HeadersInit } from "undici";
|
|
7
9
|
|
|
@@ -26,12 +28,18 @@ export const getCloudflareAPIBaseURL = getEnvironmentVariableFactory({
|
|
|
26
28
|
export async function fetchInternal<ResponseType>(
|
|
27
29
|
resource: string,
|
|
28
30
|
init: RequestInit = {},
|
|
29
|
-
queryParams?: URLSearchParams
|
|
31
|
+
queryParams?: URLSearchParams,
|
|
32
|
+
abortSignal?: AbortSignal
|
|
30
33
|
): Promise<ResponseType> {
|
|
34
|
+
assert(
|
|
35
|
+
resource.startsWith("/"),
|
|
36
|
+
`CF API fetch - resource path must start with a "/" but got "${resource}"`
|
|
37
|
+
);
|
|
31
38
|
await requireLoggedIn();
|
|
32
39
|
const apiToken = requireApiToken();
|
|
33
40
|
const headers = cloneHeaders(init.headers);
|
|
34
|
-
|
|
41
|
+
addAuthorizationHeaderIfUnspecified(headers, apiToken);
|
|
42
|
+
addUserAgent(headers);
|
|
35
43
|
|
|
36
44
|
const queryString = queryParams ? `?${queryParams.toString()}` : "";
|
|
37
45
|
const method = init.method ?? "GET";
|
|
@@ -41,11 +49,12 @@ export async function fetchInternal<ResponseType>(
|
|
|
41
49
|
method,
|
|
42
50
|
...init,
|
|
43
51
|
headers,
|
|
52
|
+
signal: abortSignal,
|
|
44
53
|
}
|
|
45
54
|
);
|
|
46
55
|
const jsonText = await response.text();
|
|
47
56
|
try {
|
|
48
|
-
return parseJSON(jsonText)
|
|
57
|
+
return parseJSON<ResponseType>(jsonText);
|
|
49
58
|
} catch (err) {
|
|
50
59
|
throw new ParseError({
|
|
51
60
|
text: "Received a malformed response from the API",
|
|
@@ -86,24 +95,17 @@ async function requireLoggedIn(): Promise<void> {
|
|
|
86
95
|
}
|
|
87
96
|
}
|
|
88
97
|
|
|
89
|
-
function
|
|
90
|
-
const authToken = getAPIToken();
|
|
91
|
-
if (!authToken) {
|
|
92
|
-
throw new Error("No API token found.");
|
|
93
|
-
}
|
|
94
|
-
return authToken;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function addAuthorizationHeader(
|
|
98
|
+
function addAuthorizationHeaderIfUnspecified(
|
|
98
99
|
headers: Record<string, string>,
|
|
99
100
|
apiToken: string
|
|
100
101
|
): void {
|
|
101
|
-
if ("Authorization" in headers) {
|
|
102
|
-
|
|
103
|
-
"The request already specifies an authorisation header - cannot add a new one."
|
|
104
|
-
);
|
|
102
|
+
if (!("Authorization" in headers)) {
|
|
103
|
+
headers["Authorization"] = `Bearer ${apiToken}`;
|
|
105
104
|
}
|
|
106
|
-
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function addUserAgent(headers: Record<string, string>): void {
|
|
108
|
+
headers["User-Agent"] = `wrangler/${wranglerVersion}`;
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
/**
|
package/src/config/config.ts
CHANGED
package/src/config/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { findUpSync } from "find-up";
|
|
|
2
2
|
import { logger } from "../logger";
|
|
3
3
|
import { parseTOML, readFileSync } from "../parse";
|
|
4
4
|
import { normalizeAndValidateConfig } from "./validation";
|
|
5
|
+
import type { CfWorkerInit } from "../worker";
|
|
5
6
|
import type { Config, RawConfig } from "./config";
|
|
6
7
|
|
|
7
8
|
export type {
|
|
@@ -61,3 +62,164 @@ export function findWranglerToml(
|
|
|
61
62
|
const configPath = findUpSync("wrangler.toml", { cwd: referencePath });
|
|
62
63
|
return configPath;
|
|
63
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Print all the bindings a worker using a given config would have access to
|
|
68
|
+
*/
|
|
69
|
+
export function printBindings(bindings: CfWorkerInit["bindings"]) {
|
|
70
|
+
const truncate = (item: string | Record<string, unknown>) => {
|
|
71
|
+
const s = typeof item === "string" ? item : JSON.stringify(item);
|
|
72
|
+
const maxLength = 40;
|
|
73
|
+
if (s.length < maxLength) {
|
|
74
|
+
return s;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return `${s.substring(0, maxLength - 3)}...`;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const output: { type: string; entries: { key: string; value: string }[] }[] =
|
|
81
|
+
[];
|
|
82
|
+
|
|
83
|
+
const {
|
|
84
|
+
data_blobs,
|
|
85
|
+
durable_objects,
|
|
86
|
+
kv_namespaces,
|
|
87
|
+
r2_buckets,
|
|
88
|
+
services,
|
|
89
|
+
text_blobs,
|
|
90
|
+
unsafe,
|
|
91
|
+
vars,
|
|
92
|
+
wasm_modules,
|
|
93
|
+
} = bindings;
|
|
94
|
+
|
|
95
|
+
if (data_blobs !== undefined && Object.keys(data_blobs).length > 0) {
|
|
96
|
+
output.push({
|
|
97
|
+
type: "Data Blobs",
|
|
98
|
+
entries: Object.entries(data_blobs).map(([key, value]) => ({
|
|
99
|
+
key,
|
|
100
|
+
value: truncate(value),
|
|
101
|
+
})),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (durable_objects !== undefined && durable_objects.bindings.length > 0) {
|
|
106
|
+
output.push({
|
|
107
|
+
type: "Durable Objects",
|
|
108
|
+
entries: durable_objects.bindings.map(
|
|
109
|
+
({ name, class_name, script_name, environment }) => {
|
|
110
|
+
let value = class_name;
|
|
111
|
+
if (script_name) {
|
|
112
|
+
value += ` (defined in ${script_name})`;
|
|
113
|
+
}
|
|
114
|
+
if (environment) {
|
|
115
|
+
value += ` - ${environment}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
key: name,
|
|
120
|
+
value,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (kv_namespaces !== undefined && kv_namespaces.length > 0) {
|
|
128
|
+
output.push({
|
|
129
|
+
type: "KV Namespaces",
|
|
130
|
+
entries: kv_namespaces.map(({ binding, id }) => {
|
|
131
|
+
return {
|
|
132
|
+
key: binding,
|
|
133
|
+
value: id,
|
|
134
|
+
};
|
|
135
|
+
}),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (r2_buckets !== undefined && r2_buckets.length > 0) {
|
|
140
|
+
output.push({
|
|
141
|
+
type: "R2 Buckets",
|
|
142
|
+
entries: r2_buckets.map(({ binding, bucket_name }) => {
|
|
143
|
+
return {
|
|
144
|
+
key: binding,
|
|
145
|
+
value: bucket_name,
|
|
146
|
+
};
|
|
147
|
+
}),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (services !== undefined && services.length > 0) {
|
|
152
|
+
output.push({
|
|
153
|
+
type: "Services",
|
|
154
|
+
entries: services.map(({ binding, service, environment }) => {
|
|
155
|
+
let value = service;
|
|
156
|
+
if (environment) {
|
|
157
|
+
value += ` - ${environment}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
key: binding,
|
|
162
|
+
value,
|
|
163
|
+
};
|
|
164
|
+
}),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (text_blobs !== undefined && Object.keys(text_blobs).length > 0) {
|
|
169
|
+
output.push({
|
|
170
|
+
type: "Text Blobs",
|
|
171
|
+
entries: Object.entries(text_blobs).map(([key, value]) => ({
|
|
172
|
+
key,
|
|
173
|
+
value: truncate(value),
|
|
174
|
+
})),
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (unsafe !== undefined && unsafe.length > 0) {
|
|
179
|
+
output.push({
|
|
180
|
+
type: "Unsafe",
|
|
181
|
+
entries: unsafe.map(({ name, type }) => ({
|
|
182
|
+
key: type,
|
|
183
|
+
value: name,
|
|
184
|
+
})),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (vars !== undefined && Object.keys(vars).length > 0) {
|
|
189
|
+
output.push({
|
|
190
|
+
type: "Vars",
|
|
191
|
+
entries: Object.entries(vars).map(([key, value]) => ({
|
|
192
|
+
key,
|
|
193
|
+
value: `"${truncate(`${value}`)}"`,
|
|
194
|
+
})),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (wasm_modules !== undefined && Object.keys(wasm_modules).length > 0) {
|
|
199
|
+
output.push({
|
|
200
|
+
type: "Wasm Modules",
|
|
201
|
+
entries: Object.entries(wasm_modules).map(([key, value]) => ({
|
|
202
|
+
key,
|
|
203
|
+
value: truncate(value),
|
|
204
|
+
})),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (output.length === 0) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const message = [
|
|
213
|
+
`Your worker has access to the following bindings:`,
|
|
214
|
+
...output
|
|
215
|
+
.map((bindingGroup) => {
|
|
216
|
+
return [
|
|
217
|
+
`- ${bindingGroup.type}:`,
|
|
218
|
+
bindingGroup.entries.map(({ key, value }) => ` - ${key}: ${value}`),
|
|
219
|
+
];
|
|
220
|
+
})
|
|
221
|
+
.flat(2),
|
|
222
|
+
].join("\n");
|
|
223
|
+
|
|
224
|
+
logger.log(message);
|
|
225
|
+
}
|
package/src/config/validation.ts
CHANGED
|
@@ -94,22 +94,6 @@ export function normalizeAndValidateConfig(
|
|
|
94
94
|
"boolean"
|
|
95
95
|
);
|
|
96
96
|
|
|
97
|
-
validateOptionalProperty(
|
|
98
|
-
diagnostics,
|
|
99
|
-
"",
|
|
100
|
-
"minify",
|
|
101
|
-
rawConfig.minify,
|
|
102
|
-
"boolean"
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
validateOptionalProperty(
|
|
106
|
-
diagnostics,
|
|
107
|
-
"",
|
|
108
|
-
"node_compat",
|
|
109
|
-
rawConfig.node_compat,
|
|
110
|
-
"boolean"
|
|
111
|
-
);
|
|
112
|
-
|
|
113
97
|
// TODO: set the default to false to turn on service environments as the default
|
|
114
98
|
const isLegacyEnv =
|
|
115
99
|
(args as { "legacy-env": boolean | undefined })["legacy-env"] ??
|
|
@@ -193,7 +177,8 @@ export function normalizeAndValidateConfig(
|
|
|
193
177
|
dev: normalizeAndValidateDev(diagnostics, rawConfig.dev ?? {}),
|
|
194
178
|
migrations: normalizeAndValidateMigrations(
|
|
195
179
|
diagnostics,
|
|
196
|
-
rawConfig.migrations ?? []
|
|
180
|
+
rawConfig.migrations ?? [],
|
|
181
|
+
activeEnv.durable_objects
|
|
197
182
|
),
|
|
198
183
|
site: normalizeAndValidateSite(
|
|
199
184
|
diagnostics,
|
|
@@ -387,7 +372,8 @@ function normalizeAndValidateDev(
|
|
|
387
372
|
*/
|
|
388
373
|
function normalizeAndValidateMigrations(
|
|
389
374
|
diagnostics: Diagnostics,
|
|
390
|
-
rawMigrations: Config["migrations"]
|
|
375
|
+
rawMigrations: Config["migrations"],
|
|
376
|
+
durableObjects: Config["durable_objects"]
|
|
391
377
|
): Config["migrations"] {
|
|
392
378
|
if (!Array.isArray(rawMigrations)) {
|
|
393
379
|
diagnostics.errors.push(
|
|
@@ -398,29 +384,38 @@ function normalizeAndValidateMigrations(
|
|
|
398
384
|
return [];
|
|
399
385
|
} else {
|
|
400
386
|
for (let i = 0; i < rawMigrations.length; i++) {
|
|
401
|
-
const
|
|
387
|
+
const { tag, new_classes, renamed_classes, deleted_classes, ...rest } =
|
|
388
|
+
rawMigrations[i];
|
|
389
|
+
|
|
390
|
+
validateAdditionalProperties(
|
|
391
|
+
diagnostics,
|
|
392
|
+
"migrations",
|
|
393
|
+
Object.keys(rest),
|
|
394
|
+
[]
|
|
395
|
+
);
|
|
396
|
+
|
|
402
397
|
validateRequiredProperty(
|
|
403
398
|
diagnostics,
|
|
404
399
|
`migrations[${i}]`,
|
|
405
400
|
`tag`,
|
|
406
|
-
|
|
401
|
+
tag,
|
|
407
402
|
"string"
|
|
408
403
|
);
|
|
409
404
|
validateOptionalTypedArray(
|
|
410
405
|
diagnostics,
|
|
411
406
|
`migrations[${i}].new_classes`,
|
|
412
|
-
|
|
407
|
+
new_classes,
|
|
413
408
|
"string"
|
|
414
409
|
);
|
|
415
|
-
if (
|
|
416
|
-
if (!Array.isArray(
|
|
410
|
+
if (renamed_classes !== undefined) {
|
|
411
|
+
if (!Array.isArray(renamed_classes)) {
|
|
417
412
|
diagnostics.errors.push(
|
|
418
413
|
`Expected "migrations[${i}].renamed_classes" to be an array of "{from: string, to: string}" objects but got ${JSON.stringify(
|
|
419
|
-
|
|
414
|
+
renamed_classes
|
|
420
415
|
)}.`
|
|
421
416
|
);
|
|
422
417
|
} else if (
|
|
423
|
-
|
|
418
|
+
renamed_classes.some(
|
|
424
419
|
(c) =>
|
|
425
420
|
typeof c !== "object" ||
|
|
426
421
|
!isRequiredProperty(c, "from", "string") ||
|
|
@@ -429,7 +424,7 @@ function normalizeAndValidateMigrations(
|
|
|
429
424
|
) {
|
|
430
425
|
diagnostics.errors.push(
|
|
431
426
|
`Expected "migrations[${i}].renamed_classes" to be an array of "{from: string, to: string}" objects but got ${JSON.stringify(
|
|
432
|
-
|
|
427
|
+
renamed_classes
|
|
433
428
|
)}.`
|
|
434
429
|
);
|
|
435
430
|
}
|
|
@@ -437,10 +432,49 @@ function normalizeAndValidateMigrations(
|
|
|
437
432
|
validateOptionalTypedArray(
|
|
438
433
|
diagnostics,
|
|
439
434
|
`migrations[${i}].deleted_classes`,
|
|
440
|
-
|
|
435
|
+
deleted_classes,
|
|
441
436
|
"string"
|
|
442
437
|
);
|
|
443
438
|
}
|
|
439
|
+
|
|
440
|
+
if (
|
|
441
|
+
Array.isArray(durableObjects?.bindings) &&
|
|
442
|
+
durableObjects.bindings.length > 0
|
|
443
|
+
) {
|
|
444
|
+
// intrinsic [durable_objects] implies [migrations]
|
|
445
|
+
const exportedDurableObjects = (durableObjects.bindings || []).filter(
|
|
446
|
+
(binding) => !binding.script_name
|
|
447
|
+
);
|
|
448
|
+
if (exportedDurableObjects.length > 0 && rawMigrations.length === 0) {
|
|
449
|
+
if (
|
|
450
|
+
!exportedDurableObjects.some(
|
|
451
|
+
(exportedDurableObject) =>
|
|
452
|
+
typeof exportedDurableObject.class_name !== "string"
|
|
453
|
+
)
|
|
454
|
+
) {
|
|
455
|
+
const durableObjectClassnames = exportedDurableObjects.map(
|
|
456
|
+
(durable) => durable.class_name
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
diagnostics.warnings.push(
|
|
460
|
+
`In wrangler.toml, you have configured [durable_objects] exported by this Worker (${durableObjectClassnames.join(
|
|
461
|
+
", "
|
|
462
|
+
)}), but no [migrations] for them. This may not work as expected until you add a [migrations] section to your wrangler.toml. Add this configuration to your wrangler.toml:
|
|
463
|
+
|
|
464
|
+
\`\`\`
|
|
465
|
+
[[migrations]]
|
|
466
|
+
tag = "v1" # Should be unique for each entry
|
|
467
|
+
new_classes = [${durableObjectClassnames
|
|
468
|
+
.map((name) => `"${name}"`)
|
|
469
|
+
.join(", ")}]
|
|
470
|
+
\`\`\`
|
|
471
|
+
|
|
472
|
+
Refer to https://developers.cloudflare.com/workers/learning/using-durable-objects/#durable-object-migrations-in-wranglertoml for more details.`
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
444
478
|
return rawMigrations;
|
|
445
479
|
}
|
|
446
480
|
}
|
|
@@ -906,8 +940,22 @@ function normalizeAndValidateEnvironment(
|
|
|
906
940
|
}
|
|
907
941
|
),
|
|
908
942
|
zone_id: rawEnv.zone_id,
|
|
909
|
-
minify:
|
|
910
|
-
|
|
943
|
+
minify: inheritable(
|
|
944
|
+
diagnostics,
|
|
945
|
+
topLevelEnv,
|
|
946
|
+
rawEnv,
|
|
947
|
+
"minify",
|
|
948
|
+
isBoolean,
|
|
949
|
+
undefined
|
|
950
|
+
),
|
|
951
|
+
node_compat: inheritable(
|
|
952
|
+
diagnostics,
|
|
953
|
+
topLevelEnv,
|
|
954
|
+
rawEnv,
|
|
955
|
+
"node_compat",
|
|
956
|
+
isBoolean,
|
|
957
|
+
undefined
|
|
958
|
+
),
|
|
911
959
|
};
|
|
912
960
|
|
|
913
961
|
return environment;
|
|
@@ -63,10 +63,15 @@ async function sessionToken(
|
|
|
63
63
|
): Promise<CfPreviewToken> {
|
|
64
64
|
const { accountId } = account;
|
|
65
65
|
const initUrl = ctx.zone
|
|
66
|
-
? `/zones/${ctx.zone
|
|
66
|
+
? `/zones/${ctx.zone}/workers/edge-preview`
|
|
67
67
|
: `/accounts/${accountId}/workers/subdomain/edge-preview`;
|
|
68
68
|
|
|
69
|
-
const { exchange_url } = await fetchResult<{ exchange_url: string }>(
|
|
69
|
+
const { exchange_url } = await fetchResult<{ exchange_url: string }>(
|
|
70
|
+
initUrl,
|
|
71
|
+
undefined,
|
|
72
|
+
undefined,
|
|
73
|
+
abortSignal
|
|
74
|
+
);
|
|
70
75
|
const { inspector_websocket, prewarm, token } = (await (
|
|
71
76
|
await fetch(exchange_url, { signal: abortSignal })
|
|
72
77
|
).json()) as { inspector_websocket: string; token: string; prewarm: string };
|
|
@@ -106,7 +111,7 @@ async function createPreviewToken(
|
|
|
106
111
|
);
|
|
107
112
|
|
|
108
113
|
const { accountId } = account;
|
|
109
|
-
const scriptId = ctx.zone ? randomId() :
|
|
114
|
+
const scriptId = worker.name || (ctx.zone ? randomId() : host.split(".")[0]);
|
|
110
115
|
const url =
|
|
111
116
|
ctx.env && !ctx.legacyEnv
|
|
112
117
|
? `/accounts/${accountId}/workers/services/${scriptId}/environments/${ctx.env}/edge-preview`
|
|
@@ -119,29 +124,34 @@ async function createPreviewToken(
|
|
|
119
124
|
const formData = createWorkerUploadForm(worker);
|
|
120
125
|
formData.set("wrangler-session-config", JSON.stringify(mode));
|
|
121
126
|
|
|
122
|
-
const { preview_token } = await fetchResult<{ preview_token: string }>(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
const { preview_token } = await fetchResult<{ preview_token: string }>(
|
|
128
|
+
url,
|
|
129
|
+
{
|
|
130
|
+
method: "POST",
|
|
131
|
+
body: formData,
|
|
132
|
+
headers: {
|
|
133
|
+
"cf-preview-upload-config-token": value,
|
|
134
|
+
},
|
|
127
135
|
},
|
|
128
|
-
|
|
136
|
+
undefined,
|
|
137
|
+
abortSignal
|
|
138
|
+
);
|
|
129
139
|
|
|
130
140
|
return {
|
|
131
141
|
value: preview_token,
|
|
132
|
-
host:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
host:
|
|
143
|
+
ctx.host ??
|
|
144
|
+
(worker.name
|
|
145
|
+
? `${
|
|
146
|
+
worker.name
|
|
147
|
+
// TODO: this should also probably have the env prefix
|
|
148
|
+
// but it doesn't appear to work yet, instead giving us the
|
|
149
|
+
// "There is nothing here yet" screen
|
|
150
|
+
// ctx.env && !ctx.legacyEnv
|
|
151
|
+
// ? `${ctx.env}.${worker.name}`
|
|
152
|
+
// : worker.name
|
|
153
|
+
}.${host.split(".").slice(1).join(".")}`
|
|
154
|
+
: host),
|
|
145
155
|
|
|
146
156
|
inspectorUrl,
|
|
147
157
|
prewarmUrl,
|