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