wrangler 2.0.24 → 2.0.27
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/miniflare-dist/index.mjs +142 -16
- package/package.json +3 -3
- package/src/__tests__/configuration.test.ts +7 -3
- package/src/__tests__/dev.test.tsx +26 -4
- package/src/__tests__/generate.test.ts +2 -4
- package/src/__tests__/helpers/mock-cfetch.ts +35 -2
- package/src/__tests__/init.test.ts +537 -359
- package/src/__tests__/jest.setup.ts +7 -0
- package/src/__tests__/metrics.test.ts +1 -1
- package/src/__tests__/pages.test.ts +14 -0
- package/src/__tests__/r2.test.ts +22 -3
- package/src/__tests__/tail.test.ts +112 -42
- package/src/__tests__/user.test.ts +11 -0
- package/src/api/dev.ts +7 -0
- package/src/bundle.ts +3 -2
- package/src/cfetch/internal.ts +56 -0
- package/src/config/config.ts +1 -1
- package/src/config/validation-helpers.ts +19 -6
- package/src/config/validation.ts +9 -3
- package/src/config-cache.ts +2 -1
- package/src/dev/dev.tsx +16 -2
- package/src/dev/local.tsx +69 -5
- package/src/dev/use-esbuild.ts +3 -0
- package/src/dev-registry.tsx +3 -0
- package/src/dev.tsx +28 -19
- package/src/generate.ts +1 -1
- package/src/index.tsx +51 -21
- package/src/init.ts +111 -38
- package/src/inspect.ts +1 -4
- package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
- package/src/metrics/metrics-config.ts +1 -1
- package/src/miniflare-cli/assets.ts +27 -16
- package/src/miniflare-cli/index.ts +124 -2
- package/src/pages/build.tsx +75 -41
- package/src/pages/constants.ts +4 -0
- package/src/pages/deployments.tsx +9 -9
- package/src/pages/dev.tsx +178 -64
- package/src/pages/errors.ts +22 -0
- package/src/pages/functions/buildPlugin.ts +4 -0
- package/src/pages/functions/buildWorker.ts +4 -0
- package/src/pages/functions/routes-consolidation.test.ts +250 -0
- package/src/pages/functions/routes-consolidation.ts +73 -0
- package/src/pages/functions/routes-transformation.test.ts +271 -0
- package/src/pages/functions/routes-transformation.ts +122 -0
- package/src/pages/functions.tsx +96 -0
- package/src/pages/index.tsx +65 -55
- package/src/pages/projects.tsx +9 -3
- package/src/pages/publish.tsx +75 -22
- package/src/pages/types.ts +9 -0
- package/src/pages/upload.tsx +6 -8
- package/src/proxy.ts +10 -0
- package/src/r2.ts +17 -4
- package/src/tail/filters.ts +3 -1
- package/src/tail/index.ts +15 -2
- package/src/tail/printing.ts +43 -3
- package/src/user/user.tsx +6 -4
- package/src/whoami.tsx +5 -5
- package/templates/pages-template-plugin.ts +16 -4
- package/templates/pages-template-worker.ts +16 -5
- package/templates/service-bindings-module-facade.js +10 -7
- package/templates/service-bindings-sw-facade.js +10 -7
- package/wrangler-dist/cli.d.ts +7 -0
- package/wrangler-dist/cli.js +1681 -1091
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fetchMock from "jest-fetch-mock";
|
|
2
2
|
import {
|
|
3
|
+
fetchDashboardScript,
|
|
3
4
|
fetchInternal,
|
|
4
5
|
fetchKVGetValue,
|
|
5
6
|
fetchR2Objects,
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
} from "../cfetch/internal";
|
|
8
9
|
import { confirm, prompt } from "../dialogs";
|
|
9
10
|
import {
|
|
11
|
+
mockFetchDashScript,
|
|
10
12
|
mockFetchInternal,
|
|
11
13
|
mockFetchKVGetValue,
|
|
12
14
|
mockFetchR2Objects,
|
|
@@ -49,6 +51,7 @@ jest.mock("../cfetch/internal");
|
|
|
49
51
|
"https://api.cloudflare.com/client/v4"
|
|
50
52
|
);
|
|
51
53
|
(fetchR2Objects as jest.Mock).mockImplementation(mockFetchR2Objects);
|
|
54
|
+
(fetchDashboardScript as jest.Mock).mockImplementation(mockFetchDashScript);
|
|
52
55
|
|
|
53
56
|
jest.mock("../dialogs");
|
|
54
57
|
|
|
@@ -106,6 +109,10 @@ jest.mock("../user/generate-auth-url", () => {
|
|
|
106
109
|
};
|
|
107
110
|
});
|
|
108
111
|
|
|
112
|
+
jest.mock("../is-ci", () => {
|
|
113
|
+
return { CI: { isCI: jest.fn().mockImplementation(() => false) } };
|
|
114
|
+
});
|
|
115
|
+
|
|
109
116
|
jest.mock("../user/generate-random-state", () => {
|
|
110
117
|
return {
|
|
111
118
|
generateRandomState: jest.fn().mockImplementation(() => "MOCK_STATE_PARAM"),
|
|
@@ -2,9 +2,9 @@ import { mkdirSync } from "node:fs";
|
|
|
2
2
|
import fetchMock from "jest-fetch-mock";
|
|
3
3
|
import { version as wranglerVersion } from "../../package.json";
|
|
4
4
|
import { purgeConfigCaches, saveToConfigCache } from "../config-cache";
|
|
5
|
+
import { CI } from "../is-ci";
|
|
5
6
|
import { logger } from "../logger";
|
|
6
7
|
import { getMetricsDispatcher, getMetricsConfig } from "../metrics";
|
|
7
|
-
import { CI } from "../metrics/is-ci";
|
|
8
8
|
import {
|
|
9
9
|
CURRENT_METRICS_DATE,
|
|
10
10
|
readMetricsConfig,
|
|
@@ -97,6 +97,20 @@ describe("pages", () => {
|
|
|
97
97
|
expect(std.out).toMatchInlineSnapshot(`
|
|
98
98
|
"🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose
|
|
99
99
|
|
|
100
|
+
[32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose[0m"
|
|
101
|
+
`);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should display for pages:functions:optimize-routes", async () => {
|
|
105
|
+
await expect(
|
|
106
|
+
runWrangler(
|
|
107
|
+
'pages functions optimize-routes --routes-path="/build/_routes.json" --output-routes-path="/build/_optimized-routes.json"'
|
|
108
|
+
)
|
|
109
|
+
).rejects.toThrowError();
|
|
110
|
+
|
|
111
|
+
expect(std.out).toMatchInlineSnapshot(`
|
|
112
|
+
"🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose
|
|
113
|
+
|
|
100
114
|
[32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose[0m"
|
|
101
115
|
`);
|
|
102
116
|
});
|
package/src/__tests__/r2.test.ts
CHANGED
|
@@ -82,10 +82,11 @@ describe("wrangler", () => {
|
|
|
82
82
|
function mockCreateRequest(expectedBucketName: string) {
|
|
83
83
|
const requests = { count: 0 };
|
|
84
84
|
setMockResponse(
|
|
85
|
-
"/accounts/:accountId/r2/buckets
|
|
86
|
-
"
|
|
87
|
-
([_url, accountId,
|
|
85
|
+
"/accounts/:accountId/r2/buckets",
|
|
86
|
+
"POST",
|
|
87
|
+
([_url, accountId], { body }) => {
|
|
88
88
|
expect(accountId).toEqual("some-account-id");
|
|
89
|
+
const bucketName = JSON.parse(body as string).name;
|
|
89
90
|
expect(bucketName).toEqual(expectedBucketName);
|
|
90
91
|
requests.count += 1;
|
|
91
92
|
}
|
|
@@ -290,6 +291,24 @@ describe("wrangler", () => {
|
|
|
290
291
|
Upload complete."
|
|
291
292
|
`);
|
|
292
293
|
});
|
|
294
|
+
|
|
295
|
+
it("should delete R2 object from bucket", async () => {
|
|
296
|
+
setMockFetchR2Objects({
|
|
297
|
+
accountId: "some-account-id",
|
|
298
|
+
bucketName: "bucketName-object-test",
|
|
299
|
+
objectName: "wormhole-img.png",
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
await runWrangler(
|
|
303
|
+
`r2 object delete bucketName-object-test/wormhole-img.png`
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
expect(std.out).toMatchInlineSnapshot(`
|
|
307
|
+
"Deleting object \\"wormhole-img.png\\" from bucket \\"bucketName-object-test\\".
|
|
308
|
+
Delete complete."
|
|
309
|
+
`);
|
|
310
|
+
});
|
|
311
|
+
|
|
293
312
|
it("should not allow `--pipe` & `--file` to run together", async () => {
|
|
294
313
|
fs.writeFileSync("wormhole-img.png", "passageway");
|
|
295
314
|
setMockFetchR2Objects({
|
|
@@ -6,7 +6,12 @@ import { mockConsoleMethods } from "./helpers/mock-console";
|
|
|
6
6
|
import { useMockIsTTY } from "./helpers/mock-istty";
|
|
7
7
|
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
8
8
|
import { runWrangler } from "./helpers/run-wrangler";
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
TailEventMessage,
|
|
11
|
+
RequestEvent,
|
|
12
|
+
ScheduledEvent,
|
|
13
|
+
AlarmEvent,
|
|
14
|
+
} from "../tail";
|
|
10
15
|
import type WebSocket from "ws";
|
|
11
16
|
|
|
12
17
|
describe("tail", () => {
|
|
@@ -113,7 +118,7 @@ describe("tail", () => {
|
|
|
113
118
|
const api = mockWebsocketAPIs();
|
|
114
119
|
await runWrangler("tail test-worker --status error");
|
|
115
120
|
await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
|
|
116
|
-
{ outcome: ["exception", "exceededCpu", "unknown"] },
|
|
121
|
+
{ outcome: ["exception", "exceededCpu", "exceededMemory", "unknown"] },
|
|
117
122
|
]);
|
|
118
123
|
});
|
|
119
124
|
|
|
@@ -121,7 +126,15 @@ describe("tail", () => {
|
|
|
121
126
|
const api = mockWebsocketAPIs();
|
|
122
127
|
await runWrangler("tail test-worker --status error --status canceled");
|
|
123
128
|
await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
|
|
124
|
-
{
|
|
129
|
+
{
|
|
130
|
+
outcome: [
|
|
131
|
+
"exception",
|
|
132
|
+
"exceededCpu",
|
|
133
|
+
"exceededMemory",
|
|
134
|
+
"unknown",
|
|
135
|
+
"canceled",
|
|
136
|
+
],
|
|
137
|
+
},
|
|
125
138
|
]);
|
|
126
139
|
});
|
|
127
140
|
|
|
@@ -208,7 +221,15 @@ describe("tail", () => {
|
|
|
208
221
|
const expectedWebsocketMessage = {
|
|
209
222
|
filters: [
|
|
210
223
|
{ sampling_rate },
|
|
211
|
-
{
|
|
224
|
+
{
|
|
225
|
+
outcome: [
|
|
226
|
+
"ok",
|
|
227
|
+
"exception",
|
|
228
|
+
"exceededCpu",
|
|
229
|
+
"exceededMemory",
|
|
230
|
+
"unknown",
|
|
231
|
+
],
|
|
232
|
+
},
|
|
212
233
|
{ method },
|
|
213
234
|
{ header: { key: "X-HELLO", query: "world" } },
|
|
214
235
|
{ client_ip },
|
|
@@ -251,6 +272,18 @@ describe("tail", () => {
|
|
|
251
272
|
expect(std.out).toMatch(deserializeToJson(serializedMessage));
|
|
252
273
|
});
|
|
253
274
|
|
|
275
|
+
it("logs alarm messages in json format", async () => {
|
|
276
|
+
const api = mockWebsocketAPIs();
|
|
277
|
+
await runWrangler("tail test-worker --format json");
|
|
278
|
+
|
|
279
|
+
const event = generateMockAlarmEvent();
|
|
280
|
+
const message = generateMockEventMessage({ event });
|
|
281
|
+
const serializedMessage = serialize(message);
|
|
282
|
+
|
|
283
|
+
api.ws.send(serializedMessage);
|
|
284
|
+
expect(std.out).toMatch(deserializeToJson(serializedMessage));
|
|
285
|
+
});
|
|
286
|
+
|
|
254
287
|
it("logs request messages in pretty format", async () => {
|
|
255
288
|
const api = mockWebsocketAPIs();
|
|
256
289
|
await runWrangler("tail test-worker --format pretty");
|
|
@@ -271,10 +304,10 @@ describe("tail", () => {
|
|
|
271
304
|
"[mock expiration date]"
|
|
272
305
|
)
|
|
273
306
|
).toMatchInlineSnapshot(`
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
307
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
308
|
+
Connected to test-worker, waiting for logs...
|
|
309
|
+
GET https://example.org/ - Ok @ [mock event timestamp]"
|
|
310
|
+
`);
|
|
278
311
|
});
|
|
279
312
|
|
|
280
313
|
it("logs scheduled messages in pretty format", async () => {
|
|
@@ -297,35 +330,61 @@ describe("tail", () => {
|
|
|
297
330
|
"[mock expiration date]"
|
|
298
331
|
)
|
|
299
332
|
).toMatchInlineSnapshot(`
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
333
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
334
|
+
Connected to test-worker, waiting for logs...
|
|
335
|
+
\\"* * * * *\\" @ [mock timestamp string] - Ok"
|
|
336
|
+
`);
|
|
304
337
|
});
|
|
305
338
|
|
|
306
|
-
it("
|
|
339
|
+
it("logs alarm messages in pretty format", async () => {
|
|
307
340
|
const api = mockWebsocketAPIs();
|
|
308
341
|
await runWrangler("tail test-worker --format pretty");
|
|
309
342
|
|
|
310
|
-
const
|
|
343
|
+
const event = generateMockAlarmEvent();
|
|
344
|
+
const message = generateMockEventMessage({ event });
|
|
311
345
|
const serializedMessage = serialize(message);
|
|
312
346
|
|
|
313
347
|
api.ws.send(serializedMessage);
|
|
314
348
|
expect(
|
|
315
349
|
std.out
|
|
316
350
|
.replace(
|
|
317
|
-
new Date(
|
|
318
|
-
"[mock
|
|
351
|
+
new Date(mockEventScheduledTime).toLocaleString(),
|
|
352
|
+
"[mock scheduled time]"
|
|
319
353
|
)
|
|
320
354
|
.replace(
|
|
321
355
|
mockTailExpiration.toLocaleString(),
|
|
322
356
|
"[mock expiration date]"
|
|
323
357
|
)
|
|
324
358
|
).toMatchInlineSnapshot(`
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
359
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
360
|
+
Connected to test-worker, waiting for logs...
|
|
361
|
+
Alarm @ [mock scheduled time] - Ok"
|
|
362
|
+
`);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it("should not crash when the tail message has a void event", async () => {
|
|
366
|
+
const api = mockWebsocketAPIs();
|
|
367
|
+
await runWrangler("tail test-worker --format pretty");
|
|
368
|
+
|
|
369
|
+
const message = generateMockEventMessage({ event: null });
|
|
370
|
+
const serializedMessage = serialize(message);
|
|
371
|
+
|
|
372
|
+
api.ws.send(serializedMessage);
|
|
373
|
+
expect(
|
|
374
|
+
std.out
|
|
375
|
+
.replace(
|
|
376
|
+
mockTailExpiration.toLocaleString(),
|
|
377
|
+
"[mock expiration date]"
|
|
378
|
+
)
|
|
379
|
+
.replace(
|
|
380
|
+
new Date(mockEventTimestamp).toLocaleString(),
|
|
381
|
+
"[mock timestamp string]"
|
|
382
|
+
)
|
|
383
|
+
).toMatchInlineSnapshot(`
|
|
384
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
385
|
+
Connected to test-worker, waiting for logs...
|
|
386
|
+
Unknown Event - Ok @ [mock timestamp string]"
|
|
387
|
+
`);
|
|
329
388
|
});
|
|
330
389
|
|
|
331
390
|
it("defaults to logging in pretty format when the output is a TTY", async () => {
|
|
@@ -349,10 +408,10 @@ describe("tail", () => {
|
|
|
349
408
|
"[mock expiration date]"
|
|
350
409
|
)
|
|
351
410
|
).toMatchInlineSnapshot(`
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
411
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
412
|
+
Connected to test-worker, waiting for logs...
|
|
413
|
+
GET https://example.org/ - Ok @ [mock event timestamp]"
|
|
414
|
+
`);
|
|
356
415
|
});
|
|
357
416
|
|
|
358
417
|
it("defaults to logging in json format when the output is not a TTY", async () => {
|
|
@@ -405,21 +464,21 @@ describe("tail", () => {
|
|
|
405
464
|
"[mock expiration date]"
|
|
406
465
|
)
|
|
407
466
|
).toMatchInlineSnapshot(`
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
467
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
468
|
+
Connected to test-worker, waiting for logs...
|
|
469
|
+
GET https://example.org/ - Ok @ [mock event timestamp]
|
|
470
|
+
(log) some string
|
|
471
|
+
(log) { complex: 'object' }
|
|
472
|
+
(error) 1234"
|
|
473
|
+
`);
|
|
415
474
|
expect(std.err).toMatchInlineSnapshot(`
|
|
416
|
-
|
|
475
|
+
"[31mX [41;31m[[41;97mERROR[41;31m][0m [1m Error: some error[0m
|
|
417
476
|
|
|
418
477
|
|
|
419
|
-
|
|
478
|
+
[31mX [41;31m[[41;97mERROR[41;31m][0m [1m Error: { complex: 'error' }[0m
|
|
420
479
|
|
|
421
|
-
|
|
422
|
-
|
|
480
|
+
"
|
|
481
|
+
`);
|
|
423
482
|
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
424
483
|
});
|
|
425
484
|
});
|
|
@@ -437,8 +496,8 @@ describe("tail", () => {
|
|
|
437
496
|
* @returns the same type we expect when deserializing in wrangler
|
|
438
497
|
*/
|
|
439
498
|
function serialize(message: TailEventMessage): WebSocket.RawData {
|
|
440
|
-
if (
|
|
441
|
-
// `ScheduledEvent`s work just fine
|
|
499
|
+
if (!isRequest(message.event)) {
|
|
500
|
+
// `ScheduledEvent`s and `TailEvent`s work just fine
|
|
442
501
|
const stringified = JSON.stringify(message);
|
|
443
502
|
return Buffer.from(stringified, "utf-8");
|
|
444
503
|
} else {
|
|
@@ -470,12 +529,12 @@ function serialize(message: TailEventMessage): WebSocket.RawData {
|
|
|
470
529
|
* Small helper to disambiguate the event types possible in a `TailEventMessage`
|
|
471
530
|
*
|
|
472
531
|
* @param event A TailEvent
|
|
473
|
-
* @returns
|
|
532
|
+
* @returns true if `event` is a RequestEvent
|
|
474
533
|
*/
|
|
475
|
-
function
|
|
476
|
-
event: ScheduledEvent | RequestEvent | undefined | null
|
|
477
|
-
): event is
|
|
478
|
-
return Boolean(event && "
|
|
534
|
+
function isRequest(
|
|
535
|
+
event: ScheduledEvent | RequestEvent | AlarmEvent | undefined | null
|
|
536
|
+
): event is RequestEvent {
|
|
537
|
+
return Boolean(event && "request" in event);
|
|
479
538
|
}
|
|
480
539
|
|
|
481
540
|
/**
|
|
@@ -559,6 +618,11 @@ const mockTailExpiration = new Date(3005, 1);
|
|
|
559
618
|
*/
|
|
560
619
|
const mockEventTimestamp = 1645454470467;
|
|
561
620
|
|
|
621
|
+
/**
|
|
622
|
+
* Default value for event time ISO strings
|
|
623
|
+
*/
|
|
624
|
+
const mockEventScheduledTime = new Date(mockEventTimestamp).toISOString();
|
|
625
|
+
|
|
562
626
|
/**
|
|
563
627
|
* Mock out the API hit during Tail deletion
|
|
564
628
|
*
|
|
@@ -696,3 +760,9 @@ function generateMockScheduledEvent(
|
|
|
696
760
|
scheduledTime: opts?.scheduledTime || mockEventTimestamp,
|
|
697
761
|
};
|
|
698
762
|
}
|
|
763
|
+
|
|
764
|
+
function generateMockAlarmEvent(opts?: Partial<AlarmEvent>): AlarmEvent {
|
|
765
|
+
return {
|
|
766
|
+
scheduledTime: opts?.scheduledTime || mockEventScheduledTime,
|
|
767
|
+
};
|
|
768
|
+
}
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import fetchMock from "jest-fetch-mock";
|
|
4
4
|
import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
|
|
5
|
+
import { CI } from "../is-ci";
|
|
5
6
|
import {
|
|
6
7
|
loginOrRefreshIfRequired,
|
|
7
8
|
readAuthConfigFile,
|
|
@@ -18,6 +19,7 @@ import type { Config } from "../config";
|
|
|
18
19
|
import type { UserAuthConfig } from "../user";
|
|
19
20
|
|
|
20
21
|
describe("User", () => {
|
|
22
|
+
let isCISpy: jest.SpyInstance;
|
|
21
23
|
runInTempDir();
|
|
22
24
|
const std = mockConsoleMethods();
|
|
23
25
|
const {
|
|
@@ -30,6 +32,10 @@ describe("User", () => {
|
|
|
30
32
|
|
|
31
33
|
const { setIsTTY } = useMockIsTTY();
|
|
32
34
|
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
isCISpy = jest.spyOn(CI, "isCI").mockReturnValue(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
33
39
|
describe("login", () => {
|
|
34
40
|
it("should login a user when `wrangler login` is run", async () => {
|
|
35
41
|
mockOAuthServerCallback();
|
|
@@ -132,6 +138,11 @@ describe("User", () => {
|
|
|
132
138
|
expect(std.err).toContain("");
|
|
133
139
|
});
|
|
134
140
|
|
|
141
|
+
it("should revert to non-interactive mode if in CI", async () => {
|
|
142
|
+
isCISpy.mockReturnValue(true);
|
|
143
|
+
await expect(loginOrRefreshIfRequired()).resolves.toEqual(false);
|
|
144
|
+
});
|
|
145
|
+
|
|
135
146
|
it("should revert to non-interactive mode if isTTY throws an error", async () => {
|
|
136
147
|
setIsTTY({
|
|
137
148
|
stdin() {
|
package/src/api/dev.ts
CHANGED
|
@@ -8,6 +8,7 @@ interface DevOptions {
|
|
|
8
8
|
env?: string;
|
|
9
9
|
ip?: string;
|
|
10
10
|
port?: number;
|
|
11
|
+
inspectorPort?: number;
|
|
11
12
|
localProtocol?: "http" | "https";
|
|
12
13
|
assets?: string;
|
|
13
14
|
site?: string;
|
|
@@ -15,6 +16,7 @@ interface DevOptions {
|
|
|
15
16
|
siteExclude?: string[];
|
|
16
17
|
nodeCompat?: boolean;
|
|
17
18
|
compatibilityDate?: string;
|
|
19
|
+
compatibilityFlags?: string[];
|
|
18
20
|
experimentalEnableLocalPersistence?: boolean;
|
|
19
21
|
liveReload?: boolean;
|
|
20
22
|
watch?: boolean;
|
|
@@ -32,6 +34,11 @@ interface DevOptions {
|
|
|
32
34
|
script_name?: string | undefined;
|
|
33
35
|
environment?: string | undefined;
|
|
34
36
|
}[];
|
|
37
|
+
r2?: {
|
|
38
|
+
binding: string;
|
|
39
|
+
bucket_name: string;
|
|
40
|
+
preview_bucket_name?: string;
|
|
41
|
+
}[];
|
|
35
42
|
showInteractiveDevSession?: boolean;
|
|
36
43
|
logLevel?: "none" | "error" | "log" | "warn" | "debug";
|
|
37
44
|
logPrefix?: string;
|
package/src/bundle.ts
CHANGED
|
@@ -192,8 +192,9 @@ export async function bundleWorker(
|
|
|
192
192
|
format: entry.format === "modules" ? "esm" : "iife",
|
|
193
193
|
target: "es2020",
|
|
194
194
|
sourcemap: true,
|
|
195
|
-
//
|
|
196
|
-
|
|
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,
|
|
197
198
|
minify,
|
|
198
199
|
metafile: true,
|
|
199
200
|
conditions: ["worker", "browser"],
|
package/src/cfetch/internal.ts
CHANGED
|
@@ -2,6 +2,7 @@ import assert from "node:assert";
|
|
|
2
2
|
import { fetch, Headers } from "undici";
|
|
3
3
|
import { version as wranglerVersion } from "../../package.json";
|
|
4
4
|
import { getEnvironmentVariableFactory } from "../environment-variables";
|
|
5
|
+
import { logger } from "../logger";
|
|
5
6
|
import { ParseError, parseJSON } from "../parse";
|
|
6
7
|
import { loginOrRefreshIfRequired, requireApiToken } from "../user";
|
|
7
8
|
import type { ApiCredentials } from "../user";
|
|
@@ -44,6 +45,13 @@ export async function fetchInternal<ResponseType>(
|
|
|
44
45
|
|
|
45
46
|
const queryString = queryParams ? `?${queryParams.toString()}` : "";
|
|
46
47
|
const method = init.method ?? "GET";
|
|
48
|
+
|
|
49
|
+
logger.debug(
|
|
50
|
+
`-- START CF API REQUEST: ${method} ${getCloudflareAPIBaseURL()}${resource}${queryString}`
|
|
51
|
+
);
|
|
52
|
+
logger.debug("HEADERS:", JSON.stringify(headers, null, 2));
|
|
53
|
+
logger.debug("INIT:", JSON.stringify(init, null, 2));
|
|
54
|
+
logger.debug("-- END CF API REQUEST");
|
|
47
55
|
const response = await fetch(
|
|
48
56
|
`${getCloudflareAPIBaseURL()}${resource}${queryString}`,
|
|
49
57
|
{
|
|
@@ -54,6 +62,15 @@ export async function fetchInternal<ResponseType>(
|
|
|
54
62
|
}
|
|
55
63
|
);
|
|
56
64
|
const jsonText = await response.text();
|
|
65
|
+
logger.debug(
|
|
66
|
+
"-- START CF API RESPONSE:",
|
|
67
|
+
response.statusText,
|
|
68
|
+
response.status
|
|
69
|
+
);
|
|
70
|
+
logger.debug("HEADERS:", JSON.stringify(response.headers, null, 2));
|
|
71
|
+
logger.debug("RESPONSE:", jsonText);
|
|
72
|
+
logger.debug("-- END CF API RESPONSE");
|
|
73
|
+
|
|
57
74
|
try {
|
|
58
75
|
return parseJSON<ResponseType>(jsonText);
|
|
59
76
|
} catch (err) {
|
|
@@ -179,3 +196,42 @@ export async function fetchR2Objects(
|
|
|
179
196
|
);
|
|
180
197
|
}
|
|
181
198
|
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* This is a wrapper STOPGAP for getting the script which returns a raw text response.
|
|
202
|
+
*/
|
|
203
|
+
export async function fetchDashboardScript(
|
|
204
|
+
resource: string,
|
|
205
|
+
bodyInit: RequestInit = {}
|
|
206
|
+
): Promise<string> {
|
|
207
|
+
await requireLoggedIn();
|
|
208
|
+
const auth = requireApiToken();
|
|
209
|
+
const headers = cloneHeaders(bodyInit.headers);
|
|
210
|
+
addAuthorizationHeaderIfUnspecified(headers, auth);
|
|
211
|
+
addUserAgent(headers);
|
|
212
|
+
|
|
213
|
+
const response = await fetch(`${getCloudflareAPIBaseURL()}${resource}`, {
|
|
214
|
+
...bodyInit,
|
|
215
|
+
headers,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (!response.ok || !response.body) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`Failed to fetch ${resource} - ${response.status}: ${response.statusText});`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const usesModules = response.headers
|
|
225
|
+
.get("content-type")
|
|
226
|
+
?.startsWith("multipart");
|
|
227
|
+
|
|
228
|
+
if (usesModules) {
|
|
229
|
+
const file = await response.text();
|
|
230
|
+
|
|
231
|
+
// Follow up on issue in Undici about multipart/form-data support & replace the workaround: https://github.com/nodejs/undici/issues/974
|
|
232
|
+
// This should be using a builtin formData() parser pattern.
|
|
233
|
+
return file.split("\n").slice(4, -4).join("\n");
|
|
234
|
+
} else {
|
|
235
|
+
return response.text();
|
|
236
|
+
}
|
|
237
|
+
}
|
package/src/config/config.ts
CHANGED
|
@@ -383,7 +383,7 @@ export const validateRequiredProperty = (
|
|
|
383
383
|
container: string,
|
|
384
384
|
key: string,
|
|
385
385
|
value: unknown,
|
|
386
|
-
type:
|
|
386
|
+
type: TypeofType,
|
|
387
387
|
choices?: unknown[]
|
|
388
388
|
): boolean => {
|
|
389
389
|
if (container) {
|
|
@@ -417,7 +417,7 @@ export const validateOptionalProperty = (
|
|
|
417
417
|
container: string,
|
|
418
418
|
key: string,
|
|
419
419
|
value: unknown,
|
|
420
|
-
type:
|
|
420
|
+
type: TypeofType,
|
|
421
421
|
choices?: unknown[]
|
|
422
422
|
): boolean => {
|
|
423
423
|
if (value !== undefined) {
|
|
@@ -440,7 +440,7 @@ export const validateTypedArray = (
|
|
|
440
440
|
diagnostics: Diagnostics,
|
|
441
441
|
container: string,
|
|
442
442
|
value: unknown,
|
|
443
|
-
type:
|
|
443
|
+
type: TypeofType
|
|
444
444
|
): boolean => {
|
|
445
445
|
let isValid = true;
|
|
446
446
|
if (!Array.isArray(value)) {
|
|
@@ -472,7 +472,7 @@ export const validateOptionalTypedArray = (
|
|
|
472
472
|
diagnostics: Diagnostics,
|
|
473
473
|
container: string,
|
|
474
474
|
value: unknown,
|
|
475
|
-
type:
|
|
475
|
+
type: TypeofType
|
|
476
476
|
) => {
|
|
477
477
|
if (value !== undefined) {
|
|
478
478
|
return validateTypedArray(diagnostics, container, value, type);
|
|
@@ -486,7 +486,7 @@ export const validateOptionalTypedArray = (
|
|
|
486
486
|
export const isRequiredProperty = <T extends object>(
|
|
487
487
|
obj: object,
|
|
488
488
|
prop: keyof T,
|
|
489
|
-
type:
|
|
489
|
+
type: TypeofType,
|
|
490
490
|
choices?: unknown[]
|
|
491
491
|
): obj is T =>
|
|
492
492
|
hasProperty<T>(obj, prop) &&
|
|
@@ -499,7 +499,7 @@ export const isRequiredProperty = <T extends object>(
|
|
|
499
499
|
export const isOptionalProperty = <T extends object>(
|
|
500
500
|
obj: object,
|
|
501
501
|
prop: keyof T,
|
|
502
|
-
type:
|
|
502
|
+
type: TypeofType
|
|
503
503
|
): obj is T => !hasProperty<T>(obj, prop) || typeof obj[prop] === type;
|
|
504
504
|
|
|
505
505
|
/**
|
|
@@ -582,3 +582,16 @@ const isRecord = (
|
|
|
582
582
|
value: unknown
|
|
583
583
|
): value is Record<string | number | symbol, unknown> =>
|
|
584
584
|
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* JavaScript `typeof` operator return values.
|
|
588
|
+
*/
|
|
589
|
+
type TypeofType =
|
|
590
|
+
| "string"
|
|
591
|
+
| "number"
|
|
592
|
+
| "bigint"
|
|
593
|
+
| "boolean"
|
|
594
|
+
| "symbol"
|
|
595
|
+
| "undefined"
|
|
596
|
+
| "object"
|
|
597
|
+
| "function";
|
package/src/config/validation.ts
CHANGED
|
@@ -357,7 +357,7 @@ function normalizeAndValidateDev(
|
|
|
357
357
|
rawDev: RawDevConfig
|
|
358
358
|
): DevConfig {
|
|
359
359
|
const {
|
|
360
|
-
ip = "
|
|
360
|
+
ip = "0.0.0.0",
|
|
361
361
|
port,
|
|
362
362
|
inspector_port,
|
|
363
363
|
local_protocol = "http",
|
|
@@ -1630,7 +1630,10 @@ const validateKVBinding: ValidatorFn = (diagnostics, field, value) => {
|
|
|
1630
1630
|
);
|
|
1631
1631
|
isValid = false;
|
|
1632
1632
|
}
|
|
1633
|
-
if (
|
|
1633
|
+
if (
|
|
1634
|
+
!isRequiredProperty(value, "id", "string") ||
|
|
1635
|
+
(value as { id: string }).id.length === 0
|
|
1636
|
+
) {
|
|
1634
1637
|
diagnostics.errors.push(
|
|
1635
1638
|
`"${field}" bindings should have a string "id" field but got ${JSON.stringify(
|
|
1636
1639
|
value
|
|
@@ -1668,7 +1671,10 @@ const validateR2Binding: ValidatorFn = (diagnostics, field, value) => {
|
|
|
1668
1671
|
);
|
|
1669
1672
|
isValid = false;
|
|
1670
1673
|
}
|
|
1671
|
-
if (
|
|
1674
|
+
if (
|
|
1675
|
+
!isRequiredProperty(value, "bucket_name", "string") ||
|
|
1676
|
+
(value as { bucket_name: string }).bucket_name.length === 0
|
|
1677
|
+
) {
|
|
1672
1678
|
diagnostics.errors.push(
|
|
1673
1679
|
`"${field}" bindings should have a string "bucket_name" field but got ${JSON.stringify(
|
|
1674
1680
|
value
|
package/src/config-cache.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { findUpSync } from "find-up";
|
|
4
|
+
import { CI } from "./is-ci";
|
|
4
5
|
import isInteractive from "./is-interactive";
|
|
5
6
|
import { logger } from "./logger";
|
|
6
7
|
|
|
@@ -29,7 +30,7 @@ const arrayFormatter = new Intl.ListFormat("en", {
|
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
function showCacheMessage(fields: string[], folder: string) {
|
|
32
|
-
if (!cacheMessageShown && isInteractive()) {
|
|
33
|
+
if (!cacheMessageShown && isInteractive() && !CI.isCI()) {
|
|
33
34
|
if (fields.length > 0) {
|
|
34
35
|
logger.log(
|
|
35
36
|
`Retrieving cached values for ${arrayFormatter.format(
|