wrangler 0.0.0-e95e5a0 → 0.0.0-e978986
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/Cloudflare_CA.pem +18 -0
- package/README.md +3 -1
- package/bin/wrangler.js +36 -4
- package/package.json +11 -7
- package/pages/functions/buildPlugin.ts +13 -0
- package/pages/functions/buildWorker.ts +14 -1
- package/pages/functions/template-plugin.ts +3 -2
- package/src/__tests__/configuration.test.ts +336 -87
- package/src/__tests__/dev.test.tsx +243 -25
- package/src/__tests__/helpers/mock-dialogs.ts +41 -1
- package/src/__tests__/index.test.ts +79 -72
- package/src/__tests__/init.test.ts +182 -131
- package/src/__tests__/jest.setup.ts +8 -0
- package/src/__tests__/kv.test.ts +173 -146
- package/src/__tests__/package-manager.test.ts +154 -7
- package/src/__tests__/pages.test.ts +381 -53
- package/src/__tests__/parse.test.ts +8 -0
- package/src/__tests__/publish.test.ts +682 -89
- package/src/__tests__/r2.test.ts +55 -36
- package/src/__tests__/secret.test.ts +39 -4
- package/src/abort.d.ts +3 -0
- package/src/bundle.ts +33 -2
- package/src/cfetch/index.ts +21 -4
- package/src/cfetch/internal.ts +17 -9
- package/src/config/config.ts +1 -1
- package/src/config/environment.ts +40 -14
- package/src/config/index.ts +162 -0
- package/src/config/validation.ts +180 -65
- package/src/create-worker-preview.ts +17 -7
- package/src/create-worker-upload-form.ts +22 -8
- package/src/dev/dev.tsx +7 -6
- package/src/dev/local.tsx +6 -0
- package/src/dev/remote.tsx +15 -1
- package/src/dialogs.tsx +48 -0
- package/src/durable.ts +102 -0
- package/src/entry.ts +64 -9
- package/src/index.tsx +357 -177
- package/src/inspect.ts +39 -0
- package/src/kv.ts +114 -25
- package/src/open-in-browser.ts +5 -12
- package/src/package-manager.ts +50 -3
- package/src/pages.tsx +271 -110
- package/src/parse.ts +23 -4
- package/src/proxy.ts +38 -22
- package/src/publish.ts +227 -113
- package/src/sites.tsx +19 -17
- package/src/user.tsx +11 -1
- package/src/worker.ts +8 -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 +36017 -22687
package/src/dev/dev.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import * as path from "node:path";
|
|
2
3
|
import { watch } from "chokidar";
|
|
3
4
|
import clipboardy from "clipboardy";
|
|
4
5
|
import commandExists from "command-exists";
|
|
@@ -9,6 +10,7 @@ import onExit from "signal-exit";
|
|
|
9
10
|
import tmp from "tmp-promise";
|
|
10
11
|
import { fetch } from "undici";
|
|
11
12
|
import { runCustomBuild } from "../entry";
|
|
13
|
+
import { openInspector } from "../inspect";
|
|
12
14
|
import { logger } from "../logger";
|
|
13
15
|
import openInBrowser from "../open-in-browser";
|
|
14
16
|
import { getAPIToken } from "../user";
|
|
@@ -242,9 +244,11 @@ function useCustomBuild(
|
|
|
242
244
|
persistent: true,
|
|
243
245
|
ignoreInitial: true,
|
|
244
246
|
}).on("all", (_event, filePath) => {
|
|
247
|
+
const relativeFile =
|
|
248
|
+
path.relative(expectedEntry.directory, expectedEntry.file) || ".";
|
|
245
249
|
//TODO: we should buffer requests to the proxy until this completes
|
|
246
250
|
logger.log(`The file ${filePath} changed, restarting build...`);
|
|
247
|
-
runCustomBuild(expectedEntry.file, build).catch((err) => {
|
|
251
|
+
runCustomBuild(expectedEntry.file, relativeFile, build).catch((err) => {
|
|
248
252
|
logger.error("Custom build failed:", err);
|
|
249
253
|
});
|
|
250
254
|
});
|
|
@@ -253,7 +257,7 @@ function useCustomBuild(
|
|
|
253
257
|
return () => {
|
|
254
258
|
watcher?.close();
|
|
255
259
|
};
|
|
256
|
-
}, [build, expectedEntry
|
|
260
|
+
}, [build, expectedEntry]);
|
|
257
261
|
}
|
|
258
262
|
|
|
259
263
|
function sleep(period: number) {
|
|
@@ -375,10 +379,7 @@ function useHotkeys(
|
|
|
375
379
|
}
|
|
376
380
|
// toggle inspector
|
|
377
381
|
case "d": {
|
|
378
|
-
await
|
|
379
|
-
`https://built-devtools.pages.dev/js_app?experiments=true&v8only=true&ws=localhost:${inspectorPort}/ws`,
|
|
380
|
-
{ forceChromium: true }
|
|
381
|
-
);
|
|
382
|
+
await openInspector(inspectorPort);
|
|
382
383
|
break;
|
|
383
384
|
}
|
|
384
385
|
// toggle local
|
package/src/dev/local.tsx
CHANGED
|
@@ -87,6 +87,11 @@ function useLocalWorker({
|
|
|
87
87
|
'⎔ A "public" folder is not yet supported in local mode.'
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
|
+
if (bindings.services && bindings.services.length > 0) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
"⎔ Service bindings are not yet supported in local mode."
|
|
93
|
+
);
|
|
94
|
+
}
|
|
90
95
|
|
|
91
96
|
// In local mode, we want to copy all referenced modules into
|
|
92
97
|
// the output bundle directory before starting up
|
|
@@ -297,6 +302,7 @@ function useLocalWorker({
|
|
|
297
302
|
bindings.durable_objects?.bindings,
|
|
298
303
|
bindings.kv_namespaces,
|
|
299
304
|
bindings.vars,
|
|
305
|
+
bindings.services,
|
|
300
306
|
compatibilityDate,
|
|
301
307
|
compatibilityFlags,
|
|
302
308
|
localPersistencePath,
|
package/src/dev/remote.tsx
CHANGED
|
@@ -185,7 +185,21 @@ export function useWorker(props: {
|
|
|
185
185
|
// we want to log the error, but not end the process
|
|
186
186
|
// since it could recover after the developer fixes whatever's wrong
|
|
187
187
|
if ((err as { code: string }).code !== "ABORT_ERR") {
|
|
188
|
-
|
|
188
|
+
// instead of logging the raw API error to the user,
|
|
189
|
+
// give them friendly instructions
|
|
190
|
+
// for error 10063 (workers.dev subdomain required)
|
|
191
|
+
if (err.code === 10063) {
|
|
192
|
+
const errorMessage =
|
|
193
|
+
"Error: You need to register a workers.dev subdomain before running the dev command in remote mode";
|
|
194
|
+
const solutionMessage =
|
|
195
|
+
"You can either enable local mode by pressing l, or register a workers.dev subdomain here:";
|
|
196
|
+
const onboardingLink = `https://dash.cloudflare.com/${accountId}/workers/onboarding`;
|
|
197
|
+
logger.error(
|
|
198
|
+
`${errorMessage}\n${solutionMessage}\n${onboardingLink}`
|
|
199
|
+
);
|
|
200
|
+
} else {
|
|
201
|
+
logger.error("Error on remote worker:", err);
|
|
202
|
+
}
|
|
189
203
|
}
|
|
190
204
|
});
|
|
191
205
|
|
package/src/dialogs.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { Box, Text, useInput, render } from "ink";
|
|
3
|
+
import SelectInput from "ink-select-input";
|
|
3
4
|
import TextInput from "ink-text-input";
|
|
4
5
|
import * as React from "react";
|
|
5
6
|
import { useState } from "react";
|
|
@@ -85,3 +86,50 @@ export async function prompt(
|
|
|
85
86
|
);
|
|
86
87
|
});
|
|
87
88
|
}
|
|
89
|
+
|
|
90
|
+
type SelectOption = {
|
|
91
|
+
value: string;
|
|
92
|
+
label: string;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
type SelectProps = {
|
|
96
|
+
text: string;
|
|
97
|
+
options: SelectOption[];
|
|
98
|
+
initialIndex: number;
|
|
99
|
+
onSelect: (value: string) => void;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function Select(props: SelectProps) {
|
|
103
|
+
return (
|
|
104
|
+
<Box flexDirection="column">
|
|
105
|
+
<Text>{props.text}</Text>
|
|
106
|
+
<SelectInput
|
|
107
|
+
initialIndex={props.initialIndex}
|
|
108
|
+
items={props.options}
|
|
109
|
+
onSelect={async (selected) => {
|
|
110
|
+
props.onSelect(selected.value);
|
|
111
|
+
}}
|
|
112
|
+
/>
|
|
113
|
+
</Box>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function select(
|
|
118
|
+
text: string,
|
|
119
|
+
options: SelectOption[],
|
|
120
|
+
initialIndex: number
|
|
121
|
+
): Promise<string> {
|
|
122
|
+
return new Promise((resolve) => {
|
|
123
|
+
const { unmount } = render(
|
|
124
|
+
<Select
|
|
125
|
+
text={text}
|
|
126
|
+
options={options}
|
|
127
|
+
initialIndex={initialIndex}
|
|
128
|
+
onSelect={(option: string) => {
|
|
129
|
+
unmount();
|
|
130
|
+
resolve(option);
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
}
|
package/src/durable.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { fetchResult } from "./cfetch";
|
|
3
|
+
import { logger } from "./logger";
|
|
4
|
+
import type { Config } from "./config";
|
|
5
|
+
import type { CfWorkerInit } from "./worker";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* For a given Worker + migrations config, figure out which migrations
|
|
9
|
+
* to upload based on the current migration tag of the deployed Worker.
|
|
10
|
+
*/
|
|
11
|
+
export async function getMigrationsToUpload(
|
|
12
|
+
scriptName: string,
|
|
13
|
+
props: {
|
|
14
|
+
accountId: string | undefined;
|
|
15
|
+
config: Config;
|
|
16
|
+
legacyEnv: boolean | undefined;
|
|
17
|
+
env: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
): Promise<CfWorkerInit["migrations"]> {
|
|
20
|
+
const { config, accountId } = props;
|
|
21
|
+
|
|
22
|
+
assert(accountId, "Missing accountId");
|
|
23
|
+
// if config.migrations
|
|
24
|
+
let migrations;
|
|
25
|
+
if (config.migrations.length > 0) {
|
|
26
|
+
// get current migration tag
|
|
27
|
+
type ScriptData = { id: string; migration_tag?: string };
|
|
28
|
+
let script: ScriptData | undefined;
|
|
29
|
+
if (!props.legacyEnv) {
|
|
30
|
+
try {
|
|
31
|
+
if (props.env) {
|
|
32
|
+
const scriptData = await fetchResult<{
|
|
33
|
+
script: ScriptData;
|
|
34
|
+
}>(
|
|
35
|
+
`/accounts/${accountId}/workers/services/${scriptName}/environments/${props.env}`
|
|
36
|
+
);
|
|
37
|
+
script = scriptData.script;
|
|
38
|
+
} else {
|
|
39
|
+
const scriptData = await fetchResult<{
|
|
40
|
+
default_environment: {
|
|
41
|
+
script: ScriptData;
|
|
42
|
+
};
|
|
43
|
+
}>(`/accounts/${accountId}/workers/services/${scriptName}`);
|
|
44
|
+
script = scriptData.default_environment.script;
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (
|
|
48
|
+
![
|
|
49
|
+
10090, // corresponds to workers.api.error.service_not_found, so the script wasn't previously published at all
|
|
50
|
+
10092, // workers.api.error.environment_not_found, so the script wasn't published to this environment yet
|
|
51
|
+
].includes((err as { code: number }).code)
|
|
52
|
+
) {
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
// else it's a 404, no script found, and we can proceed
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
const scripts = await fetchResult<ScriptData[]>(
|
|
59
|
+
`/accounts/${accountId}/workers/scripts`
|
|
60
|
+
);
|
|
61
|
+
script = scripts.find(({ id }) => id === scriptName);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (script?.migration_tag) {
|
|
65
|
+
// was already published once
|
|
66
|
+
const scriptMigrationTag = script.migration_tag;
|
|
67
|
+
const foundIndex = config.migrations.findIndex(
|
|
68
|
+
(migration) => migration.tag === scriptMigrationTag
|
|
69
|
+
);
|
|
70
|
+
if (foundIndex === -1) {
|
|
71
|
+
logger.warn(
|
|
72
|
+
`The published script ${scriptName} has a migration tag "${script.migration_tag}, which was not found in wrangler.toml. You may have already deleted it. Applying all available migrations to the script...`
|
|
73
|
+
);
|
|
74
|
+
migrations = {
|
|
75
|
+
old_tag: script.migration_tag,
|
|
76
|
+
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
77
|
+
steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
|
|
78
|
+
};
|
|
79
|
+
} else {
|
|
80
|
+
if (foundIndex !== config.migrations.length - 1) {
|
|
81
|
+
// there are new migrations to send up
|
|
82
|
+
migrations = {
|
|
83
|
+
old_tag: script.migration_tag,
|
|
84
|
+
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
85
|
+
steps: config.migrations
|
|
86
|
+
.slice(foundIndex + 1)
|
|
87
|
+
.map(({ tag: _tag, ...rest }) => rest),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// else, we're up to date, no migrations to send
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
// first time publishing durable objects to this script,
|
|
94
|
+
// so we send all the migrations
|
|
95
|
+
migrations = {
|
|
96
|
+
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
97
|
+
steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return migrations;
|
|
102
|
+
}
|
package/src/entry.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
2
|
+
import { existsSync, statSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import * as esbuild from "esbuild";
|
|
5
5
|
import { execaCommand } from "execa";
|
|
@@ -45,11 +45,16 @@ export async function getEntry(
|
|
|
45
45
|
file = path.resolve(directory, config.main);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
const relativeFile = path.relative(directory, file) || ".";
|
|
49
|
+
await runCustomBuild(file, relativeFile, config.build);
|
|
49
50
|
|
|
50
51
|
if (fileExists(file) === false) {
|
|
51
52
|
throw new Error(
|
|
52
|
-
|
|
53
|
+
getMissingEntryPointMessage(
|
|
54
|
+
`The entry-point file at "${relativeFile}" was not found.`,
|
|
55
|
+
file,
|
|
56
|
+
relativeFile
|
|
57
|
+
)
|
|
53
58
|
);
|
|
54
59
|
}
|
|
55
60
|
const format = await guessWorkerFormat(
|
|
@@ -90,7 +95,8 @@ export async function getEntry(
|
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
export async function runCustomBuild(
|
|
93
|
-
|
|
98
|
+
expectedEntryAbsolute: string,
|
|
99
|
+
expectedEntryRelative: string,
|
|
94
100
|
build: Config["build"]
|
|
95
101
|
) {
|
|
96
102
|
if (build?.command) {
|
|
@@ -105,12 +111,14 @@ export async function runCustomBuild(
|
|
|
105
111
|
...(build.cwd && { cwd: build.cwd }),
|
|
106
112
|
});
|
|
107
113
|
|
|
108
|
-
if (fileExists(
|
|
114
|
+
if (fileExists(expectedEntryAbsolute) === false) {
|
|
109
115
|
throw new Error(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
116
|
+
getMissingEntryPointMessage(
|
|
117
|
+
`The expected output file at "${expectedEntryRelative}" was not found after running custom build: ${build.command}.\n` +
|
|
118
|
+
"The `main` property in wrangler.toml should point to the file generated by the custom build.",
|
|
119
|
+
expectedEntryAbsolute,
|
|
120
|
+
expectedEntryRelative
|
|
121
|
+
)
|
|
114
122
|
);
|
|
115
123
|
}
|
|
116
124
|
}
|
|
@@ -265,3 +273,50 @@ function generateAddScriptNameExamples(
|
|
|
265
273
|
})
|
|
266
274
|
.join("\n");
|
|
267
275
|
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Generate an appropriate message for when the entry-point is missing.
|
|
279
|
+
*
|
|
280
|
+
* To be more helpful to developers, we check whether there is a suitable file
|
|
281
|
+
* nearby to the expected file path.
|
|
282
|
+
*/
|
|
283
|
+
function getMissingEntryPointMessage(
|
|
284
|
+
message: string,
|
|
285
|
+
absoluteEntryPointPath: string,
|
|
286
|
+
relativeEntryPointPath: string
|
|
287
|
+
): string {
|
|
288
|
+
if (
|
|
289
|
+
existsSync(absoluteEntryPointPath) &&
|
|
290
|
+
statSync(absoluteEntryPointPath).isDirectory()
|
|
291
|
+
) {
|
|
292
|
+
// The expected entry-point is a directory, so offer further guidance.
|
|
293
|
+
message += `\nThe provided entry-point path, "${relativeEntryPointPath}", points to a directory, rather than a file.\n`;
|
|
294
|
+
|
|
295
|
+
// Perhaps we can even guess what the correct path should be...
|
|
296
|
+
const possiblePaths: string[] = [];
|
|
297
|
+
for (const basenamePath of [
|
|
298
|
+
"worker",
|
|
299
|
+
"dist/worker",
|
|
300
|
+
"index",
|
|
301
|
+
"dist/index",
|
|
302
|
+
]) {
|
|
303
|
+
for (const extension of [".ts", ".tsx", ".js", ".jsx"]) {
|
|
304
|
+
const filePath = basenamePath + extension;
|
|
305
|
+
if (fileExists(path.resolve(absoluteEntryPointPath, filePath))) {
|
|
306
|
+
possiblePaths.push(path.join(relativeEntryPointPath, filePath));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (possiblePaths.length > 0) {
|
|
312
|
+
message +=
|
|
313
|
+
`\nDid you mean to set the main field to${
|
|
314
|
+
possiblePaths.length > 1 ? " one of" : ""
|
|
315
|
+
}:\n` +
|
|
316
|
+
"```\n" +
|
|
317
|
+
possiblePaths.map((filePath) => `main = "./${filePath}"\n`).join("") +
|
|
318
|
+
"```";
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return message;
|
|
322
|
+
}
|