wrangler 2.0.22 → 2.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/bin/wrangler.js +1 -1
- package/miniflare-dist/index.mjs +643 -7
- package/package.json +17 -5
- package/src/__tests__/configuration.test.ts +89 -17
- package/src/__tests__/dev.test.tsx +121 -8
- package/src/__tests__/generate.test.ts +93 -0
- package/src/__tests__/helpers/mock-cfetch.ts +54 -2
- package/src/__tests__/index.test.ts +10 -27
- package/src/__tests__/jest.setup.ts +31 -1
- package/src/__tests__/kv.test.ts +82 -61
- package/src/__tests__/metrics.test.ts +5 -0
- package/src/__tests__/publish.test.ts +573 -254
- package/src/__tests__/r2.test.ts +173 -71
- package/src/__tests__/tail.test.ts +93 -39
- package/src/__tests__/user.test.ts +1 -0
- package/src/__tests__/validate-dev-props.test.ts +56 -0
- package/src/__tests__/version.test.ts +35 -0
- package/src/__tests__/whoami.test.tsx +60 -1
- package/src/api/dev.ts +49 -9
- package/src/bundle.ts +298 -37
- package/src/cfetch/internal.ts +34 -2
- package/src/config/config.ts +15 -3
- package/src/config/environment.ts +40 -8
- package/src/config/index.ts +13 -0
- package/src/config/validation.ts +111 -9
- package/src/create-worker-preview.ts +3 -1
- package/src/create-worker-upload-form.ts +25 -0
- package/src/dev/dev.tsx +145 -31
- package/src/dev/local.tsx +116 -24
- package/src/dev/remote.tsx +39 -12
- package/src/dev/use-esbuild.ts +28 -0
- package/src/dev/validate-dev-props.ts +31 -0
- package/src/dev-registry.tsx +160 -0
- package/src/dev.tsx +148 -67
- package/src/generate.ts +112 -14
- package/src/index.tsx +252 -7
- package/src/inspect.ts +90 -5
- package/src/metrics/index.ts +1 -0
- package/src/metrics/metrics-dispatcher.ts +1 -0
- package/src/metrics/metrics-usage-headers.ts +24 -0
- package/src/metrics/send-event.ts +2 -2
- package/src/miniflare-cli/assets.ts +546 -0
- package/src/miniflare-cli/index.ts +157 -6
- package/src/module-collection.ts +3 -3
- package/src/pages/build.tsx +36 -28
- package/src/pages/constants.ts +4 -0
- package/src/pages/deployments.tsx +10 -10
- package/src/pages/dev.tsx +155 -651
- package/src/pages/functions/buildPlugin.ts +4 -0
- package/src/pages/functions/buildWorker.ts +4 -0
- package/src/pages/functions/routes-consolidation.test.ts +66 -0
- package/src/pages/functions/routes-consolidation.ts +29 -0
- package/src/pages/functions/routes-transformation.test.ts +271 -0
- package/src/pages/functions/routes-transformation.ts +125 -0
- package/src/pages/projects.tsx +9 -3
- package/src/pages/publish.tsx +57 -15
- package/src/pages/types.ts +9 -0
- package/src/pages/upload.tsx +38 -21
- package/src/publish.ts +139 -112
- package/src/r2.ts +81 -0
- package/src/tail/index.ts +15 -2
- package/src/tail/printing.ts +41 -3
- package/src/user/choose-account.tsx +20 -11
- package/src/user/user.tsx +20 -2
- package/src/whoami.tsx +79 -1
- package/src/worker.ts +12 -0
- package/templates/first-party-worker-module-facade.ts +18 -0
- package/templates/format-dev-errors.ts +32 -0
- package/templates/pages-shim.ts +9 -0
- package/templates/{static-asset-facade.js → serve-static-assets.ts} +21 -7
- package/templates/service-bindings-module-facade.js +51 -0
- package/templates/service-bindings-sw-facade.js +39 -0
- package/wrangler-dist/cli.d.ts +38 -3
- package/wrangler-dist/cli.js +45244 -25199
|
@@ -205,11 +205,12 @@ describe("WhoAmI component", () => {
|
|
|
205
205
|
);
|
|
206
206
|
});
|
|
207
207
|
|
|
208
|
-
it("should display the user's email and
|
|
208
|
+
it("should display the user's email, accounts and OAuth scopes", async () => {
|
|
209
209
|
const user: UserInfo = {
|
|
210
210
|
authType: "OAuth Token",
|
|
211
211
|
apiToken: "some-oauth-token",
|
|
212
212
|
email: "user@example.com",
|
|
213
|
+
tokenPermissions: ["scope1:read", "scope2:write", "scope3"],
|
|
213
214
|
accounts: [
|
|
214
215
|
{ name: "Account One", id: "account-1" },
|
|
215
216
|
{ name: "Account Two", id: "account-2" },
|
|
@@ -226,5 +227,63 @@ describe("WhoAmI component", () => {
|
|
|
226
227
|
expect(lastFrame()).toMatch(/Account One .+ account-1/);
|
|
227
228
|
expect(lastFrame()).toMatch(/Account Two .+ account-2/);
|
|
228
229
|
expect(lastFrame()).toMatch(/Account Three .+ account-3/);
|
|
230
|
+
expect(lastFrame()).toContain(
|
|
231
|
+
"Token Permissions: If scopes are missing, you may need to logout and re-login."
|
|
232
|
+
);
|
|
233
|
+
expect(lastFrame()).toContain("- scope1 (read)");
|
|
234
|
+
expect(lastFrame()).toContain("- scope2 (write)");
|
|
235
|
+
expect(lastFrame()).toContain("- scope3");
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// For the case where the cache hasn't updated to include the scopes array
|
|
239
|
+
it("should display the user's email and accounts, but no OAuth scopes if none provided", async () => {
|
|
240
|
+
const user: UserInfo = {
|
|
241
|
+
authType: "OAuth Token",
|
|
242
|
+
apiToken: "some-oauth-token",
|
|
243
|
+
email: "user@example.com",
|
|
244
|
+
tokenPermissions: undefined,
|
|
245
|
+
accounts: [
|
|
246
|
+
{ name: "Account One", id: "account-1" },
|
|
247
|
+
{ name: "Account Two", id: "account-2" },
|
|
248
|
+
{ name: "Account Three", id: "account-3" },
|
|
249
|
+
],
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const { lastFrame } = render(<WhoAmI user={user}></WhoAmI>);
|
|
253
|
+
|
|
254
|
+
expect(lastFrame()).toContain(
|
|
255
|
+
"You are logged in with an OAuth Token, associated with the email 'user@example.com'!"
|
|
256
|
+
);
|
|
257
|
+
expect(lastFrame()).toMatch(/Account Name .+ Account ID/);
|
|
258
|
+
expect(lastFrame()).toMatch(/Account One .+ account-1/);
|
|
259
|
+
expect(lastFrame()).toMatch(/Account Two .+ account-2/);
|
|
260
|
+
expect(lastFrame()).toMatch(/Account Three .+ account-3/);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("should display the user's email, accounts and link to view token permissions for non-OAuth tokens", async () => {
|
|
264
|
+
const user: UserInfo = {
|
|
265
|
+
authType: "API Token",
|
|
266
|
+
apiToken: "some-api-token",
|
|
267
|
+
email: "user@example.com",
|
|
268
|
+
tokenPermissions: undefined,
|
|
269
|
+
accounts: [
|
|
270
|
+
{ name: "Account One", id: "account-1" },
|
|
271
|
+
{ name: "Account Two", id: "account-2" },
|
|
272
|
+
{ name: "Account Three", id: "account-3" },
|
|
273
|
+
],
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const { lastFrame } = render(<WhoAmI user={user}></WhoAmI>);
|
|
277
|
+
|
|
278
|
+
expect(lastFrame()).toContain(
|
|
279
|
+
"You are logged in with an API Token, associated with the email 'user@example.com'!"
|
|
280
|
+
);
|
|
281
|
+
expect(lastFrame()).toMatch(/Account Name .+ Account ID/);
|
|
282
|
+
expect(lastFrame()).toMatch(/Account One .+ account-1/);
|
|
283
|
+
expect(lastFrame()).toMatch(/Account Two .+ account-2/);
|
|
284
|
+
expect(lastFrame()).toMatch(/Account Three .+ account-3/);
|
|
285
|
+
expect(lastFrame()).toContain(
|
|
286
|
+
"To see token permissions visit https://dash.cloudflare.com/profile/api-tokens"
|
|
287
|
+
);
|
|
229
288
|
});
|
|
230
289
|
});
|
package/src/api/dev.ts
CHANGED
|
@@ -1,51 +1,91 @@
|
|
|
1
1
|
import { startDev } from "../dev";
|
|
2
2
|
import { logger } from "../logger";
|
|
3
3
|
|
|
4
|
+
import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli";
|
|
4
5
|
import type { RequestInit, Response } from "undici";
|
|
5
6
|
|
|
6
7
|
interface DevOptions {
|
|
7
8
|
env?: string;
|
|
8
9
|
ip?: string;
|
|
9
10
|
port?: number;
|
|
11
|
+
inspectorPort?: number;
|
|
10
12
|
localProtocol?: "http" | "https";
|
|
11
13
|
assets?: string;
|
|
12
14
|
site?: string;
|
|
13
15
|
siteInclude?: string[];
|
|
14
16
|
siteExclude?: string[];
|
|
15
17
|
nodeCompat?: boolean;
|
|
18
|
+
compatibilityDate?: string;
|
|
16
19
|
experimentalEnableLocalPersistence?: boolean;
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
liveReload?: boolean;
|
|
21
|
+
watch?: boolean;
|
|
22
|
+
vars: {
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
};
|
|
25
|
+
kv?: {
|
|
26
|
+
binding: string;
|
|
27
|
+
id: string;
|
|
28
|
+
preview_id?: string;
|
|
29
|
+
}[];
|
|
30
|
+
durableObjects?: {
|
|
31
|
+
name: string;
|
|
32
|
+
class_name: string;
|
|
33
|
+
script_name?: string | undefined;
|
|
34
|
+
environment?: string | undefined;
|
|
35
|
+
}[];
|
|
36
|
+
r2?: {
|
|
37
|
+
binding: string;
|
|
38
|
+
bucket_name: string;
|
|
39
|
+
preview_bucket_name?: string;
|
|
40
|
+
}[];
|
|
41
|
+
showInteractiveDevSession?: boolean;
|
|
42
|
+
logLevel?: "none" | "error" | "log" | "warn" | "debug";
|
|
43
|
+
logPrefix?: string;
|
|
44
|
+
inspect?: boolean;
|
|
45
|
+
forceLocal?: boolean;
|
|
46
|
+
enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
|
|
47
|
+
_?: (string | number)[]; //yargs wants this
|
|
48
|
+
$0?: string; //yargs wants this
|
|
19
49
|
}
|
|
20
50
|
/**
|
|
21
51
|
* unstable_dev starts a wrangler dev server, and returns a promise that resolves with utility functions to interact with it.
|
|
22
52
|
* @param {string} script
|
|
23
53
|
* @param {DevOptions} options
|
|
24
54
|
*/
|
|
25
|
-
export async function unstable_dev(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
55
|
+
export async function unstable_dev(
|
|
56
|
+
script: string,
|
|
57
|
+
options: DevOptions,
|
|
58
|
+
disableExperimentalWarning?: boolean
|
|
59
|
+
) {
|
|
60
|
+
if (!disableExperimentalWarning) {
|
|
61
|
+
logger.warn(
|
|
62
|
+
`unstable_dev() is experimental\nunstable_dev()'s behaviour will likely change in future releases`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
29
65
|
|
|
30
66
|
return new Promise<{
|
|
31
67
|
stop: () => void;
|
|
32
68
|
fetch: (init?: RequestInit) => Promise<Response | undefined>;
|
|
69
|
+
waitUntilExit: () => Promise<void>;
|
|
33
70
|
}>((resolve) => {
|
|
34
71
|
//lmao
|
|
35
72
|
return new Promise<Awaited<ReturnType<typeof startDev>>>((ready) => {
|
|
36
73
|
const devServer = startDev({
|
|
37
74
|
script: script,
|
|
38
|
-
...options,
|
|
39
|
-
local: true,
|
|
40
|
-
onReady: () => ready(devServer),
|
|
41
75
|
inspect: false,
|
|
42
76
|
logLevel: "none",
|
|
43
77
|
showInteractiveDevSession: false,
|
|
78
|
+
_: [],
|
|
79
|
+
$0: "",
|
|
80
|
+
...options,
|
|
81
|
+
local: true,
|
|
82
|
+
onReady: () => ready(devServer),
|
|
44
83
|
});
|
|
45
84
|
}).then((devServer) => {
|
|
46
85
|
resolve({
|
|
47
86
|
stop: devServer.stop,
|
|
48
87
|
fetch: devServer.fetch,
|
|
88
|
+
waitUntilExit: devServer.devReactElement.waitUntilExit,
|
|
49
89
|
});
|
|
50
90
|
});
|
|
51
91
|
});
|
package/src/bundle.ts
CHANGED
|
@@ -5,8 +5,10 @@ import * as path from "node:path";
|
|
|
5
5
|
import NodeGlobalsPolyfills from "@esbuild-plugins/node-globals-polyfill";
|
|
6
6
|
import NodeModulesPolyfills from "@esbuild-plugins/node-modules-polyfill";
|
|
7
7
|
import * as esbuild from "esbuild";
|
|
8
|
+
import tmp from "tmp-promise";
|
|
8
9
|
import createModuleCollector from "./module-collection";
|
|
9
10
|
import type { Config } from "./config";
|
|
11
|
+
import type { WorkerRegistry } from "./dev-registry";
|
|
10
12
|
import type { Entry } from "./entry";
|
|
11
13
|
import type { CfModule } from "./worker";
|
|
12
14
|
|
|
@@ -15,8 +17,15 @@ type BundleResult = {
|
|
|
15
17
|
resolvedEntryPointPath: string;
|
|
16
18
|
bundleType: "esm" | "commonjs";
|
|
17
19
|
stop: (() => void) | undefined;
|
|
20
|
+
sourceMapPath?: string | undefined;
|
|
18
21
|
};
|
|
19
22
|
|
|
23
|
+
type StaticAssetsConfig =
|
|
24
|
+
| (Config["assets"] & {
|
|
25
|
+
bypassCache: boolean | undefined;
|
|
26
|
+
})
|
|
27
|
+
| undefined;
|
|
28
|
+
|
|
20
29
|
/**
|
|
21
30
|
* Searches for any uses of node's builtin modules, and throws an error if it
|
|
22
31
|
* finds anything. This plugin is only used when nodeCompat is not enabled.
|
|
@@ -52,6 +61,7 @@ export async function bundleWorker(
|
|
|
52
61
|
destination: string,
|
|
53
62
|
options: {
|
|
54
63
|
serveAssetsFromWorker: boolean;
|
|
64
|
+
assets: StaticAssetsConfig;
|
|
55
65
|
jsxFactory: string | undefined;
|
|
56
66
|
jsxFragment: string | undefined;
|
|
57
67
|
rules: Config["rules"];
|
|
@@ -61,6 +71,9 @@ export async function bundleWorker(
|
|
|
61
71
|
nodeCompat: boolean | undefined;
|
|
62
72
|
define: Config["define"];
|
|
63
73
|
checkFetch: boolean;
|
|
74
|
+
services: Config["services"];
|
|
75
|
+
workerDefinitions: WorkerRegistry | undefined;
|
|
76
|
+
firstPartyWorkerDevFacade: boolean | undefined;
|
|
64
77
|
}
|
|
65
78
|
): Promise<BundleResult> {
|
|
66
79
|
const {
|
|
@@ -73,7 +86,17 @@ export async function bundleWorker(
|
|
|
73
86
|
minify,
|
|
74
87
|
nodeCompat,
|
|
75
88
|
checkFetch,
|
|
89
|
+
assets,
|
|
90
|
+
workerDefinitions,
|
|
91
|
+
services,
|
|
92
|
+
firstPartyWorkerDevFacade,
|
|
76
93
|
} = options;
|
|
94
|
+
|
|
95
|
+
// We create a temporary directory for any oneoff files we
|
|
96
|
+
// need to create. This is separate from the main build
|
|
97
|
+
// directory (`destination`).
|
|
98
|
+
const tmpDir = await tmp.dir({ unsafeCleanup: true });
|
|
99
|
+
|
|
77
100
|
const entryDirectory = path.dirname(entry.file);
|
|
78
101
|
const moduleCollector = createModuleCollector({
|
|
79
102
|
wrangler1xlegacyModuleReferences: {
|
|
@@ -92,24 +115,94 @@ export async function bundleWorker(
|
|
|
92
115
|
rules,
|
|
93
116
|
});
|
|
94
117
|
|
|
118
|
+
// In dev, we want to patch `fetch()` with a special version that looks
|
|
119
|
+
// for bad usages and can warn the user about them; so we inject
|
|
120
|
+
// `checked-fetch.js` to do so. However, with yarn 3 style pnp,
|
|
121
|
+
// we need to extract that file to an accessible place before injecting
|
|
122
|
+
// it in, hence this code here.
|
|
123
|
+
|
|
124
|
+
const checkedFetchFileToInject = path.join(tmpDir.path, "checked-fetch.js");
|
|
125
|
+
|
|
126
|
+
if (checkFetch && !fs.existsSync(checkedFetchFileToInject)) {
|
|
127
|
+
fs.mkdirSync(tmpDir.path, {
|
|
128
|
+
recursive: true,
|
|
129
|
+
});
|
|
130
|
+
fs.writeFileSync(
|
|
131
|
+
checkedFetchFileToInject,
|
|
132
|
+
fs.readFileSync(path.resolve(__dirname, "../templates/checked-fetch.js"))
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// At this point, we take the opportunity to "wrap" any input workers
|
|
137
|
+
// with any extra functionality we may want to add. This is done by
|
|
138
|
+
// passing the entry point through a pipeline of functions that return
|
|
139
|
+
// a new entry point, that we call "middleware" or "facades".
|
|
140
|
+
// Look at implementations of these functions to learn more.
|
|
141
|
+
|
|
142
|
+
type MiddlewareFn = (arg0: Entry) => Promise<Entry>;
|
|
143
|
+
const middleware: (false | undefined | MiddlewareFn)[] = [
|
|
144
|
+
// serve static assets
|
|
145
|
+
serveAssetsFromWorker &&
|
|
146
|
+
((currentEntry: Entry) => {
|
|
147
|
+
return applyStaticAssetFacade(currentEntry, tmpDir.path, assets);
|
|
148
|
+
}),
|
|
149
|
+
// format errors nicely
|
|
150
|
+
// We use an env var here because we don't actually
|
|
151
|
+
// want to expose this to the user. It's only used internally to
|
|
152
|
+
// experiment with middleware as a teaching exercise.
|
|
153
|
+
process.env.FORMAT_WRANGLER_ERRORS === "true" &&
|
|
154
|
+
((currentEntry: Entry) => {
|
|
155
|
+
return applyFormatDevErrorsFacade(currentEntry, tmpDir.path);
|
|
156
|
+
}),
|
|
157
|
+
// bind to other dev instances/service bindings
|
|
158
|
+
workerDefinitions &&
|
|
159
|
+
Object.keys(workerDefinitions).length > 0 &&
|
|
160
|
+
services &&
|
|
161
|
+
services.length > 0 &&
|
|
162
|
+
((currentEntry: Entry) => {
|
|
163
|
+
return applyMultiWorkerDevFacade(
|
|
164
|
+
currentEntry,
|
|
165
|
+
tmpDir.path,
|
|
166
|
+
services,
|
|
167
|
+
workerDefinitions
|
|
168
|
+
);
|
|
169
|
+
}),
|
|
170
|
+
// Simulate internal environment when using first party workers in dev
|
|
171
|
+
firstPartyWorkerDevFacade === true &&
|
|
172
|
+
((currentEntry: Entry) => {
|
|
173
|
+
return applyFirstPartyWorkerDevFacade(currentEntry, tmpDir.path);
|
|
174
|
+
}),
|
|
175
|
+
].filter(Boolean);
|
|
176
|
+
|
|
177
|
+
let inputEntry = entry;
|
|
178
|
+
|
|
179
|
+
for (const middlewareFn of middleware as MiddlewareFn[]) {
|
|
180
|
+
inputEntry = await middlewareFn(inputEntry);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// At this point, inputEntry points to the entry point we want to build.
|
|
184
|
+
|
|
95
185
|
const result = await esbuild.build({
|
|
96
|
-
|
|
186
|
+
entryPoints: [inputEntry.file],
|
|
97
187
|
bundle: true,
|
|
98
188
|
absWorkingDir: entry.directory,
|
|
99
189
|
outdir: destination,
|
|
100
|
-
inject: checkFetch
|
|
101
|
-
? [path.resolve(__dirname, "../templates/checked-fetch.js")]
|
|
102
|
-
: [],
|
|
190
|
+
inject: checkFetch ? [checkedFetchFileToInject] : [],
|
|
103
191
|
external: ["__STATIC_CONTENT_MANIFEST"],
|
|
104
192
|
format: entry.format === "modules" ? "esm" : "iife",
|
|
105
193
|
target: "es2020",
|
|
106
194
|
sourcemap: true,
|
|
195
|
+
// Include a reference to the output folder in the sourcemap.
|
|
196
|
+
// This is omitted by default, but we need it to properly resolve source paths in error output.
|
|
197
|
+
sourceRoot: destination,
|
|
107
198
|
minify,
|
|
108
199
|
metafile: true,
|
|
109
200
|
conditions: ["worker", "browser"],
|
|
110
201
|
...(process.env.NODE_ENV && {
|
|
111
202
|
define: {
|
|
112
|
-
|
|
203
|
+
// use process.env["NODE_ENV" + ""] so that esbuild doesn't replace it
|
|
204
|
+
// when we do a build of wrangler. (re: https://github.com/cloudflare/wrangler2/issues/1477)
|
|
205
|
+
"process.env.NODE_ENV": `"${process.env["NODE_ENV" + ""]}"`,
|
|
113
206
|
...(nodeCompat ? { global: "globalThis" } : {}),
|
|
114
207
|
...(checkFetch ? { fetch: "checkedFetch" } : {}),
|
|
115
208
|
...options.define,
|
|
@@ -151,6 +244,10 @@ export async function bundleWorker(
|
|
|
151
244
|
const entryPointExports = entryPointOutputs[0][1].exports;
|
|
152
245
|
const bundleType = entryPointExports.length > 0 ? "esm" : "commonjs";
|
|
153
246
|
|
|
247
|
+
const sourceMapPath = Object.keys(result.metafile.outputs).filter((_path) =>
|
|
248
|
+
_path.includes(".map")
|
|
249
|
+
)[0];
|
|
250
|
+
|
|
154
251
|
return {
|
|
155
252
|
modules: moduleCollector.modules,
|
|
156
253
|
resolvedEntryPointPath: path.resolve(
|
|
@@ -159,45 +256,209 @@ export async function bundleWorker(
|
|
|
159
256
|
),
|
|
160
257
|
bundleType,
|
|
161
258
|
stop: result.stop,
|
|
259
|
+
sourceMapPath,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* A simple plugin to alias modules and mark them as external
|
|
265
|
+
*/
|
|
266
|
+
function esbuildAliasExternalPlugin(
|
|
267
|
+
aliases: Record<string, string>
|
|
268
|
+
): esbuild.Plugin {
|
|
269
|
+
return {
|
|
270
|
+
name: "alias",
|
|
271
|
+
setup(build) {
|
|
272
|
+
build.onResolve({ filter: /.*/g }, (args) => {
|
|
273
|
+
// If it's the entrypoint, let it be as is
|
|
274
|
+
if (args.kind === "entry-point") {
|
|
275
|
+
return {
|
|
276
|
+
path: args.path,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
// If it's not a recognised alias, then throw an error
|
|
280
|
+
if (!Object.keys(aliases).includes(args.path)) {
|
|
281
|
+
throw new Error("unrecognized module: " + args.path);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Otherwise, return the alias
|
|
285
|
+
return {
|
|
286
|
+
path: aliases[args.path as keyof typeof aliases],
|
|
287
|
+
external: true,
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
},
|
|
162
291
|
};
|
|
163
292
|
}
|
|
164
293
|
|
|
165
|
-
|
|
294
|
+
/**
|
|
295
|
+
* A middleware that catches any thrown errors, and instead formats
|
|
296
|
+
* them to be rendered in a browser. This middleware is for demonstration
|
|
297
|
+
* purposes only, and is not intended to be used in production (or even dev!)
|
|
298
|
+
*/
|
|
299
|
+
async function applyFormatDevErrorsFacade(
|
|
300
|
+
entry: Entry,
|
|
301
|
+
tmpDirPath: string
|
|
302
|
+
): Promise<Entry> {
|
|
303
|
+
const targetPath = path.join(tmpDirPath, "format-dev-errors.entry.js");
|
|
304
|
+
await esbuild.build({
|
|
305
|
+
entryPoints: [path.resolve(__dirname, "../templates/format-dev-errors.ts")],
|
|
306
|
+
bundle: true,
|
|
307
|
+
sourcemap: true,
|
|
308
|
+
format: "esm",
|
|
309
|
+
plugins: [
|
|
310
|
+
esbuildAliasExternalPlugin({
|
|
311
|
+
__ENTRY_POINT__: entry.file,
|
|
312
|
+
}),
|
|
313
|
+
],
|
|
314
|
+
outfile: targetPath,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
...entry,
|
|
319
|
+
file: targetPath,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
166
322
|
|
|
167
323
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* If we are using the experimental asset handling, then the entry point is
|
|
171
|
-
* actually a shim worker that will either return an asset from a KV store,
|
|
172
|
-
* or delegate to the actual worker.
|
|
324
|
+
* A middleware that serves static assets from a worker.
|
|
325
|
+
* This powers --assets / config.assets
|
|
173
326
|
*/
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
327
|
+
|
|
328
|
+
async function applyStaticAssetFacade(
|
|
329
|
+
entry: Entry,
|
|
330
|
+
tmpDirPath: string,
|
|
331
|
+
assets: StaticAssetsConfig
|
|
332
|
+
): Promise<Entry> {
|
|
333
|
+
const targetPath = path.join(tmpDirPath, "serve-static-assets.entry.js");
|
|
334
|
+
|
|
335
|
+
await esbuild.build({
|
|
336
|
+
entryPoints: [
|
|
337
|
+
path.resolve(__dirname, "../templates/serve-static-assets.ts"),
|
|
338
|
+
],
|
|
339
|
+
bundle: true,
|
|
340
|
+
format: "esm",
|
|
341
|
+
sourcemap: true,
|
|
342
|
+
plugins: [
|
|
343
|
+
esbuildAliasExternalPlugin({
|
|
344
|
+
__ENTRY_POINT__: entry.file,
|
|
345
|
+
__KV_ASSET_HANDLER__: path.join(__dirname, "../kv-asset-handler.js"),
|
|
346
|
+
__STATIC_CONTENT_MANIFEST: "__STATIC_CONTENT_MANIFEST",
|
|
347
|
+
}),
|
|
348
|
+
],
|
|
349
|
+
define: {
|
|
350
|
+
__CACHE_CONTROL_OPTIONS__: JSON.stringify(
|
|
351
|
+
typeof assets === "object"
|
|
352
|
+
? {
|
|
353
|
+
browserTTL:
|
|
354
|
+
assets.browser_TTL || 172800 /* 2 days: 2* 60 * 60 * 24 */,
|
|
355
|
+
bypassCache: assets.bypassCache,
|
|
356
|
+
}
|
|
357
|
+
: {}
|
|
358
|
+
),
|
|
359
|
+
__SERVE_SINGLE_PAGE_APP__: JSON.stringify(
|
|
360
|
+
typeof assets === "object" ? assets.serve_single_page_app : false
|
|
361
|
+
),
|
|
362
|
+
},
|
|
363
|
+
outfile: targetPath,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
...entry,
|
|
368
|
+
file: targetPath,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* A middleware that enables service bindings to be used in dev,
|
|
374
|
+
* binding to other love wrangler dev instances
|
|
375
|
+
*/
|
|
376
|
+
|
|
377
|
+
async function applyMultiWorkerDevFacade(
|
|
378
|
+
entry: Entry,
|
|
379
|
+
tmpDirPath: string,
|
|
380
|
+
services: Config["services"],
|
|
381
|
+
workerDefinitions: WorkerRegistry
|
|
382
|
+
) {
|
|
383
|
+
const targetPath = path.join(tmpDirPath, "serve-static-assets.entry.js");
|
|
384
|
+
const serviceMap = Object.fromEntries(
|
|
385
|
+
(services || []).map((serviceBinding) => [
|
|
386
|
+
serviceBinding.binding,
|
|
387
|
+
workerDefinitions[serviceBinding.service] || null,
|
|
388
|
+
])
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
await esbuild.build({
|
|
392
|
+
entryPoints: [
|
|
393
|
+
path.join(
|
|
394
|
+
__dirname,
|
|
395
|
+
entry.format === "modules"
|
|
396
|
+
? "../templates/service-bindings-module-facade.js"
|
|
397
|
+
: "../templates/service-bindings-sw-facade.js"
|
|
398
|
+
),
|
|
399
|
+
],
|
|
400
|
+
bundle: true,
|
|
401
|
+
sourcemap: true,
|
|
402
|
+
format: "esm",
|
|
403
|
+
plugins: [
|
|
404
|
+
esbuildAliasExternalPlugin({
|
|
405
|
+
__ENTRY_POINT__: entry.file,
|
|
406
|
+
}),
|
|
407
|
+
],
|
|
408
|
+
define: {
|
|
409
|
+
__WORKERS__: JSON.stringify(serviceMap),
|
|
410
|
+
},
|
|
411
|
+
outfile: targetPath,
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
return {
|
|
415
|
+
...entry,
|
|
416
|
+
file: targetPath,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* A middleware that makes first party workers "work" in
|
|
422
|
+
* our dev environments. Is applied during wrangler dev
|
|
423
|
+
* when config.first_party_worker is true
|
|
424
|
+
*/
|
|
425
|
+
async function applyFirstPartyWorkerDevFacade(
|
|
426
|
+
entry: Entry,
|
|
427
|
+
tmpDirPath: string
|
|
428
|
+
) {
|
|
429
|
+
if (entry.format !== "modules") {
|
|
430
|
+
throw new Error(
|
|
431
|
+
"First party workers must be in the modules format. See https://developers.cloudflare.com/workers/learning/migrating-to-module-workers/"
|
|
432
|
+
);
|
|
200
433
|
}
|
|
434
|
+
|
|
435
|
+
const targetPath = path.join(
|
|
436
|
+
tmpDirPath,
|
|
437
|
+
"first-party-worker-module-facade.entry.js"
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
await esbuild.build({
|
|
441
|
+
entryPoints: [
|
|
442
|
+
path.resolve(
|
|
443
|
+
__dirname,
|
|
444
|
+
"../templates/first-party-worker-module-facade.ts"
|
|
445
|
+
),
|
|
446
|
+
],
|
|
447
|
+
bundle: true,
|
|
448
|
+
format: "esm",
|
|
449
|
+
sourcemap: true,
|
|
450
|
+
plugins: [
|
|
451
|
+
esbuildAliasExternalPlugin({
|
|
452
|
+
__ENTRY_POINT__: entry.file,
|
|
453
|
+
}),
|
|
454
|
+
],
|
|
455
|
+
outfile: targetPath,
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
return {
|
|
459
|
+
...entry,
|
|
460
|
+
file: targetPath,
|
|
461
|
+
};
|
|
201
462
|
}
|
|
202
463
|
|
|
203
464
|
/**
|
package/src/cfetch/internal.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ParseError, parseJSON } from "../parse";
|
|
|
6
6
|
import { loginOrRefreshIfRequired, requireApiToken } from "../user";
|
|
7
7
|
import type { ApiCredentials } from "../user";
|
|
8
8
|
import type { URLSearchParams } from "node:url";
|
|
9
|
-
import type { RequestInit, HeadersInit } from "undici";
|
|
9
|
+
import type { RequestInit, HeadersInit, Response } from "undici";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Get the URL to use to access the Cloudflare API.
|
|
@@ -124,7 +124,6 @@ function addUserAgent(headers: Record<string, string>): void {
|
|
|
124
124
|
* Note: any calls to fetchKVGetValue must call encodeURIComponent on key
|
|
125
125
|
* before passing it
|
|
126
126
|
*/
|
|
127
|
-
|
|
128
127
|
export async function fetchKVGetValue(
|
|
129
128
|
accountId: string,
|
|
130
129
|
namespaceId: string,
|
|
@@ -147,3 +146,36 @@ export async function fetchKVGetValue(
|
|
|
147
146
|
);
|
|
148
147
|
}
|
|
149
148
|
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The implementation for fetching a R2 object from Cloudflare API.
|
|
152
|
+
* We have a special implementation to handle the non-standard API response
|
|
153
|
+
* that doesn't return JSON, likely due to the streaming nature.
|
|
154
|
+
*
|
|
155
|
+
* note: The implementation should be called from light wrappers for
|
|
156
|
+
* different methods (GET, PUT)
|
|
157
|
+
*/
|
|
158
|
+
type ResponseWithBody = Response & { body: NonNullable<Response["body"]> };
|
|
159
|
+
export async function fetchR2Objects(
|
|
160
|
+
resource: string,
|
|
161
|
+
bodyInit: RequestInit = {}
|
|
162
|
+
): Promise<ResponseWithBody> {
|
|
163
|
+
await requireLoggedIn();
|
|
164
|
+
const auth = requireApiToken();
|
|
165
|
+
const headers = cloneHeaders(bodyInit.headers);
|
|
166
|
+
addAuthorizationHeaderIfUnspecified(headers, auth);
|
|
167
|
+
addUserAgent(headers);
|
|
168
|
+
|
|
169
|
+
const response = await fetch(`${getCloudflareAPIBaseURL()}${resource}`, {
|
|
170
|
+
...bodyInit,
|
|
171
|
+
headers,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
if (response.ok && response.body) {
|
|
175
|
+
return response as ResponseWithBody;
|
|
176
|
+
} else {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`Failed to fetch ${resource} - ${response.status}: ${response.statusText});`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
package/src/config/config.ts
CHANGED
|
@@ -127,8 +127,13 @@ export interface ConfigFields<Dev extends RawDevConfig> {
|
|
|
127
127
|
* This can either be a string, or an object with additional config fields.
|
|
128
128
|
*/
|
|
129
129
|
assets:
|
|
130
|
-
|
|
|
131
|
-
|
|
130
|
+
| {
|
|
131
|
+
bucket: string;
|
|
132
|
+
include: string[];
|
|
133
|
+
exclude: string[];
|
|
134
|
+
browser_TTL: number | undefined;
|
|
135
|
+
serve_single_page_app: boolean;
|
|
136
|
+
}
|
|
132
137
|
| undefined;
|
|
133
138
|
|
|
134
139
|
/**
|
|
@@ -169,7 +174,7 @@ export interface DevConfig {
|
|
|
169
174
|
/**
|
|
170
175
|
* IP address for the local dev server to listen on,
|
|
171
176
|
*
|
|
172
|
-
* @default `
|
|
177
|
+
* @default `0.0.0.0`
|
|
173
178
|
*/
|
|
174
179
|
ip: string;
|
|
175
180
|
|
|
@@ -180,6 +185,13 @@ export interface DevConfig {
|
|
|
180
185
|
*/
|
|
181
186
|
port: number | undefined;
|
|
182
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Port for the local dev server's inspector to listen on
|
|
190
|
+
*
|
|
191
|
+
* @default `9229`
|
|
192
|
+
*/
|
|
193
|
+
inspector_port: number | undefined;
|
|
194
|
+
|
|
183
195
|
/**
|
|
184
196
|
* Protocol that local wrangler dev server listens to requests on.
|
|
185
197
|
*
|