wrangler 0.0.0-e043479 → 0.0.0-e0b9366
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/package.json +10 -4
- package/pages/functions/filepath-routing.test.ts +125 -25
- package/pages/functions/filepath-routing.ts +6 -3
- package/src/__tests__/configuration.test.ts +479 -114
- package/src/__tests__/dev.test.tsx +162 -8
- package/src/__tests__/dev2.test.tsx +152 -0
- package/src/__tests__/guess-worker-format.test.ts +9 -17
- package/src/__tests__/helpers/mock-account-id.ts +4 -0
- package/src/__tests__/helpers/mock-cfetch.ts +3 -3
- package/src/__tests__/helpers/mock-kv.ts +9 -16
- package/src/__tests__/helpers/run-in-tmp.ts +3 -0
- package/src/__tests__/https-options.test.ts +127 -0
- package/src/__tests__/index.test.ts +125 -0
- package/src/__tests__/jest.setup.ts +1 -1
- package/src/__tests__/kv.test.ts +27 -53
- package/src/__tests__/logout.test.ts +15 -13
- package/src/__tests__/pages.test.ts +44 -0
- package/src/__tests__/publish.test.ts +504 -32
- package/src/__tests__/route.test.ts +45 -0
- package/src/__tests__/whoami.test.tsx +21 -7
- package/src/bundle.ts +20 -11
- package/src/cfetch/internal.ts +11 -5
- package/src/config/config.ts +53 -44
- package/src/config/validation-helpers.ts +110 -12
- package/src/config/validation.ts +59 -41
- package/src/{api/preview.ts → create-worker-preview.ts} +23 -4
- package/src/{api/form_data.ts → create-worker-upload-form.ts} +1 -1
- package/src/dev/dev.tsx +357 -0
- package/src/dev/local.tsx +241 -0
- package/src/dev/remote.tsx +201 -0
- package/src/dev/use-esbuild.ts +100 -0
- package/src/entry.ts +111 -9
- package/src/https-options.ts +122 -0
- package/src/index.tsx +171 -109
- package/src/inspect.ts +9 -1
- package/src/{kv.tsx → kv.ts} +0 -0
- package/src/module-collection.ts +85 -2
- package/src/open-in-browser.ts +14 -2
- package/src/pages.tsx +1 -1
- package/src/proxy.ts +102 -40
- package/src/publish.ts +41 -50
- package/src/reporting.ts +25 -9
- package/src/selfsigned.d.ts +29 -0
- package/src/sites.tsx +51 -16
- package/src/user.tsx +132 -77
- package/src/{api/worker.ts → worker.ts} +0 -23
- package/wrangler-dist/cli.js +5012 -3107
- package/wrangler-dist/cli.js.map +3 -3
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/dev.tsx +0 -960
- package/src/guess-worker-format.ts +0 -68
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
2
3
|
import patchConsole from "patch-console";
|
|
3
|
-
import Dev from "../dev";
|
|
4
|
+
import Dev from "../dev/dev";
|
|
4
5
|
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
|
|
5
6
|
import { setMockResponse, unsetAllMocks } from "./helpers/mock-cfetch";
|
|
6
7
|
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
@@ -9,7 +10,6 @@ import { runWrangler } from "./helpers/run-wrangler";
|
|
|
9
10
|
import writeWranglerToml from "./helpers/write-wrangler-toml";
|
|
10
11
|
|
|
11
12
|
describe("Dev component", () => {
|
|
12
|
-
beforeEach(() => {});
|
|
13
13
|
mockAccountId();
|
|
14
14
|
mockApiToken();
|
|
15
15
|
runInTempDir();
|
|
@@ -135,8 +135,8 @@ describe("Dev component", () => {
|
|
|
135
135
|
});
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
it("should, in order, use args.host/config.dev.host/args.routes/config.route
|
|
139
|
-
// This test might seem like it's testing
|
|
138
|
+
it("should, in order, use args.host/config.dev.host/args.routes/(config.route|config.routes)", async () => {
|
|
139
|
+
// This test might seem like it's testing implementation details, but let's be specific and consider it a spec
|
|
140
140
|
|
|
141
141
|
fs.writeFileSync("index.js", `export default {};`);
|
|
142
142
|
|
|
@@ -158,7 +158,6 @@ describe("Dev component", () => {
|
|
|
158
158
|
writeWranglerToml({
|
|
159
159
|
main: "index.js",
|
|
160
160
|
route: "https://4.some-host.com/some/path/*",
|
|
161
|
-
routes: ["http://5.some-host.com/some/path/*"],
|
|
162
161
|
});
|
|
163
162
|
await runWrangler("dev");
|
|
164
163
|
expect((Dev as jest.Mock).mock.calls[0][0].zone).toEqual({
|
|
@@ -172,7 +171,6 @@ describe("Dev component", () => {
|
|
|
172
171
|
writeWranglerToml({
|
|
173
172
|
main: "index.js",
|
|
174
173
|
route: "https://4.some-host.com/some/path/*",
|
|
175
|
-
routes: ["http://5.some-host.com/some/path/*"],
|
|
176
174
|
});
|
|
177
175
|
await runWrangler("dev --routes http://3.some-host.com/some/path/*");
|
|
178
176
|
expect((Dev as jest.Mock).mock.calls[0][0].zone).toEqual({
|
|
@@ -189,7 +187,6 @@ describe("Dev component", () => {
|
|
|
189
187
|
host: `2.some-host.com`,
|
|
190
188
|
},
|
|
191
189
|
route: "4.some-host.com/some/path/*",
|
|
192
|
-
routes: ["5.some-host.com/some/path/*"],
|
|
193
190
|
});
|
|
194
191
|
await runWrangler("dev --routes http://3.some-host.com/some/path/*");
|
|
195
192
|
expect((Dev as jest.Mock).mock.calls[0][0].zone).toEqual({
|
|
@@ -206,7 +203,6 @@ describe("Dev component", () => {
|
|
|
206
203
|
host: `2.some-host.com`,
|
|
207
204
|
},
|
|
208
205
|
route: "4.some-host.com/some/path/*",
|
|
209
|
-
routes: ["5.some-host.com/some/path/*"],
|
|
210
206
|
});
|
|
211
207
|
await runWrangler(
|
|
212
208
|
"dev --routes http://3.some-host.com/some/path/* --host 1.some-host.com"
|
|
@@ -231,6 +227,164 @@ describe("Dev component", () => {
|
|
|
231
227
|
);
|
|
232
228
|
});
|
|
233
229
|
});
|
|
230
|
+
|
|
231
|
+
describe("custom builds", () => {
|
|
232
|
+
it("should run a custom build before starting `dev`", async () => {
|
|
233
|
+
writeWranglerToml({
|
|
234
|
+
build: {
|
|
235
|
+
command: `node -e "console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')"`,
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
await runWrangler("dev index.js");
|
|
240
|
+
|
|
241
|
+
expect(readFileSync("index.js", "utf-8")).toMatchInlineSnapshot(
|
|
242
|
+
`"export default { fetch(){ return new Response(123) } }"`
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// and the command would pass through
|
|
246
|
+
expect((Dev as jest.Mock).mock.calls[0][0].buildCommand).toEqual({
|
|
247
|
+
command:
|
|
248
|
+
"node -e \"console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')\"",
|
|
249
|
+
cwd: undefined,
|
|
250
|
+
watch_dir: undefined,
|
|
251
|
+
});
|
|
252
|
+
expect(std.out).toMatchInlineSnapshot(
|
|
253
|
+
`"running: node -e \\"console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')\\""`
|
|
254
|
+
);
|
|
255
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
256
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (process.platform !== "win32") {
|
|
260
|
+
it("should run a custom build of multiple steps combined by && before starting `dev`", async () => {
|
|
261
|
+
writeWranglerToml({
|
|
262
|
+
build: {
|
|
263
|
+
command: `echo "custom build" && echo "export default { fetch(){ return new Response(123) } }" > index.js`,
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
await runWrangler("dev index.js");
|
|
268
|
+
|
|
269
|
+
expect(readFileSync("index.js", "utf-8")).toMatchInlineSnapshot(`
|
|
270
|
+
"export default { fetch(){ return new Response(123) } }
|
|
271
|
+
"
|
|
272
|
+
`);
|
|
273
|
+
|
|
274
|
+
expect(std.out).toMatchInlineSnapshot(
|
|
275
|
+
`"running: echo \\"custom build\\" && echo \\"export default { fetch(){ return new Response(123) } }\\" > index.js"`
|
|
276
|
+
);
|
|
277
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
278
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
it("should throw an error if the entry doesn't exist after the build finishes", async () => {
|
|
283
|
+
writeWranglerToml({
|
|
284
|
+
main: "index.js",
|
|
285
|
+
build: {
|
|
286
|
+
command: `node -e "console.log('custom build');"`,
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
await expect(
|
|
291
|
+
runWrangler("dev")
|
|
292
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
293
|
+
`"Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\""`
|
|
294
|
+
);
|
|
295
|
+
expect(std.out).toMatchInlineSnapshot(
|
|
296
|
+
`"running: node -e \\"console.log('custom build');\\""`
|
|
297
|
+
);
|
|
298
|
+
expect(std.err).toMatchInlineSnapshot(`
|
|
299
|
+
"Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\"
|
|
300
|
+
|
|
301
|
+
[32m%s[0m If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
|
|
302
|
+
`);
|
|
303
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe("upstream-protocol", () => {
|
|
308
|
+
it("should default upstream-protocol to `https`", async () => {
|
|
309
|
+
writeWranglerToml({
|
|
310
|
+
main: "index.js",
|
|
311
|
+
});
|
|
312
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
313
|
+
await runWrangler("dev");
|
|
314
|
+
expect((Dev as jest.Mock).mock.calls[0][0].upstreamProtocol).toEqual(
|
|
315
|
+
"https"
|
|
316
|
+
);
|
|
317
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
318
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
319
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("should warn if `--upstream-protocol=http` is used", async () => {
|
|
323
|
+
writeWranglerToml({
|
|
324
|
+
main: "index.js",
|
|
325
|
+
});
|
|
326
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
327
|
+
await runWrangler("dev --upstream-protocol=http");
|
|
328
|
+
expect((Dev as jest.Mock).mock.calls[0][0].upstreamProtocol).toEqual(
|
|
329
|
+
"http"
|
|
330
|
+
);
|
|
331
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
332
|
+
expect(std.warn).toMatchInlineSnapshot(`
|
|
333
|
+
"Setting upstream-protocol to http is not currently implemented.
|
|
334
|
+
If this is required in your project, please add your use case to the following issue:
|
|
335
|
+
https://github.com/cloudflare/wrangler2/issues/583."
|
|
336
|
+
`);
|
|
337
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
describe("local-protocol", () => {
|
|
342
|
+
it("should default local-protocol to `http`", async () => {
|
|
343
|
+
writeWranglerToml({
|
|
344
|
+
main: "index.js",
|
|
345
|
+
});
|
|
346
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
347
|
+
await runWrangler("dev");
|
|
348
|
+
expect((Dev as jest.Mock).mock.calls[0][0].localProtocol).toEqual("http");
|
|
349
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
350
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
351
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("should use to `local_protocol` from `wrangler.toml`, if available", async () => {
|
|
355
|
+
writeWranglerToml({
|
|
356
|
+
main: "index.js",
|
|
357
|
+
dev: {
|
|
358
|
+
local_protocol: "https",
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
362
|
+
await runWrangler("dev");
|
|
363
|
+
expect((Dev as jest.Mock).mock.calls[0][0].localProtocol).toEqual(
|
|
364
|
+
"https"
|
|
365
|
+
);
|
|
366
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
367
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
368
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it("should use --local-protocol command line arg, if provided", async () => {
|
|
372
|
+
// Here we show that the command line overrides the wrangler.toml by
|
|
373
|
+
// setting the config to https, and then setting it back to http on the command line.
|
|
374
|
+
writeWranglerToml({
|
|
375
|
+
main: "index.js",
|
|
376
|
+
dev: {
|
|
377
|
+
local_protocol: "https",
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
381
|
+
await runWrangler("dev --local-protocol=http");
|
|
382
|
+
expect((Dev as jest.Mock).mock.calls[0][0].localProtocol).toEqual("http");
|
|
383
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
384
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
385
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
386
|
+
});
|
|
387
|
+
});
|
|
234
388
|
});
|
|
235
389
|
|
|
236
390
|
function mockGetZones(domain: string, zones: { id: string }[] = []) {
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import TestRenderer from "react-test-renderer";
|
|
4
|
+
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
|
|
5
|
+
import { unsetAllMocks } from "./helpers/mock-cfetch";
|
|
6
|
+
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
7
|
+
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
8
|
+
import type { DevProps, default as DevType } from "../dev/dev";
|
|
9
|
+
|
|
10
|
+
function sleep(period = 100): Promise<void> {
|
|
11
|
+
return new Promise((resolve) => setTimeout(resolve, period));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// we use this ["mock"] form to avoid esbuild-jest from rewriting it
|
|
15
|
+
jest["mock"]("../proxy", () => {
|
|
16
|
+
return {
|
|
17
|
+
usePreviewServer() {},
|
|
18
|
+
waitForPortToBeAvailable() {},
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
jest["mock"]("../inspect", () => {
|
|
22
|
+
return () => {};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const Dev: typeof DevType = jest.requireActual("../dev/dev").DevImplementation;
|
|
26
|
+
|
|
27
|
+
mockAccountId();
|
|
28
|
+
mockApiToken();
|
|
29
|
+
runInTempDir();
|
|
30
|
+
mockConsoleMethods();
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
unsetAllMocks();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("dev", () => {
|
|
36
|
+
it("should render", async () => {
|
|
37
|
+
fs.writeFileSync("./index.js", `export default {}`);
|
|
38
|
+
|
|
39
|
+
const testRenderer = await renderDev({
|
|
40
|
+
entry: {
|
|
41
|
+
file: "./index.js",
|
|
42
|
+
directory: process.cwd(),
|
|
43
|
+
format: "modules",
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
expect(testRenderer.toJSON()).toMatchInlineSnapshot(`
|
|
47
|
+
<ink-box
|
|
48
|
+
style={
|
|
49
|
+
Object {
|
|
50
|
+
"borderStyle": "round",
|
|
51
|
+
"flexDirection": "row",
|
|
52
|
+
"flexGrow": 0,
|
|
53
|
+
"flexShrink": 1,
|
|
54
|
+
"marginBottom": 0,
|
|
55
|
+
"marginLeft": 0,
|
|
56
|
+
"marginRight": 0,
|
|
57
|
+
"marginTop": 0,
|
|
58
|
+
"paddingBottom": 0,
|
|
59
|
+
"paddingLeft": 1,
|
|
60
|
+
"paddingRight": 1,
|
|
61
|
+
"paddingTop": 0,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
>
|
|
65
|
+
<ink-text
|
|
66
|
+
internal_transform={[Function]}
|
|
67
|
+
style={
|
|
68
|
+
Object {
|
|
69
|
+
"flexDirection": "row",
|
|
70
|
+
"flexGrow": 0,
|
|
71
|
+
"flexShrink": 1,
|
|
72
|
+
"textWrap": "wrap",
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
>
|
|
76
|
+
B to open a browser, D to open Devtools, L to turn off local mode, X to exit
|
|
77
|
+
</ink-text>
|
|
78
|
+
</ink-box>
|
|
79
|
+
`);
|
|
80
|
+
await TestRenderer.act(async () => {
|
|
81
|
+
// TODO: get rid of these sleep statements
|
|
82
|
+
await sleep(50);
|
|
83
|
+
testRenderer.unmount();
|
|
84
|
+
await sleep(50);
|
|
85
|
+
});
|
|
86
|
+
await sleep(50);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
async function renderDev({
|
|
91
|
+
name,
|
|
92
|
+
entry = { file: "index.js", directory: "", format: "modules" },
|
|
93
|
+
port,
|
|
94
|
+
inspectorPort = 9229,
|
|
95
|
+
accountId,
|
|
96
|
+
legacyEnv = true,
|
|
97
|
+
initialMode = "local",
|
|
98
|
+
jsxFactory,
|
|
99
|
+
jsxFragment,
|
|
100
|
+
localProtocol = "http",
|
|
101
|
+
upstreamProtocol = "https",
|
|
102
|
+
rules = [],
|
|
103
|
+
bindings = {
|
|
104
|
+
kv_namespaces: [],
|
|
105
|
+
vars: {},
|
|
106
|
+
durable_objects: { bindings: [] },
|
|
107
|
+
r2_buckets: [],
|
|
108
|
+
wasm_modules: undefined,
|
|
109
|
+
text_blobs: undefined,
|
|
110
|
+
unsafe: [],
|
|
111
|
+
},
|
|
112
|
+
public: publicDir,
|
|
113
|
+
assetPaths,
|
|
114
|
+
compatibilityDate,
|
|
115
|
+
compatibilityFlags,
|
|
116
|
+
usageModel,
|
|
117
|
+
buildCommand = {},
|
|
118
|
+
enableLocalPersistence = false,
|
|
119
|
+
env,
|
|
120
|
+
zone,
|
|
121
|
+
}: Partial<DevProps>): Promise<TestRenderer.ReactTestRenderer> {
|
|
122
|
+
let instance: TestRenderer.ReactTestRenderer | undefined;
|
|
123
|
+
await TestRenderer.act(async () => {
|
|
124
|
+
instance = TestRenderer.create(
|
|
125
|
+
<Dev
|
|
126
|
+
name={name}
|
|
127
|
+
entry={entry}
|
|
128
|
+
env={env}
|
|
129
|
+
rules={rules}
|
|
130
|
+
port={port}
|
|
131
|
+
inspectorPort={inspectorPort}
|
|
132
|
+
legacyEnv={legacyEnv}
|
|
133
|
+
buildCommand={buildCommand}
|
|
134
|
+
initialMode={initialMode}
|
|
135
|
+
localProtocol={localProtocol}
|
|
136
|
+
upstreamProtocol={upstreamProtocol}
|
|
137
|
+
jsxFactory={jsxFactory}
|
|
138
|
+
jsxFragment={jsxFragment}
|
|
139
|
+
accountId={accountId}
|
|
140
|
+
assetPaths={assetPaths}
|
|
141
|
+
public={publicDir}
|
|
142
|
+
compatibilityDate={compatibilityDate}
|
|
143
|
+
compatibilityFlags={compatibilityFlags}
|
|
144
|
+
usageModel={usageModel}
|
|
145
|
+
bindings={bindings}
|
|
146
|
+
enableLocalPersistence={enableLocalPersistence}
|
|
147
|
+
zone={zone}
|
|
148
|
+
/>
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
return instance as TestRenderer.ReactTestRenderer;
|
|
152
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { writeFile } from "fs/promises";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import guessWorkerFormat from "../
|
|
3
|
+
import guessWorkerFormat from "../entry";
|
|
4
4
|
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
5
5
|
|
|
6
6
|
describe("guess worker format", () => {
|
|
@@ -10,10 +10,8 @@ describe("guess worker format", () => {
|
|
|
10
10
|
// Note that this isn't actually a valid worker, because it's missing
|
|
11
11
|
// a fetch handler. Regardless, our heuristic is simply to check for exports.
|
|
12
12
|
const guess = await guessWorkerFormat(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
directory: process.cwd(),
|
|
16
|
-
},
|
|
13
|
+
path.join(process.cwd(), "./index.ts"),
|
|
14
|
+
process.cwd(),
|
|
17
15
|
undefined
|
|
18
16
|
);
|
|
19
17
|
expect(guess).toBe("modules");
|
|
@@ -25,10 +23,8 @@ describe("guess worker format", () => {
|
|
|
25
23
|
// a fetch listener. Regardless, our heuristic is simply to check for
|
|
26
24
|
// the lack of exports.
|
|
27
25
|
const guess = await guessWorkerFormat(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
directory: process.cwd(),
|
|
31
|
-
},
|
|
26
|
+
path.join(process.cwd(), "./index.ts"),
|
|
27
|
+
process.cwd(),
|
|
32
28
|
undefined
|
|
33
29
|
);
|
|
34
30
|
expect(guess).toBe("service-worker");
|
|
@@ -38,10 +34,8 @@ describe("guess worker format", () => {
|
|
|
38
34
|
await writeFile("./index.ts", "export default {};");
|
|
39
35
|
await expect(
|
|
40
36
|
guessWorkerFormat(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
directory: process.cwd(),
|
|
44
|
-
},
|
|
37
|
+
path.join(process.cwd(), "./index.ts"),
|
|
38
|
+
process.cwd(),
|
|
45
39
|
"service-worker"
|
|
46
40
|
)
|
|
47
41
|
).rejects.toThrow(
|
|
@@ -53,10 +47,8 @@ describe("guess worker format", () => {
|
|
|
53
47
|
await writeFile("./index.ts", "");
|
|
54
48
|
await expect(
|
|
55
49
|
guessWorkerFormat(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
directory: process.cwd(),
|
|
59
|
-
},
|
|
50
|
+
path.join(process.cwd(), "./index.ts"),
|
|
51
|
+
process.cwd(),
|
|
60
52
|
"modules"
|
|
61
53
|
)
|
|
62
54
|
).rejects.toThrow(
|
|
@@ -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,7 +1,7 @@
|
|
|
1
1
|
import { URL, URLSearchParams } from "node:url";
|
|
2
2
|
import { pathToRegexp } from "path-to-regexp";
|
|
3
3
|
import { getCloudflareApiBaseUrl } from "../../cfetch";
|
|
4
|
-
import type { FetchResult } from "../../cfetch";
|
|
4
|
+
import type { FetchResult, FetchError } from "../../cfetch";
|
|
5
5
|
import type { RequestInit } from "undici";
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -143,8 +143,8 @@ export function setMockResponse<ResponseType>(
|
|
|
143
143
|
export async function createFetchResult<ResponseType>(
|
|
144
144
|
result: ResponseType | Promise<ResponseType>,
|
|
145
145
|
success = true,
|
|
146
|
-
errors = [],
|
|
147
|
-
messages = [],
|
|
146
|
+
errors: FetchError[] = [],
|
|
147
|
+
messages: string[] = [],
|
|
148
148
|
result_info?: unknown
|
|
149
149
|
): Promise<FetchResult<ResponseType>> {
|
|
150
150
|
return result_info
|
|
@@ -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
|
);
|
|
@@ -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
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { getHttpsOptions } from "../https-options";
|
|
5
|
+
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
6
|
+
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
7
|
+
|
|
8
|
+
describe("getHttpsOptions()", () => {
|
|
9
|
+
runInTempDir({ homedir: "./home" });
|
|
10
|
+
const std = mockConsoleMethods();
|
|
11
|
+
|
|
12
|
+
it("should use cached values if they have not expired", async () => {
|
|
13
|
+
fs.mkdirSync(resolve(os.homedir(), ".wrangler/local-cert"), {
|
|
14
|
+
recursive: true,
|
|
15
|
+
});
|
|
16
|
+
fs.writeFileSync(
|
|
17
|
+
resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
|
|
18
|
+
"PRIVATE KEY"
|
|
19
|
+
);
|
|
20
|
+
fs.writeFileSync(
|
|
21
|
+
resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
|
|
22
|
+
"PUBLIC KEY"
|
|
23
|
+
);
|
|
24
|
+
const result = await getHttpsOptions();
|
|
25
|
+
expect(result.key).toEqual("PRIVATE KEY");
|
|
26
|
+
expect(result.cert).toEqual("PUBLIC KEY");
|
|
27
|
+
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
28
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
29
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should generate and cache new keys if none are cached", async () => {
|
|
33
|
+
const result = await getHttpsOptions();
|
|
34
|
+
const key = fs.readFileSync(
|
|
35
|
+
resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
|
|
36
|
+
"utf8"
|
|
37
|
+
);
|
|
38
|
+
const cert = fs.readFileSync(
|
|
39
|
+
resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
|
|
40
|
+
"utf8"
|
|
41
|
+
);
|
|
42
|
+
expect(result.key).toEqual(key);
|
|
43
|
+
expect(result.cert).toEqual(cert);
|
|
44
|
+
expect(std.out).toMatchInlineSnapshot(
|
|
45
|
+
`"Generating new self-signed certificate..."`
|
|
46
|
+
);
|
|
47
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
48
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should generate and cache new keys if cached files have expired", async () => {
|
|
52
|
+
fs.mkdirSync(resolve(os.homedir(), ".wrangler/local-cert"), {
|
|
53
|
+
recursive: true,
|
|
54
|
+
});
|
|
55
|
+
const ORIGINAL_KEY = "EXPIRED PRIVATE KEY";
|
|
56
|
+
const ORIGINAL_CERT = "EXPIRED PUBLIC KEY";
|
|
57
|
+
fs.writeFileSync(
|
|
58
|
+
resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
|
|
59
|
+
ORIGINAL_KEY
|
|
60
|
+
);
|
|
61
|
+
fs.writeFileSync(
|
|
62
|
+
resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
|
|
63
|
+
ORIGINAL_CERT
|
|
64
|
+
);
|
|
65
|
+
mockStatSync(/\.pem$/, { mtimeMs: new Date(2000).valueOf() });
|
|
66
|
+
|
|
67
|
+
const result = await getHttpsOptions();
|
|
68
|
+
const key = fs.readFileSync(
|
|
69
|
+
resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
|
|
70
|
+
"utf8"
|
|
71
|
+
);
|
|
72
|
+
const cert = fs.readFileSync(
|
|
73
|
+
resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
|
|
74
|
+
"utf8"
|
|
75
|
+
);
|
|
76
|
+
expect(key).not.toEqual(ORIGINAL_KEY);
|
|
77
|
+
expect(cert).not.toEqual(ORIGINAL_CERT);
|
|
78
|
+
expect(result.key).toEqual(key);
|
|
79
|
+
expect(result.cert).toEqual(cert);
|
|
80
|
+
expect(std.out).toMatchInlineSnapshot(
|
|
81
|
+
`"Generating new self-signed certificate..."`
|
|
82
|
+
);
|
|
83
|
+
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
84
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("should warn if not able to write to the cache", async () => {
|
|
88
|
+
mockWriteFileSyncThrow(/\.pem$/);
|
|
89
|
+
await getHttpsOptions();
|
|
90
|
+
expect(
|
|
91
|
+
fs.existsSync(resolve(os.homedir(), ".wrangler/local-cert/key.pem"))
|
|
92
|
+
).toBe(false);
|
|
93
|
+
expect(
|
|
94
|
+
fs.existsSync(resolve(os.homedir(), ".wrangler/local-cert/cert.pem"))
|
|
95
|
+
).toBe(false);
|
|
96
|
+
expect(std.out).toMatchInlineSnapshot(
|
|
97
|
+
`"Generating new self-signed certificate..."`
|
|
98
|
+
);
|
|
99
|
+
expect(std.warn).toMatchInlineSnapshot(`
|
|
100
|
+
"Unable to cache generated self-signed certificate in home/.wrangler/local-cert.
|
|
101
|
+
ERROR: Cannot write file"
|
|
102
|
+
`);
|
|
103
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
function mockStatSync(matcher: RegExp, stats: Partial<fs.Stats>) {
|
|
108
|
+
const originalStatSync = jest.requireActual("node:fs").statSync;
|
|
109
|
+
jest.spyOn(fs, "statSync").mockImplementation((statPath, options) => {
|
|
110
|
+
return matcher.test(statPath.toString())
|
|
111
|
+
? (stats as fs.Stats)
|
|
112
|
+
: originalStatSync(statPath, options);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function mockWriteFileSyncThrow(matcher: RegExp) {
|
|
117
|
+
const originalWriteFileSync = jest.requireActual("node:fs").writeFileSync;
|
|
118
|
+
jest
|
|
119
|
+
.spyOn(fs, "writeFileSync")
|
|
120
|
+
.mockImplementation((filePath, data, options) => {
|
|
121
|
+
if (matcher.test(filePath.toString())) {
|
|
122
|
+
throw new Error("ERROR: Cannot write file");
|
|
123
|
+
} else {
|
|
124
|
+
return originalWriteFileSync(filePath, data, options);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|