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.
Files changed (51) hide show
  1. package/package.json +10 -4
  2. package/pages/functions/filepath-routing.test.ts +125 -25
  3. package/pages/functions/filepath-routing.ts +6 -3
  4. package/src/__tests__/configuration.test.ts +479 -114
  5. package/src/__tests__/dev.test.tsx +162 -8
  6. package/src/__tests__/dev2.test.tsx +152 -0
  7. package/src/__tests__/guess-worker-format.test.ts +9 -17
  8. package/src/__tests__/helpers/mock-account-id.ts +4 -0
  9. package/src/__tests__/helpers/mock-cfetch.ts +3 -3
  10. package/src/__tests__/helpers/mock-kv.ts +9 -16
  11. package/src/__tests__/helpers/run-in-tmp.ts +3 -0
  12. package/src/__tests__/https-options.test.ts +127 -0
  13. package/src/__tests__/index.test.ts +125 -0
  14. package/src/__tests__/jest.setup.ts +1 -1
  15. package/src/__tests__/kv.test.ts +27 -53
  16. package/src/__tests__/logout.test.ts +15 -13
  17. package/src/__tests__/pages.test.ts +44 -0
  18. package/src/__tests__/publish.test.ts +504 -32
  19. package/src/__tests__/route.test.ts +45 -0
  20. package/src/__tests__/whoami.test.tsx +21 -7
  21. package/src/bundle.ts +20 -11
  22. package/src/cfetch/internal.ts +11 -5
  23. package/src/config/config.ts +53 -44
  24. package/src/config/validation-helpers.ts +110 -12
  25. package/src/config/validation.ts +59 -41
  26. package/src/{api/preview.ts → create-worker-preview.ts} +23 -4
  27. package/src/{api/form_data.ts → create-worker-upload-form.ts} +1 -1
  28. package/src/dev/dev.tsx +357 -0
  29. package/src/dev/local.tsx +241 -0
  30. package/src/dev/remote.tsx +201 -0
  31. package/src/dev/use-esbuild.ts +100 -0
  32. package/src/entry.ts +111 -9
  33. package/src/https-options.ts +122 -0
  34. package/src/index.tsx +171 -109
  35. package/src/inspect.ts +9 -1
  36. package/src/{kv.tsx → kv.ts} +0 -0
  37. package/src/module-collection.ts +85 -2
  38. package/src/open-in-browser.ts +14 -2
  39. package/src/pages.tsx +1 -1
  40. package/src/proxy.ts +102 -40
  41. package/src/publish.ts +41 -50
  42. package/src/reporting.ts +25 -9
  43. package/src/selfsigned.d.ts +29 -0
  44. package/src/sites.tsx +51 -16
  45. package/src/user.tsx +132 -77
  46. package/src/{api/worker.ts → worker.ts} +0 -23
  47. package/wrangler-dist/cli.js +5012 -3107
  48. package/wrangler-dist/cli.js.map +3 -3
  49. package/src/__tests__/helpers/mock-user.ts +0 -27
  50. package/src/dev.tsx +0 -960
  51. 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/config.routes", async () => {
139
- // This test might seem like it's testing implmentation details, but let's be specific and consider it a spec
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
+ %s 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 "../guess-worker-format";
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
- file: path.join(process.cwd(), "./index.ts"),
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
- file: path.join(process.cwd(), "./index.ts"),
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
- file: path.join(process.cwd(), "./index.ts"),
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
- file: path.join(process.cwd(), "./index.ts"),
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: string[],
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
- const expectedKeyObjects = expectedKeys.map((name) => ({
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 (expectedKeyObjects.length <= keysPerRequest) {
24
- return createFetchResult(expectedKeyObjects);
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 < expectedKeyObjects.length ? end : blankCursorValue;
29
- return createFetchResult(
30
- expectedKeyObjects.slice(start, end),
31
- true,
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
+ }