wrangler 0.0.0-e928f94 → 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 +27 -14
- package/bin/wrangler.js +48 -4
- package/miniflare-dist/index.mjs +4886 -0
- package/package.json +74 -34
- package/pages/functions/buildPlugin.ts +105 -0
- package/pages/functions/buildWorker.ts +151 -0
- package/pages/functions/filepath-routing.test.ts +234 -0
- package/pages/functions/filepath-routing.ts +189 -0
- package/pages/functions/identifiers.ts +78 -0
- package/pages/functions/routes.ts +156 -0
- package/pages/functions/template-plugin.ts +147 -0
- package/pages/functions/template-worker.ts +143 -0
- package/src/__tests__/config-cache.test.ts +36 -0
- package/src/__tests__/configuration.test.ts +3374 -0
- package/src/__tests__/dev.test.tsx +980 -0
- package/src/__tests__/guess-worker-format.test.ts +85 -0
- package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
- package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
- package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
- package/src/__tests__/helpers/mock-account-id.ts +48 -0
- package/src/__tests__/helpers/mock-bin.ts +36 -0
- package/src/__tests__/helpers/mock-cfetch.ts +211 -0
- package/src/__tests__/helpers/mock-console.ts +87 -0
- package/src/__tests__/helpers/mock-dialogs.ts +130 -0
- package/src/__tests__/helpers/mock-http-server.ts +46 -0
- package/src/__tests__/helpers/mock-istty.ts +27 -0
- package/src/__tests__/helpers/mock-kv.ts +33 -0
- package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
- package/src/__tests__/helpers/mock-stdin.ts +103 -0
- package/src/__tests__/helpers/mock-web-socket.ts +29 -0
- package/src/__tests__/helpers/run-in-tmp.ts +39 -0
- package/src/__tests__/helpers/run-wrangler.ts +16 -0
- package/src/__tests__/helpers/write-worker-source.ts +31 -0
- package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
- package/src/__tests__/https-options.test.ts +130 -0
- package/src/__tests__/index.test.ts +233 -130
- package/src/__tests__/init.test.ts +1567 -0
- package/src/__tests__/jest.setup.ts +77 -0
- package/src/__tests__/kv.test.ts +1481 -0
- package/src/__tests__/logger.test.ts +115 -0
- package/src/__tests__/package-manager.test.ts +353 -0
- package/src/__tests__/pages.test.ts +657 -0
- package/src/__tests__/parse.test.ts +330 -0
- package/src/__tests__/publish.test.ts +5468 -0
- package/src/__tests__/r2.test.ts +233 -0
- package/src/__tests__/route.test.ts +45 -0
- package/src/__tests__/secret.test.ts +481 -0
- package/src/__tests__/tail.test.ts +690 -0
- package/src/__tests__/user.test.ts +131 -0
- package/src/__tests__/whoami.test.tsx +105 -0
- package/src/abort.d.ts +3 -0
- package/src/bundle.ts +200 -0
- package/src/cfetch/index.ts +121 -0
- package/src/cfetch/internal.ts +145 -0
- package/src/cli.ts +10 -7
- package/src/config/README.md +107 -0
- package/src/config/config.ts +234 -0
- package/src/config/diagnostics.ts +80 -0
- package/src/config/environment.ts +420 -0
- package/src/config/index.ts +225 -0
- package/src/config/validation-helpers.ts +575 -0
- package/src/config/validation.ts +1539 -0
- package/src/config-cache.ts +45 -0
- package/src/create-worker-preview.ts +185 -0
- package/src/create-worker-upload-form.ts +254 -0
- package/src/dev/dev-vars.ts +36 -0
- package/src/dev/dev.tsx +419 -0
- package/src/dev/local.tsx +318 -0
- package/src/dev/remote.tsx +227 -0
- package/src/dev/use-esbuild.ts +118 -0
- package/src/dialogs.tsx +67 -17
- package/src/durable.ts +102 -0
- package/src/entry.ts +322 -0
- package/src/environment-variables.ts +35 -0
- package/src/errors.ts +11 -0
- package/src/https-options.ts +123 -0
- package/src/index.tsx +2031 -1041
- package/src/inspect.ts +662 -0
- package/src/intl-polyfill.d.ts +139 -0
- package/src/kv.ts +408 -0
- package/src/logger.ts +66 -0
- package/src/miniflare-cli/README.md +30 -0
- package/src/miniflare-cli/enum-keys.ts +17 -0
- package/src/miniflare-cli/index.ts +41 -0
- package/src/miniflare-cli/request-context.ts +40 -0
- package/src/module-collection.ts +229 -35
- package/src/open-in-browser.ts +19 -0
- package/src/package-manager.ts +174 -0
- package/src/pages.tsx +1444 -260
- package/src/parse.ts +202 -0
- package/src/paths.ts +26 -0
- package/src/proxy.ts +465 -0
- package/src/publish.ts +498 -261
- package/src/r2.ts +50 -0
- package/src/selfsigned.d.ts +29 -0
- package/src/sites.tsx +240 -60
- package/src/tail/filters.ts +279 -0
- package/src/tail/index.ts +299 -0
- package/src/tail/printing.ts +67 -0
- package/src/update-check.ts +19 -0
- package/src/user.tsx +395 -221
- package/src/whoami.tsx +70 -0
- package/src/worker.ts +180 -0
- package/templates/gitignore +171 -0
- package/templates/new-worker-scheduled.js +17 -0
- package/templates/new-worker-scheduled.ts +32 -0
- package/templates/new-worker.js +15 -0
- package/templates/new-worker.ts +30 -0
- package/{static-asset-facade.js → templates/static-asset-facade.js} +1 -1
- package/templates/tsconfig.json +105 -0
- package/wrangler-dist/cli.js +118298 -116056
- package/src/__tests__/fixtures/init/.gitkeep +0 -0
- package/src/__tests__/mock-cfetch.js +0 -46
- package/src/api/form_data.ts +0 -141
- package/src/api/inspect.ts +0 -441
- package/src/api/preview.ts +0 -128
- package/src/api/worker.ts +0 -161
- package/src/cfetch.ts +0 -72
- package/src/config.ts +0 -122
- package/src/dev.tsx +0 -724
- package/src/kv.tsx +0 -211
- package/src/tail.tsx +0 -71
- package/wrangler-dist/cli.js.map +0 -7
package/src/index.tsx
CHANGED
|
@@ -1,122 +1,214 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { writeFile, mkdir } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { setTimeout } from "node:timers/promises";
|
|
5
|
+
import TOML from "@iarna/toml";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { execa } from "execa";
|
|
8
|
+
import { findUp } from "find-up";
|
|
9
|
+
import getPort from "get-port";
|
|
2
10
|
import { render } from "ink";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
11
|
+
import React from "react";
|
|
12
|
+
import onExit from "signal-exit";
|
|
13
|
+
import supportsColor from "supports-color";
|
|
14
|
+
import { setGlobalDispatcher, ProxyAgent } from "undici";
|
|
5
15
|
import makeCLI from "yargs";
|
|
6
|
-
import { hideBin } from "yargs/helpers";
|
|
7
|
-
import type yargs from "yargs";
|
|
8
|
-
import { findUp } from "find-up";
|
|
9
|
-
import TOML from "@iarna/toml";
|
|
10
|
-
import type { Config } from "./config";
|
|
11
|
-
import { confirm, prompt } from "./dialogs";
|
|
12
16
|
import { version as wranglerVersion } from "../package.json";
|
|
17
|
+
import { fetchResult } from "./cfetch";
|
|
18
|
+
import { findWranglerToml, printBindings, readConfig } from "./config";
|
|
19
|
+
import { createWorkerUploadForm } from "./create-worker-upload-form";
|
|
20
|
+
import Dev from "./dev/dev";
|
|
21
|
+
import { getVarsForDev } from "./dev/dev-vars";
|
|
22
|
+
import { confirm, prompt, select } from "./dialogs";
|
|
23
|
+
import { getEntry } from "./entry";
|
|
24
|
+
import { DeprecationError } from "./errors";
|
|
25
|
+
import {
|
|
26
|
+
getKVNamespaceId,
|
|
27
|
+
listKVNamespaces,
|
|
28
|
+
listKVNamespaceKeys,
|
|
29
|
+
putKVKeyValue,
|
|
30
|
+
putKVBulkKeyValue,
|
|
31
|
+
deleteKVBulkKeyValue,
|
|
32
|
+
createKVNamespace,
|
|
33
|
+
isValidKVNamespaceBinding,
|
|
34
|
+
getKVKeyValue,
|
|
35
|
+
isKVKeyValue,
|
|
36
|
+
unexpectedKVKeyValueProps,
|
|
37
|
+
deleteKVNamespace,
|
|
38
|
+
deleteKVKeyValue,
|
|
39
|
+
} from "./kv";
|
|
40
|
+
import { logger } from "./logger";
|
|
41
|
+
import { getPackageManager } from "./package-manager";
|
|
42
|
+
import { pages, pagesBetaWarning } from "./pages";
|
|
43
|
+
import {
|
|
44
|
+
formatMessage,
|
|
45
|
+
ParseError,
|
|
46
|
+
parseJSON,
|
|
47
|
+
parsePackageJSON,
|
|
48
|
+
parseTOML,
|
|
49
|
+
readFileSync,
|
|
50
|
+
} from "./parse";
|
|
51
|
+
import publish from "./publish";
|
|
52
|
+
import { createR2Bucket, deleteR2Bucket, listR2Buckets } from "./r2";
|
|
53
|
+
import { getAssetPaths } from "./sites";
|
|
54
|
+
import {
|
|
55
|
+
createTail,
|
|
56
|
+
jsonPrintLogs,
|
|
57
|
+
prettyPrintLogs,
|
|
58
|
+
translateCLICommandToFilterMessage,
|
|
59
|
+
} from "./tail";
|
|
60
|
+
import { updateCheck } from "./update-check";
|
|
13
61
|
import {
|
|
14
62
|
login,
|
|
15
63
|
logout,
|
|
16
64
|
listScopes,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
getAccountId,
|
|
65
|
+
validateScopeKeys,
|
|
66
|
+
requireAuth,
|
|
20
67
|
} from "./user";
|
|
21
|
-
import {
|
|
22
|
-
getNamespaceId,
|
|
23
|
-
listNamespaces,
|
|
24
|
-
listNamespaceKeys,
|
|
25
|
-
putKeyValue,
|
|
26
|
-
putBulkKeyValue,
|
|
27
|
-
deleteBulkKeyValue,
|
|
28
|
-
} from "./kv";
|
|
68
|
+
import { whoami } from "./whoami";
|
|
29
69
|
|
|
30
|
-
import {
|
|
70
|
+
import type { Config } from "./config";
|
|
71
|
+
import type { TailCLIFilters } from "./tail";
|
|
72
|
+
import type { RawData } from "ws";
|
|
73
|
+
import type { CommandModule } from "yargs";
|
|
74
|
+
import type Yargs from "yargs";
|
|
75
|
+
|
|
76
|
+
type ConfigPath = string | undefined;
|
|
77
|
+
|
|
78
|
+
const resetColor = "\x1b[0m";
|
|
79
|
+
const fgGreenColor = "\x1b[32m";
|
|
80
|
+
const DEFAULT_LOCAL_PORT = 8787;
|
|
81
|
+
|
|
82
|
+
const proxy =
|
|
83
|
+
process.env.https_proxy ||
|
|
84
|
+
process.env.HTTPS_PROXY ||
|
|
85
|
+
process.env.http_proxy ||
|
|
86
|
+
process.env.HTTP_PROXY ||
|
|
87
|
+
undefined;
|
|
88
|
+
|
|
89
|
+
if (proxy) {
|
|
90
|
+
setGlobalDispatcher(new ProxyAgent(proxy));
|
|
91
|
+
}
|
|
31
92
|
|
|
32
|
-
|
|
93
|
+
function getRules(config: Config): Config["rules"] {
|
|
94
|
+
const rules = config.rules ?? config.build?.upload?.rules ?? [];
|
|
33
95
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
96
|
+
if (config.rules && config.build?.upload?.rules) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`You cannot configure both [rules] and [build.upload.rules] in your wrangler.toml. Delete the \`build.upload\` section.`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
38
101
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
import * as fs from "node:fs";
|
|
43
|
-
import { execa } from "execa";
|
|
102
|
+
if (config.build?.upload?.rules) {
|
|
103
|
+
logger.warn(
|
|
104
|
+
`Deprecation: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:
|
|
44
105
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (!path) {
|
|
48
|
-
path = await findUp("wrangler.toml");
|
|
49
|
-
// TODO - terminate this early instead of going all the way to the root
|
|
106
|
+
${TOML.stringify({ rules: config.build.upload.rules })}`
|
|
107
|
+
);
|
|
50
108
|
}
|
|
109
|
+
return rules;
|
|
110
|
+
}
|
|
51
111
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
112
|
+
async function printWranglerBanner() {
|
|
113
|
+
// Let's not print this in tests
|
|
114
|
+
if (typeof jest !== "undefined") {
|
|
115
|
+
return;
|
|
56
116
|
}
|
|
57
117
|
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
118
|
+
const text = ` ⛅️ wrangler ${wranglerVersion} ${await updateCheck()}`;
|
|
119
|
+
|
|
120
|
+
logger.log(
|
|
121
|
+
text +
|
|
122
|
+
"\n" +
|
|
123
|
+
(supportsColor.stdout
|
|
124
|
+
? chalk.hex("#FF8800")("-".repeat(text.length))
|
|
125
|
+
: "-".repeat(text.length))
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function isLegacyEnv(config: Config): boolean {
|
|
130
|
+
// We only read from config here, because we've already accounted for
|
|
131
|
+
// args["legacy-env"] in https://github.com/cloudflare/wrangler2/blob/b24aeb5722370c2e04bce97a84a1fa1e55725d79/packages/wrangler/src/config/validation.ts#L94-L98
|
|
132
|
+
return config.legacy_env;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getScriptName(
|
|
136
|
+
args: { name: string | undefined; env: string | undefined },
|
|
137
|
+
config: Config
|
|
138
|
+
): string | undefined {
|
|
139
|
+
if (args.name && isLegacyEnv(config) && args.env) {
|
|
140
|
+
throw new CommandLineArgsError(
|
|
141
|
+
"In legacy environment mode you cannot use --name and --env together. If you want to specify a Worker name for a specific environment you can add the following to your wrangler.toml config:" +
|
|
142
|
+
`
|
|
143
|
+
[env.${args.env}]
|
|
144
|
+
name = "${args.name}"
|
|
145
|
+
`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return args.name ?? config.name;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Alternative to the getScriptName() because special Legacy cases allowed "name", and "env" together in Wrangler1
|
|
154
|
+
*/
|
|
155
|
+
function getLegacyScriptName(
|
|
156
|
+
args: { name: string | undefined; env: string | undefined },
|
|
157
|
+
config: Config
|
|
158
|
+
) {
|
|
159
|
+
return args.name && args.env && isLegacyEnv(config)
|
|
160
|
+
? `${args.name}-${args.env}`
|
|
161
|
+
: args.name ?? config.name;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get a promise to the streamed input from stdin.
|
|
166
|
+
*
|
|
167
|
+
* This function can be used to grab the incoming stream of data from, say,
|
|
168
|
+
* piping the output of another process into the wrangler process.
|
|
169
|
+
*/
|
|
170
|
+
function readFromStdin(): Promise<string> {
|
|
171
|
+
return new Promise((resolve, reject) => {
|
|
172
|
+
const stdin = process.stdin;
|
|
173
|
+
const chunks: string[] = [];
|
|
174
|
+
|
|
175
|
+
// When there is data ready to be read, the `readable` event will be triggered.
|
|
176
|
+
// In the handler for `readable` we call `read()` over and over until all the available data has been read.
|
|
177
|
+
stdin.on("readable", () => {
|
|
178
|
+
let chunk;
|
|
179
|
+
while (null !== (chunk = stdin.read())) {
|
|
180
|
+
chunks.push(chunk);
|
|
78
181
|
}
|
|
79
182
|
});
|
|
80
|
-
});
|
|
81
183
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Object.keys(config[field] || {}).forEach((fieldKey) => {
|
|
87
|
-
if (!(fieldKey in config.env[env][field])) {
|
|
88
|
-
console.error(
|
|
89
|
-
`In your configuration, "${field}.${fieldKey}" exists at a top level, but not on "env.${env}". This is not what you probably want, since the field "${field}" is not inherited by environments. Please add "${field}.${fieldKey}" to "env.${env}".`
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
184
|
+
// When the streamed data is complete the `end` event will be triggered.
|
|
185
|
+
// In the handler for `end` we join the chunks together and resolve the promise.
|
|
186
|
+
stdin.on("end", () => {
|
|
187
|
+
resolve(chunks.join(""));
|
|
93
188
|
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// todo: validate, add defaults
|
|
97
|
-
// let's just do some basics for now
|
|
98
|
-
|
|
99
|
-
// @ts-expect-error we're being sneaky here for now
|
|
100
|
-
config.__path__ = path;
|
|
101
189
|
|
|
102
|
-
|
|
190
|
+
// If there is an `error` event then the handler will reject the promise.
|
|
191
|
+
stdin.on("error", (err) => {
|
|
192
|
+
reject(err);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
103
195
|
}
|
|
104
196
|
|
|
105
197
|
// a helper to demand one of a set of options
|
|
106
198
|
// via https://github.com/yargs/yargs/issues/1093#issuecomment-491299261
|
|
107
199
|
function demandOneOfOption(...options: string[]) {
|
|
108
|
-
return function (argv:
|
|
200
|
+
return function (argv: Yargs.Arguments) {
|
|
109
201
|
const count = options.filter((option) => argv[option]).length;
|
|
110
202
|
const lastOption = options.pop();
|
|
111
203
|
|
|
112
204
|
if (count === 0) {
|
|
113
|
-
throw new
|
|
205
|
+
throw new CommandLineArgsError(
|
|
114
206
|
`Exactly one of the arguments ${options.join(
|
|
115
207
|
", "
|
|
116
208
|
)} and ${lastOption} is required`
|
|
117
209
|
);
|
|
118
210
|
} else if (count > 1) {
|
|
119
|
-
throw new
|
|
211
|
+
throw new CommandLineArgsError(
|
|
120
212
|
`Arguments ${options.join(
|
|
121
213
|
", "
|
|
122
214
|
)} and ${lastOption} are mutually exclusive`
|
|
@@ -127,30 +219,65 @@ function demandOneOfOption(...options: string[]) {
|
|
|
127
219
|
};
|
|
128
220
|
}
|
|
129
221
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
222
|
+
/**
|
|
223
|
+
* Remove trailing white space from inputs.
|
|
224
|
+
* Matching Wrangler legacy behavior with handling inputs
|
|
225
|
+
*/
|
|
226
|
+
function trimTrailingWhitespace(str: string) {
|
|
227
|
+
return str.trimEnd();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
class CommandLineArgsError extends Error {}
|
|
231
|
+
|
|
232
|
+
function createCLIParser(argv: string[]) {
|
|
233
|
+
const wrangler = makeCLI(argv)
|
|
234
|
+
.strict()
|
|
235
|
+
// We handle errors ourselves in a try-catch around `yargs.parse`.
|
|
236
|
+
// If you want the "help info" to be displayed then throw an instance of `CommandLineArgsError`.
|
|
237
|
+
// Otherwise we just log the error that was thrown without any "help info".
|
|
238
|
+
.showHelpOnFail(false)
|
|
239
|
+
.fail((msg, error) => {
|
|
240
|
+
if (!error || error.name === "YError") {
|
|
241
|
+
// If there is no error or the error is a "YError", then this came from yargs own validation
|
|
242
|
+
// Wrap it in a `CommandLineArgsError` so that we can handle it appropriately further up.
|
|
243
|
+
error = new CommandLineArgsError(msg);
|
|
139
244
|
}
|
|
140
|
-
|
|
245
|
+
throw error;
|
|
246
|
+
})
|
|
141
247
|
.scriptName("wrangler")
|
|
142
248
|
.wrap(null);
|
|
143
249
|
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
250
|
+
// Default help command that supports the subcommands
|
|
251
|
+
const subHelp: CommandModule = {
|
|
252
|
+
command: ["*"],
|
|
253
|
+
handler: async (args) => {
|
|
254
|
+
setImmediate(() =>
|
|
255
|
+
wrangler.parse([...args._.map((a) => `${a}`), "--help"])
|
|
256
|
+
);
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
wrangler.command(
|
|
260
|
+
["*"],
|
|
261
|
+
false,
|
|
262
|
+
() => {},
|
|
263
|
+
(args) => {
|
|
264
|
+
if (args._.length > 0) {
|
|
265
|
+
throw new CommandLineArgsError(`Unknown command: ${args._}.`);
|
|
266
|
+
} else {
|
|
267
|
+
wrangler.showHelp("log");
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
);
|
|
149
271
|
|
|
150
|
-
//
|
|
272
|
+
// You will note that we use the form for all commands where we use the builder function
|
|
273
|
+
// to define options and subcommands.
|
|
274
|
+
// Further we return the result of this builder even though it's not completely necessary.
|
|
275
|
+
// The reason is that it's required for type inference of the args in the handle function.
|
|
276
|
+
// I wish we could enforce this pattern, but this comment will have to do for now.
|
|
277
|
+
// (It's also annoying that choices[] doesn't get inferred as an enum. 🤷♂.)
|
|
151
278
|
|
|
152
279
|
// [DEPRECATED] generate
|
|
153
|
-
|
|
280
|
+
wrangler.command(
|
|
154
281
|
// we definitely want to move away from us cloning github templates
|
|
155
282
|
// we can do something better here, let's see
|
|
156
283
|
"generate [name] [template]",
|
|
@@ -162,574 +289,1337 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
162
289
|
default: "worker",
|
|
163
290
|
})
|
|
164
291
|
.positional("template", {
|
|
165
|
-
describe: "
|
|
292
|
+
describe: "The URL of a GitHub template",
|
|
166
293
|
default: "https://github.com/cloudflare/worker-template",
|
|
167
294
|
});
|
|
168
295
|
},
|
|
169
|
-
() => {
|
|
296
|
+
(generateArgs) => {
|
|
170
297
|
// "👯 [DEPRECATED]. Scaffold a Cloudflare Workers project from a public GitHub repository.",
|
|
171
|
-
|
|
172
|
-
"`wrangler generate` has been deprecated
|
|
298
|
+
throw new DeprecationError(
|
|
299
|
+
"`wrangler generate` has been deprecated.\n" +
|
|
300
|
+
"Try running `wrangler init` to generate a basic Worker, or cloning the template repository instead:\n\n" +
|
|
301
|
+
"```\n" +
|
|
302
|
+
`git clone ${generateArgs.template}\n` +
|
|
303
|
+
"```\n\n" +
|
|
304
|
+
"Please refer to https://developers.cloudflare.com/workers/wrangler/deprecations/#generate for more information."
|
|
173
305
|
);
|
|
174
306
|
}
|
|
175
307
|
);
|
|
176
308
|
|
|
177
309
|
// init
|
|
178
|
-
|
|
310
|
+
wrangler.command(
|
|
179
311
|
"init [name]",
|
|
180
312
|
"📥 Create a wrangler.toml configuration file",
|
|
181
313
|
(yargs) => {
|
|
182
|
-
return yargs
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
314
|
+
return yargs
|
|
315
|
+
.positional("name", {
|
|
316
|
+
describe: "The name of your worker",
|
|
317
|
+
type: "string",
|
|
318
|
+
})
|
|
319
|
+
.option("type", {
|
|
320
|
+
describe: "The type of worker to create",
|
|
321
|
+
type: "string",
|
|
322
|
+
choices: ["rust", "javascript", "webpack"],
|
|
323
|
+
hidden: true,
|
|
324
|
+
deprecated: true,
|
|
325
|
+
})
|
|
326
|
+
.option("site", {
|
|
327
|
+
hidden: true,
|
|
328
|
+
type: "boolean",
|
|
329
|
+
deprecated: true,
|
|
330
|
+
})
|
|
331
|
+
.option("yes", {
|
|
332
|
+
describe: 'Answer "yes" to any prompts for new projects',
|
|
333
|
+
type: "boolean",
|
|
334
|
+
alias: "y",
|
|
335
|
+
});
|
|
186
336
|
},
|
|
187
|
-
async () => {
|
|
188
|
-
|
|
189
|
-
if (
|
|
190
|
-
|
|
337
|
+
async (args) => {
|
|
338
|
+
await printWranglerBanner();
|
|
339
|
+
if (args.type) {
|
|
340
|
+
let message = "The --type option is no longer supported.";
|
|
341
|
+
if (args.type === "webpack") {
|
|
342
|
+
message +=
|
|
343
|
+
"\nIf you wish to use webpack then you will need to create a custom build.";
|
|
344
|
+
// TODO: Add a link to docs
|
|
345
|
+
}
|
|
346
|
+
throw new CommandLineArgsError(message);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const devDepsToInstall: string[] = [];
|
|
350
|
+
const instructions: string[] = [];
|
|
351
|
+
let shouldRunPackageManagerInstall = false;
|
|
352
|
+
const creationDirectory = path.resolve(process.cwd(), args.name ?? "");
|
|
353
|
+
|
|
354
|
+
if (args.site) {
|
|
355
|
+
const gitDirectory =
|
|
356
|
+
creationDirectory !== process.cwd()
|
|
357
|
+
? path.basename(creationDirectory)
|
|
358
|
+
: "my-site";
|
|
359
|
+
const message =
|
|
360
|
+
"The --site option is no longer supported.\n" +
|
|
361
|
+
"If you wish to create a brand new Worker Sites project then clone the `worker-sites-template` starter repository:\n\n" +
|
|
362
|
+
"```\n" +
|
|
363
|
+
`git clone --depth=1 --branch=wrangler2 https://github.com/cloudflare/worker-sites-template ${gitDirectory}\n` +
|
|
364
|
+
`cd ${gitDirectory}\n` +
|
|
365
|
+
"```\n\n" +
|
|
366
|
+
"Find out more about how to create and maintain Sites projects at https://developers.cloudflare.com/workers/platform/sites.\n" +
|
|
367
|
+
"Have you considered using Cloudflare Pages instead? See https://pages.cloudflare.com/.";
|
|
368
|
+
throw new CommandLineArgsError(message);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// TODO: make sure args.name is a valid identifier for a worker name
|
|
372
|
+
const workerName = path
|
|
373
|
+
.basename(creationDirectory)
|
|
374
|
+
.toLowerCase()
|
|
375
|
+
.replaceAll(/[^a-z0-9\-_]/gm, "-");
|
|
376
|
+
|
|
377
|
+
const packageManager = await getPackageManager(creationDirectory);
|
|
378
|
+
|
|
379
|
+
// TODO: ask which directory to make the worker in (defaults to args.name)
|
|
380
|
+
// TODO: if args.name isn't provided, ask what to name the worker
|
|
381
|
+
|
|
382
|
+
const wranglerTomlDestination = path.join(
|
|
383
|
+
creationDirectory,
|
|
384
|
+
"./wrangler.toml"
|
|
385
|
+
);
|
|
386
|
+
let justCreatedWranglerToml = false;
|
|
387
|
+
|
|
388
|
+
if (fs.existsSync(wranglerTomlDestination)) {
|
|
389
|
+
logger.warn(
|
|
390
|
+
`${path.relative(
|
|
391
|
+
process.cwd(),
|
|
392
|
+
wranglerTomlDestination
|
|
393
|
+
)} already exists!`
|
|
394
|
+
);
|
|
395
|
+
const shouldContinue = await confirm(
|
|
396
|
+
"Do you want to continue initializing this project?"
|
|
397
|
+
);
|
|
398
|
+
if (!shouldContinue) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
191
401
|
} else {
|
|
402
|
+
await mkdir(creationDirectory, { recursive: true });
|
|
192
403
|
const compatibilityDate = new Date().toISOString().substring(0, 10);
|
|
404
|
+
|
|
193
405
|
try {
|
|
194
406
|
await writeFile(
|
|
195
|
-
|
|
196
|
-
|
|
407
|
+
wranglerTomlDestination,
|
|
408
|
+
TOML.stringify({
|
|
409
|
+
name: workerName,
|
|
410
|
+
compatibility_date: compatibilityDate,
|
|
411
|
+
}) + "\n"
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
logger.log(
|
|
415
|
+
`✨ Created ${path.relative(
|
|
416
|
+
process.cwd(),
|
|
417
|
+
wranglerTomlDestination
|
|
418
|
+
)}`
|
|
197
419
|
);
|
|
198
|
-
|
|
199
|
-
// TODO: suggest next steps?
|
|
420
|
+
justCreatedWranglerToml = true;
|
|
200
421
|
} catch (err) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
422
|
+
throw new Error(
|
|
423
|
+
`Failed to create ${path.relative(
|
|
424
|
+
process.cwd(),
|
|
425
|
+
wranglerTomlDestination
|
|
426
|
+
)}.\n${(err as Error).message ?? err}`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const yesFlag = args.yes ?? false;
|
|
432
|
+
|
|
433
|
+
const isInsideGitProject = Boolean(
|
|
434
|
+
await findUp(".git", { cwd: creationDirectory, type: "directory" })
|
|
435
|
+
);
|
|
436
|
+
let isGitInstalled;
|
|
437
|
+
try {
|
|
438
|
+
isGitInstalled = (await execa("git", ["--version"])).exitCode === 0;
|
|
439
|
+
} catch (err) {
|
|
440
|
+
isGitInstalled = false;
|
|
441
|
+
}
|
|
442
|
+
if (!isInsideGitProject && isGitInstalled) {
|
|
443
|
+
const shouldInitGit =
|
|
444
|
+
yesFlag ||
|
|
445
|
+
(await confirm("Would you like to use git to manage this Worker?"));
|
|
446
|
+
if (shouldInitGit) {
|
|
447
|
+
await execa("git", ["init", "--initial-branch=main"], {
|
|
448
|
+
cwd: creationDirectory,
|
|
449
|
+
});
|
|
450
|
+
await writeFile(
|
|
451
|
+
path.join(creationDirectory, ".gitignore"),
|
|
452
|
+
readFileSync(path.join(__dirname, "../templates/gitignore"))
|
|
453
|
+
);
|
|
454
|
+
logger.log(
|
|
455
|
+
args.name && args.name !== "."
|
|
456
|
+
? `✨ Initialized git repository at ${path.relative(
|
|
457
|
+
process.cwd(),
|
|
458
|
+
creationDirectory
|
|
459
|
+
)}`
|
|
460
|
+
: `✨ Initialized git repository`
|
|
461
|
+
);
|
|
204
462
|
}
|
|
205
463
|
}
|
|
206
464
|
|
|
207
|
-
|
|
208
|
-
|
|
465
|
+
let pathToPackageJson = await findUp("package.json", {
|
|
466
|
+
cwd: creationDirectory,
|
|
467
|
+
});
|
|
468
|
+
let shouldCreatePackageJson = false;
|
|
209
469
|
|
|
210
470
|
if (!pathToPackageJson) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
471
|
+
// If no package.json exists, ask to create one
|
|
472
|
+
shouldCreatePackageJson =
|
|
473
|
+
yesFlag ||
|
|
474
|
+
(await confirm(
|
|
475
|
+
"No package.json found. Would you like to create one?"
|
|
476
|
+
));
|
|
477
|
+
|
|
478
|
+
if (shouldCreatePackageJson) {
|
|
214
479
|
await writeFile(
|
|
215
|
-
path.join(
|
|
480
|
+
path.join(creationDirectory, "./package.json"),
|
|
216
481
|
JSON.stringify(
|
|
217
482
|
{
|
|
218
|
-
name:
|
|
219
|
-
version: "0.0.
|
|
483
|
+
name: workerName,
|
|
484
|
+
version: "0.0.0",
|
|
485
|
+
devDependencies: {
|
|
486
|
+
wrangler: wranglerVersion,
|
|
487
|
+
},
|
|
488
|
+
private: true,
|
|
220
489
|
},
|
|
221
490
|
null,
|
|
222
491
|
" "
|
|
223
492
|
) + "\n"
|
|
224
493
|
);
|
|
225
|
-
|
|
226
|
-
|
|
494
|
+
|
|
495
|
+
shouldRunPackageManagerInstall = true;
|
|
496
|
+
pathToPackageJson = path.join(creationDirectory, "package.json");
|
|
497
|
+
logger.log(
|
|
498
|
+
`✨ Created ${path.relative(process.cwd(), pathToPackageJson)}`
|
|
499
|
+
);
|
|
227
500
|
} else {
|
|
228
501
|
return;
|
|
229
502
|
}
|
|
503
|
+
} else {
|
|
504
|
+
// If package.json exists and wrangler isn't installed,
|
|
505
|
+
// then ask to add wrangler to devDependencies
|
|
506
|
+
const packageJson = parsePackageJSON(
|
|
507
|
+
readFileSync(pathToPackageJson),
|
|
508
|
+
pathToPackageJson
|
|
509
|
+
);
|
|
510
|
+
if (
|
|
511
|
+
!(
|
|
512
|
+
packageJson.devDependencies?.wrangler ||
|
|
513
|
+
packageJson.dependencies?.wrangler
|
|
514
|
+
)
|
|
515
|
+
) {
|
|
516
|
+
const shouldInstall =
|
|
517
|
+
yesFlag ||
|
|
518
|
+
(await confirm(
|
|
519
|
+
`Would you like to install wrangler into ${path.relative(
|
|
520
|
+
process.cwd(),
|
|
521
|
+
pathToPackageJson
|
|
522
|
+
)}?`
|
|
523
|
+
));
|
|
524
|
+
if (shouldInstall) {
|
|
525
|
+
devDepsToInstall.push(`wrangler@${wranglerVersion}`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
230
528
|
}
|
|
231
529
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
530
|
+
let isTypescriptProject = false;
|
|
531
|
+
let pathToTSConfig = await findUp("tsconfig.json", {
|
|
532
|
+
cwd: creationDirectory,
|
|
533
|
+
});
|
|
236
534
|
if (!pathToTSConfig) {
|
|
237
|
-
|
|
535
|
+
// If there's no tsconfig, offer to create one
|
|
536
|
+
// and install @cloudflare/workers-types
|
|
537
|
+
if (yesFlag || (await confirm("Would you like to use TypeScript?"))) {
|
|
538
|
+
isTypescriptProject = true;
|
|
539
|
+
await writeFile(
|
|
540
|
+
path.join(creationDirectory, "./tsconfig.json"),
|
|
541
|
+
readFileSync(path.join(__dirname, "../templates/tsconfig.json"))
|
|
542
|
+
);
|
|
543
|
+
devDepsToInstall.push("@cloudflare/workers-types");
|
|
544
|
+
devDepsToInstall.push("typescript");
|
|
545
|
+
pathToTSConfig = path.join(creationDirectory, "tsconfig.json");
|
|
546
|
+
logger.log(
|
|
547
|
+
`✨ Created ${path.relative(process.cwd(), pathToTSConfig)}`
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
} else {
|
|
551
|
+
isTypescriptProject = true;
|
|
552
|
+
// If there's a tsconfig, check if @cloudflare/workers-types
|
|
553
|
+
// is already installed, and offer to install it if not
|
|
554
|
+
const packageJson = parsePackageJSON(
|
|
555
|
+
readFileSync(pathToPackageJson),
|
|
556
|
+
pathToPackageJson
|
|
557
|
+
);
|
|
558
|
+
if (
|
|
559
|
+
!(
|
|
560
|
+
packageJson.devDependencies?.["@cloudflare/workers-types"] ||
|
|
561
|
+
packageJson.dependencies?.["@cloudflare/workers-types"]
|
|
562
|
+
)
|
|
563
|
+
) {
|
|
564
|
+
const shouldInstall = await confirm(
|
|
565
|
+
"Would you like to install the type definitions for Workers into your package.json?"
|
|
566
|
+
);
|
|
567
|
+
if (shouldInstall) {
|
|
568
|
+
devDepsToInstall.push("@cloudflare/workers-types");
|
|
569
|
+
// We don't update the tsconfig.json because
|
|
570
|
+
// it could be complicated in existing projects
|
|
571
|
+
// and we don't want to break them. Instead, we simply
|
|
572
|
+
// tell the user that they need to update their tsconfig.json
|
|
573
|
+
instructions.push(
|
|
574
|
+
`🚨 Please add "@cloudflare/workers-types" to compilerOptions.types in ${path.relative(
|
|
575
|
+
process.cwd(),
|
|
576
|
+
pathToTSConfig
|
|
577
|
+
)}`
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const packageJsonContent = parsePackageJSON(
|
|
584
|
+
readFileSync(pathToPackageJson),
|
|
585
|
+
pathToPackageJson
|
|
586
|
+
);
|
|
587
|
+
const shouldWritePackageJsonScripts =
|
|
588
|
+
!packageJsonContent.scripts?.start &&
|
|
589
|
+
!packageJsonContent.scripts?.publish &&
|
|
590
|
+
shouldCreatePackageJson;
|
|
591
|
+
|
|
592
|
+
/*
|
|
593
|
+
* Passes the array of accumulated devDeps to install through to
|
|
594
|
+
* the package manager. Also generates a human-readable list
|
|
595
|
+
* of packages it installed.
|
|
596
|
+
* If there are no devDeps to install, optionally runs
|
|
597
|
+
* the package manager's install command.
|
|
598
|
+
*/
|
|
599
|
+
async function installPackages(
|
|
600
|
+
shouldRunInstall: boolean,
|
|
601
|
+
depsToInstall: string[]
|
|
602
|
+
) {
|
|
603
|
+
//lets install the devDeps they asked for
|
|
604
|
+
//and run their package manager's install command if needed
|
|
605
|
+
if (depsToInstall.length > 0) {
|
|
606
|
+
const formatter = new Intl.ListFormat("en", {
|
|
607
|
+
style: "long",
|
|
608
|
+
type: "conjunction",
|
|
609
|
+
});
|
|
610
|
+
await packageManager.addDevDeps(...depsToInstall);
|
|
611
|
+
const versionlessPackages = depsToInstall.map((dep) =>
|
|
612
|
+
dep === `wrangler@${wranglerVersion}` ? "wrangler" : dep
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
logger.log(
|
|
616
|
+
`✨ Installed ${formatter.format(
|
|
617
|
+
versionlessPackages
|
|
618
|
+
)} into devDependencies`
|
|
619
|
+
);
|
|
620
|
+
} else {
|
|
621
|
+
if (shouldRunInstall) {
|
|
622
|
+
await packageManager.install();
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
async function writePackageJsonScriptsAndUpdateWranglerToml(
|
|
628
|
+
isWritingScripts: boolean,
|
|
629
|
+
isCreatingWranglerToml: boolean,
|
|
630
|
+
packagePath: string,
|
|
631
|
+
scriptPath: string
|
|
632
|
+
) {
|
|
633
|
+
if (isCreatingWranglerToml) {
|
|
634
|
+
// rewrite wrangler.toml with main = "path/to/script"
|
|
635
|
+
const parsedWranglerToml = parseTOML(
|
|
636
|
+
readFileSync(wranglerTomlDestination)
|
|
637
|
+
);
|
|
638
|
+
fs.writeFileSync(
|
|
639
|
+
wranglerTomlDestination,
|
|
640
|
+
TOML.stringify({
|
|
641
|
+
name: parsedWranglerToml.name,
|
|
642
|
+
main: scriptPath,
|
|
643
|
+
compatibility_date: parsedWranglerToml.compatibility_date,
|
|
644
|
+
})
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
const isNamedWorker =
|
|
648
|
+
isCreatingWranglerToml && path.dirname(packagePath) !== process.cwd();
|
|
649
|
+
|
|
650
|
+
if (isWritingScripts) {
|
|
238
651
|
await writeFile(
|
|
239
|
-
|
|
652
|
+
packagePath,
|
|
240
653
|
JSON.stringify(
|
|
241
654
|
{
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
655
|
+
...packageJsonContent,
|
|
656
|
+
scripts: {
|
|
657
|
+
...packageJsonContent.scripts,
|
|
658
|
+
start: isCreatingWranglerToml
|
|
659
|
+
? `wrangler dev`
|
|
660
|
+
: `wrangler dev ${scriptPath}`,
|
|
661
|
+
deploy: isCreatingWranglerToml
|
|
662
|
+
? `wrangler publish`
|
|
663
|
+
: `wrangler publish ${scriptPath}`,
|
|
247
664
|
},
|
|
248
665
|
},
|
|
249
666
|
null,
|
|
250
|
-
|
|
667
|
+
2
|
|
251
668
|
) + "\n"
|
|
252
669
|
);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
670
|
+
instructions.push(
|
|
671
|
+
`\nTo start developing your Worker, run \`${
|
|
672
|
+
isNamedWorker ? `cd ${args.name} && ` : ""
|
|
673
|
+
}npm start\``
|
|
674
|
+
);
|
|
675
|
+
instructions.push(
|
|
676
|
+
`To publish your Worker to the Internet, run \`npm run deploy\``
|
|
260
677
|
);
|
|
261
|
-
|
|
678
|
+
} else {
|
|
679
|
+
instructions.push(
|
|
680
|
+
`\nTo start developing your Worker, run \`npx wrangler dev\`${
|
|
681
|
+
isCreatingWranglerToml ? "" : ` ${scriptPath}`
|
|
682
|
+
}`
|
|
683
|
+
);
|
|
684
|
+
instructions.push(
|
|
685
|
+
`To publish your Worker to the Internet, run \`npx wrangler publish\`${
|
|
686
|
+
isCreatingWranglerToml ? "" : ` ${scriptPath}`
|
|
687
|
+
}`
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
async function getNewWorkerType(newWorkerFilename: string) {
|
|
693
|
+
return select(
|
|
694
|
+
`Would you like to create a Worker at ${newWorkerFilename}?`,
|
|
695
|
+
[
|
|
696
|
+
{
|
|
697
|
+
value: "none",
|
|
698
|
+
label: "None",
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
value: "fetch",
|
|
702
|
+
label: "Fetch handler",
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
value: "scheduled",
|
|
706
|
+
label: "Scheduled handler",
|
|
707
|
+
},
|
|
708
|
+
],
|
|
709
|
+
1
|
|
710
|
+
) as Promise<"none" | "fetch" | "scheduled">;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function getNewWorkerTemplate(
|
|
714
|
+
lang: "js" | "ts",
|
|
715
|
+
workerType: "fetch" | "scheduled"
|
|
716
|
+
) {
|
|
717
|
+
const templates = {
|
|
718
|
+
"js-fetch": "new-worker.js",
|
|
719
|
+
"js-scheduled": "new-worker-scheduled.js",
|
|
720
|
+
"ts-fetch": "new-worker.ts",
|
|
721
|
+
"ts-scheduled": "new-worker-scheduled.ts",
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
return templates[`${lang}-${workerType}`];
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (isTypescriptProject) {
|
|
728
|
+
if (!fs.existsSync(path.join(creationDirectory, "./src/index.ts"))) {
|
|
729
|
+
const newWorkerFilename = path.relative(
|
|
730
|
+
process.cwd(),
|
|
731
|
+
path.join(creationDirectory, "./src/index.ts")
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
const newWorkerType = yesFlag
|
|
735
|
+
? "fetch"
|
|
736
|
+
: await getNewWorkerType(newWorkerFilename);
|
|
737
|
+
|
|
738
|
+
if (newWorkerType !== "none") {
|
|
739
|
+
const template = getNewWorkerTemplate("ts", newWorkerType);
|
|
740
|
+
|
|
741
|
+
await mkdir(path.join(creationDirectory, "./src"), {
|
|
742
|
+
recursive: true,
|
|
743
|
+
});
|
|
744
|
+
await writeFile(
|
|
745
|
+
path.join(creationDirectory, "./src/index.ts"),
|
|
746
|
+
readFileSync(path.join(__dirname, `../templates/${template}`))
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
logger.log(
|
|
750
|
+
`✨ Created ${path.relative(
|
|
751
|
+
process.cwd(),
|
|
752
|
+
path.join(creationDirectory, "./src/index.ts")
|
|
753
|
+
)}`
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
await writePackageJsonScriptsAndUpdateWranglerToml(
|
|
757
|
+
shouldWritePackageJsonScripts,
|
|
758
|
+
justCreatedWranglerToml,
|
|
759
|
+
pathToPackageJson,
|
|
760
|
+
"src/index.ts"
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
} else {
|
|
765
|
+
if (!fs.existsSync(path.join(creationDirectory, "./src/index.js"))) {
|
|
766
|
+
const newWorkerFilename = path.relative(
|
|
767
|
+
process.cwd(),
|
|
768
|
+
path.join(creationDirectory, "./src/index.js")
|
|
769
|
+
);
|
|
770
|
+
|
|
771
|
+
const newWorkerType = yesFlag
|
|
772
|
+
? "fetch"
|
|
773
|
+
: await getNewWorkerType(newWorkerFilename);
|
|
774
|
+
|
|
775
|
+
if (newWorkerType !== "none") {
|
|
776
|
+
const template = getNewWorkerTemplate("js", newWorkerType);
|
|
777
|
+
|
|
778
|
+
await mkdir(path.join(creationDirectory, "./src"), {
|
|
779
|
+
recursive: true,
|
|
780
|
+
});
|
|
781
|
+
await writeFile(
|
|
782
|
+
path.join(creationDirectory, "./src/index.js"),
|
|
783
|
+
readFileSync(path.join(__dirname, `../templates/${template}`))
|
|
784
|
+
);
|
|
785
|
+
|
|
786
|
+
logger.log(
|
|
787
|
+
`✨ Created ${path.relative(
|
|
788
|
+
process.cwd(),
|
|
789
|
+
path.join(creationDirectory, "./src/index.js")
|
|
790
|
+
)}`
|
|
791
|
+
);
|
|
792
|
+
|
|
793
|
+
await writePackageJsonScriptsAndUpdateWranglerToml(
|
|
794
|
+
shouldWritePackageJsonScripts,
|
|
795
|
+
justCreatedWranglerToml,
|
|
796
|
+
pathToPackageJson,
|
|
797
|
+
"src/index.js"
|
|
798
|
+
);
|
|
799
|
+
}
|
|
262
800
|
}
|
|
263
801
|
}
|
|
802
|
+
// install packages as the final step of init
|
|
803
|
+
try {
|
|
804
|
+
await installPackages(shouldRunPackageManagerInstall, devDepsToInstall);
|
|
805
|
+
} catch (e) {
|
|
806
|
+
// fetching packages could fail due to loss of internet, etc
|
|
807
|
+
// we should let folks know we failed to fetch, but their
|
|
808
|
+
// workers project is still ready to go
|
|
809
|
+
logger.error(e instanceof Error ? e.message : e);
|
|
810
|
+
instructions.push(
|
|
811
|
+
"\n🚨 wrangler was unable to fetch your npm packages, but your project is ready to go"
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// let users know what to do now
|
|
816
|
+
instructions.forEach((instruction) => logger.log(instruction));
|
|
264
817
|
}
|
|
265
818
|
);
|
|
266
819
|
|
|
267
820
|
// build
|
|
268
|
-
|
|
821
|
+
wrangler.command(
|
|
269
822
|
"build",
|
|
270
823
|
false,
|
|
271
824
|
(yargs) => {
|
|
272
825
|
return yargs.option("env", {
|
|
273
826
|
describe: "Perform on a specific environment",
|
|
827
|
+
type: "string",
|
|
274
828
|
});
|
|
275
829
|
},
|
|
276
|
-
() => {
|
|
830
|
+
async (buildArgs) => {
|
|
277
831
|
// "[DEPRECATED] 🦀 Build your project (if applicable)",
|
|
278
|
-
console.error(
|
|
279
|
-
"`wrangler build` has been deprecated, please refer to TODO://some/path for alternatives"
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
);
|
|
283
832
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
833
|
+
const envFlag = buildArgs.env ? ` --env=${buildArgs.env}` : "";
|
|
834
|
+
logger.log(
|
|
835
|
+
formatMessage({
|
|
836
|
+
kind: "warning",
|
|
837
|
+
text: "Deprecation: `wrangler build` has been deprecated.",
|
|
838
|
+
notes: [
|
|
839
|
+
{
|
|
840
|
+
text: "Please refer to https://developers.cloudflare.com/workers/wrangler/migration/deprecations/#build for more information.",
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
text: `Attempting to run \`wrangler publish --dry-run --outdir=dist${envFlag}\` for you instead:`,
|
|
844
|
+
},
|
|
845
|
+
],
|
|
296
846
|
})
|
|
297
|
-
|
|
298
|
-
describe: "allows to choose your set of OAuth scopes.",
|
|
299
|
-
array: true,
|
|
300
|
-
type: "string",
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
// TODO: scopes
|
|
304
|
-
},
|
|
305
|
-
async (args) => {
|
|
306
|
-
if (args["scopes-list"]) {
|
|
307
|
-
listScopes();
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
if (args.scopes) {
|
|
311
|
-
if (args.scopes.length === 0) {
|
|
312
|
-
// don't allow no scopes to be passed, that would be weird
|
|
313
|
-
listScopes();
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
await login({ scopes: args.scopes });
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
await login();
|
|
320
|
-
|
|
321
|
-
// TODO: would be nice if it optionally saved login
|
|
322
|
-
// creds inside node_modules/.cache or something
|
|
323
|
-
// this way you could have multiple users on a single machine
|
|
324
|
-
}
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
// logout
|
|
328
|
-
yargs.command(
|
|
329
|
-
// this needs scopes as an option?
|
|
330
|
-
"logout",
|
|
331
|
-
false, // we don't need to show this in the menu
|
|
332
|
-
// "🚪 Logout from Cloudflare",
|
|
333
|
-
() => {},
|
|
334
|
-
async () => {
|
|
335
|
-
await logout();
|
|
336
|
-
}
|
|
337
|
-
);
|
|
847
|
+
);
|
|
338
848
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
(args) => {
|
|
346
|
-
console.log(":whoami", args);
|
|
849
|
+
await createCLIParser([
|
|
850
|
+
"publish",
|
|
851
|
+
"--dry-run",
|
|
852
|
+
"--outdir=dist",
|
|
853
|
+
...(buildArgs.env ? ["--env", buildArgs.env] : []),
|
|
854
|
+
]).parse();
|
|
347
855
|
}
|
|
348
856
|
);
|
|
349
857
|
|
|
350
858
|
// config
|
|
351
|
-
|
|
859
|
+
wrangler.command(
|
|
352
860
|
"config",
|
|
353
861
|
false,
|
|
354
862
|
() => {},
|
|
355
863
|
() => {
|
|
356
864
|
// "🕵️ Authenticate Wrangler with a Cloudflare API Token",
|
|
357
|
-
|
|
358
|
-
"`wrangler config` has been deprecated, please refer to
|
|
865
|
+
throw new DeprecationError(
|
|
866
|
+
"`wrangler config` has been deprecated, please refer to https://developers.cloudflare.com/workers/wrangler/migration/deprecations/#config for alternatives"
|
|
359
867
|
);
|
|
360
868
|
}
|
|
361
869
|
);
|
|
362
870
|
|
|
363
871
|
// dev
|
|
364
|
-
|
|
365
|
-
"dev
|
|
872
|
+
wrangler.command(
|
|
873
|
+
"dev [script]",
|
|
366
874
|
"👂 Start a local server for developing your worker",
|
|
367
875
|
(yargs) => {
|
|
368
876
|
return yargs
|
|
369
|
-
.positional("
|
|
877
|
+
.positional("script", {
|
|
878
|
+
describe: "The path to an entry point for your worker",
|
|
879
|
+
type: "string",
|
|
880
|
+
})
|
|
370
881
|
.option("name", {
|
|
371
|
-
describe: "
|
|
882
|
+
describe: "Name of the worker",
|
|
372
883
|
type: "string",
|
|
884
|
+
requiresArg: true,
|
|
373
885
|
})
|
|
374
886
|
.option("format", {
|
|
375
887
|
choices: ["modules", "service-worker"] as const,
|
|
376
888
|
describe: "Choose an entry type",
|
|
889
|
+
deprecated: true,
|
|
377
890
|
})
|
|
378
891
|
.option("env", {
|
|
379
892
|
describe: "Perform on a specific environment",
|
|
380
893
|
type: "string",
|
|
381
|
-
|
|
894
|
+
requiresArg: true,
|
|
895
|
+
alias: "e",
|
|
896
|
+
})
|
|
897
|
+
.option("compatibility-date", {
|
|
898
|
+
describe: "Date to use for compatibility checks",
|
|
899
|
+
type: "string",
|
|
900
|
+
requiresArg: true,
|
|
901
|
+
})
|
|
902
|
+
.option("compatibility-flags", {
|
|
903
|
+
describe: "Flags to use for compatibility checks",
|
|
904
|
+
alias: "compatibility-flag",
|
|
905
|
+
type: "string",
|
|
906
|
+
requiresArg: true,
|
|
907
|
+
array: true,
|
|
908
|
+
})
|
|
909
|
+
.option("latest", {
|
|
910
|
+
describe: "Use the latest version of the worker runtime",
|
|
911
|
+
type: "boolean",
|
|
912
|
+
default: true,
|
|
382
913
|
})
|
|
383
914
|
.option("ip", {
|
|
384
|
-
describe: "IP address to listen on",
|
|
915
|
+
describe: "IP address to listen on, defaults to `localhost`",
|
|
385
916
|
type: "string",
|
|
386
|
-
|
|
917
|
+
requiresArg: true,
|
|
387
918
|
})
|
|
388
919
|
.option("port", {
|
|
389
|
-
describe: "Port to listen on
|
|
920
|
+
describe: "Port to listen on",
|
|
921
|
+
type: "number",
|
|
922
|
+
})
|
|
923
|
+
.option("inspector-port", {
|
|
924
|
+
describe: "Port for devtools to connect to",
|
|
390
925
|
type: "number",
|
|
391
|
-
|
|
926
|
+
})
|
|
927
|
+
.option("routes", {
|
|
928
|
+
describe: "Routes to upload",
|
|
929
|
+
alias: "route",
|
|
930
|
+
type: "string",
|
|
931
|
+
requiresArg: true,
|
|
932
|
+
array: true,
|
|
392
933
|
})
|
|
393
934
|
.option("host", {
|
|
394
935
|
type: "string",
|
|
936
|
+
requiresArg: true,
|
|
395
937
|
describe:
|
|
396
938
|
"Host to forward requests to, defaults to the zone of project",
|
|
397
939
|
})
|
|
398
940
|
.option("local-protocol", {
|
|
399
|
-
default: "http",
|
|
400
941
|
describe: "Protocol to listen to requests on, defaults to http.",
|
|
401
|
-
choices: ["http", "https"],
|
|
942
|
+
choices: ["http", "https"] as const,
|
|
402
943
|
})
|
|
403
|
-
.option("public", {
|
|
944
|
+
.option("experimental-public", {
|
|
404
945
|
describe: "Static assets to be served",
|
|
405
946
|
type: "string",
|
|
947
|
+
requiresArg: true,
|
|
406
948
|
})
|
|
407
949
|
.option("site", {
|
|
408
950
|
describe: "Root folder of static assets for Workers Sites",
|
|
409
951
|
type: "string",
|
|
952
|
+
requiresArg: true,
|
|
953
|
+
})
|
|
954
|
+
.option("site-include", {
|
|
955
|
+
describe:
|
|
956
|
+
"Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.",
|
|
957
|
+
type: "string",
|
|
958
|
+
requiresArg: true,
|
|
959
|
+
array: true,
|
|
960
|
+
})
|
|
961
|
+
.option("site-exclude", {
|
|
962
|
+
describe:
|
|
963
|
+
"Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.",
|
|
964
|
+
type: "string",
|
|
965
|
+
requiresArg: true,
|
|
966
|
+
array: true,
|
|
410
967
|
})
|
|
411
968
|
.option("upstream-protocol", {
|
|
412
|
-
default: "https",
|
|
413
969
|
describe:
|
|
414
970
|
"Protocol to forward requests to host on, defaults to https.",
|
|
415
|
-
choices: ["http", "https"],
|
|
971
|
+
choices: ["http", "https"] as const,
|
|
416
972
|
})
|
|
417
973
|
.option("jsx-factory", {
|
|
418
974
|
describe: "The function that is called for each JSX element",
|
|
419
975
|
type: "string",
|
|
976
|
+
requiresArg: true,
|
|
977
|
+
})
|
|
978
|
+
.option("jsx-fragment", {
|
|
979
|
+
describe: "The function that is called for each JSX fragment",
|
|
980
|
+
type: "string",
|
|
981
|
+
requiresArg: true,
|
|
982
|
+
})
|
|
983
|
+
.option("tsconfig", {
|
|
984
|
+
describe: "Path to a custom tsconfig.json file",
|
|
985
|
+
type: "string",
|
|
986
|
+
requiresArg: true,
|
|
987
|
+
})
|
|
988
|
+
.option("local", {
|
|
989
|
+
alias: "l",
|
|
990
|
+
describe: "Run on my machine",
|
|
991
|
+
type: "boolean",
|
|
992
|
+
default: false, // I bet this will a point of contention. We'll revisit it.
|
|
993
|
+
})
|
|
994
|
+
.option("minify", {
|
|
995
|
+
describe: "Minify the script",
|
|
996
|
+
type: "boolean",
|
|
997
|
+
})
|
|
998
|
+
.option("node-compat", {
|
|
999
|
+
describe: "Enable node.js compatibility",
|
|
1000
|
+
type: "boolean",
|
|
1001
|
+
})
|
|
1002
|
+
.option("experimental-enable-local-persistence", {
|
|
1003
|
+
describe: "Enable persistence for this session (only for local mode)",
|
|
1004
|
+
type: "boolean",
|
|
420
1005
|
})
|
|
421
|
-
.option("
|
|
422
|
-
describe: "
|
|
423
|
-
type: "
|
|
1006
|
+
.option("inspect", {
|
|
1007
|
+
describe: "Enable dev tools",
|
|
1008
|
+
type: "boolean",
|
|
1009
|
+
deprecated: true,
|
|
1010
|
+
})
|
|
1011
|
+
.option("legacy-env", {
|
|
1012
|
+
type: "boolean",
|
|
1013
|
+
describe: "Use legacy environments",
|
|
1014
|
+
hidden: true,
|
|
424
1015
|
});
|
|
425
1016
|
},
|
|
426
1017
|
async (args) => {
|
|
427
|
-
|
|
428
|
-
const
|
|
1018
|
+
await printWranglerBanner();
|
|
1019
|
+
const configPath =
|
|
1020
|
+
(args.config as ConfigPath) ||
|
|
1021
|
+
(args.script && findWranglerToml(path.dirname(args.script)));
|
|
1022
|
+
const config = readConfig(configPath, args);
|
|
1023
|
+
const entry = await getEntry(args, config, "dev");
|
|
1024
|
+
|
|
1025
|
+
if (config.services && config.services.length > 0) {
|
|
1026
|
+
logger.warn(
|
|
1027
|
+
`This worker is bound to live services: ${config.services
|
|
1028
|
+
.map(
|
|
1029
|
+
(service) =>
|
|
1030
|
+
`${service.binding} (${service.service}${
|
|
1031
|
+
service.environment ? `@${service.environment}` : ""
|
|
1032
|
+
})`
|
|
1033
|
+
)
|
|
1034
|
+
.join(", ")}`
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
if (args.inspect) {
|
|
1039
|
+
logger.warn(
|
|
1040
|
+
"Passing --inspect is unnecessary, now you can always connect to devtools."
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
if (args["experimental-public"]) {
|
|
1045
|
+
logger.warn(
|
|
1046
|
+
"The --experimental-public field is experimental and will change in the future."
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
if (args.public) {
|
|
1051
|
+
throw new Error(
|
|
1052
|
+
"The --public field has been renamed to --experimental-public, and will change behaviour in the future."
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
const upstreamProtocol =
|
|
1057
|
+
args["upstream-protocol"] || config.dev.upstream_protocol;
|
|
1058
|
+
if (upstreamProtocol === "http") {
|
|
1059
|
+
logger.warn(
|
|
1060
|
+
"Setting upstream-protocol to http is not currently implemented.\n" +
|
|
1061
|
+
"If this is required in your project, please add your use case to the following issue:\n" +
|
|
1062
|
+
"https://github.com/cloudflare/wrangler2/issues/583."
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
const accountId = !args.local ? await requireAuth(config) : undefined;
|
|
1067
|
+
|
|
1068
|
+
// TODO: if worker_dev = false and no routes, then error (only for dev)
|
|
1069
|
+
|
|
1070
|
+
/**
|
|
1071
|
+
* Given something that resembles a URL,
|
|
1072
|
+
* try to extract a host from it
|
|
1073
|
+
*/
|
|
1074
|
+
function getHost(urlLike: string): string | undefined {
|
|
1075
|
+
// strip leading * / *.
|
|
1076
|
+
urlLike = urlLike.replace(/^\*(\.)?/g, "");
|
|
1077
|
+
|
|
1078
|
+
if (
|
|
1079
|
+
!(urlLike.startsWith("http://") || urlLike.startsWith("https://"))
|
|
1080
|
+
) {
|
|
1081
|
+
urlLike = "http://" + urlLike;
|
|
1082
|
+
}
|
|
1083
|
+
return new URL(urlLike).host;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* Given something that resembles a host,
|
|
1088
|
+
* try to infer a zone id from it
|
|
1089
|
+
*/
|
|
1090
|
+
async function getZoneId(host: string): Promise<string | undefined> {
|
|
1091
|
+
const zones = await fetchResult<{ id: string }[]>(
|
|
1092
|
+
`/zones`,
|
|
1093
|
+
{},
|
|
1094
|
+
new URLSearchParams({ name: host })
|
|
1095
|
+
);
|
|
1096
|
+
return zones[0]?.id;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// When we're given a host (in one of the above ways), we do 2 things:
|
|
1100
|
+
// - We try to extract a host from it
|
|
1101
|
+
// - We try to get a zone id from the host
|
|
1102
|
+
//
|
|
1103
|
+
// So it turns out it's particularly hard to get a 'valid' domain
|
|
1104
|
+
// from a string, so we don't even try to validate TLDs, etc.
|
|
1105
|
+
// Once we get something that looks like w.x.y.z-ish, we then try to
|
|
1106
|
+
// get a zone id for it, by lopping off subdomains until we get a hit
|
|
1107
|
+
// from the API. That's it!
|
|
429
1108
|
|
|
430
|
-
|
|
1109
|
+
let zone: { host: string; id: string } | undefined;
|
|
431
1110
|
|
|
432
1111
|
if (!args.local) {
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
1112
|
+
const hostLike =
|
|
1113
|
+
args.host ||
|
|
1114
|
+
config.dev.host ||
|
|
1115
|
+
(args.routes && args.routes[0]) ||
|
|
1116
|
+
config.route ||
|
|
1117
|
+
(config.routes && config.routes[0]);
|
|
1118
|
+
|
|
1119
|
+
let zoneId: string | undefined =
|
|
1120
|
+
typeof hostLike === "object" && "zone_id" in hostLike
|
|
1121
|
+
? hostLike.zone_id
|
|
1122
|
+
: undefined;
|
|
1123
|
+
|
|
1124
|
+
const host =
|
|
1125
|
+
typeof hostLike === "string"
|
|
1126
|
+
? getHost(hostLike)
|
|
1127
|
+
: typeof hostLike === "object"
|
|
1128
|
+
? "zone_name" in hostLike
|
|
1129
|
+
? getHost(hostLike.zone_name)
|
|
1130
|
+
: getHost(hostLike.pattern)
|
|
1131
|
+
: undefined;
|
|
1132
|
+
|
|
1133
|
+
const hostPieces =
|
|
1134
|
+
typeof host === "string" ? host.split(".") : undefined;
|
|
1135
|
+
|
|
1136
|
+
while (!zoneId && hostPieces && hostPieces.length > 1) {
|
|
1137
|
+
zoneId = await getZoneId(hostPieces.join("."));
|
|
1138
|
+
hostPieces.shift();
|
|
438
1139
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
console.error("No account id found, quitting...");
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
1140
|
+
|
|
1141
|
+
if (host && !zoneId) {
|
|
1142
|
+
throw new Error(`Could not find zone for ${hostLike}`);
|
|
445
1143
|
}
|
|
1144
|
+
|
|
1145
|
+
zone =
|
|
1146
|
+
typeof zoneId === "string" && typeof host === "string"
|
|
1147
|
+
? {
|
|
1148
|
+
host,
|
|
1149
|
+
id: zoneId,
|
|
1150
|
+
}
|
|
1151
|
+
: undefined;
|
|
446
1152
|
}
|
|
447
1153
|
|
|
448
|
-
|
|
1154
|
+
const nodeCompat = args.nodeCompat ?? config.node_compat;
|
|
1155
|
+
if (nodeCompat) {
|
|
1156
|
+
logger.warn(
|
|
1157
|
+
"Enabling node.js compatibility mode for built-ins and globals. This is experimental and has serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
449
1160
|
|
|
450
|
-
const
|
|
1161
|
+
const bindings = {
|
|
1162
|
+
kv_namespaces: config.kv_namespaces?.map(
|
|
1163
|
+
({ binding, preview_id, id: _id }) => {
|
|
1164
|
+
// In `dev`, we make folks use a separate kv namespace called
|
|
1165
|
+
// `preview_id` instead of `id` so that they don't
|
|
1166
|
+
// break production data. So here we check that a `preview_id`
|
|
1167
|
+
// has actually been configured.
|
|
1168
|
+
// This whole block of code will be obsoleted in the future
|
|
1169
|
+
// when we have copy-on-write for previews on edge workers.
|
|
1170
|
+
if (!preview_id) {
|
|
1171
|
+
// TODO: This error has to be a _lot_ better, ideally just asking
|
|
1172
|
+
// to create a preview namespace for the user automatically
|
|
1173
|
+
throw new Error(
|
|
1174
|
+
`In development, you should use a separate kv namespace than the one you'd use in production. Please create a new kv namespace with "wrangler kv:namespace create <name> --preview" and add its id as preview_id to the kv_namespace "${binding}" in your wrangler.toml`
|
|
1175
|
+
); // Ugh, I really don't like this message very much
|
|
1176
|
+
}
|
|
1177
|
+
return {
|
|
1178
|
+
binding,
|
|
1179
|
+
id: preview_id,
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
),
|
|
1183
|
+
// Use a copy of combinedVars since we're modifying it later
|
|
1184
|
+
vars: getVarsForDev(config),
|
|
1185
|
+
wasm_modules: config.wasm_modules,
|
|
1186
|
+
text_blobs: config.text_blobs,
|
|
1187
|
+
data_blobs: config.data_blobs,
|
|
1188
|
+
durable_objects: config.durable_objects,
|
|
1189
|
+
r2_buckets: config.r2_buckets?.map(
|
|
1190
|
+
({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
|
|
1191
|
+
// same idea as kv namespace preview id,
|
|
1192
|
+
// same copy-on-write TODO
|
|
1193
|
+
if (!preview_bucket_name) {
|
|
1194
|
+
throw new Error(
|
|
1195
|
+
`In development, you should use a separate r2 bucket than the one you'd use in production. Please create a new r2 bucket with "wrangler r2 bucket create <name>" and add its name as preview_bucket_name to the r2_buckets "${binding}" in your wrangler.toml`
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
return {
|
|
1199
|
+
binding,
|
|
1200
|
+
bucket_name: preview_bucket_name,
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
),
|
|
1204
|
+
services: config.services,
|
|
1205
|
+
unsafe: config.unsafe?.bindings,
|
|
1206
|
+
};
|
|
451
1207
|
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
1208
|
+
// mask anything that was overridden in .dev.vars
|
|
1209
|
+
// so that we don't log potential secrets into the terminal
|
|
1210
|
+
const maskedVars = { ...bindings.vars };
|
|
1211
|
+
for (const key of Object.keys(maskedVars)) {
|
|
1212
|
+
if (maskedVars[key] !== config.vars[key]) {
|
|
1213
|
+
// This means it was overridden in .dev.vars
|
|
1214
|
+
// so let's mask it
|
|
1215
|
+
maskedVars[key] = "(hidden)";
|
|
459
1216
|
}
|
|
460
|
-
// TODO: if not already published, publish a draft worker
|
|
461
1217
|
}
|
|
462
1218
|
|
|
463
|
-
|
|
1219
|
+
// now log all available bindings into the terminal
|
|
1220
|
+
printBindings({
|
|
1221
|
+
...bindings,
|
|
1222
|
+
vars: maskedVars,
|
|
1223
|
+
});
|
|
1224
|
+
|
|
1225
|
+
const { waitUntilExit } = render(
|
|
464
1226
|
<Dev
|
|
465
|
-
name={args
|
|
466
|
-
entry={
|
|
467
|
-
|
|
1227
|
+
name={getScriptName(args, config)}
|
|
1228
|
+
entry={entry}
|
|
1229
|
+
env={args.env}
|
|
1230
|
+
zone={zone}
|
|
1231
|
+
rules={getRules(config)}
|
|
1232
|
+
legacyEnv={isLegacyEnv(config)}
|
|
1233
|
+
minify={args.minify ?? config.minify}
|
|
1234
|
+
nodeCompat={nodeCompat}
|
|
1235
|
+
build={config.build || {}}
|
|
468
1236
|
initialMode={args.local ? "local" : "remote"}
|
|
469
|
-
jsxFactory={args["jsx-factory"] ||
|
|
470
|
-
jsxFragment={args["jsx-fragment"] ||
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
1237
|
+
jsxFactory={args["jsx-factory"] || config.jsx_factory}
|
|
1238
|
+
jsxFragment={args["jsx-fragment"] || config.jsx_fragment}
|
|
1239
|
+
tsconfig={args.tsconfig ?? config.tsconfig}
|
|
1240
|
+
upstreamProtocol={upstreamProtocol}
|
|
1241
|
+
localProtocol={args["local-protocol"] || config.dev.local_protocol}
|
|
1242
|
+
enableLocalPersistence={
|
|
1243
|
+
args["experimental-enable-local-persistence"] || false
|
|
1244
|
+
}
|
|
1245
|
+
accountId={accountId}
|
|
1246
|
+
assetPaths={getAssetPaths(
|
|
1247
|
+
config,
|
|
1248
|
+
args.site,
|
|
1249
|
+
args.siteInclude,
|
|
1250
|
+
args.siteExclude
|
|
1251
|
+
)}
|
|
1252
|
+
port={
|
|
1253
|
+
args.port ||
|
|
1254
|
+
config.dev.port ||
|
|
1255
|
+
(await getPort({ port: DEFAULT_LOCAL_PORT }))
|
|
1256
|
+
}
|
|
1257
|
+
ip={args.ip || config.dev.ip}
|
|
1258
|
+
inspectorPort={
|
|
1259
|
+
args["inspector-port"] ?? (await getPort({ port: 9229 }))
|
|
1260
|
+
}
|
|
1261
|
+
public={args["experimental-public"]}
|
|
1262
|
+
compatibilityDate={getDevCompatibilityDate(
|
|
1263
|
+
config,
|
|
1264
|
+
args["compatibility-date"]
|
|
1265
|
+
)}
|
|
1266
|
+
compatibilityFlags={
|
|
1267
|
+
args["compatibility-flags"] || config.compatibility_flags
|
|
1268
|
+
}
|
|
477
1269
|
usageModel={config.usage_model}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
...(envRootObj?.kv_namespaces || []).reduce(
|
|
481
|
-
(obj, { binding, preview_id }) => {
|
|
482
|
-
if (!preview_id) {
|
|
483
|
-
// TODO: This error has to be a _lot_ better, ideally just asking
|
|
484
|
-
// to create a preview namespace for the user automatically
|
|
485
|
-
throw new Error(
|
|
486
|
-
"kv namespaces need a preview id during dev mode"
|
|
487
|
-
);
|
|
488
|
-
}
|
|
489
|
-
return { ...obj, [binding]: { namespaceId: preview_id } };
|
|
490
|
-
},
|
|
491
|
-
{}
|
|
492
|
-
),
|
|
493
|
-
...(envRootObj?.durable_objects?.bindings || []).reduce(
|
|
494
|
-
(obj, { name, class_name, script_name }) => {
|
|
495
|
-
return { ...obj, [name]: { class_name, script_name } };
|
|
496
|
-
},
|
|
497
|
-
{}
|
|
498
|
-
),
|
|
499
|
-
}}
|
|
1270
|
+
bindings={bindings}
|
|
1271
|
+
crons={config.triggers.crons}
|
|
500
1272
|
/>
|
|
501
1273
|
);
|
|
1274
|
+
await waitUntilExit();
|
|
502
1275
|
}
|
|
503
1276
|
);
|
|
504
1277
|
|
|
505
1278
|
// publish
|
|
506
|
-
|
|
1279
|
+
wrangler.command(
|
|
507
1280
|
"publish [script]",
|
|
508
1281
|
"🆙 Publish your Worker to Cloudflare.",
|
|
509
1282
|
(yargs) => {
|
|
510
1283
|
return yargs
|
|
511
1284
|
.option("env", {
|
|
512
1285
|
type: "string",
|
|
1286
|
+
requiresArg: true,
|
|
513
1287
|
describe: "Perform on a specific environment",
|
|
1288
|
+
alias: "e",
|
|
514
1289
|
})
|
|
515
1290
|
.positional("script", {
|
|
516
|
-
describe: "
|
|
1291
|
+
describe: "The path to an entry point for your worker",
|
|
517
1292
|
type: "string",
|
|
1293
|
+
requiresArg: true,
|
|
518
1294
|
})
|
|
519
1295
|
.option("name", {
|
|
520
|
-
describe: "
|
|
1296
|
+
describe: "Name of the worker",
|
|
1297
|
+
type: "string",
|
|
1298
|
+
requiresArg: true,
|
|
1299
|
+
})
|
|
1300
|
+
.option("outdir", {
|
|
1301
|
+
describe: "Output directory for the bundled worker",
|
|
1302
|
+
type: "string",
|
|
1303
|
+
requiresArg: true,
|
|
1304
|
+
})
|
|
1305
|
+
.option("format", {
|
|
1306
|
+
choices: ["modules", "service-worker"] as const,
|
|
1307
|
+
describe: "Choose an entry type",
|
|
1308
|
+
deprecated: true,
|
|
1309
|
+
})
|
|
1310
|
+
.option("compatibility-date", {
|
|
1311
|
+
describe: "Date to use for compatibility checks",
|
|
521
1312
|
type: "string",
|
|
1313
|
+
requiresArg: true,
|
|
1314
|
+
})
|
|
1315
|
+
.option("compatibility-flags", {
|
|
1316
|
+
describe: "Flags to use for compatibility checks",
|
|
1317
|
+
alias: "compatibility-flag",
|
|
1318
|
+
type: "string",
|
|
1319
|
+
requiresArg: true,
|
|
1320
|
+
array: true,
|
|
1321
|
+
})
|
|
1322
|
+
.option("latest", {
|
|
1323
|
+
describe: "Use the latest version of the worker runtime",
|
|
1324
|
+
type: "boolean",
|
|
1325
|
+
default: false,
|
|
522
1326
|
})
|
|
523
|
-
.option("public", {
|
|
1327
|
+
.option("experimental-public", {
|
|
524
1328
|
describe: "Static assets to be served",
|
|
525
1329
|
type: "string",
|
|
1330
|
+
requiresArg: true,
|
|
526
1331
|
})
|
|
527
1332
|
.option("site", {
|
|
528
1333
|
describe: "Root folder of static assets for Workers Sites",
|
|
529
1334
|
type: "string",
|
|
1335
|
+
requiresArg: true,
|
|
1336
|
+
})
|
|
1337
|
+
.option("site-include", {
|
|
1338
|
+
describe:
|
|
1339
|
+
"Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.",
|
|
1340
|
+
type: "string",
|
|
1341
|
+
requiresArg: true,
|
|
1342
|
+
array: true,
|
|
1343
|
+
})
|
|
1344
|
+
.option("site-exclude", {
|
|
1345
|
+
describe:
|
|
1346
|
+
"Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.",
|
|
1347
|
+
type: "string",
|
|
1348
|
+
requiresArg: true,
|
|
1349
|
+
array: true,
|
|
530
1350
|
})
|
|
531
1351
|
.option("triggers", {
|
|
532
1352
|
describe: "cron schedules to attach",
|
|
533
1353
|
alias: ["schedule", "schedules"],
|
|
534
|
-
type: "
|
|
1354
|
+
type: "string",
|
|
1355
|
+
requiresArg: true,
|
|
1356
|
+
array: true,
|
|
535
1357
|
})
|
|
536
1358
|
.option("routes", {
|
|
537
|
-
describe: "
|
|
1359
|
+
describe: "Routes to upload",
|
|
538
1360
|
alias: "route",
|
|
539
|
-
type: "
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
describe: "experimental support for services",
|
|
543
|
-
type: "boolean",
|
|
544
|
-
default: "false",
|
|
545
|
-
hidden: true,
|
|
1361
|
+
type: "string",
|
|
1362
|
+
requiresArg: true,
|
|
1363
|
+
array: true,
|
|
546
1364
|
})
|
|
547
1365
|
.option("jsx-factory", {
|
|
548
1366
|
describe: "The function that is called for each JSX element",
|
|
549
1367
|
type: "string",
|
|
1368
|
+
requiresArg: true,
|
|
550
1369
|
})
|
|
551
1370
|
.option("jsx-fragment", {
|
|
552
1371
|
describe: "The function that is called for each JSX fragment",
|
|
553
1372
|
type: "string",
|
|
1373
|
+
requiresArg: true,
|
|
1374
|
+
})
|
|
1375
|
+
.option("tsconfig", {
|
|
1376
|
+
describe: "Path to a custom tsconfig.json file",
|
|
1377
|
+
type: "string",
|
|
1378
|
+
requiresArg: true,
|
|
1379
|
+
})
|
|
1380
|
+
.option("minify", {
|
|
1381
|
+
describe: "Minify the script",
|
|
1382
|
+
type: "boolean",
|
|
1383
|
+
})
|
|
1384
|
+
.option("node-compat", {
|
|
1385
|
+
describe: "Enable node.js compatibility",
|
|
1386
|
+
type: "boolean",
|
|
1387
|
+
})
|
|
1388
|
+
.option("dry-run", {
|
|
1389
|
+
describe: "Don't actually publish",
|
|
1390
|
+
type: "boolean",
|
|
1391
|
+
})
|
|
1392
|
+
.option("legacy-env", {
|
|
1393
|
+
type: "boolean",
|
|
1394
|
+
describe: "Use legacy environments",
|
|
1395
|
+
hidden: true,
|
|
554
1396
|
});
|
|
555
1397
|
},
|
|
556
1398
|
async (args) => {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
1399
|
+
await printWranglerBanner();
|
|
1400
|
+
if (args["experimental-public"]) {
|
|
1401
|
+
logger.warn(
|
|
1402
|
+
"The --experimental-public field is experimental and will change in the future."
|
|
1403
|
+
);
|
|
1404
|
+
}
|
|
1405
|
+
if (args.public) {
|
|
1406
|
+
throw new Error(
|
|
1407
|
+
"The --public field has been renamed to --experimental-public, and will change behaviour in the future."
|
|
1408
|
+
);
|
|
560
1409
|
}
|
|
561
|
-
const config = args.config as Config;
|
|
562
1410
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
if (!config.account_id) {
|
|
574
|
-
console.error("No account id found, quitting...");
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
}
|
|
1411
|
+
const configPath =
|
|
1412
|
+
(args.config as ConfigPath) ||
|
|
1413
|
+
(args.script && findWranglerToml(path.dirname(args.script)));
|
|
1414
|
+
const config = readConfig(configPath, args);
|
|
1415
|
+
const entry = await getEntry(args, config, "publish");
|
|
1416
|
+
|
|
1417
|
+
if (args.latest) {
|
|
1418
|
+
logger.warn(
|
|
1419
|
+
"Using the latest version of the Workers runtime. To silence this warning, please choose a specific version of the runtime with --compatibility-date, or add a compatibility_date to your wrangler.toml.\n"
|
|
1420
|
+
);
|
|
578
1421
|
}
|
|
579
1422
|
|
|
580
|
-
|
|
1423
|
+
const accountId = args.dryRun ? undefined : await requireAuth(config);
|
|
1424
|
+
|
|
1425
|
+
const assetPaths = getAssetPaths(
|
|
1426
|
+
config,
|
|
1427
|
+
args["experimental-public"] || args.site,
|
|
1428
|
+
args.siteInclude,
|
|
1429
|
+
args.siteExclude
|
|
1430
|
+
);
|
|
581
1431
|
|
|
582
1432
|
await publish({
|
|
583
|
-
config
|
|
584
|
-
|
|
585
|
-
|
|
1433
|
+
config,
|
|
1434
|
+
accountId,
|
|
1435
|
+
name: getScriptName(args, config),
|
|
1436
|
+
rules: getRules(config),
|
|
1437
|
+
entry,
|
|
586
1438
|
env: args.env,
|
|
1439
|
+
compatibilityDate: args.latest
|
|
1440
|
+
? new Date().toISOString().substring(0, 10)
|
|
1441
|
+
: args["compatibility-date"],
|
|
1442
|
+
compatibilityFlags: args["compatibility-flags"],
|
|
587
1443
|
triggers: args.triggers,
|
|
588
1444
|
jsxFactory: args["jsx-factory"],
|
|
589
1445
|
jsxFragment: args["jsx-fragment"],
|
|
1446
|
+
tsconfig: args.tsconfig,
|
|
590
1447
|
routes: args.routes,
|
|
591
|
-
|
|
592
|
-
|
|
1448
|
+
assetPaths,
|
|
1449
|
+
legacyEnv: isLegacyEnv(config),
|
|
1450
|
+
minify: args.minify,
|
|
1451
|
+
nodeCompat: args.nodeCompat,
|
|
1452
|
+
experimentalPublic: args["experimental-public"] !== undefined,
|
|
1453
|
+
outDir: args.outdir,
|
|
1454
|
+
dryRun: args.dryRun,
|
|
593
1455
|
});
|
|
594
1456
|
}
|
|
595
1457
|
);
|
|
596
1458
|
|
|
597
1459
|
// tail
|
|
598
|
-
|
|
1460
|
+
wrangler.command(
|
|
599
1461
|
"tail [name]",
|
|
600
|
-
"🦚 Starts a log tailing session for a
|
|
1462
|
+
"🦚 Starts a log tailing session for a published Worker.",
|
|
601
1463
|
(yargs) => {
|
|
602
|
-
return
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
1464
|
+
return yargs
|
|
1465
|
+
.positional("name", {
|
|
1466
|
+
describe: "Name of the worker",
|
|
1467
|
+
type: "string",
|
|
1468
|
+
})
|
|
1469
|
+
.option("format", {
|
|
1470
|
+
default: process.stdout.isTTY ? "pretty" : "json",
|
|
1471
|
+
choices: ["json", "pretty"],
|
|
1472
|
+
describe: "The format of log entries",
|
|
1473
|
+
})
|
|
1474
|
+
.option("status", {
|
|
1475
|
+
choices: ["ok", "error", "canceled"],
|
|
1476
|
+
describe: "Filter by invocation status",
|
|
1477
|
+
array: true,
|
|
1478
|
+
})
|
|
1479
|
+
.option("header", {
|
|
1480
|
+
type: "string",
|
|
1481
|
+
requiresArg: true,
|
|
1482
|
+
describe: "Filter by HTTP header",
|
|
1483
|
+
})
|
|
1484
|
+
.option("method", {
|
|
1485
|
+
type: "string",
|
|
1486
|
+
requiresArg: true,
|
|
1487
|
+
describe: "Filter by HTTP method",
|
|
1488
|
+
array: true,
|
|
1489
|
+
})
|
|
1490
|
+
.option("sampling-rate", {
|
|
1491
|
+
type: "number",
|
|
1492
|
+
describe: "Adds a percentage of requests to log sampling rate",
|
|
1493
|
+
})
|
|
1494
|
+
.option("search", {
|
|
1495
|
+
type: "string",
|
|
1496
|
+
requiresArg: true,
|
|
1497
|
+
describe: "Filter by a text match in console.log messages",
|
|
1498
|
+
})
|
|
1499
|
+
.option("ip", {
|
|
1500
|
+
type: "string",
|
|
1501
|
+
requiresArg: true,
|
|
1502
|
+
describe:
|
|
1503
|
+
'Filter by the IP address the request originates from. Use "self" to filter for your own IP',
|
|
1504
|
+
array: true,
|
|
1505
|
+
})
|
|
1506
|
+
.option("env", {
|
|
1507
|
+
type: "string",
|
|
1508
|
+
requiresArg: true,
|
|
1509
|
+
describe: "Perform on a specific environment",
|
|
1510
|
+
alias: "e",
|
|
1511
|
+
})
|
|
1512
|
+
.option("debug", {
|
|
1513
|
+
type: "boolean",
|
|
1514
|
+
hidden: true,
|
|
1515
|
+
default: false,
|
|
1516
|
+
describe:
|
|
1517
|
+
"If a log would have been filtered out, send it through anyway alongside the filter which would have blocked it.",
|
|
1518
|
+
})
|
|
1519
|
+
.option("legacy-env", {
|
|
1520
|
+
type: "boolean",
|
|
1521
|
+
describe: "Use legacy environments",
|
|
1522
|
+
hidden: true,
|
|
1523
|
+
});
|
|
640
1524
|
},
|
|
641
1525
|
async (args) => {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
if (!(args.name || config.name)) {
|
|
645
|
-
console.error("Missing script name");
|
|
646
|
-
return;
|
|
1526
|
+
if (args.format === "pretty") {
|
|
1527
|
+
await printWranglerBanner();
|
|
647
1528
|
}
|
|
648
|
-
const
|
|
649
|
-
args.env ? `-${args.env}` : ""
|
|
650
|
-
}`;
|
|
1529
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
651
1530
|
|
|
652
|
-
|
|
1531
|
+
const scriptName = getLegacyScriptName(args, config);
|
|
653
1532
|
|
|
654
|
-
if (!
|
|
655
|
-
|
|
656
|
-
if (!loggedIn) {
|
|
657
|
-
// didn't login, let's just quit
|
|
658
|
-
console.log("Did not login, quitting...");
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
661
|
-
if (!config.account_id) {
|
|
662
|
-
config.account_id = await getAccountId();
|
|
663
|
-
if (!config.account_id) {
|
|
664
|
-
console.error("No account id found, quitting...");
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
1533
|
+
if (!scriptName) {
|
|
1534
|
+
throw new Error("Missing script name");
|
|
668
1535
|
}
|
|
669
1536
|
|
|
670
|
-
|
|
1537
|
+
const accountId = await requireAuth(config);
|
|
671
1538
|
|
|
672
|
-
const
|
|
673
|
-
|
|
674
|
-
const filters = {
|
|
675
|
-
status: args.status as "ok" | "error" | "canceled",
|
|
1539
|
+
const cliFilters: TailCLIFilters = {
|
|
1540
|
+
status: args.status as ("ok" | "error" | "canceled")[] | undefined,
|
|
676
1541
|
header: args.header,
|
|
677
1542
|
method: args.method,
|
|
678
|
-
|
|
1543
|
+
samplingRate: args["sampling-rate"],
|
|
679
1544
|
search: args.search,
|
|
1545
|
+
clientIp: args.ip,
|
|
680
1546
|
};
|
|
681
1547
|
|
|
682
|
-
const
|
|
683
|
-
|
|
1548
|
+
const filters = translateCLICommandToFilterMessage(
|
|
1549
|
+
cliFilters,
|
|
1550
|
+
args.debug
|
|
1551
|
+
);
|
|
684
1552
|
|
|
685
|
-
|
|
686
|
-
|
|
1553
|
+
const { tail, expiration, deleteTail } = await createTail(
|
|
1554
|
+
accountId,
|
|
1555
|
+
scriptName,
|
|
1556
|
+
filters,
|
|
1557
|
+
!isLegacyEnv(config) ? args.env : undefined
|
|
687
1558
|
);
|
|
688
1559
|
|
|
689
|
-
|
|
1560
|
+
const scriptDisplayName = `${scriptName}${
|
|
1561
|
+
args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
|
|
1562
|
+
}`;
|
|
1563
|
+
|
|
1564
|
+
if (args.format === "pretty") {
|
|
1565
|
+
logger.log(
|
|
1566
|
+
`Successfully created tail, expires at ${expiration.toLocaleString()}`
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
onExit(async () => {
|
|
690
1571
|
tail.terminate();
|
|
691
|
-
deleteTail();
|
|
1572
|
+
await deleteTail();
|
|
692
1573
|
});
|
|
693
1574
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
1575
|
+
const printLog: (data: RawData) => void =
|
|
1576
|
+
args.format === "pretty" ? prettyPrintLogs : jsonPrintLogs;
|
|
1577
|
+
|
|
1578
|
+
tail.on("message", printLog);
|
|
697
1579
|
|
|
698
1580
|
while (tail.readyState !== tail.OPEN) {
|
|
699
1581
|
switch (tail.readyState) {
|
|
700
1582
|
case tail.CONNECTING:
|
|
701
|
-
await setTimeout(
|
|
1583
|
+
await setTimeout(100);
|
|
702
1584
|
break;
|
|
703
1585
|
case tail.CLOSING:
|
|
704
|
-
await setTimeout(
|
|
1586
|
+
await setTimeout(100);
|
|
705
1587
|
break;
|
|
706
1588
|
case tail.CLOSED:
|
|
707
|
-
|
|
1589
|
+
throw new Error(
|
|
1590
|
+
`Connection to ${scriptDisplayName} closed unexpectedly.`
|
|
1591
|
+
);
|
|
708
1592
|
}
|
|
709
1593
|
}
|
|
710
1594
|
|
|
711
|
-
|
|
1595
|
+
if (args.format === "pretty") {
|
|
1596
|
+
logger.log(`Connected to ${scriptDisplayName}, waiting for logs...`);
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
tail.on("close", async () => {
|
|
1600
|
+
tail.terminate();
|
|
1601
|
+
await deleteTail();
|
|
1602
|
+
});
|
|
712
1603
|
}
|
|
713
1604
|
);
|
|
714
1605
|
|
|
715
1606
|
// preview
|
|
716
|
-
|
|
1607
|
+
wrangler.command(
|
|
717
1608
|
"preview [method] [body]",
|
|
718
1609
|
false,
|
|
719
1610
|
(yargs) => {
|
|
720
1611
|
return yargs
|
|
721
1612
|
.positional("method", {
|
|
1613
|
+
type: "string",
|
|
722
1614
|
describe: "Type of request to preview your worker",
|
|
723
|
-
choices: ["GET", "POST"],
|
|
724
|
-
default: ["GET"],
|
|
725
1615
|
})
|
|
726
1616
|
.positional("body", {
|
|
727
1617
|
type: "string",
|
|
728
1618
|
describe: "Body string to post to your preview worker request.",
|
|
729
|
-
default: "Null",
|
|
730
1619
|
})
|
|
731
1620
|
.option("env", {
|
|
732
1621
|
type: "string",
|
|
1622
|
+
requiresArg: true,
|
|
733
1623
|
describe: "Perform on a specific environment",
|
|
734
1624
|
})
|
|
735
1625
|
.option("watch", {
|
|
@@ -738,53 +1628,150 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
738
1628
|
type: "boolean",
|
|
739
1629
|
});
|
|
740
1630
|
},
|
|
741
|
-
() => {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
1631
|
+
async (args) => {
|
|
1632
|
+
if (args.method || args.body) {
|
|
1633
|
+
throw new DeprecationError(
|
|
1634
|
+
"The `wrangler preview` command has been deprecated.\n" +
|
|
1635
|
+
"Try using `wrangler dev` to to try out a worker during development.\n"
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
// Delegate to `wrangler dev`
|
|
1640
|
+
logger.warn(
|
|
1641
|
+
"***************************************************\n" +
|
|
1642
|
+
"The `wrangler preview` command has been deprecated.\n" +
|
|
1643
|
+
"Attempting to run `wrangler dev` instead.\n" +
|
|
1644
|
+
"***************************************************\n"
|
|
1645
|
+
);
|
|
1646
|
+
|
|
1647
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
1648
|
+
const entry = await getEntry({}, config, "dev");
|
|
1649
|
+
|
|
1650
|
+
const accountId = await requireAuth(config);
|
|
1651
|
+
|
|
1652
|
+
const { waitUntilExit } = render(
|
|
1653
|
+
<Dev
|
|
1654
|
+
name={config.name}
|
|
1655
|
+
entry={entry}
|
|
1656
|
+
rules={getRules(config)}
|
|
1657
|
+
env={args.env}
|
|
1658
|
+
zone={undefined}
|
|
1659
|
+
legacyEnv={isLegacyEnv(config)}
|
|
1660
|
+
build={config.build || {}}
|
|
1661
|
+
minify={undefined}
|
|
1662
|
+
nodeCompat={config.node_compat}
|
|
1663
|
+
initialMode={args.local ? "local" : "remote"}
|
|
1664
|
+
jsxFactory={config.jsx_factory}
|
|
1665
|
+
jsxFragment={config.jsx_fragment}
|
|
1666
|
+
tsconfig={config.tsconfig}
|
|
1667
|
+
upstreamProtocol={config.dev.upstream_protocol}
|
|
1668
|
+
localProtocol={config.dev.local_protocol}
|
|
1669
|
+
enableLocalPersistence={false}
|
|
1670
|
+
accountId={accountId}
|
|
1671
|
+
assetPaths={undefined}
|
|
1672
|
+
port={
|
|
1673
|
+
config.dev.port || (await getPort({ port: DEFAULT_LOCAL_PORT }))
|
|
1674
|
+
}
|
|
1675
|
+
ip={config.dev.ip}
|
|
1676
|
+
public={undefined}
|
|
1677
|
+
compatibilityDate={getDevCompatibilityDate(config)}
|
|
1678
|
+
compatibilityFlags={config.compatibility_flags}
|
|
1679
|
+
usageModel={config.usage_model}
|
|
1680
|
+
bindings={{
|
|
1681
|
+
kv_namespaces: config.kv_namespaces?.map(
|
|
1682
|
+
({ binding, preview_id, id: _id }) => {
|
|
1683
|
+
// In `dev`, we make folks use a separate kv namespace called
|
|
1684
|
+
// `preview_id` instead of `id` so that they don't
|
|
1685
|
+
// break production data. So here we check that a `preview_id`
|
|
1686
|
+
// has actually been configured.
|
|
1687
|
+
// This whole block of code will be obsoleted in the future
|
|
1688
|
+
// when we have copy-on-write for previews on edge workers.
|
|
1689
|
+
if (!preview_id) {
|
|
1690
|
+
// TODO: This error has to be a _lot_ better, ideally just asking
|
|
1691
|
+
// to create a preview namespace for the user automatically
|
|
1692
|
+
throw new Error(
|
|
1693
|
+
`In development, you should use a separate kv namespace than the one you'd use in production. Please create a new kv namespace with "wrangler kv:namespace create <name> --preview" and add its id as preview_id to the kv_namespace "${binding}" in your wrangler.toml`
|
|
1694
|
+
); // Ugh, I really don't like this message very much
|
|
1695
|
+
}
|
|
1696
|
+
return {
|
|
1697
|
+
binding,
|
|
1698
|
+
id: preview_id,
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
),
|
|
1702
|
+
vars: config.vars,
|
|
1703
|
+
wasm_modules: config.wasm_modules,
|
|
1704
|
+
text_blobs: config.text_blobs,
|
|
1705
|
+
data_blobs: config.data_blobs,
|
|
1706
|
+
durable_objects: config.durable_objects,
|
|
1707
|
+
r2_buckets: config.r2_buckets?.map(
|
|
1708
|
+
({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
|
|
1709
|
+
// same idea as kv namespace preview id,
|
|
1710
|
+
// same copy-on-write TODO
|
|
1711
|
+
if (!preview_bucket_name) {
|
|
1712
|
+
throw new Error(
|
|
1713
|
+
`In development, you should use a separate r2 bucket than the one you'd use in production. Please create a new r2 bucket with "wrangler r2 bucket create <name>" and add its name as preview_bucket_name to the r2_buckets "${binding}" in your wrangler.toml`
|
|
1714
|
+
);
|
|
1715
|
+
}
|
|
1716
|
+
return {
|
|
1717
|
+
binding,
|
|
1718
|
+
bucket_name: preview_bucket_name,
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
),
|
|
1722
|
+
services: config.services,
|
|
1723
|
+
unsafe: config.unsafe?.bindings,
|
|
1724
|
+
}}
|
|
1725
|
+
crons={config.triggers.crons}
|
|
1726
|
+
inspectorPort={await getPort({ port: 9229 })}
|
|
1727
|
+
/>
|
|
745
1728
|
);
|
|
1729
|
+
await waitUntilExit();
|
|
746
1730
|
}
|
|
747
1731
|
);
|
|
748
1732
|
|
|
749
|
-
// route
|
|
750
|
-
|
|
1733
|
+
// [DEPRECATED] route
|
|
1734
|
+
wrangler.command(
|
|
751
1735
|
"route",
|
|
752
1736
|
false, // I think we want to hide this command
|
|
753
1737
|
// "➡️ List or delete worker routes",
|
|
754
|
-
(
|
|
755
|
-
return
|
|
1738
|
+
(routeYargs) => {
|
|
1739
|
+
return routeYargs
|
|
756
1740
|
.command(
|
|
757
1741
|
"list",
|
|
758
|
-
"List
|
|
1742
|
+
"List the routes associated with a zone",
|
|
759
1743
|
(yargs) => {
|
|
760
1744
|
return yargs
|
|
761
1745
|
.option("env", {
|
|
762
1746
|
type: "string",
|
|
1747
|
+
requiresArg: true,
|
|
763
1748
|
describe: "Perform on a specific environment",
|
|
764
1749
|
})
|
|
765
1750
|
.option("zone", {
|
|
766
1751
|
type: "string",
|
|
767
|
-
|
|
1752
|
+
requiresArg: true,
|
|
1753
|
+
describe: "Zone id",
|
|
768
1754
|
})
|
|
769
1755
|
.positional("zone", {
|
|
770
|
-
describe: "
|
|
1756
|
+
describe: "Zone id",
|
|
771
1757
|
type: "string",
|
|
772
1758
|
});
|
|
773
1759
|
},
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1760
|
+
() => {
|
|
1761
|
+
// "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
|
|
1762
|
+
const deprecationNotice =
|
|
1763
|
+
"`wrangler route list` has been deprecated.";
|
|
1764
|
+
const futureRoutes =
|
|
1765
|
+
"Refer to wrangler.toml for a list of routes the worker will be deployed to upon publishing.";
|
|
1766
|
+
const presentRoutes =
|
|
1767
|
+
"Refer to the Cloudflare Dashboard to see the routes this worker is currently running on.";
|
|
1768
|
+
throw new DeprecationError(
|
|
1769
|
+
`${deprecationNotice}\n${futureRoutes}\n${presentRoutes}`
|
|
1770
|
+
);
|
|
784
1771
|
}
|
|
785
1772
|
)
|
|
786
1773
|
.command(
|
|
787
|
-
"delete
|
|
1774
|
+
"delete [id]",
|
|
788
1775
|
"Delete a route associated with a zone",
|
|
789
1776
|
(yargs) => {
|
|
790
1777
|
return yargs
|
|
@@ -794,33 +1781,36 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
794
1781
|
})
|
|
795
1782
|
.option("zone", {
|
|
796
1783
|
type: "string",
|
|
1784
|
+
requiresArg: true,
|
|
797
1785
|
describe: "zone id",
|
|
798
1786
|
})
|
|
799
1787
|
.option("env", {
|
|
800
1788
|
type: "string",
|
|
1789
|
+
requiresArg: true,
|
|
801
1790
|
describe: "Perform on a specific environment",
|
|
802
1791
|
});
|
|
803
1792
|
},
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
console.log(
|
|
813
|
-
await cfetch(`/zones/${zone}/workers/routes/${args.id}`, {
|
|
814
|
-
method: "DELETE",
|
|
815
|
-
})
|
|
816
|
-
);
|
|
1793
|
+
() => {
|
|
1794
|
+
// "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
|
|
1795
|
+
const deprecationNotice =
|
|
1796
|
+
"`wrangler route delete` has been deprecated.";
|
|
1797
|
+
const shouldDo =
|
|
1798
|
+
"Remove the unwanted route(s) from wrangler.toml and run `wrangler publish` to remove your worker from those routes.";
|
|
1799
|
+
throw new DeprecationError(`${deprecationNotice}\n${shouldDo}`);
|
|
817
1800
|
}
|
|
818
1801
|
);
|
|
1802
|
+
},
|
|
1803
|
+
() => {
|
|
1804
|
+
// "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
|
|
1805
|
+
const deprecationNotice = "`wrangler route` has been deprecated.";
|
|
1806
|
+
const shouldDo =
|
|
1807
|
+
"Please use wrangler.toml and/or `wrangler publish --routes` to modify routes";
|
|
1808
|
+
throw new DeprecationError(`${deprecationNotice}\n${shouldDo}`);
|
|
819
1809
|
}
|
|
820
1810
|
);
|
|
821
1811
|
|
|
822
1812
|
// subdomain
|
|
823
|
-
|
|
1813
|
+
wrangler.command(
|
|
824
1814
|
"subdomain [name]",
|
|
825
1815
|
false,
|
|
826
1816
|
// "👷 Create or change your workers.dev subdomain.",
|
|
@@ -828,182 +1818,202 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
828
1818
|
return yargs.positional("name", { type: "string" });
|
|
829
1819
|
},
|
|
830
1820
|
() => {
|
|
831
|
-
|
|
832
|
-
"`wrangler subdomain` has been deprecated, please refer to
|
|
1821
|
+
throw new DeprecationError(
|
|
1822
|
+
"`wrangler subdomain` has been deprecated, please refer to https://developers.cloudflare.com/workers/wrangler/migration/deprecations/#subdomain for alternatives"
|
|
833
1823
|
);
|
|
834
1824
|
}
|
|
835
1825
|
);
|
|
836
1826
|
|
|
837
1827
|
// secret
|
|
838
|
-
|
|
1828
|
+
wrangler.command(
|
|
839
1829
|
"secret",
|
|
840
1830
|
"🤫 Generate a secret that can be referenced in the worker script",
|
|
841
|
-
(
|
|
842
|
-
return
|
|
1831
|
+
(secretYargs) => {
|
|
1832
|
+
return secretYargs
|
|
1833
|
+
.command(subHelp)
|
|
1834
|
+
.option("legacy-env", {
|
|
1835
|
+
type: "boolean",
|
|
1836
|
+
describe: "Use legacy environments",
|
|
1837
|
+
hidden: true,
|
|
1838
|
+
})
|
|
843
1839
|
.command(
|
|
844
1840
|
"put <key>",
|
|
845
1841
|
"Create or update a secret variable for a script",
|
|
846
1842
|
(yargs) => {
|
|
847
1843
|
return yargs
|
|
848
1844
|
.positional("key", {
|
|
849
|
-
describe: "The variable name to be accessible in the script
|
|
1845
|
+
describe: "The variable name to be accessible in the script",
|
|
850
1846
|
type: "string",
|
|
851
1847
|
})
|
|
852
1848
|
.option("name", {
|
|
853
|
-
describe: "
|
|
1849
|
+
describe: "Name of the worker",
|
|
854
1850
|
type: "string",
|
|
1851
|
+
requiresArg: true,
|
|
855
1852
|
})
|
|
856
1853
|
.option("env", {
|
|
857
1854
|
type: "string",
|
|
1855
|
+
requiresArg: true,
|
|
858
1856
|
describe:
|
|
859
|
-
"Binds the secret to the
|
|
1857
|
+
"Binds the secret to the Worker of the specific environment",
|
|
1858
|
+
alias: "e",
|
|
860
1859
|
});
|
|
861
1860
|
},
|
|
862
1861
|
async (args) => {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
867
|
-
const config = args.config as Config;
|
|
1862
|
+
await printWranglerBanner();
|
|
1863
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
868
1864
|
|
|
869
|
-
|
|
870
|
-
const scriptName = args.name || config.name;
|
|
1865
|
+
const scriptName = getLegacyScriptName(args, config);
|
|
871
1866
|
if (!scriptName) {
|
|
872
|
-
|
|
873
|
-
return;
|
|
1867
|
+
throw new Error("Missing script name");
|
|
874
1868
|
}
|
|
875
1869
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
if (!args.local) {
|
|
879
|
-
const loggedIn = await loginOrRefreshIfRequired();
|
|
880
|
-
if (!loggedIn) {
|
|
881
|
-
// didn't login, let's just quit
|
|
882
|
-
console.log("Did not login, quitting...");
|
|
883
|
-
return;
|
|
884
|
-
}
|
|
885
|
-
if (!config.account_id) {
|
|
886
|
-
config.account_id = await getAccountId();
|
|
887
|
-
if (!config.account_id) {
|
|
888
|
-
console.error("No account id found, quitting...");
|
|
889
|
-
return;
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
}
|
|
1870
|
+
const accountId = await requireAuth(config);
|
|
893
1871
|
|
|
894
|
-
|
|
1872
|
+
const isInteractive = process.stdin.isTTY;
|
|
1873
|
+
const secretValue = trimTrailingWhitespace(
|
|
1874
|
+
isInteractive
|
|
1875
|
+
? await prompt("Enter a secret value:", "password")
|
|
1876
|
+
: await readFromStdin()
|
|
1877
|
+
);
|
|
895
1878
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1879
|
+
logger.log(
|
|
1880
|
+
`🌀 Creating the secret for script ${scriptName} ${
|
|
1881
|
+
args.env && !isLegacyEnv(config) ? `(${args.env})` : ""
|
|
1882
|
+
}`
|
|
899
1883
|
);
|
|
1884
|
+
|
|
900
1885
|
async function submitSecret() {
|
|
901
|
-
|
|
902
|
-
|
|
1886
|
+
const url =
|
|
1887
|
+
!args.env || isLegacyEnv(config)
|
|
1888
|
+
? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
|
|
1889
|
+
: `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
|
|
1890
|
+
|
|
1891
|
+
return await fetchResult(url, {
|
|
1892
|
+
method: "PUT",
|
|
1893
|
+
headers: { "Content-Type": "application/json" },
|
|
1894
|
+
body: JSON.stringify({
|
|
1895
|
+
name: args.key,
|
|
1896
|
+
text: secretValue,
|
|
1897
|
+
type: "secret_text",
|
|
1898
|
+
}),
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
const createDraftWorker = async () => {
|
|
1903
|
+
// TODO: log a warning
|
|
1904
|
+
await fetchResult(
|
|
1905
|
+
!isLegacyEnv(config) && args.env
|
|
1906
|
+
? `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}`
|
|
1907
|
+
: `/accounts/${accountId}/workers/scripts/${scriptName}`,
|
|
903
1908
|
{
|
|
904
1909
|
method: "PUT",
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1910
|
+
body: createWorkerUploadForm({
|
|
1911
|
+
name: scriptName,
|
|
1912
|
+
main: {
|
|
1913
|
+
name: scriptName,
|
|
1914
|
+
content: `export default { fetch() {} }`,
|
|
1915
|
+
type: "esm",
|
|
1916
|
+
},
|
|
1917
|
+
bindings: {
|
|
1918
|
+
kv_namespaces: [],
|
|
1919
|
+
vars: {},
|
|
1920
|
+
durable_objects: { bindings: [] },
|
|
1921
|
+
r2_buckets: [],
|
|
1922
|
+
services: [],
|
|
1923
|
+
wasm_modules: {},
|
|
1924
|
+
text_blobs: {},
|
|
1925
|
+
data_blobs: {},
|
|
1926
|
+
unsafe: [],
|
|
1927
|
+
},
|
|
1928
|
+
modules: [],
|
|
1929
|
+
migrations: undefined,
|
|
1930
|
+
compatibility_date: undefined,
|
|
1931
|
+
compatibility_flags: undefined,
|
|
1932
|
+
usage_model: undefined,
|
|
910
1933
|
}),
|
|
911
1934
|
}
|
|
912
1935
|
);
|
|
1936
|
+
};
|
|
1937
|
+
|
|
1938
|
+
function isMissingWorkerError(e: unknown): e is { code: 10007 } {
|
|
1939
|
+
return (
|
|
1940
|
+
typeof e === "object" &&
|
|
1941
|
+
e !== null &&
|
|
1942
|
+
(e as { code: number }).code === 10007
|
|
1943
|
+
);
|
|
913
1944
|
}
|
|
914
1945
|
|
|
915
1946
|
try {
|
|
916
|
-
|
|
1947
|
+
await submitSecret();
|
|
917
1948
|
} catch (e) {
|
|
918
|
-
if (e
|
|
919
|
-
//
|
|
920
|
-
await
|
|
921
|
-
|
|
922
|
-
{
|
|
923
|
-
method: "PUT",
|
|
924
|
-
// @ts-expect-error TODO: fix this error!
|
|
925
|
-
body: toFormData({
|
|
926
|
-
main: {
|
|
927
|
-
name: scriptName,
|
|
928
|
-
content: `export default { fetch() {} }`,
|
|
929
|
-
type: "esm",
|
|
930
|
-
},
|
|
931
|
-
variables: {},
|
|
932
|
-
modules: [],
|
|
933
|
-
}),
|
|
934
|
-
}
|
|
935
|
-
);
|
|
936
|
-
|
|
937
|
-
// and then try again
|
|
938
|
-
console.log(await submitSecret());
|
|
1949
|
+
if (isMissingWorkerError(e)) {
|
|
1950
|
+
// create a draft worker and try again
|
|
1951
|
+
await createDraftWorker();
|
|
1952
|
+
await submitSecret();
|
|
939
1953
|
// TODO: delete the draft worker if this failed too?
|
|
1954
|
+
} else {
|
|
1955
|
+
throw e;
|
|
940
1956
|
}
|
|
941
1957
|
}
|
|
1958
|
+
|
|
1959
|
+
logger.log(`✨ Success! Uploaded secret ${args.key}`);
|
|
942
1960
|
}
|
|
943
1961
|
)
|
|
944
1962
|
.command(
|
|
945
1963
|
"delete <key>",
|
|
946
1964
|
"Delete a secret variable from a script",
|
|
947
|
-
(yargs) => {
|
|
1965
|
+
async (yargs) => {
|
|
1966
|
+
await printWranglerBanner();
|
|
948
1967
|
return yargs
|
|
949
1968
|
.positional("key", {
|
|
950
|
-
describe: "The variable name to be accessible in the script
|
|
1969
|
+
describe: "The variable name to be accessible in the script",
|
|
951
1970
|
type: "string",
|
|
952
1971
|
})
|
|
953
1972
|
.option("name", {
|
|
954
|
-
describe: "
|
|
1973
|
+
describe: "Name of the worker",
|
|
955
1974
|
type: "string",
|
|
1975
|
+
requiresArg: true,
|
|
956
1976
|
})
|
|
957
1977
|
.option("env", {
|
|
958
1978
|
type: "string",
|
|
1979
|
+
requiresArg: true,
|
|
959
1980
|
describe:
|
|
960
|
-
"Binds the secret to the
|
|
1981
|
+
"Binds the secret to the Worker of the specific environment",
|
|
1982
|
+
alias: "e",
|
|
961
1983
|
});
|
|
962
1984
|
},
|
|
963
1985
|
async (args) => {
|
|
964
|
-
|
|
965
|
-
console.error("--local not implemented for this command yet");
|
|
966
|
-
return;
|
|
967
|
-
}
|
|
968
|
-
const config = args.config as Config;
|
|
969
|
-
|
|
970
|
-
// TODO: use environment (how does current wrangler do it?)
|
|
971
|
-
const scriptName = args.name || config.name;
|
|
972
|
-
if (!scriptName) {
|
|
973
|
-
throw new Error("Missing script name");
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
// -- snip, extract --
|
|
1986
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
977
1987
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
// didn't login, let's just quit
|
|
982
|
-
console.log("Did not login, quitting...");
|
|
983
|
-
return;
|
|
984
|
-
}
|
|
985
|
-
if (!config.account_id) {
|
|
986
|
-
config.account_id = await getAccountId();
|
|
987
|
-
if (!config.account_id) {
|
|
988
|
-
console.error("No account id found, quitting...");
|
|
989
|
-
return;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
1988
|
+
const scriptName = getLegacyScriptName(args, config);
|
|
1989
|
+
if (!scriptName) {
|
|
1990
|
+
throw new Error("Missing script name");
|
|
992
1991
|
}
|
|
993
1992
|
|
|
994
|
-
|
|
1993
|
+
const accountId = await requireAuth(config);
|
|
995
1994
|
|
|
996
|
-
if (
|
|
997
|
-
|
|
998
|
-
`
|
|
1995
|
+
if (
|
|
1996
|
+
await confirm(
|
|
1997
|
+
`Are you sure you want to permanently delete the variable ${
|
|
1998
|
+
args.key
|
|
1999
|
+
} on the script ${scriptName}${
|
|
2000
|
+
args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
|
|
2001
|
+
}?`
|
|
2002
|
+
)
|
|
2003
|
+
) {
|
|
2004
|
+
logger.log(
|
|
2005
|
+
`🌀 Deleting the secret ${args.key} on script ${scriptName}${
|
|
2006
|
+
args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
|
|
2007
|
+
}`
|
|
999
2008
|
);
|
|
1000
2009
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
`/accounts/${
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
);
|
|
2010
|
+
const url =
|
|
2011
|
+
!args.env || isLegacyEnv(config)
|
|
2012
|
+
? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
|
|
2013
|
+
: `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
|
|
2014
|
+
|
|
2015
|
+
await fetchResult(`${url}/${args.key}`, { method: "DELETE" });
|
|
2016
|
+
logger.log(`✨ Success! Deleted secret ${args.key}`);
|
|
1007
2017
|
}
|
|
1008
2018
|
}
|
|
1009
2019
|
)
|
|
@@ -1013,54 +2023,34 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1013
2023
|
(yargs) => {
|
|
1014
2024
|
return yargs
|
|
1015
2025
|
.option("name", {
|
|
1016
|
-
describe: "
|
|
2026
|
+
describe: "Name of the worker",
|
|
1017
2027
|
type: "string",
|
|
2028
|
+
requiresArg: true,
|
|
1018
2029
|
})
|
|
1019
2030
|
.option("env", {
|
|
1020
2031
|
type: "string",
|
|
2032
|
+
requiresArg: true,
|
|
1021
2033
|
describe:
|
|
1022
|
-
"Binds the secret to the
|
|
2034
|
+
"Binds the secret to the Worker of the specific environment.",
|
|
2035
|
+
alias: "e",
|
|
1023
2036
|
});
|
|
1024
2037
|
},
|
|
1025
2038
|
async (args) => {
|
|
1026
|
-
|
|
1027
|
-
console.error("--local not implemented for this command yet");
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
const config = args.config as Config;
|
|
2039
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
1031
2040
|
|
|
1032
|
-
|
|
1033
|
-
const scriptName = args.name || config.name;
|
|
2041
|
+
const scriptName = getLegacyScriptName(args, config);
|
|
1034
2042
|
if (!scriptName) {
|
|
1035
|
-
|
|
1036
|
-
return;
|
|
2043
|
+
throw new Error("Missing script name");
|
|
1037
2044
|
}
|
|
1038
2045
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
if (!args.local) {
|
|
1042
|
-
const loggedIn = await loginOrRefreshIfRequired();
|
|
1043
|
-
if (!loggedIn) {
|
|
1044
|
-
// didn't login, let's just quit
|
|
1045
|
-
console.log("Did not login, quitting...");
|
|
1046
|
-
return;
|
|
1047
|
-
}
|
|
1048
|
-
if (!config.account_id) {
|
|
1049
|
-
config.account_id = await getAccountId();
|
|
1050
|
-
if (!config.account_id) {
|
|
1051
|
-
console.error("No account id found, quitting...");
|
|
1052
|
-
return;
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
2046
|
+
const accountId = await requireAuth(config);
|
|
1056
2047
|
|
|
1057
|
-
|
|
2048
|
+
const url =
|
|
2049
|
+
!args.env || isLegacyEnv(config)
|
|
2050
|
+
? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
|
|
2051
|
+
: `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
|
|
1058
2052
|
|
|
1059
|
-
|
|
1060
|
-
await cfetch(
|
|
1061
|
-
`/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets`
|
|
1062
|
-
)
|
|
1063
|
-
);
|
|
2053
|
+
logger.log(JSON.stringify(await fetchResult(url), null, " "));
|
|
1064
2054
|
}
|
|
1065
2055
|
);
|
|
1066
2056
|
}
|
|
@@ -1068,11 +2058,12 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1068
2058
|
|
|
1069
2059
|
// kv
|
|
1070
2060
|
// :namespace
|
|
1071
|
-
|
|
2061
|
+
wrangler.command(
|
|
1072
2062
|
"kv:namespace",
|
|
1073
2063
|
"🗂️ Interact with your Workers KV Namespaces",
|
|
1074
|
-
(
|
|
1075
|
-
return
|
|
2064
|
+
(namespaceYargs) => {
|
|
2065
|
+
return namespaceYargs
|
|
2066
|
+
.command(subHelp)
|
|
1076
2067
|
.command(
|
|
1077
2068
|
"create <namespace>",
|
|
1078
2069
|
"Create a new namespace",
|
|
@@ -1081,10 +2072,13 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1081
2072
|
.positional("namespace", {
|
|
1082
2073
|
describe: "The name of the new namespace",
|
|
1083
2074
|
type: "string",
|
|
2075
|
+
demandOption: true,
|
|
1084
2076
|
})
|
|
1085
2077
|
.option("env", {
|
|
1086
2078
|
type: "string",
|
|
2079
|
+
requiresArg: true,
|
|
1087
2080
|
describe: "Perform on a specific environment",
|
|
2081
|
+
alias: "e",
|
|
1088
2082
|
})
|
|
1089
2083
|
.option("preview", {
|
|
1090
2084
|
type: "boolean",
|
|
@@ -1092,88 +2086,44 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1092
2086
|
});
|
|
1093
2087
|
},
|
|
1094
2088
|
async (args) => {
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
2089
|
+
await printWranglerBanner();
|
|
2090
|
+
|
|
2091
|
+
if (!isValidKVNamespaceBinding(args.namespace)) {
|
|
2092
|
+
throw new CommandLineArgsError(
|
|
2093
|
+
`The namespace binding name "${args.namespace}" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number.`
|
|
1100
2094
|
);
|
|
1101
2095
|
}
|
|
1102
|
-
|
|
2096
|
+
|
|
2097
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
1103
2098
|
if (!config.name) {
|
|
1104
|
-
|
|
2099
|
+
logger.warn(
|
|
1105
2100
|
"No configured name present, using `worker` as a prefix for the title"
|
|
1106
2101
|
);
|
|
1107
2102
|
}
|
|
1108
2103
|
|
|
1109
|
-
const
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
if (/[\W]+/.test(args.namespace)) {
|
|
1114
|
-
throw new Error("invalid binding name, needs to be js friendly");
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
if (args.local) {
|
|
1118
|
-
const { Miniflare } = await import("miniflare");
|
|
1119
|
-
const mf = new Miniflare({
|
|
1120
|
-
kvPersist: (args.kvPersist as string) || true,
|
|
1121
|
-
// TODO: these options shouldn't be required
|
|
1122
|
-
script: ` `, // has to be a string with at least one char
|
|
1123
|
-
});
|
|
1124
|
-
mf.getKVNamespace(title); // this should "create" the namespace
|
|
1125
|
-
console.log(`✨ Success! Created KV namespace ${title}`);
|
|
1126
|
-
return;
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
// -- snip, extract --
|
|
1130
|
-
|
|
1131
|
-
if (!args.local) {
|
|
1132
|
-
const loggedIn = await loginOrRefreshIfRequired();
|
|
1133
|
-
if (!loggedIn) {
|
|
1134
|
-
// didn't login, let's just quit
|
|
1135
|
-
console.log("Did not login, quitting...");
|
|
1136
|
-
return;
|
|
1137
|
-
}
|
|
1138
|
-
if (!config.account_id) {
|
|
1139
|
-
config.account_id = await getAccountId();
|
|
1140
|
-
if (!config.account_id) {
|
|
1141
|
-
console.error("No account id found, quitting...");
|
|
1142
|
-
return;
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
2104
|
+
const name = config.name || "worker";
|
|
2105
|
+
const environment = args.env ? `-${args.env}` : "";
|
|
2106
|
+
const preview = args.preview ? "_preview" : "";
|
|
2107
|
+
const title = `${name}${environment}-${args.namespace}${preview}`;
|
|
1146
2108
|
|
|
1147
|
-
|
|
2109
|
+
const accountId = await requireAuth(config);
|
|
1148
2110
|
|
|
1149
2111
|
// TODO: generate a binding name stripping non alphanumeric chars
|
|
1150
2112
|
|
|
1151
|
-
|
|
2113
|
+
logger.log(`🌀 Creating namespace with title "${title}"`);
|
|
2114
|
+
const namespaceId = await createKVNamespace(accountId, title);
|
|
1152
2115
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
"Content-Type": "application/json",
|
|
1159
|
-
},
|
|
1160
|
-
body: JSON.stringify({
|
|
1161
|
-
title,
|
|
1162
|
-
}),
|
|
1163
|
-
}
|
|
1164
|
-
);
|
|
1165
|
-
|
|
1166
|
-
console.log("✨ Success!");
|
|
1167
|
-
console.log(
|
|
1168
|
-
`Add the following to your configuration file in your kv_namespaces array${
|
|
1169
|
-
args.env ? ` under [env.${args.env}]` : ""
|
|
1170
|
-
}:`
|
|
2116
|
+
logger.log("✨ Success!");
|
|
2117
|
+
const envString = args.env ? ` under [env.${args.env}]` : "";
|
|
2118
|
+
const previewString = args.preview ? "preview_" : "";
|
|
2119
|
+
logger.log(
|
|
2120
|
+
`Add the following to your configuration file in your kv_namespaces array${envString}:`
|
|
1171
2121
|
);
|
|
1172
|
-
|
|
1173
|
-
`{ binding = "${args.namespace}", ${
|
|
1174
|
-
args.preview ? "preview_" : ""
|
|
1175
|
-
}id = "${response.id}" }`
|
|
2122
|
+
logger.log(
|
|
2123
|
+
`{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }`
|
|
1176
2124
|
);
|
|
2125
|
+
|
|
2126
|
+
// TODO: automatically write this block to the wrangler.toml config file??
|
|
1177
2127
|
}
|
|
1178
2128
|
)
|
|
1179
2129
|
.command(
|
|
@@ -1181,41 +2131,14 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1181
2131
|
"Outputs a list of all KV namespaces associated with your account id.",
|
|
1182
2132
|
{},
|
|
1183
2133
|
async (args) => {
|
|
1184
|
-
|
|
1185
|
-
console.error(`local mode is not yet supported for this command`);
|
|
1186
|
-
return;
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
const config = args.config as Config;
|
|
1190
|
-
|
|
1191
|
-
// -- snip, extract --
|
|
1192
|
-
|
|
1193
|
-
if (!args.local) {
|
|
1194
|
-
const loggedIn = await loginOrRefreshIfRequired();
|
|
1195
|
-
if (!loggedIn) {
|
|
1196
|
-
// didn't login, let's just quit
|
|
1197
|
-
console.log("Did not login, quitting...");
|
|
1198
|
-
return;
|
|
1199
|
-
}
|
|
1200
|
-
if (!config.account_id) {
|
|
1201
|
-
config.account_id = await getAccountId();
|
|
1202
|
-
if (!config.account_id) {
|
|
1203
|
-
console.error("No account id found, quitting...");
|
|
1204
|
-
return;
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
2134
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
1208
2135
|
|
|
1209
|
-
|
|
2136
|
+
const accountId = await requireAuth(config);
|
|
1210
2137
|
|
|
1211
2138
|
// TODO: we should show bindings if they exist for given ids
|
|
1212
2139
|
|
|
1213
|
-
|
|
1214
|
-
JSON.stringify(
|
|
1215
|
-
await listNamespaces(config.account_id),
|
|
1216
|
-
null,
|
|
1217
|
-
" "
|
|
1218
|
-
)
|
|
2140
|
+
logger.log(
|
|
2141
|
+
JSON.stringify(await listKVNamespaces(accountId), null, " ")
|
|
1219
2142
|
);
|
|
1220
2143
|
}
|
|
1221
2144
|
)
|
|
@@ -1226,16 +2149,20 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1226
2149
|
return yargs
|
|
1227
2150
|
.option("binding", {
|
|
1228
2151
|
type: "string",
|
|
2152
|
+
requiresArg: true,
|
|
1229
2153
|
describe: "The name of the namespace to delete",
|
|
1230
2154
|
})
|
|
1231
2155
|
.option("namespace-id", {
|
|
1232
2156
|
type: "string",
|
|
2157
|
+
requiresArg: true,
|
|
1233
2158
|
describe: "The id of the namespace to delete",
|
|
1234
2159
|
})
|
|
1235
2160
|
.check(demandOneOfOption("binding", "namespace-id"))
|
|
1236
2161
|
.option("env", {
|
|
1237
2162
|
type: "string",
|
|
2163
|
+
requiresArg: true,
|
|
1238
2164
|
describe: "Perform on a specific environment",
|
|
2165
|
+
alias: "e",
|
|
1239
2166
|
})
|
|
1240
2167
|
.option("preview", {
|
|
1241
2168
|
type: "boolean",
|
|
@@ -1243,49 +2170,21 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1243
2170
|
});
|
|
1244
2171
|
},
|
|
1245
2172
|
async (args) => {
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
return;
|
|
1249
|
-
}
|
|
1250
|
-
const config = args.config as Config;
|
|
1251
|
-
|
|
1252
|
-
const id =
|
|
1253
|
-
args["namespace-id"] ||
|
|
1254
|
-
(args.env
|
|
1255
|
-
? config.env[args.env] || {}
|
|
1256
|
-
: config
|
|
1257
|
-
).kv_namespaces.find(
|
|
1258
|
-
(namespace) => namespace.binding === args.binding
|
|
1259
|
-
)[args.preview ? "preview_id" : "id"];
|
|
1260
|
-
if (!id) {
|
|
1261
|
-
throw new Error("Are you sure? id not found");
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
// -- snip, extract --
|
|
1265
|
-
|
|
1266
|
-
if (!args.local) {
|
|
1267
|
-
const loggedIn = await loginOrRefreshIfRequired();
|
|
1268
|
-
if (!loggedIn) {
|
|
1269
|
-
// didn't login, let's just quit
|
|
1270
|
-
console.log("Did not login, quitting...");
|
|
1271
|
-
return;
|
|
1272
|
-
}
|
|
2173
|
+
await printWranglerBanner();
|
|
2174
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
1273
2175
|
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
2176
|
+
let id;
|
|
2177
|
+
try {
|
|
2178
|
+
id = getKVNamespaceId(args, config);
|
|
2179
|
+
} catch (e) {
|
|
2180
|
+
throw new CommandLineArgsError(
|
|
2181
|
+
"Not able to delete namespace.\n" + ((e as Error).message ?? e)
|
|
2182
|
+
);
|
|
1281
2183
|
}
|
|
1282
2184
|
|
|
1283
|
-
|
|
2185
|
+
const accountId = await requireAuth(config);
|
|
1284
2186
|
|
|
1285
|
-
await
|
|
1286
|
-
`/accounts/${config.account_id}/storage/kv/namespaces/${id}`,
|
|
1287
|
-
{ method: "DELETE" }
|
|
1288
|
-
);
|
|
2187
|
+
await deleteKVNamespace(accountId, id);
|
|
1289
2188
|
|
|
1290
2189
|
// TODO: recommend they remove it from wrangler.toml
|
|
1291
2190
|
|
|
@@ -1310,11 +2209,12 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1310
2209
|
);
|
|
1311
2210
|
|
|
1312
2211
|
// :key
|
|
1313
|
-
|
|
2212
|
+
wrangler.command(
|
|
1314
2213
|
"kv:key",
|
|
1315
2214
|
"🔑 Individually manage Workers KV key-value pairs",
|
|
1316
|
-
(
|
|
1317
|
-
return
|
|
2215
|
+
(kvKeyYargs) => {
|
|
2216
|
+
return kvKeyYargs
|
|
2217
|
+
.command(subHelp)
|
|
1318
2218
|
.command(
|
|
1319
2219
|
"put <key> [value]",
|
|
1320
2220
|
"Writes a single key/value pair to the given namespace.",
|
|
@@ -1322,24 +2222,29 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1322
2222
|
return yargs
|
|
1323
2223
|
.positional("key", {
|
|
1324
2224
|
type: "string",
|
|
1325
|
-
describe: "The key to write to
|
|
2225
|
+
describe: "The key to write to",
|
|
2226
|
+
demandOption: true,
|
|
1326
2227
|
})
|
|
1327
2228
|
.positional("value", {
|
|
1328
2229
|
type: "string",
|
|
1329
|
-
describe: "The value to write
|
|
2230
|
+
describe: "The value to write",
|
|
1330
2231
|
})
|
|
1331
2232
|
.option("binding", {
|
|
1332
2233
|
type: "string",
|
|
1333
|
-
|
|
2234
|
+
requiresArg: true,
|
|
2235
|
+
describe: "The binding of the namespace to write to",
|
|
1334
2236
|
})
|
|
1335
2237
|
.option("namespace-id", {
|
|
1336
2238
|
type: "string",
|
|
1337
|
-
|
|
2239
|
+
requiresArg: true,
|
|
2240
|
+
describe: "The id of the namespace to write to",
|
|
1338
2241
|
})
|
|
1339
2242
|
.check(demandOneOfOption("binding", "namespace-id"))
|
|
1340
2243
|
.option("env", {
|
|
1341
2244
|
type: "string",
|
|
2245
|
+
requiresArg: true,
|
|
1342
2246
|
describe: "Perform on a specific environment",
|
|
2247
|
+
alias: "e",
|
|
1343
2248
|
})
|
|
1344
2249
|
.option("preview", {
|
|
1345
2250
|
type: "boolean",
|
|
@@ -1347,7 +2252,7 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1347
2252
|
})
|
|
1348
2253
|
.option("ttl", {
|
|
1349
2254
|
type: "number",
|
|
1350
|
-
describe: "Time for which the entries should be visible
|
|
2255
|
+
describe: "Time for which the entries should be visible",
|
|
1351
2256
|
})
|
|
1352
2257
|
.option("expiration", {
|
|
1353
2258
|
type: "number",
|
|
@@ -1356,60 +2261,36 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1356
2261
|
})
|
|
1357
2262
|
.option("path", {
|
|
1358
2263
|
type: "string",
|
|
1359
|
-
|
|
2264
|
+
requiresArg: true,
|
|
2265
|
+
describe: "Read value from the file at a given path",
|
|
1360
2266
|
})
|
|
1361
2267
|
.check(demandOneOfOption("value", "path"));
|
|
1362
2268
|
},
|
|
1363
2269
|
async ({ key, ttl, expiration, ...args }) => {
|
|
1364
|
-
|
|
2270
|
+
await printWranglerBanner();
|
|
2271
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
2272
|
+
const namespaceId = getKVNamespaceId(args, config);
|
|
2273
|
+
// One of `args.path` and `args.value` must be defined
|
|
1365
2274
|
const value = args.path
|
|
1366
|
-
?
|
|
1367
|
-
:
|
|
1368
|
-
|
|
2275
|
+
? readFileSync(args.path)
|
|
2276
|
+
: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2277
|
+
args.value!;
|
|
1369
2278
|
|
|
1370
2279
|
if (args.path) {
|
|
1371
|
-
|
|
1372
|
-
`
|
|
2280
|
+
logger.log(
|
|
2281
|
+
`Writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}.`
|
|
1373
2282
|
);
|
|
1374
2283
|
} else {
|
|
1375
|
-
|
|
1376
|
-
`
|
|
2284
|
+
logger.log(
|
|
2285
|
+
`Writing the value "${value}" to key "${key}" on namespace ${namespaceId}.`
|
|
1377
2286
|
);
|
|
1378
2287
|
}
|
|
1379
2288
|
|
|
1380
|
-
|
|
1381
|
-
const { Miniflare } = await import("miniflare");
|
|
1382
|
-
const mf = new Miniflare({
|
|
1383
|
-
kvPersist: (args.kvPersist as string) || true,
|
|
1384
|
-
// TODO: these options shouldn't be required
|
|
1385
|
-
script: ` `, // has to be a string with at least one char
|
|
1386
|
-
});
|
|
1387
|
-
const ns = await mf.getKVNamespace(namespaceId);
|
|
1388
|
-
await ns.put(key, value, { expiration, expirationTtl: ttl });
|
|
1389
|
-
return;
|
|
1390
|
-
}
|
|
1391
|
-
// -- snip, extract --
|
|
1392
|
-
|
|
1393
|
-
if (!args.local) {
|
|
1394
|
-
const loggedIn = await loginOrRefreshIfRequired();
|
|
1395
|
-
if (!loggedIn) {
|
|
1396
|
-
// didn't login, let's just quit
|
|
1397
|
-
console.log("Did not login, quitting...");
|
|
1398
|
-
return;
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
if (!config.account_id) {
|
|
1402
|
-
config.account_id = await getAccountId();
|
|
1403
|
-
if (!config.account_id) {
|
|
1404
|
-
console.error("No account id found, quitting...");
|
|
1405
|
-
return;
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
// -- snip, end --
|
|
2289
|
+
const accountId = await requireAuth(config);
|
|
1411
2290
|
|
|
1412
|
-
await
|
|
2291
|
+
await putKVKeyValue(accountId, namespaceId, {
|
|
2292
|
+
key,
|
|
2293
|
+
value,
|
|
1413
2294
|
expiration,
|
|
1414
2295
|
expiration_ttl: ttl,
|
|
1415
2296
|
});
|
|
@@ -1422,65 +2303,46 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1422
2303
|
return yargs
|
|
1423
2304
|
.option("binding", {
|
|
1424
2305
|
type: "string",
|
|
2306
|
+
requiresArg: true,
|
|
1425
2307
|
describe: "The name of the namespace to list",
|
|
1426
2308
|
})
|
|
1427
2309
|
.option("namespace-id", {
|
|
1428
2310
|
type: "string",
|
|
2311
|
+
requiresArg: true,
|
|
1429
2312
|
describe: "The id of the namespace to list",
|
|
1430
2313
|
})
|
|
1431
2314
|
.check(demandOneOfOption("binding", "namespace-id"))
|
|
1432
2315
|
.option("env", {
|
|
1433
2316
|
type: "string",
|
|
2317
|
+
requiresArg: true,
|
|
1434
2318
|
describe: "Perform on a specific environment",
|
|
2319
|
+
alias: "e",
|
|
2320
|
+
})
|
|
2321
|
+
.option("preview", {
|
|
2322
|
+
type: "boolean",
|
|
2323
|
+
// In the case of listing keys we will default to non-preview mode
|
|
2324
|
+
default: false,
|
|
2325
|
+
describe: "Interact with a preview namespace",
|
|
1435
2326
|
})
|
|
1436
2327
|
.option("prefix", {
|
|
1437
2328
|
type: "string",
|
|
2329
|
+
requiresArg: true,
|
|
1438
2330
|
describe: "A prefix to filter listed keys",
|
|
1439
2331
|
});
|
|
1440
2332
|
},
|
|
1441
2333
|
async ({ prefix, ...args }) => {
|
|
1442
2334
|
// TODO: support for limit+cursor (pagination)
|
|
2335
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
2336
|
+
const namespaceId = getKVNamespaceId(args, config);
|
|
1443
2337
|
|
|
1444
|
-
const
|
|
1445
|
-
const config = args.config as Config;
|
|
1446
|
-
|
|
1447
|
-
if (args.local) {
|
|
1448
|
-
const { Miniflare } = await import("miniflare");
|
|
1449
|
-
const mf = new Miniflare({
|
|
1450
|
-
kvPersist: (args.kvPersist as string) || true,
|
|
1451
|
-
// TODO: these options shouldn't be required
|
|
1452
|
-
script: ` `, // has to be a string with at least one char
|
|
1453
|
-
});
|
|
1454
|
-
const ns = await mf.getKVNamespace(namespaceId);
|
|
1455
|
-
const listResponse = await ns.list({ prefix });
|
|
1456
|
-
console.log(JSON.stringify(listResponse.keys, null, " ")); // TODO: paginate, collate
|
|
1457
|
-
return;
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
// -- snip, extract --
|
|
1461
|
-
|
|
1462
|
-
if (!args.local) {
|
|
1463
|
-
const loggedIn = await loginOrRefreshIfRequired();
|
|
1464
|
-
if (!loggedIn) {
|
|
1465
|
-
// didn't login, let's just quit
|
|
1466
|
-
console.log("Did not login, quitting...");
|
|
1467
|
-
return;
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
if (!config.account_id) {
|
|
1471
|
-
config.account_id = await getAccountId();
|
|
1472
|
-
if (!config.account_id) {
|
|
1473
|
-
console.error("No account id found, quitting...");
|
|
1474
|
-
return;
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
// -- snip, end --
|
|
2338
|
+
const accountId = await requireAuth(config);
|
|
1480
2339
|
|
|
1481
|
-
|
|
1482
|
-
|
|
2340
|
+
const results = await listKVNamespaceKeys(
|
|
2341
|
+
accountId,
|
|
2342
|
+
namespaceId,
|
|
2343
|
+
prefix
|
|
1483
2344
|
);
|
|
2345
|
+
logger.log(JSON.stringify(results, undefined, 2));
|
|
1484
2346
|
}
|
|
1485
2347
|
)
|
|
1486
2348
|
.command(
|
|
@@ -1491,71 +2353,43 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1491
2353
|
.positional("key", {
|
|
1492
2354
|
describe: "The key value to get.",
|
|
1493
2355
|
type: "string",
|
|
2356
|
+
demandOption: true,
|
|
1494
2357
|
})
|
|
1495
2358
|
.option("binding", {
|
|
1496
2359
|
type: "string",
|
|
2360
|
+
requiresArg: true,
|
|
1497
2361
|
describe: "The name of the namespace to get from",
|
|
1498
2362
|
})
|
|
1499
2363
|
.option("namespace-id", {
|
|
1500
2364
|
type: "string",
|
|
2365
|
+
requiresArg: true,
|
|
1501
2366
|
describe: "The id of the namespace to get from",
|
|
1502
2367
|
})
|
|
1503
2368
|
.check(demandOneOfOption("binding", "namespace-id"))
|
|
1504
2369
|
.option("env", {
|
|
1505
2370
|
type: "string",
|
|
2371
|
+
requiresArg: true,
|
|
1506
2372
|
describe: "Perform on a specific environment",
|
|
2373
|
+
alias: "e",
|
|
2374
|
+
})
|
|
2375
|
+
.option("preview", {
|
|
2376
|
+
type: "boolean",
|
|
2377
|
+
describe: "Interact with a preview namespace",
|
|
1507
2378
|
})
|
|
1508
2379
|
.option("preview", {
|
|
1509
2380
|
type: "boolean",
|
|
2381
|
+
// In the case of getting key values we will default to non-preview mode
|
|
2382
|
+
default: false,
|
|
1510
2383
|
describe: "Interact with a preview namespace",
|
|
1511
2384
|
});
|
|
1512
2385
|
},
|
|
1513
2386
|
async ({ key, ...args }) => {
|
|
1514
|
-
const
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
if (args.local) {
|
|
1518
|
-
const { Miniflare } = await import("miniflare");
|
|
1519
|
-
const mf = new Miniflare({
|
|
1520
|
-
kvPersist: (args.kvPersist as string) || true,
|
|
1521
|
-
// TODO: these options shouldn't be required
|
|
1522
|
-
script: ` `, // has to be a string with at least one char
|
|
1523
|
-
});
|
|
1524
|
-
const ns = await mf.getKVNamespace(namespaceId);
|
|
1525
|
-
console.log(await ns.get(key));
|
|
1526
|
-
return;
|
|
1527
|
-
}
|
|
2387
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
2388
|
+
const namespaceId = getKVNamespaceId(args, config);
|
|
1528
2389
|
|
|
1529
|
-
|
|
2390
|
+
const accountId = await requireAuth(config);
|
|
1530
2391
|
|
|
1531
|
-
|
|
1532
|
-
const loggedIn = await loginOrRefreshIfRequired();
|
|
1533
|
-
if (!loggedIn) {
|
|
1534
|
-
// didn't login, let's just quit
|
|
1535
|
-
console.log("Did not login, quitting...");
|
|
1536
|
-
return;
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
if (!config.account_id) {
|
|
1540
|
-
config.account_id = await getAccountId();
|
|
1541
|
-
if (!config.account_id) {
|
|
1542
|
-
console.error("No account id found, quitting...");
|
|
1543
|
-
return;
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
// -- snip, end --
|
|
1549
|
-
|
|
1550
|
-
// annoyingly, the API for this one doesn't return the
|
|
1551
|
-
// data in the 'standard' format. goddamit.
|
|
1552
|
-
// That's why we have the fallthrough response in cfetch.
|
|
1553
|
-
// Oh well.
|
|
1554
|
-
console.log(
|
|
1555
|
-
await cfetch(
|
|
1556
|
-
`/accounts/${config.account_id}/storage/kv/namespaces/${namespaceId}/values/${key}`
|
|
1557
|
-
)
|
|
1558
|
-
);
|
|
2392
|
+
logger.log(await getKVKeyValue(accountId, namespaceId, key));
|
|
1559
2393
|
}
|
|
1560
2394
|
)
|
|
1561
2395
|
.command(
|
|
@@ -1566,19 +2400,24 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1566
2400
|
.positional("key", {
|
|
1567
2401
|
describe: "The key value to delete",
|
|
1568
2402
|
type: "string",
|
|
2403
|
+
demandOption: true,
|
|
1569
2404
|
})
|
|
1570
2405
|
.option("binding", {
|
|
1571
2406
|
type: "string",
|
|
2407
|
+
requiresArg: true,
|
|
1572
2408
|
describe: "The name of the namespace to delete from",
|
|
1573
2409
|
})
|
|
1574
2410
|
.option("namespace-id", {
|
|
1575
2411
|
type: "string",
|
|
2412
|
+
requiresArg: true,
|
|
1576
2413
|
describe: "The id of the namespace to delete from",
|
|
1577
2414
|
})
|
|
1578
2415
|
.check(demandOneOfOption("binding", "namespace-id"))
|
|
1579
2416
|
.option("env", {
|
|
1580
2417
|
type: "string",
|
|
2418
|
+
requiresArg: true,
|
|
1581
2419
|
describe: "Perform on a specific environment",
|
|
2420
|
+
alias: "e",
|
|
1582
2421
|
})
|
|
1583
2422
|
.option("preview", {
|
|
1584
2423
|
type: "boolean",
|
|
@@ -1586,62 +2425,29 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1586
2425
|
});
|
|
1587
2426
|
},
|
|
1588
2427
|
async ({ key, ...args }) => {
|
|
1589
|
-
|
|
2428
|
+
await printWranglerBanner();
|
|
2429
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
2430
|
+
const namespaceId = getKVNamespaceId(args, config);
|
|
1590
2431
|
|
|
1591
|
-
|
|
1592
|
-
`
|
|
2432
|
+
logger.log(
|
|
2433
|
+
`Deleting the key "${key}" on namespace ${namespaceId}.`
|
|
1593
2434
|
);
|
|
1594
2435
|
|
|
1595
|
-
|
|
1596
|
-
const { Miniflare } = await import("miniflare");
|
|
1597
|
-
const mf = new Miniflare({
|
|
1598
|
-
kvPersist: (args.kvPersist as string) || true,
|
|
1599
|
-
// TODO: these options shouldn't be required
|
|
1600
|
-
script: ` `, // has to be a string with at least one char
|
|
1601
|
-
});
|
|
1602
|
-
const ns = await mf.getKVNamespace(namespaceId);
|
|
1603
|
-
console.log(await ns.delete(key));
|
|
1604
|
-
return;
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
const config = args.config as Config;
|
|
1608
|
-
|
|
1609
|
-
// -- snip, extract --
|
|
1610
|
-
|
|
1611
|
-
if (!args.local) {
|
|
1612
|
-
const loggedIn = await loginOrRefreshIfRequired();
|
|
1613
|
-
if (!loggedIn) {
|
|
1614
|
-
// didn't login, let's just quit
|
|
1615
|
-
console.log("Did not login, quitting...");
|
|
1616
|
-
return;
|
|
1617
|
-
}
|
|
1618
|
-
|
|
1619
|
-
if (!config.account_id) {
|
|
1620
|
-
config.account_id = await getAccountId();
|
|
1621
|
-
if (!config.account_id) {
|
|
1622
|
-
console.error("No account id found, quitting...");
|
|
1623
|
-
return;
|
|
1624
|
-
}
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1628
|
-
// -- snip, end --
|
|
2436
|
+
const accountId = await requireAuth(config);
|
|
1629
2437
|
|
|
1630
|
-
await
|
|
1631
|
-
`/accounts/${config.account_id}/storage/kv/namespaces/${namespaceId}/values/${key}`,
|
|
1632
|
-
{ method: "DELETE" }
|
|
1633
|
-
);
|
|
2438
|
+
await deleteKVKeyValue(accountId, namespaceId, key);
|
|
1634
2439
|
}
|
|
1635
2440
|
);
|
|
1636
2441
|
}
|
|
1637
2442
|
);
|
|
1638
2443
|
|
|
1639
2444
|
// :bulk
|
|
1640
|
-
|
|
2445
|
+
wrangler.command(
|
|
1641
2446
|
"kv:bulk",
|
|
1642
2447
|
"💪 Interact with multiple Workers KV key-value pairs at once",
|
|
1643
|
-
(
|
|
1644
|
-
return
|
|
2448
|
+
(kvBulkYargs) => {
|
|
2449
|
+
return kvBulkYargs
|
|
2450
|
+
.command(subHelp)
|
|
1645
2451
|
.command(
|
|
1646
2452
|
"put <filename>",
|
|
1647
2453
|
"Upload multiple key-value pairs to a namespace",
|
|
@@ -1650,19 +2456,24 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1650
2456
|
.positional("filename", {
|
|
1651
2457
|
describe: `The JSON file of key-value pairs to upload, in form [{"key":..., "value":...}"...]`,
|
|
1652
2458
|
type: "string",
|
|
2459
|
+
demandOption: true,
|
|
1653
2460
|
})
|
|
1654
2461
|
.option("binding", {
|
|
1655
2462
|
type: "string",
|
|
1656
|
-
|
|
2463
|
+
requiresArg: true,
|
|
2464
|
+
describe: "The name of the namespace to insert values into",
|
|
1657
2465
|
})
|
|
1658
2466
|
.option("namespace-id", {
|
|
1659
2467
|
type: "string",
|
|
1660
|
-
|
|
2468
|
+
requiresArg: true,
|
|
2469
|
+
describe: "The id of the namespace to insert values into",
|
|
1661
2470
|
})
|
|
1662
2471
|
.check(demandOneOfOption("binding", "namespace-id"))
|
|
1663
2472
|
.option("env", {
|
|
1664
2473
|
type: "string",
|
|
2474
|
+
requiresArg: true,
|
|
1665
2475
|
describe: "Perform on a specific environment",
|
|
2476
|
+
alias: "e",
|
|
1666
2477
|
})
|
|
1667
2478
|
.option("preview", {
|
|
1668
2479
|
type: "boolean",
|
|
@@ -1670,189 +2481,368 @@ export async function main(argv: string[]): Promise<void> {
|
|
|
1670
2481
|
});
|
|
1671
2482
|
},
|
|
1672
2483
|
async ({ filename, ...args }) => {
|
|
2484
|
+
await printWranglerBanner();
|
|
1673
2485
|
// The simplest implementation I could think of.
|
|
1674
2486
|
// This could be made more efficient with a streaming parser/uploader
|
|
1675
2487
|
// but we'll do that in the future if needed.
|
|
1676
2488
|
|
|
1677
|
-
const
|
|
1678
|
-
const
|
|
1679
|
-
const content =
|
|
1680
|
-
let parsedContent;
|
|
1681
|
-
try {
|
|
1682
|
-
parsedContent = JSON.parse(content);
|
|
1683
|
-
} catch (err) {
|
|
1684
|
-
console.error(`could not parse json from ${filename}`);
|
|
1685
|
-
throw err;
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
if (args.local) {
|
|
1689
|
-
const { Miniflare } = await import("miniflare");
|
|
1690
|
-
const mf = new Miniflare({
|
|
1691
|
-
kvPersist: (args.kvPersist as string) || true,
|
|
1692
|
-
// TODO: these options shouldn't be required
|
|
1693
|
-
script: ` `, // has to be a string with at least one char
|
|
1694
|
-
});
|
|
1695
|
-
const ns = await mf.getKVNamespace(namespaceId);
|
|
1696
|
-
for (const {
|
|
1697
|
-
key,
|
|
1698
|
-
value,
|
|
1699
|
-
expiration,
|
|
1700
|
-
expiration_ttl,
|
|
1701
|
-
} of parsedContent) {
|
|
1702
|
-
await ns.put(key, value, {
|
|
1703
|
-
expiration,
|
|
1704
|
-
expirationTtl: expiration_ttl,
|
|
1705
|
-
});
|
|
1706
|
-
}
|
|
2489
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
2490
|
+
const namespaceId = getKVNamespaceId(args, config);
|
|
2491
|
+
const content = parseJSON(readFileSync(filename), filename);
|
|
1707
2492
|
|
|
1708
|
-
|
|
2493
|
+
if (!Array.isArray(content)) {
|
|
2494
|
+
throw new Error(
|
|
2495
|
+
`Unexpected JSON input from "${filename}".\n` +
|
|
2496
|
+
`Expected an array of key-value objects but got type "${typeof content}".`
|
|
2497
|
+
);
|
|
1709
2498
|
}
|
|
1710
2499
|
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
const
|
|
1715
|
-
if (!
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
2500
|
+
const errors: string[] = [];
|
|
2501
|
+
const warnings: string[] = [];
|
|
2502
|
+
for (let i = 0; i < content.length; i++) {
|
|
2503
|
+
const keyValue = content[i];
|
|
2504
|
+
if (!isKVKeyValue(keyValue)) {
|
|
2505
|
+
errors.push(
|
|
2506
|
+
`The item at index ${i} is ${JSON.stringify(keyValue)}`
|
|
2507
|
+
);
|
|
2508
|
+
} else {
|
|
2509
|
+
const props = unexpectedKVKeyValueProps(keyValue);
|
|
2510
|
+
if (props.length > 0) {
|
|
2511
|
+
warnings.push(
|
|
2512
|
+
`The item at index ${i} contains unexpected properties: ${JSON.stringify(
|
|
2513
|
+
props
|
|
2514
|
+
)}.`
|
|
2515
|
+
);
|
|
1726
2516
|
}
|
|
1727
2517
|
}
|
|
1728
2518
|
}
|
|
2519
|
+
if (warnings.length > 0) {
|
|
2520
|
+
logger.warn(
|
|
2521
|
+
`Unexpected key-value properties in "${filename}".\n` +
|
|
2522
|
+
warnings.join("\n")
|
|
2523
|
+
);
|
|
2524
|
+
}
|
|
2525
|
+
if (errors.length > 0) {
|
|
2526
|
+
throw new Error(
|
|
2527
|
+
`Unexpected JSON input from "${filename}".\n` +
|
|
2528
|
+
`Each item in the array should be an object that matches:\n\n` +
|
|
2529
|
+
`interface KeyValue {\n` +
|
|
2530
|
+
` key: string;\n` +
|
|
2531
|
+
` value: string;\n` +
|
|
2532
|
+
` expiration?: number;\n` +
|
|
2533
|
+
` expiration_ttl?: number;\n` +
|
|
2534
|
+
` metadata?: object;\n` +
|
|
2535
|
+
` base64?: boolean;\n` +
|
|
2536
|
+
`}\n\n` +
|
|
2537
|
+
errors.join("\n")
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
1729
2540
|
|
|
1730
|
-
|
|
2541
|
+
const accountId = await requireAuth(config);
|
|
2542
|
+
await putKVBulkKeyValue(accountId, namespaceId, content);
|
|
1731
2543
|
|
|
1732
|
-
|
|
1733
|
-
await putBulkKeyValue(config.account_id, namespaceId, content)
|
|
1734
|
-
);
|
|
2544
|
+
logger.log("Success!");
|
|
1735
2545
|
}
|
|
1736
2546
|
)
|
|
1737
2547
|
.command(
|
|
1738
2548
|
"delete <filename>",
|
|
1739
|
-
"
|
|
2549
|
+
"Delete multiple key-value pairs from a namespace",
|
|
1740
2550
|
(yargs) => {
|
|
1741
2551
|
return yargs
|
|
1742
2552
|
.positional("filename", {
|
|
1743
|
-
describe: `The JSON file of
|
|
2553
|
+
describe: `The JSON file of keys to delete, in the form ["key1", "key2", ...]`,
|
|
1744
2554
|
type: "string",
|
|
2555
|
+
demandOption: true,
|
|
1745
2556
|
})
|
|
1746
2557
|
.option("binding", {
|
|
1747
2558
|
type: "string",
|
|
2559
|
+
requiresArg: true,
|
|
1748
2560
|
describe: "The name of the namespace to delete from",
|
|
1749
2561
|
})
|
|
1750
2562
|
.option("namespace-id", {
|
|
1751
2563
|
type: "string",
|
|
2564
|
+
requiresArg: true,
|
|
1752
2565
|
describe: "The id of the namespace to delete from",
|
|
1753
2566
|
})
|
|
1754
2567
|
.check(demandOneOfOption("binding", "namespace-id"))
|
|
1755
2568
|
.option("env", {
|
|
1756
2569
|
type: "string",
|
|
2570
|
+
requiresArg: true,
|
|
1757
2571
|
describe: "Perform on a specific environment",
|
|
2572
|
+
alias: "e",
|
|
1758
2573
|
})
|
|
1759
2574
|
.option("preview", {
|
|
1760
2575
|
type: "boolean",
|
|
1761
2576
|
describe: "Interact with a preview namespace",
|
|
2577
|
+
})
|
|
2578
|
+
.option("force", {
|
|
2579
|
+
type: "boolean",
|
|
2580
|
+
alias: "f",
|
|
2581
|
+
describe: "Do not ask for confirmation before deleting",
|
|
1762
2582
|
});
|
|
1763
2583
|
},
|
|
1764
2584
|
async ({ filename, ...args }) => {
|
|
1765
|
-
|
|
1766
|
-
const config = args.config as
|
|
1767
|
-
const
|
|
1768
|
-
let parsedContent;
|
|
1769
|
-
try {
|
|
1770
|
-
parsedContent = JSON.parse(content);
|
|
1771
|
-
} catch (err) {
|
|
1772
|
-
console.error(`could not parse json from ${filename}`);
|
|
1773
|
-
throw err;
|
|
1774
|
-
}
|
|
2585
|
+
await printWranglerBanner();
|
|
2586
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
2587
|
+
const namespaceId = getKVNamespaceId(args, config);
|
|
1775
2588
|
|
|
1776
|
-
if (args.
|
|
1777
|
-
const
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
const ns = await mf.getKVNamespace(namespaceId);
|
|
1784
|
-
for (const key of parsedContent) {
|
|
1785
|
-
await ns.delete(key);
|
|
2589
|
+
if (!args.force) {
|
|
2590
|
+
const result = await confirm(
|
|
2591
|
+
`Are you sure you want to delete all the keys read from "${filename}" from kv-namespace with id "${namespaceId}"?`
|
|
2592
|
+
);
|
|
2593
|
+
if (!result) {
|
|
2594
|
+
logger.log(`Not deleting keys read from "${filename}".`);
|
|
2595
|
+
return;
|
|
1786
2596
|
}
|
|
1787
|
-
|
|
1788
|
-
return;
|
|
1789
2597
|
}
|
|
1790
2598
|
|
|
1791
|
-
|
|
2599
|
+
const content = parseJSON(
|
|
2600
|
+
readFileSync(filename),
|
|
2601
|
+
filename
|
|
2602
|
+
) as string[];
|
|
1792
2603
|
|
|
1793
|
-
if (!
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
}
|
|
2604
|
+
if (!Array.isArray(content)) {
|
|
2605
|
+
throw new Error(
|
|
2606
|
+
`Unexpected JSON input from "${filename}".\n` +
|
|
2607
|
+
`Expected an array of strings but got:\n${content}`
|
|
2608
|
+
);
|
|
2609
|
+
}
|
|
1800
2610
|
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
2611
|
+
const errors: string[] = [];
|
|
2612
|
+
for (let i = 0; i < content.length; i++) {
|
|
2613
|
+
const key = content[i];
|
|
2614
|
+
if (typeof key !== "string") {
|
|
2615
|
+
errors.push(
|
|
2616
|
+
`The item at index ${i} is type: "${typeof key}" - ${JSON.stringify(
|
|
2617
|
+
key
|
|
2618
|
+
)}`
|
|
2619
|
+
);
|
|
1807
2620
|
}
|
|
1808
2621
|
}
|
|
2622
|
+
if (errors.length > 0) {
|
|
2623
|
+
throw new Error(
|
|
2624
|
+
`Unexpected JSON input from "${filename}".\n` +
|
|
2625
|
+
`Expected an array of strings.\n` +
|
|
2626
|
+
errors.join("\n")
|
|
2627
|
+
);
|
|
2628
|
+
}
|
|
1809
2629
|
|
|
1810
|
-
|
|
2630
|
+
const accountId = await requireAuth(config);
|
|
1811
2631
|
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
);
|
|
2632
|
+
await deleteKVBulkKeyValue(accountId, namespaceId, content);
|
|
2633
|
+
|
|
2634
|
+
logger.log("Success!");
|
|
1815
2635
|
}
|
|
1816
2636
|
);
|
|
1817
2637
|
}
|
|
1818
2638
|
);
|
|
1819
2639
|
|
|
1820
|
-
|
|
2640
|
+
wrangler.command(
|
|
2641
|
+
"pages",
|
|
2642
|
+
"⚡️ Configure Cloudflare Pages",
|
|
2643
|
+
async (pagesYargs) => {
|
|
2644
|
+
await pages(pagesYargs.command(subHelp).epilogue(pagesBetaWarning));
|
|
2645
|
+
}
|
|
2646
|
+
);
|
|
1821
2647
|
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
2648
|
+
wrangler.command("r2", "📦 Interact with an R2 store", (r2Yargs) => {
|
|
2649
|
+
return r2Yargs
|
|
2650
|
+
.command(subHelp)
|
|
2651
|
+
.command("bucket", "Manage R2 buckets", (r2BucketYargs) => {
|
|
2652
|
+
r2BucketYargs.command(
|
|
2653
|
+
"create <name>",
|
|
2654
|
+
"Create a new R2 bucket",
|
|
2655
|
+
(yargs) => {
|
|
2656
|
+
return yargs.positional("name", {
|
|
2657
|
+
describe: "The name of the new bucket",
|
|
2658
|
+
type: "string",
|
|
2659
|
+
demandOption: true,
|
|
2660
|
+
});
|
|
2661
|
+
},
|
|
2662
|
+
async (args) => {
|
|
2663
|
+
await printWranglerBanner();
|
|
2664
|
+
|
|
2665
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
2666
|
+
|
|
2667
|
+
const accountId = await requireAuth(config);
|
|
2668
|
+
|
|
2669
|
+
logger.log(`Creating bucket ${args.name}.`);
|
|
2670
|
+
await createR2Bucket(accountId, args.name);
|
|
2671
|
+
logger.log(`Created bucket ${args.name}.`);
|
|
2672
|
+
}
|
|
2673
|
+
);
|
|
2674
|
+
|
|
2675
|
+
r2BucketYargs.command("list", "List R2 buckets", {}, async (args) => {
|
|
2676
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
2677
|
+
|
|
2678
|
+
const accountId = await requireAuth(config);
|
|
2679
|
+
|
|
2680
|
+
logger.log(JSON.stringify(await listR2Buckets(accountId), null, 2));
|
|
2681
|
+
});
|
|
2682
|
+
|
|
2683
|
+
r2BucketYargs.command(
|
|
2684
|
+
"delete <name>",
|
|
2685
|
+
"Delete an R2 bucket",
|
|
2686
|
+
(yargs) => {
|
|
2687
|
+
return yargs.positional("name", {
|
|
2688
|
+
describe: "The name of the bucket to delete",
|
|
2689
|
+
type: "string",
|
|
2690
|
+
demandOption: true,
|
|
2691
|
+
});
|
|
2692
|
+
},
|
|
2693
|
+
async (args) => {
|
|
2694
|
+
await printWranglerBanner();
|
|
2695
|
+
|
|
2696
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
2697
|
+
|
|
2698
|
+
const accountId = await requireAuth(config);
|
|
2699
|
+
|
|
2700
|
+
logger.log(`Deleting bucket ${args.name}.`);
|
|
2701
|
+
await deleteR2Bucket(accountId, args.name);
|
|
2702
|
+
logger.log(`Deleted bucket ${args.name}.`);
|
|
2703
|
+
}
|
|
2704
|
+
);
|
|
2705
|
+
return r2BucketYargs;
|
|
2706
|
+
});
|
|
2707
|
+
});
|
|
2708
|
+
|
|
2709
|
+
/**
|
|
2710
|
+
* User Group: login, logout, and whoami
|
|
2711
|
+
* TODO: group commands into User group similar to .group() for flags in yargs
|
|
2712
|
+
*/
|
|
2713
|
+
// login
|
|
2714
|
+
wrangler.command(
|
|
2715
|
+
// this needs scopes as an option?
|
|
2716
|
+
"login",
|
|
2717
|
+
"🔓 Login to Cloudflare",
|
|
2718
|
+
(yargs) => {
|
|
2719
|
+
// TODO: This needs some copy editing
|
|
2720
|
+
// I mean, this entire app does, but this too.
|
|
2721
|
+
return yargs
|
|
2722
|
+
.option("scopes-list", {
|
|
2723
|
+
describe: "List all the available OAuth scopes with descriptions",
|
|
2724
|
+
})
|
|
2725
|
+
.option("scopes", {
|
|
2726
|
+
describe: "Pick the set of applicable OAuth scopes when logging in",
|
|
2727
|
+
array: true,
|
|
2728
|
+
type: "string",
|
|
2729
|
+
requiresArg: true,
|
|
2730
|
+
});
|
|
1837
2731
|
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
2732
|
+
// TODO: scopes
|
|
2733
|
+
},
|
|
2734
|
+
async (args) => {
|
|
2735
|
+
await printWranglerBanner();
|
|
2736
|
+
if (args["scopes-list"]) {
|
|
2737
|
+
listScopes();
|
|
2738
|
+
return;
|
|
2739
|
+
}
|
|
2740
|
+
if (args.scopes) {
|
|
2741
|
+
if (args.scopes.length === 0) {
|
|
2742
|
+
// don't allow no scopes to be passed, that would be weird
|
|
2743
|
+
listScopes();
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
if (!validateScopeKeys(args.scopes)) {
|
|
2747
|
+
throw new CommandLineArgsError(
|
|
2748
|
+
`One of ${args.scopes} is not a valid authentication scope. Run "wrangler login --list-scopes" to see the valid scopes.`
|
|
2749
|
+
);
|
|
2750
|
+
}
|
|
2751
|
+
await login({ scopes: args.scopes });
|
|
2752
|
+
return;
|
|
1852
2753
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
2754
|
+
await login();
|
|
2755
|
+
|
|
2756
|
+
// TODO: would be nice if it optionally saved login
|
|
2757
|
+
// credentials inside node_modules/.cache or something
|
|
2758
|
+
// this way you could have multiple users on a single machine
|
|
2759
|
+
}
|
|
2760
|
+
);
|
|
2761
|
+
|
|
2762
|
+
// logout
|
|
2763
|
+
wrangler.command(
|
|
2764
|
+
// this needs scopes as an option?
|
|
2765
|
+
"logout",
|
|
2766
|
+
"🚪 Logout from Cloudflare",
|
|
2767
|
+
() => {},
|
|
2768
|
+
async () => {
|
|
2769
|
+
await printWranglerBanner();
|
|
2770
|
+
await logout();
|
|
2771
|
+
}
|
|
2772
|
+
);
|
|
2773
|
+
|
|
2774
|
+
// whoami
|
|
2775
|
+
wrangler.command(
|
|
2776
|
+
"whoami",
|
|
2777
|
+
"🕵️ Retrieve your user info and test your auth config",
|
|
2778
|
+
() => {},
|
|
2779
|
+
async () => {
|
|
2780
|
+
await printWranglerBanner();
|
|
2781
|
+
await whoami();
|
|
2782
|
+
}
|
|
1857
2783
|
);
|
|
2784
|
+
|
|
2785
|
+
wrangler.option("config", {
|
|
2786
|
+
alias: "c",
|
|
2787
|
+
describe: "Path to .toml configuration file",
|
|
2788
|
+
type: "string",
|
|
2789
|
+
requiresArg: true,
|
|
2790
|
+
});
|
|
2791
|
+
|
|
2792
|
+
wrangler.group(["config", "help", "version"], "Flags:");
|
|
2793
|
+
wrangler.help().alias("h", "help");
|
|
2794
|
+
wrangler.version(wranglerVersion).alias("v", "version");
|
|
2795
|
+
wrangler.exitProcess(false);
|
|
2796
|
+
|
|
2797
|
+
return wrangler;
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
export async function main(argv: string[]): Promise<void> {
|
|
2801
|
+
const wrangler = createCLIParser(argv);
|
|
2802
|
+
try {
|
|
2803
|
+
await wrangler.parse();
|
|
2804
|
+
} catch (e) {
|
|
2805
|
+
logger.log(""); // Just adds a bit of space
|
|
2806
|
+
if (e instanceof CommandLineArgsError) {
|
|
2807
|
+
logger.error(e.message);
|
|
2808
|
+
// We are not able to ask the `wrangler` CLI parser to show help for a subcommand programmatically.
|
|
2809
|
+
// The workaround is to re-run the parsing with an additional `--help` flag, which will result in the correct help message being displayed.
|
|
2810
|
+
// The `wrangler` object is "frozen"; we cannot reuse that with different args, so we must create a new CLI parser to generate the help message.
|
|
2811
|
+
await createCLIParser([...argv, "--help"]).parse();
|
|
2812
|
+
} else if (e instanceof ParseError) {
|
|
2813
|
+
e.notes.push({
|
|
2814
|
+
text: "\nIf you think this is a bug, please open an issue at: https://github.com/cloudflare/wrangler2/issues/new/choose",
|
|
2815
|
+
});
|
|
2816
|
+
logger.log(formatMessage(e));
|
|
2817
|
+
} else {
|
|
2818
|
+
logger.error(e instanceof Error ? e.message : e);
|
|
2819
|
+
logger.log(
|
|
2820
|
+
`${fgGreenColor}%s${resetColor}`,
|
|
2821
|
+
"If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
|
|
2822
|
+
);
|
|
2823
|
+
}
|
|
2824
|
+
throw e;
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
function getDevCompatibilityDate(
|
|
2829
|
+
config: Config,
|
|
2830
|
+
compatibilityDate = config.compatibility_date
|
|
2831
|
+
) {
|
|
2832
|
+
const currentDate = new Date().toISOString().substring(0, 10);
|
|
2833
|
+
if (config.configPath !== undefined && compatibilityDate === undefined) {
|
|
2834
|
+
logger.warn(
|
|
2835
|
+
`No compatibility_date was specified. Using today's date: ${currentDate}.\n` +
|
|
2836
|
+
"Add one to your wrangler.toml file:\n" +
|
|
2837
|
+
"```\n" +
|
|
2838
|
+
`compatibility_date = "${currentDate}"\n` +
|
|
2839
|
+
"```\n" +
|
|
2840
|
+
"or pass it in your terminal:\n" +
|
|
2841
|
+
"```\n" +
|
|
2842
|
+
`--compatibility-date=${currentDate}\n` +
|
|
2843
|
+
"```\n" +
|
|
2844
|
+
"See https://developers.cloudflare.com/workers/platform/compatibility-dates for more information."
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
return compatibilityDate ?? currentDate;
|
|
1858
2848
|
}
|