wrangler 0.0.0-e95e5a0 → 0.0.0-e978986

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 (52) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +3 -1
  3. package/bin/wrangler.js +36 -4
  4. package/package.json +11 -7
  5. package/pages/functions/buildPlugin.ts +13 -0
  6. package/pages/functions/buildWorker.ts +14 -1
  7. package/pages/functions/template-plugin.ts +3 -2
  8. package/src/__tests__/configuration.test.ts +336 -87
  9. package/src/__tests__/dev.test.tsx +243 -25
  10. package/src/__tests__/helpers/mock-dialogs.ts +41 -1
  11. package/src/__tests__/index.test.ts +79 -72
  12. package/src/__tests__/init.test.ts +182 -131
  13. package/src/__tests__/jest.setup.ts +8 -0
  14. package/src/__tests__/kv.test.ts +173 -146
  15. package/src/__tests__/package-manager.test.ts +154 -7
  16. package/src/__tests__/pages.test.ts +381 -53
  17. package/src/__tests__/parse.test.ts +8 -0
  18. package/src/__tests__/publish.test.ts +682 -89
  19. package/src/__tests__/r2.test.ts +55 -36
  20. package/src/__tests__/secret.test.ts +39 -4
  21. package/src/abort.d.ts +3 -0
  22. package/src/bundle.ts +33 -2
  23. package/src/cfetch/index.ts +21 -4
  24. package/src/cfetch/internal.ts +17 -9
  25. package/src/config/config.ts +1 -1
  26. package/src/config/environment.ts +40 -14
  27. package/src/config/index.ts +162 -0
  28. package/src/config/validation.ts +180 -65
  29. package/src/create-worker-preview.ts +17 -7
  30. package/src/create-worker-upload-form.ts +22 -8
  31. package/src/dev/dev.tsx +7 -6
  32. package/src/dev/local.tsx +6 -0
  33. package/src/dev/remote.tsx +15 -1
  34. package/src/dialogs.tsx +48 -0
  35. package/src/durable.ts +102 -0
  36. package/src/entry.ts +64 -9
  37. package/src/index.tsx +357 -177
  38. package/src/inspect.ts +39 -0
  39. package/src/kv.ts +114 -25
  40. package/src/open-in-browser.ts +5 -12
  41. package/src/package-manager.ts +50 -3
  42. package/src/pages.tsx +271 -110
  43. package/src/parse.ts +23 -4
  44. package/src/proxy.ts +38 -22
  45. package/src/publish.ts +227 -113
  46. package/src/sites.tsx +19 -17
  47. package/src/user.tsx +11 -1
  48. package/src/worker.ts +8 -0
  49. package/templates/new-worker-scheduled.js +17 -0
  50. package/templates/new-worker-scheduled.ts +32 -0
  51. package/templates/new-worker.ts +16 -1
  52. package/wrangler-dist/cli.js +36017 -22687
package/src/inspect.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import assert from "node:assert";
2
2
  import { createServer } from "node:http";
3
+ import os from "node:os";
3
4
  import { URL } from "node:url";
4
5
 
6
+ import open from "open";
5
7
  import { useEffect, useRef, useState } from "react";
6
8
  import WebSocket, { WebSocketServer } from "ws";
7
9
  import { version } from "../package.json";
@@ -621,3 +623,40 @@ function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void {
621
623
  logger.warn("console event:", evt);
622
624
  }
623
625
  }
626
+
627
+ /**
628
+ * Opens the chrome debugger
629
+ */
630
+ export const openInspector = async (inspectorPort: number) => {
631
+ const url = `https://built-devtools.pages.dev/js_app?experiments=true&v8only=true&ws=localhost:${inspectorPort}/ws`;
632
+ const errorMessage =
633
+ "Failed to open inspector.\nInspector depends on having a Chromium-based browser installed, maybe you need to install one?";
634
+
635
+ // see: https://github.com/sindresorhus/open/issues/177#issue-610016699
636
+ let braveBrowser: string;
637
+ switch (os.platform()) {
638
+ case "darwin":
639
+ case "win32":
640
+ braveBrowser = "Brave";
641
+ break;
642
+ default:
643
+ braveBrowser = "brave";
644
+ }
645
+
646
+ const childProcess = await open(url, {
647
+ app: [
648
+ {
649
+ name: open.apps.chrome,
650
+ },
651
+ {
652
+ name: braveBrowser,
653
+ },
654
+ {
655
+ name: open.apps.edge,
656
+ },
657
+ ],
658
+ });
659
+ childProcess.on("error", () => {
660
+ logger.warn(errorMessage);
661
+ });
662
+ };
package/src/kv.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { URLSearchParams } from "node:url";
2
2
  import { fetchListResult, fetchResult, fetchKVGetValue } from "./cfetch";
3
+ import { logger } from "./logger";
3
4
  import type { Config } from "./config";
4
5
 
5
6
  /** The largest number of kv items we can pass to the API in a single request. */
@@ -19,7 +20,7 @@ type KvArgs = {
19
20
  *
20
21
  * @returns the generated id of the created namespace.
21
22
  */
22
- export async function createNamespace(
23
+ export async function createKVNamespace(
23
24
  accountId: string,
24
25
  title: string
25
26
  ): Promise<string> {
@@ -51,7 +52,7 @@ export interface KVNamespaceInfo {
51
52
  /**
52
53
  * Fetch a list of all the namespaces under the given `accountId`.
53
54
  */
54
- export async function listNamespaces(
55
+ export async function listKVNamespaces(
55
56
  accountId: string
56
57
  ): Promise<KVNamespaceInfo[]> {
57
58
  const pageSize = 100;
@@ -83,7 +84,7 @@ export interface NamespaceKeyInfo {
83
84
  metadata?: { [key: string]: unknown };
84
85
  }
85
86
 
86
- export async function listNamespaceKeys(
87
+ export async function listKVNamespaceKeys(
87
88
  accountId: string,
88
89
  namespaceId: string,
89
90
  prefix = ""
@@ -95,6 +96,16 @@ export async function listNamespaceKeys(
95
96
  );
96
97
  }
97
98
 
99
+ export async function deleteKVNamespace(
100
+ accountId: string,
101
+ namespaceId: string
102
+ ) {
103
+ return await fetchResult<{ id: string }>(
104
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}`,
105
+ { method: "DELETE" }
106
+ );
107
+ }
108
+
98
109
  /**
99
110
  * Information about a key-value pair, including its "metadata" fields.
100
111
  */
@@ -116,12 +127,52 @@ const KeyValueKeys = new Set([
116
127
  "base64",
117
128
  ]);
118
129
 
130
+ /**
131
+ * The object has the specified property.
132
+ */
133
+ function hasProperty<T extends object>(
134
+ obj: object,
135
+ property: keyof T
136
+ ): obj is T {
137
+ return property in obj;
138
+ }
139
+
140
+ /**
141
+ * The object has a required property of the specified type.
142
+ */
143
+ function hasTypedProperty<T extends object>(
144
+ obj: object,
145
+ property: keyof T,
146
+ type: string
147
+ ): obj is T {
148
+ return hasProperty(obj, property) && typeof obj[property] === type;
149
+ }
150
+
151
+ /**
152
+ * The object an optional property, of the specified type.
153
+ */
154
+ function hasOptionalTypedProperty<T extends object>(
155
+ obj: object,
156
+ property: keyof T,
157
+ type: string
158
+ ): obj is Omit<T, typeof property> | T {
159
+ return !hasProperty(obj, property) || typeof obj[property] === type;
160
+ }
161
+
119
162
  /**
120
163
  * Is the given object a valid `KeyValue` type?
121
164
  */
122
- export function isKeyValue(keyValue: object): keyValue is KeyValue {
123
- const props = Object.keys(keyValue);
124
- if (!props.includes("key") || !props.includes("value")) {
165
+ export function isKVKeyValue(keyValue: unknown): keyValue is KeyValue {
166
+ if (
167
+ keyValue === null ||
168
+ typeof keyValue !== "object" ||
169
+ !hasTypedProperty(keyValue, "key", "string") ||
170
+ !hasTypedProperty(keyValue, "value", "string") ||
171
+ !hasOptionalTypedProperty(keyValue, "expiration", "number") ||
172
+ !hasOptionalTypedProperty(keyValue, "expiration_ttl", "number") ||
173
+ !hasOptionalTypedProperty(keyValue, "base64", "boolean") ||
174
+ !hasOptionalTypedProperty(keyValue, "metadata", "object")
175
+ ) {
125
176
  return false;
126
177
  }
127
178
  return true;
@@ -130,12 +181,12 @@ export function isKeyValue(keyValue: object): keyValue is KeyValue {
130
181
  /**
131
182
  * Get all the properties on the `keyValue` that are not expected.
132
183
  */
133
- export function unexpectedKeyValueProps(keyValue: KeyValue): string[] {
184
+ export function unexpectedKVKeyValueProps(keyValue: KeyValue): string[] {
134
185
  const props = Object.keys(keyValue);
135
186
  return props.filter((prop) => !KeyValueKeys.has(prop));
136
187
  }
137
188
 
138
- export async function putKeyValue(
189
+ export async function putKVKeyValue(
139
190
  accountId: string,
140
191
  namespaceId: string,
141
192
  keyValue: KeyValue
@@ -151,29 +202,66 @@ export async function putKeyValue(
151
202
  }
152
203
  }
153
204
  return await fetchResult(
154
- `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${keyValue.key}`,
205
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${encodeURIComponent(
206
+ keyValue.key
207
+ )}`,
155
208
  { method: "PUT", body: keyValue.value },
156
209
  searchParams
157
210
  );
158
211
  }
159
212
 
160
- export async function getKeyValue(
213
+ export async function getKVKeyValue(
161
214
  accountId: string,
162
215
  namespaceId: string,
163
216
  key: string
164
217
  ): Promise<string> {
165
- return await fetchKVGetValue(accountId, namespaceId, key);
218
+ return await fetchKVGetValue(accountId, namespaceId, encodeURIComponent(key));
166
219
  }
167
220
 
168
- export async function putBulkKeyValue(
221
+ export async function deleteKVKeyValue(
222
+ accountId: string,
223
+ namespaceId: string,
224
+ key: string
225
+ ) {
226
+ return await fetchResult(
227
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${encodeURIComponent(
228
+ key
229
+ )}`,
230
+ { method: "DELETE" }
231
+ );
232
+ }
233
+
234
+ /**
235
+ * Formatter for converting e.g. 5328 --> 5,328
236
+ */
237
+ const formatNumber = new Intl.NumberFormat("en-US", {
238
+ notation: "standard",
239
+ }).format;
240
+
241
+ /**
242
+ * Helper function for bulk requests, logs ongoing output to console.
243
+ */
244
+ function logBulkProgress(
245
+ operation: "put" | "delete",
246
+ index: number,
247
+ total: number
248
+ ) {
249
+ logger.log(
250
+ `${operation === "put" ? "Uploaded" : "Deleted"} ${Math.floor(
251
+ (100 * index) / total
252
+ )}% (${formatNumber(index)} out of ${formatNumber(total)})`
253
+ );
254
+ }
255
+
256
+ export async function putKVBulkKeyValue(
169
257
  accountId: string,
170
258
  namespaceId: string,
171
259
  keyValues: KeyValue[],
172
- progressCallback: (index: number, total: number) => void
260
+ quiet = false
173
261
  ) {
174
262
  for (let index = 0; index < keyValues.length; index += BATCH_KEY_MAX) {
175
- if (progressCallback && keyValues.length > BATCH_KEY_MAX) {
176
- progressCallback(index, keyValues.length);
263
+ if (!quiet && keyValues.length > BATCH_KEY_MAX) {
264
+ logBulkProgress("put", index, keyValues.length);
177
265
  }
178
266
 
179
267
  await fetchResult(
@@ -185,20 +273,21 @@ export async function putBulkKeyValue(
185
273
  }
186
274
  );
187
275
  }
188
- if (progressCallback && keyValues.length > BATCH_KEY_MAX) {
189
- progressCallback(keyValues.length, keyValues.length);
276
+
277
+ if (!quiet && keyValues.length > BATCH_KEY_MAX) {
278
+ logBulkProgress("put", keyValues.length, keyValues.length);
190
279
  }
191
280
  }
192
281
 
193
- export async function deleteBulkKeyValue(
282
+ export async function deleteKVBulkKeyValue(
194
283
  accountId: string,
195
284
  namespaceId: string,
196
285
  keys: string[],
197
- progressCallback: (index: number, total: number) => void
286
+ quiet = false
198
287
  ) {
199
288
  for (let index = 0; index < keys.length; index += BATCH_KEY_MAX) {
200
- if (progressCallback && keys.length > BATCH_KEY_MAX) {
201
- progressCallback(index, keys.length);
289
+ if (!quiet && keys.length > BATCH_KEY_MAX) {
290
+ logBulkProgress("delete", index, keys.length);
202
291
  }
203
292
 
204
293
  await fetchResult(
@@ -210,12 +299,12 @@ export async function deleteBulkKeyValue(
210
299
  }
211
300
  );
212
301
  }
213
- if (progressCallback && keys.length > BATCH_KEY_MAX) {
214
- progressCallback(keys.length, keys.length);
302
+ if (!quiet && keys.length > BATCH_KEY_MAX) {
303
+ logBulkProgress("delete", keys.length, keys.length);
215
304
  }
216
305
  }
217
306
 
218
- export function getNamespaceId(
307
+ export function getKVNamespaceId(
219
308
  { preview, binding, "namespace-id": namespaceId }: KvArgs,
220
309
  config: Config
221
310
  ): string {
@@ -310,7 +399,7 @@ export function getNamespaceId(
310
399
  /**
311
400
  * KV namespace binding names must be valid JS identifiers.
312
401
  */
313
- export function isValidNamespaceBinding(
402
+ export function isValidKVNamespaceBinding(
314
403
  binding: string | undefined
315
404
  ): binding is string {
316
405
  return (
@@ -8,19 +8,12 @@ import { logger } from "./logger";
8
8
  * StackBlitz, remote servers), it doesn't just crash the process.
9
9
  *
10
10
  * @param url the URL to point the browser at
11
- * @param options open a Chromium-based browser instead of the default
12
11
  */
13
- export default async function openInBrowser(
14
- url: string,
15
- { forceChromium }: { forceChromium: boolean } = { forceChromium: false }
16
- ): Promise<void> {
17
- const options: open.Options | undefined = forceChromium
18
- ? {
19
- app: [{ name: open.apps.chrome }, { name: open.apps.edge }],
20
- }
21
- : undefined;
22
- const childProcess = await open(url, options);
12
+ export default async function openInBrowser(url: string): Promise<void> {
13
+ const errorMessage = `Failed to open ${url} in a browser`;
14
+
15
+ const childProcess = await open(url);
23
16
  childProcess.on("error", () => {
24
- logger.warn(`Failed to open ${url} in a browser.`);
17
+ logger.warn(errorMessage);
25
18
  });
26
19
  }
@@ -5,15 +5,20 @@ import { logger } from "./logger";
5
5
 
6
6
  export interface PackageManager {
7
7
  cwd: string;
8
- type: "npm" | "yarn";
8
+ type: "npm" | "yarn" | "pnpm";
9
9
  addDevDeps(...packages: string[]): Promise<void>;
10
10
  install(): Promise<void>;
11
11
  }
12
12
 
13
13
  export async function getPackageManager(cwd: string): Promise<PackageManager> {
14
- const [hasYarn, hasNpm] = await Promise.all([supportsYarn(), supportsNpm()]);
14
+ const [hasYarn, hasNpm, hasPnpm] = await Promise.all([
15
+ supportsYarn(),
16
+ supportsNpm(),
17
+ supportsPnpm(),
18
+ ]);
15
19
  const hasYarnLock = existsSync(join(cwd, "yarn.lock"));
16
20
  const hasNpmLock = existsSync(join(cwd, "package-lock.json"));
21
+ const hasPnpmLock = existsSync(join(cwd, "pnpm-lock.yaml"));
17
22
 
18
23
  if (hasNpmLock) {
19
24
  if (hasNpm) {
@@ -28,6 +33,18 @@ export async function getPackageManager(cwd: string): Promise<PackageManager> {
28
33
  );
29
34
  return { ...YarnPackageManager, cwd };
30
35
  }
36
+ } else if (hasPnpmLock) {
37
+ if (hasPnpm) {
38
+ logger.log(
39
+ "Using pnpm as package manager, as there is already a pnpm-lock.yaml file."
40
+ );
41
+ return { ...PnpmPackageManager, cwd };
42
+ } else {
43
+ logger.warn(
44
+ "There is already a pnpm-lock.yaml file but could not find pnpm on the PATH."
45
+ );
46
+ // will simply fallback to the first found of [npm, yaml, pnpm] in the next if round.
47
+ }
31
48
  } else if (hasYarnLock) {
32
49
  if (hasYarn) {
33
50
  logger.log(
@@ -49,9 +66,12 @@ export async function getPackageManager(cwd: string): Promise<PackageManager> {
49
66
  } else if (hasYarn) {
50
67
  logger.log("Using yarn as package manager.");
51
68
  return { ...YarnPackageManager, cwd };
69
+ } else if (hasPnpm) {
70
+ logger.log("Using pnpm as package manager.");
71
+ return { ...PnpmPackageManager, cwd };
52
72
  } else {
53
73
  throw new Error(
54
- "Unable to find a package manager. Supported managers are: npm and yarn."
74
+ "Unable to find a package manager. Supported managers are: npm, yarn, and pnpm."
55
75
  );
56
76
  }
57
77
  }
@@ -86,6 +106,29 @@ const NpmPackageManager: PackageManager = {
86
106
  },
87
107
  };
88
108
 
109
+ /**
110
+ * Manage packages using pnpm
111
+ */
112
+ const PnpmPackageManager: PackageManager = {
113
+ cwd: process.cwd(),
114
+ type: "pnpm",
115
+ /** Add and install a new devDependency into the local package.json. */
116
+ async addDevDeps(...packages: string[]): Promise<void> {
117
+ await execa("pnpm", ["install", ...packages, "--save-dev"], {
118
+ stdio: "inherit",
119
+ cwd: this.cwd,
120
+ });
121
+ },
122
+
123
+ /** Install all the dependencies in the local package.json. */
124
+ async install(): Promise<void> {
125
+ await execa("pnpm", ["install"], {
126
+ stdio: "inherit",
127
+ cwd: this.cwd,
128
+ });
129
+ },
130
+ };
131
+
89
132
  /**
90
133
  * Manage packages using yarn
91
134
  */
@@ -125,3 +168,7 @@ function supportsYarn(): Promise<boolean> {
125
168
  function supportsNpm(): Promise<boolean> {
126
169
  return supports("npm");
127
170
  }
171
+
172
+ function supportsPnpm(): Promise<boolean> {
173
+ return supports("pnpm");
174
+ }