wrangler 0.0.0-e95bea2 → 0.0.0-ea8e701
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 +3 -3
- package/bin/wrangler.js +10 -1
- package/miniflare-dist/index.mjs +79 -0
- package/miniflare-dist/index.mjs.map +7 -0
- package/package.json +7 -6
- package/pages/functions/filepath-routing.test.ts +125 -25
- package/pages/functions/filepath-routing.ts +6 -3
- package/src/__tests__/configuration.test.ts +826 -223
- package/src/__tests__/dev.test.tsx +145 -9
- package/src/__tests__/guess-worker-format.test.ts +26 -1
- package/src/__tests__/helpers/mock-account-id.ts +4 -0
- package/src/__tests__/helpers/mock-kv.ts +9 -16
- package/src/__tests__/helpers/mock-oauth-flow.ts +178 -0
- package/src/__tests__/helpers/run-in-tmp.ts +3 -0
- package/src/__tests__/index.test.ts +9 -13
- package/src/__tests__/jest.setup.ts +5 -0
- package/src/__tests__/kv.test.ts +43 -71
- package/src/__tests__/pages.test.ts +48 -10
- package/src/__tests__/publish.test.ts +1456 -327
- package/src/__tests__/r2.test.ts +4 -4
- package/src/__tests__/tail.test.ts +78 -4
- package/src/__tests__/user.test.ts +83 -0
- package/src/__tests__/whoami.test.tsx +16 -7
- package/src/bundle.ts +15 -2
- package/src/cfetch/internal.ts +9 -5
- package/src/config/README.md +6 -6
- package/src/config/config.ts +17 -78
- package/src/config/environment.ts +67 -1
- package/src/config/index.ts +13 -3
- package/src/config/validation-helpers.ts +177 -11
- package/src/config/validation.ts +305 -100
- package/src/dev/dev.tsx +76 -41
- package/src/dev/local.tsx +105 -114
- package/src/dev/remote.tsx +2 -0
- package/src/dev/use-esbuild.ts +4 -0
- package/src/entry.ts +103 -6
- package/src/index.tsx +251 -189
- package/src/intl-polyfill.d.ts +139 -0
- package/src/{kv/index.tsx → kv.ts} +8 -28
- package/src/miniflare-cli/README.md +30 -0
- package/src/miniflare-cli/enum-keys.ts +17 -0
- package/src/miniflare-cli/index.ts +37 -0
- package/src/module-collection.ts +58 -53
- package/src/pages.tsx +26 -10
- package/src/proxy.ts +5 -3
- package/src/publish.ts +17 -21
- package/src/sites.tsx +67 -25
- package/src/tail/index.ts +1 -1
- package/src/tail/printing.ts +2 -2
- package/src/user.tsx +161 -81
- package/wrangler-dist/cli.js +3418 -1437
- package/wrangler-dist/cli.js.map +3 -3
- package/src/__tests__/dev2.test.tsx +0 -152
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/__tests__/logout.test.ts +0 -50
|
@@ -9,7 +9,7 @@ import { runInTempDir } from "./helpers/run-in-tmp";
|
|
|
9
9
|
import { runWrangler } from "./helpers/run-wrangler";
|
|
10
10
|
import writeWranglerToml from "./helpers/write-wrangler-toml";
|
|
11
11
|
|
|
12
|
-
describe("
|
|
12
|
+
describe("wrangler dev", () => {
|
|
13
13
|
mockAccountId();
|
|
14
14
|
mockApiToken();
|
|
15
15
|
runInTempDir();
|
|
@@ -37,6 +37,57 @@ describe("Dev component", () => {
|
|
|
37
37
|
[32m%s[0m If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
|
|
38
38
|
`);
|
|
39
39
|
});
|
|
40
|
+
|
|
41
|
+
it("should use `main` from the top-level environment", async () => {
|
|
42
|
+
writeWranglerToml({
|
|
43
|
+
main: "index.js",
|
|
44
|
+
});
|
|
45
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
46
|
+
await runWrangler("dev");
|
|
47
|
+
expect((Dev as jest.Mock).mock.calls[0][0].entry.file).toMatch(
|
|
48
|
+
/index\.js$/
|
|
49
|
+
);
|
|
50
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
51
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
52
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should use `main` from a named environment", async () => {
|
|
56
|
+
writeWranglerToml({
|
|
57
|
+
env: {
|
|
58
|
+
ENV1: {
|
|
59
|
+
main: "index.js",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
64
|
+
await runWrangler("dev --env=ENV1");
|
|
65
|
+
expect((Dev as jest.Mock).mock.calls[0][0].entry.file).toMatch(
|
|
66
|
+
/index\.js$/
|
|
67
|
+
);
|
|
68
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
69
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
70
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should use `main` from a named environment, rather than the top-level", async () => {
|
|
74
|
+
writeWranglerToml({
|
|
75
|
+
main: "other.js",
|
|
76
|
+
env: {
|
|
77
|
+
ENV1: {
|
|
78
|
+
main: "index.js",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
83
|
+
await runWrangler("dev --env=ENV1");
|
|
84
|
+
expect((Dev as jest.Mock).mock.calls[0][0].entry.file).toMatch(
|
|
85
|
+
/index\.js$/
|
|
86
|
+
);
|
|
87
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
88
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
89
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
90
|
+
});
|
|
40
91
|
});
|
|
41
92
|
|
|
42
93
|
describe("host", () => {
|
|
@@ -135,7 +186,7 @@ describe("Dev component", () => {
|
|
|
135
186
|
});
|
|
136
187
|
});
|
|
137
188
|
|
|
138
|
-
it("should, in order, use args.host/config.dev.host/args.routes/config.route
|
|
189
|
+
it("should, in order, use args.host/config.dev.host/args.routes/(config.route|config.routes)", async () => {
|
|
139
190
|
// This test might seem like it's testing implementation details, but let's be specific and consider it a spec
|
|
140
191
|
|
|
141
192
|
fs.writeFileSync("index.js", `export default {};`);
|
|
@@ -158,7 +209,6 @@ describe("Dev component", () => {
|
|
|
158
209
|
writeWranglerToml({
|
|
159
210
|
main: "index.js",
|
|
160
211
|
route: "https://4.some-host.com/some/path/*",
|
|
161
|
-
routes: ["http://5.some-host.com/some/path/*"],
|
|
162
212
|
});
|
|
163
213
|
await runWrangler("dev");
|
|
164
214
|
expect((Dev as jest.Mock).mock.calls[0][0].zone).toEqual({
|
|
@@ -172,7 +222,6 @@ describe("Dev component", () => {
|
|
|
172
222
|
writeWranglerToml({
|
|
173
223
|
main: "index.js",
|
|
174
224
|
route: "https://4.some-host.com/some/path/*",
|
|
175
|
-
routes: ["http://5.some-host.com/some/path/*"],
|
|
176
225
|
});
|
|
177
226
|
await runWrangler("dev --routes http://3.some-host.com/some/path/*");
|
|
178
227
|
expect((Dev as jest.Mock).mock.calls[0][0].zone).toEqual({
|
|
@@ -189,7 +238,6 @@ describe("Dev component", () => {
|
|
|
189
238
|
host: `2.some-host.com`,
|
|
190
239
|
},
|
|
191
240
|
route: "4.some-host.com/some/path/*",
|
|
192
|
-
routes: ["5.some-host.com/some/path/*"],
|
|
193
241
|
});
|
|
194
242
|
await runWrangler("dev --routes http://3.some-host.com/some/path/*");
|
|
195
243
|
expect((Dev as jest.Mock).mock.calls[0][0].zone).toEqual({
|
|
@@ -206,7 +254,6 @@ describe("Dev component", () => {
|
|
|
206
254
|
host: `2.some-host.com`,
|
|
207
255
|
},
|
|
208
256
|
route: "4.some-host.com/some/path/*",
|
|
209
|
-
routes: ["5.some-host.com/some/path/*"],
|
|
210
257
|
});
|
|
211
258
|
await runWrangler(
|
|
212
259
|
"dev --routes http://3.some-host.com/some/path/* --host 1.some-host.com"
|
|
@@ -230,6 +277,15 @@ describe("Dev component", () => {
|
|
|
230
277
|
`"Could not find zone for some-host.com"`
|
|
231
278
|
);
|
|
232
279
|
});
|
|
280
|
+
|
|
281
|
+
it("should not try to resolve a zone when starting in local mode", async () => {
|
|
282
|
+
writeWranglerToml({
|
|
283
|
+
main: "index.js",
|
|
284
|
+
});
|
|
285
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
286
|
+
await runWrangler("dev --host some-host.com --local");
|
|
287
|
+
expect((Dev as jest.Mock).mock.calls[0][0].zone).toEqual(undefined);
|
|
288
|
+
});
|
|
233
289
|
});
|
|
234
290
|
|
|
235
291
|
describe("custom builds", () => {
|
|
@@ -247,11 +303,11 @@ describe("Dev component", () => {
|
|
|
247
303
|
);
|
|
248
304
|
|
|
249
305
|
// and the command would pass through
|
|
250
|
-
expect((Dev as jest.Mock).mock.calls[0][0].
|
|
306
|
+
expect((Dev as jest.Mock).mock.calls[0][0].build).toEqual({
|
|
251
307
|
command:
|
|
252
308
|
"node -e \"console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')\"",
|
|
253
309
|
cwd: undefined,
|
|
254
|
-
watch_dir:
|
|
310
|
+
watch_dir: "src",
|
|
255
311
|
});
|
|
256
312
|
expect(std.out).toMatchInlineSnapshot(
|
|
257
313
|
`"running: node -e \\"console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')\\""`
|
|
@@ -355,7 +411,7 @@ describe("Dev component", () => {
|
|
|
355
411
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
356
412
|
});
|
|
357
413
|
|
|
358
|
-
it("should use
|
|
414
|
+
it("should use `local_protocol` from `wrangler.toml`, if available", async () => {
|
|
359
415
|
writeWranglerToml({
|
|
360
416
|
main: "index.js",
|
|
361
417
|
dev: {
|
|
@@ -389,6 +445,86 @@ describe("Dev component", () => {
|
|
|
389
445
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
390
446
|
});
|
|
391
447
|
});
|
|
448
|
+
|
|
449
|
+
describe("ip", () => {
|
|
450
|
+
it("should default ip to localhost", async () => {
|
|
451
|
+
writeWranglerToml({
|
|
452
|
+
main: "index.js",
|
|
453
|
+
});
|
|
454
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
455
|
+
await runWrangler("dev");
|
|
456
|
+
expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("localhost");
|
|
457
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
458
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
459
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it("should use to `ip` from `wrangler.toml`, if available", async () => {
|
|
463
|
+
writeWranglerToml({
|
|
464
|
+
main: "index.js",
|
|
465
|
+
dev: {
|
|
466
|
+
ip: "0.0.0.0",
|
|
467
|
+
},
|
|
468
|
+
});
|
|
469
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
470
|
+
await runWrangler("dev");
|
|
471
|
+
expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("0.0.0.0");
|
|
472
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
473
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
474
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it("should use --ip command line arg, if provided", async () => {
|
|
478
|
+
writeWranglerToml({
|
|
479
|
+
main: "index.js",
|
|
480
|
+
dev: {
|
|
481
|
+
ip: "1.1.1.1",
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
485
|
+
await runWrangler("dev --ip=0.0.0.0");
|
|
486
|
+
expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("0.0.0.0");
|
|
487
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
488
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
489
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
describe("durable_objects", () => {
|
|
494
|
+
it("should warn if there is one or more remote Durable Object", async () => {
|
|
495
|
+
writeWranglerToml({
|
|
496
|
+
main: "index.js",
|
|
497
|
+
durable_objects: {
|
|
498
|
+
bindings: [
|
|
499
|
+
{ name: "NAME_1", class_name: "CLASS_1" },
|
|
500
|
+
{
|
|
501
|
+
name: "NAME_2",
|
|
502
|
+
class_name: "CLASS_2",
|
|
503
|
+
script_name: "SCRIPT_A",
|
|
504
|
+
},
|
|
505
|
+
{ name: "NAME_3", class_name: "CLASS_3" },
|
|
506
|
+
{
|
|
507
|
+
name: "NAME_4",
|
|
508
|
+
class_name: "CLASS_4",
|
|
509
|
+
script_name: "SCRIPT_B",
|
|
510
|
+
},
|
|
511
|
+
],
|
|
512
|
+
},
|
|
513
|
+
});
|
|
514
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
515
|
+
await runWrangler("dev");
|
|
516
|
+
expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("localhost");
|
|
517
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
518
|
+
expect(std.warn).toMatchInlineSnapshot(`
|
|
519
|
+
"WARNING: You have Durable Object bindings, which are not defined locally in the worker being developed.
|
|
520
|
+
Be aware that changes to the data stored in these Durable Objects will be permanent and affect the live instances.
|
|
521
|
+
Remote Durable Objects that are affected:
|
|
522
|
+
- {\\"name\\":\\"NAME_2\\",\\"class_name\\":\\"CLASS_2\\",\\"script_name\\":\\"SCRIPT_A\\"}
|
|
523
|
+
- {\\"name\\":\\"NAME_4\\",\\"class_name\\":\\"CLASS_4\\",\\"script_name\\":\\"SCRIPT_B\\"}"
|
|
524
|
+
`);
|
|
525
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
526
|
+
});
|
|
527
|
+
});
|
|
392
528
|
});
|
|
393
529
|
|
|
394
530
|
function mockGetZones(domain: string, zones: { id: string }[] = []) {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { writeFile } from "fs/promises";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import guessWorkerFormat from "../entry";
|
|
4
|
+
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
4
5
|
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
5
6
|
|
|
6
7
|
describe("guess worker format", () => {
|
|
7
8
|
runInTempDir();
|
|
9
|
+
const std = mockConsoleMethods();
|
|
8
10
|
it('should detect a "modules" worker', async () => {
|
|
9
11
|
await writeFile("./index.ts", "export default {};");
|
|
10
12
|
// Note that this isn't actually a valid worker, because it's missing
|
|
@@ -39,7 +41,7 @@ describe("guess worker format", () => {
|
|
|
39
41
|
"service-worker"
|
|
40
42
|
)
|
|
41
43
|
).rejects.toThrow(
|
|
42
|
-
"You configured this worker to be a 'service-worker', but the file you are trying to build appears to have
|
|
44
|
+
"You configured this worker to be a 'service-worker', but the file you are trying to build appears to have a `default` export like a module worker. Please pass `--format modules`, or simply remove the configuration."
|
|
43
45
|
);
|
|
44
46
|
});
|
|
45
47
|
|
|
@@ -55,4 +57,27 @@ describe("guess worker format", () => {
|
|
|
55
57
|
"You configured this worker to be 'modules', but the file you are trying to build doesn't export a handler. Please pass `--format service-worker`, or simply remove the configuration."
|
|
56
58
|
);
|
|
57
59
|
});
|
|
60
|
+
|
|
61
|
+
it("should not error if a .js entry point has jsx", async () => {
|
|
62
|
+
await writeFile("./index.js", "console.log(<div/>)");
|
|
63
|
+
const guess = await guessWorkerFormat(
|
|
64
|
+
path.join(process.cwd(), "./index.js"),
|
|
65
|
+
process.cwd(),
|
|
66
|
+
undefined
|
|
67
|
+
);
|
|
68
|
+
expect(guess).toBe("service-worker");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("logs a warning when a worker has exports, but not a default one", async () => {
|
|
72
|
+
await writeFile("./index.ts", "export const foo = 1;");
|
|
73
|
+
const guess = await guessWorkerFormat(
|
|
74
|
+
path.join(process.cwd(), "./index.ts"),
|
|
75
|
+
process.cwd(),
|
|
76
|
+
undefined
|
|
77
|
+
);
|
|
78
|
+
expect(guess).toBe("service-worker");
|
|
79
|
+
expect(std.warn).toMatchInlineSnapshot(
|
|
80
|
+
`"The entrypoint index.ts has exports like an ES Module, but hasn't defined a default export like a module worker normally would. Building the worker using \\"service-worker\\" format..."`
|
|
81
|
+
);
|
|
82
|
+
});
|
|
58
83
|
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { reinitialiseAuthTokens } from "../../user";
|
|
2
|
+
|
|
1
3
|
const ORIGINAL_CLOUDFLARE_API_TOKEN = process.env.CLOUDFLARE_API_TOKEN;
|
|
2
4
|
const ORIGINAL_CLOUDFLARE_ACCOUNT_ID = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
3
5
|
|
|
@@ -16,6 +18,8 @@ export function mockApiToken({
|
|
|
16
18
|
} else {
|
|
17
19
|
process.env.CLOUDFLARE_API_TOKEN = apiToken;
|
|
18
20
|
}
|
|
21
|
+
// Now we have updated the environment, we must reinitialize the user auth state.
|
|
22
|
+
reinitialiseAuthTokens();
|
|
19
23
|
});
|
|
20
24
|
afterEach(() => {
|
|
21
25
|
process.env.CLOUDFLARE_API_TOKEN = ORIGINAL_CLOUDFLARE_API_TOKEN;
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { createFetchResult, setMockRawResponse } from "./mock-cfetch";
|
|
2
|
+
import type { NamespaceKeyInfo } from "../../kv";
|
|
2
3
|
|
|
3
4
|
export function mockKeyListRequest(
|
|
4
5
|
expectedNamespaceId: string,
|
|
5
|
-
expectedKeys:
|
|
6
|
+
expectedKeys: NamespaceKeyInfo[],
|
|
6
7
|
keysPerRequest = 1000,
|
|
7
8
|
blankCursorValue: "" | undefined | null = undefined
|
|
8
9
|
) {
|
|
9
10
|
const requests = { count: 0 };
|
|
10
11
|
// See https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys
|
|
11
|
-
|
|
12
|
-
name,
|
|
13
|
-
expiration: 123456789,
|
|
14
|
-
metadata: {},
|
|
15
|
-
}));
|
|
12
|
+
|
|
16
13
|
setMockRawResponse(
|
|
17
14
|
"/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
|
|
18
15
|
"GET",
|
|
@@ -20,19 +17,15 @@ export function mockKeyListRequest(
|
|
|
20
17
|
requests.count++;
|
|
21
18
|
expect(accountId).toEqual("some-account-id");
|
|
22
19
|
expect(namespaceId).toEqual(expectedNamespaceId);
|
|
23
|
-
if (
|
|
24
|
-
return createFetchResult(
|
|
20
|
+
if (expectedKeys.length <= keysPerRequest) {
|
|
21
|
+
return createFetchResult(expectedKeys);
|
|
25
22
|
} else {
|
|
26
23
|
const start = parseInt(query.get("cursor") ?? "0") || 0;
|
|
27
24
|
const end = start + keysPerRequest;
|
|
28
|
-
const cursor = end <
|
|
29
|
-
return createFetchResult(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
[],
|
|
33
|
-
[],
|
|
34
|
-
{ cursor }
|
|
35
|
-
);
|
|
25
|
+
const cursor = end < expectedKeys.length ? end : blankCursorValue;
|
|
26
|
+
return createFetchResult(expectedKeys.slice(start, end), true, [], [], {
|
|
27
|
+
cursor,
|
|
28
|
+
});
|
|
36
29
|
}
|
|
37
30
|
}
|
|
38
31
|
);
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { ChildProcess } from "child_process";
|
|
2
|
+
import fetchMock from "jest-fetch-mock";
|
|
3
|
+
import { Request } from "undici";
|
|
4
|
+
const { fetch } = jest.requireActual("undici") as {
|
|
5
|
+
fetch: (input: string) => Promise<unknown>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// the response to send when wrangler wants an oauth grant
|
|
9
|
+
let oauthGrantResponse: GrantResponseOptions | "timeout" = {};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A mock implementation for `openInBrowser` that sends an oauth grant response
|
|
13
|
+
* to wrangler. Use it like this:
|
|
14
|
+
*
|
|
15
|
+
* ```js
|
|
16
|
+
* jest.mock("../open-in-browser");
|
|
17
|
+
* (openInBrowser as jest.Mock).mockImplementation(mockOpenInBrowser);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export const mockOpenInBrowser = async (url: string, ..._args: unknown[]) => {
|
|
21
|
+
const { searchParams } = new URL(url);
|
|
22
|
+
if (oauthGrantResponse === "timeout") {
|
|
23
|
+
throw "unimplemented";
|
|
24
|
+
} else {
|
|
25
|
+
const queryParams = toQueryParams(oauthGrantResponse, searchParams);
|
|
26
|
+
// don't await this -- it will block the rest of the login flow
|
|
27
|
+
fetch(`${searchParams.get("redirect_uri")}?${queryParams}`).catch((e) => {
|
|
28
|
+
throw new Error(
|
|
29
|
+
"Failed to send OAuth Grant to wrangler, maybe the server was closed?",
|
|
30
|
+
e as Error
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
return new ChildProcess();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Functions to help with mocking various parts of the OAuth Flow
|
|
39
|
+
*/
|
|
40
|
+
export const mockOAuthFlow = () => {
|
|
41
|
+
afterEach(() => {
|
|
42
|
+
fetchMock.resetMocks();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const mockGrantAuthorization = ({
|
|
46
|
+
respondWith,
|
|
47
|
+
}: {
|
|
48
|
+
respondWith: "timeout" | "success" | "failure" | GrantResponseOptions;
|
|
49
|
+
}) => {
|
|
50
|
+
if (respondWith === "failure") {
|
|
51
|
+
oauthGrantResponse = {
|
|
52
|
+
error: "access_denied",
|
|
53
|
+
};
|
|
54
|
+
} else if (respondWith === "success") {
|
|
55
|
+
oauthGrantResponse = {
|
|
56
|
+
code: "test-oauth-code",
|
|
57
|
+
};
|
|
58
|
+
} else if (respondWith === "timeout") {
|
|
59
|
+
oauthGrantResponse = "timeout";
|
|
60
|
+
} else {
|
|
61
|
+
oauthGrantResponse = respondWith;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const mockRevokeAuthorization = () => {
|
|
66
|
+
const outcome = {
|
|
67
|
+
actual: new Request("https://example.org"),
|
|
68
|
+
expected: new Request("https://dash.cloudflare.com/oauth2/revoke", {
|
|
69
|
+
method: "POST",
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
fetchMock.mockIf(outcome.expected.url, async (req) => {
|
|
74
|
+
outcome.actual = req;
|
|
75
|
+
return "";
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return outcome;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const mockGrantAccessToken = ({
|
|
82
|
+
respondWith,
|
|
83
|
+
}: {
|
|
84
|
+
respondWith: MockTokenResponse;
|
|
85
|
+
}) => {
|
|
86
|
+
const outcome = {
|
|
87
|
+
actual: new Request("https://example.org"),
|
|
88
|
+
expected: new Request("https://dash.cloudflare.com/oauth2/token", {
|
|
89
|
+
method: "POST",
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
fetchMock.mockOnceIf(outcome.expected.url, async (req) => {
|
|
94
|
+
outcome.actual = req;
|
|
95
|
+
return makeTokenResponse(respondWith);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return outcome;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
mockGrantAuthorization,
|
|
103
|
+
mockRevokeAuthorization,
|
|
104
|
+
mockGrantAccessToken,
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
type GrantResponseOptions = {
|
|
109
|
+
code?: string;
|
|
110
|
+
error?: ErrorType | ErrorType[];
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const toQueryParams = (
|
|
114
|
+
{ code, error }: GrantResponseOptions,
|
|
115
|
+
wranglerRequestParams: URLSearchParams
|
|
116
|
+
): string => {
|
|
117
|
+
const queryParams = [];
|
|
118
|
+
if (code) {
|
|
119
|
+
queryParams.push(`code=${code}`);
|
|
120
|
+
}
|
|
121
|
+
if (error) {
|
|
122
|
+
const stringifiedErr = Array.isArray(error) ? error.join(",") : error;
|
|
123
|
+
queryParams.push(`error=${stringifiedErr}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
queryParams.push(`state=${wranglerRequestParams.get("state")}`);
|
|
127
|
+
|
|
128
|
+
return queryParams.join("&");
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
type ErrorType =
|
|
132
|
+
| "invalid_request"
|
|
133
|
+
| "invalid_grant"
|
|
134
|
+
| "unauthorized_client"
|
|
135
|
+
| "access_denied"
|
|
136
|
+
| "unsupported_response_type"
|
|
137
|
+
| "invalid_scope"
|
|
138
|
+
| "server_error"
|
|
139
|
+
| "temporarily_unavailable"
|
|
140
|
+
| "invalid_client"
|
|
141
|
+
| "unsupported_grant_type"
|
|
142
|
+
| "invalid_json"
|
|
143
|
+
| "invalid_token"
|
|
144
|
+
| "test-token-grant-error";
|
|
145
|
+
|
|
146
|
+
type MockTokenResponse =
|
|
147
|
+
| "ok"
|
|
148
|
+
| "error"
|
|
149
|
+
| {
|
|
150
|
+
access_token: string;
|
|
151
|
+
expires_in: number;
|
|
152
|
+
refresh_token: string;
|
|
153
|
+
scope: string;
|
|
154
|
+
}
|
|
155
|
+
| {
|
|
156
|
+
error: ErrorType;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const makeTokenResponse = (partialResponse: MockTokenResponse): string => {
|
|
160
|
+
let fullResponse: MockTokenResponse;
|
|
161
|
+
|
|
162
|
+
if (partialResponse === "ok") {
|
|
163
|
+
fullResponse = {
|
|
164
|
+
access_token: "test-access-token",
|
|
165
|
+
expires_in: 100000,
|
|
166
|
+
refresh_token: "test-refresh-token",
|
|
167
|
+
scope: "account:read",
|
|
168
|
+
};
|
|
169
|
+
} else if (partialResponse === "error") {
|
|
170
|
+
fullResponse = {
|
|
171
|
+
error: "test-token-grant-error",
|
|
172
|
+
};
|
|
173
|
+
} else {
|
|
174
|
+
fullResponse = partialResponse;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return JSON.stringify(fullResponse);
|
|
178
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { reinitialiseAuthTokens } from "../../user";
|
|
4
5
|
|
|
5
6
|
const originalCwd = process.cwd();
|
|
6
7
|
|
|
@@ -19,6 +20,8 @@ export function runInTempDir({ homedir }: { homedir?: string } = {}) {
|
|
|
19
20
|
// This is because the module gets transpiled so that the "method" `homedir()` gets converted to a
|
|
20
21
|
// getter that is not configurable (and so cannot be spied upon).
|
|
21
22
|
jest.spyOn(os, "homedir").mockReturnValue(homedir);
|
|
23
|
+
// Now that we have changed the home directory location, we must reinitialize the user auth state
|
|
24
|
+
reinitialiseAuthTokens();
|
|
22
25
|
}
|
|
23
26
|
});
|
|
24
27
|
|
|
@@ -36,7 +36,6 @@ describe("wrangler", () => {
|
|
|
36
36
|
|
|
37
37
|
Commands:
|
|
38
38
|
wrangler init [name] 📥 Create a wrangler.toml configuration file
|
|
39
|
-
wrangler whoami 🕵️ Retrieve your user info and test your auth config
|
|
40
39
|
wrangler dev [script] 👂 Start a local server for developing your worker
|
|
41
40
|
wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
|
|
42
41
|
wrangler tail [name] 🦚 Starts a log tailing session for a published Worker.
|
|
@@ -46,6 +45,9 @@ describe("wrangler", () => {
|
|
|
46
45
|
wrangler kv:bulk 💪 Interact with multiple Workers KV key-value pairs at once
|
|
47
46
|
wrangler pages ⚡️ Configure Cloudflare Pages
|
|
48
47
|
wrangler r2 📦 Interact with an R2 store
|
|
48
|
+
wrangler login 🔓 Login to Cloudflare
|
|
49
|
+
wrangler logout 🚪 Logout from Cloudflare
|
|
50
|
+
wrangler whoami 🕵️ Retrieve your user info and test your auth config
|
|
49
51
|
|
|
50
52
|
Flags:
|
|
51
53
|
-c, --config Path to .toml configuration file [string]
|
|
@@ -63,7 +65,7 @@ describe("wrangler", () => {
|
|
|
63
65
|
await expect(
|
|
64
66
|
runWrangler("invalid-command")
|
|
65
67
|
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
66
|
-
`"Unknown
|
|
68
|
+
`"Unknown argument: invalid-command"`
|
|
67
69
|
);
|
|
68
70
|
|
|
69
71
|
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
@@ -72,7 +74,6 @@ describe("wrangler", () => {
|
|
|
72
74
|
|
|
73
75
|
Commands:
|
|
74
76
|
wrangler init [name] 📥 Create a wrangler.toml configuration file
|
|
75
|
-
wrangler whoami 🕵️ Retrieve your user info and test your auth config
|
|
76
77
|
wrangler dev [script] 👂 Start a local server for developing your worker
|
|
77
78
|
wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
|
|
78
79
|
wrangler tail [name] 🦚 Starts a log tailing session for a published Worker.
|
|
@@ -82,6 +83,9 @@ describe("wrangler", () => {
|
|
|
82
83
|
wrangler kv:bulk 💪 Interact with multiple Workers KV key-value pairs at once
|
|
83
84
|
wrangler pages ⚡️ Configure Cloudflare Pages
|
|
84
85
|
wrangler r2 📦 Interact with an R2 store
|
|
86
|
+
wrangler login 🔓 Login to Cloudflare
|
|
87
|
+
wrangler logout 🚪 Logout from Cloudflare
|
|
88
|
+
wrangler whoami 🕵️ Retrieve your user info and test your auth config
|
|
85
89
|
|
|
86
90
|
Flags:
|
|
87
91
|
-c, --config Path to .toml configuration file [string]
|
|
@@ -89,7 +93,7 @@ describe("wrangler", () => {
|
|
|
89
93
|
-v, --version Show version number [boolean]
|
|
90
94
|
--legacy-env Use legacy environments [boolean]
|
|
91
95
|
|
|
92
|
-
Unknown
|
|
96
|
+
Unknown argument: invalid-command"
|
|
93
97
|
`);
|
|
94
98
|
});
|
|
95
99
|
});
|
|
@@ -812,14 +816,6 @@ describe("wrangler", () => {
|
|
|
812
816
|
expect(fs.existsSync("./wrangler.toml")).toBe(true);
|
|
813
817
|
});
|
|
814
818
|
|
|
815
|
-
it("should error if `--type` is used", async () => {
|
|
816
|
-
await expect(
|
|
817
|
-
runWrangler("init --type")
|
|
818
|
-
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
819
|
-
`"The --type option is no longer supported."`
|
|
820
|
-
);
|
|
821
|
-
});
|
|
822
|
-
|
|
823
819
|
it("should error if `--type javascript` is used", async () => {
|
|
824
820
|
await expect(
|
|
825
821
|
runWrangler("init --type javascript")
|
|
@@ -854,7 +850,7 @@ describe("wrangler", () => {
|
|
|
854
850
|
Try using \`wrangler dev\` to to try out a worker during development.
|
|
855
851
|
"
|
|
856
852
|
`);
|
|
857
|
-
await expect(runWrangler(
|
|
853
|
+
await expect(runWrangler(`preview GET "SomeBody"`)).rejects
|
|
858
854
|
.toThrowErrorMatchingInlineSnapshot(`
|
|
859
855
|
"DEPRECATION WARNING:
|
|
860
856
|
The \`wrangler preview\` command has been deprecated.
|
|
@@ -5,7 +5,9 @@ import {
|
|
|
5
5
|
getCloudflareAPIBaseURL,
|
|
6
6
|
} from "../cfetch/internal";
|
|
7
7
|
import { confirm, prompt } from "../dialogs";
|
|
8
|
+
import openInBrowser from "../open-in-browser";
|
|
8
9
|
import { mockFetchInternal, mockFetchKVGetValue } from "./helpers/mock-cfetch";
|
|
10
|
+
import { mockOpenInBrowser as mockOpenInBrowserForOAuthFlow } from "./helpers/mock-oauth-flow";
|
|
9
11
|
import { MockWebSocket } from "./helpers/mock-web-socket";
|
|
10
12
|
|
|
11
13
|
jest.mock("ws", () => {
|
|
@@ -67,3 +69,6 @@ jest.mock("../dev/dev", () => {
|
|
|
67
69
|
return null;
|
|
68
70
|
});
|
|
69
71
|
});
|
|
72
|
+
|
|
73
|
+
jest.mock("../open-in-browser");
|
|
74
|
+
(openInBrowser as jest.Mock).mockImplementation(mockOpenInBrowserForOAuthFlow);
|