wrangler 0.0.0-e3cab74 → 0.0.0-e452a35

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 (102) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +27 -14
  3. package/bin/wrangler.js +49 -5
  4. package/miniflare-dist/index.mjs +4886 -0
  5. package/package.json +42 -28
  6. package/pages/functions/buildPlugin.ts +105 -0
  7. package/pages/functions/buildWorker.ts +93 -4
  8. package/pages/functions/filepath-routing.test.ts +136 -25
  9. package/pages/functions/filepath-routing.ts +14 -3
  10. package/pages/functions/routes.ts +6 -1
  11. package/pages/functions/template-plugin.ts +147 -0
  12. package/pages/functions/template-worker.ts +21 -8
  13. package/src/__tests__/config-cache.test.ts +36 -0
  14. package/src/__tests__/configuration.test.ts +1514 -367
  15. package/src/__tests__/dev.test.tsx +870 -84
  16. package/src/__tests__/guess-worker-format.test.ts +37 -18
  17. package/src/__tests__/helpers/mock-account-id.ts +22 -4
  18. package/src/__tests__/helpers/mock-cfetch.ts +8 -4
  19. package/src/__tests__/helpers/mock-console.ts +35 -10
  20. package/src/__tests__/helpers/mock-dialogs.ts +26 -1
  21. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  22. package/src/__tests__/helpers/mock-istty.ts +27 -0
  23. package/src/__tests__/helpers/mock-kv.ts +9 -16
  24. package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
  25. package/src/__tests__/helpers/mock-stdin.ts +103 -0
  26. package/src/__tests__/helpers/run-in-tmp.ts +10 -0
  27. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  28. package/src/__tests__/https-options.test.ts +130 -0
  29. package/src/__tests__/index.test.ts +137 -747
  30. package/src/__tests__/init.test.ts +1557 -0
  31. package/src/__tests__/jest.setup.ts +28 -8
  32. package/src/__tests__/kv.test.ts +598 -258
  33. package/src/__tests__/logger.test.ts +115 -0
  34. package/src/__tests__/pages.test.ts +657 -0
  35. package/src/__tests__/parse.test.ts +8 -0
  36. package/src/__tests__/publish.test.ts +4003 -941
  37. package/src/__tests__/r2.test.ts +69 -34
  38. package/src/__tests__/route.test.ts +45 -0
  39. package/src/__tests__/secret.test.ts +228 -73
  40. package/src/__tests__/tail.test.ts +146 -20
  41. package/src/__tests__/user.test.ts +131 -0
  42. package/src/__tests__/whoami.test.tsx +26 -7
  43. package/src/abort.d.ts +3 -0
  44. package/src/bundle.ts +74 -7
  45. package/src/cfetch/index.ts +21 -4
  46. package/src/cfetch/internal.ts +20 -14
  47. package/src/cli.ts +5 -11
  48. package/src/config/README.md +7 -7
  49. package/src/config/config.ts +69 -100
  50. package/src/config/environment.ts +119 -4
  51. package/src/config/index.ts +177 -4
  52. package/src/config/validation-helpers.ts +228 -23
  53. package/src/config/validation.ts +687 -174
  54. package/src/config-cache.ts +45 -0
  55. package/src/{api/preview.ts → create-worker-preview.ts} +74 -36
  56. package/src/{api/form_data.ts → create-worker-upload-form.ts} +55 -18
  57. package/src/dev/dev-vars.ts +36 -0
  58. package/src/dev/dev.tsx +422 -0
  59. package/src/dev/local.tsx +318 -0
  60. package/src/dev/remote.tsx +227 -0
  61. package/src/dev/use-esbuild.ts +118 -0
  62. package/src/dialogs.tsx +16 -6
  63. package/src/durable.ts +102 -0
  64. package/src/entry.ts +279 -14
  65. package/src/environment-variables.ts +3 -1
  66. package/src/errors.ts +11 -0
  67. package/src/https-options.ts +123 -0
  68. package/src/index.tsx +1268 -806
  69. package/src/inspect.ts +64 -11
  70. package/src/intl-polyfill.d.ts +139 -0
  71. package/src/{kv.tsx → kv.ts} +195 -62
  72. package/src/logger.ts +66 -0
  73. package/src/miniflare-cli/README.md +30 -0
  74. package/src/miniflare-cli/enum-keys.ts +17 -0
  75. package/src/miniflare-cli/index.ts +41 -0
  76. package/src/miniflare-cli/request-context.ts +40 -0
  77. package/src/module-collection.ts +94 -6
  78. package/src/open-in-browser.ts +9 -3
  79. package/src/package-manager.ts +9 -8
  80. package/src/pages.tsx +1057 -75
  81. package/src/parse.ts +24 -5
  82. package/src/proxy.ts +143 -54
  83. package/src/publish.ts +444 -229
  84. package/src/selfsigned.d.ts +29 -0
  85. package/src/sites.tsx +98 -44
  86. package/src/tail/index.ts +2 -2
  87. package/src/tail/printing.ts +13 -8
  88. package/src/update-check.ts +19 -0
  89. package/src/user.tsx +263 -109
  90. package/src/whoami.tsx +2 -1
  91. package/src/{api/worker.ts → worker.ts} +24 -30
  92. package/templates/gitignore +171 -0
  93. package/templates/new-worker.ts +16 -1
  94. package/templates/static-asset-facade.js +1 -1
  95. package/wrangler-dist/cli.js +55345 -69246
  96. package/src/__tests__/helpers/mock-user.ts +0 -27
  97. package/src/__tests__/logout.test.ts +0 -50
  98. package/src/__tests__/sentry.test.ts +0 -146
  99. package/src/dev.tsx +0 -961
  100. package/src/guess-worker-format.ts +0 -68
  101. package/src/reporting.ts +0 -115
  102. package/wrangler-dist/cli.js.map +0 -7
@@ -0,0 +1,29 @@
1
+ declare module "selfsigned" {
2
+ import type { pki } from "node-forge";
3
+
4
+ export type Attributes = pki.CertificateField[];
5
+
6
+ export interface Options {
7
+ days?: number;
8
+ keySize?: number;
9
+ extensions?: unknown[];
10
+ algorithm?: string;
11
+ pkcs7?: boolean;
12
+ clientCertificate?: boolean;
13
+ clientCertificateCN?: string;
14
+ }
15
+
16
+ export interface Certificate {
17
+ private: string;
18
+ public: string;
19
+ cert: string;
20
+ fingerprint: string;
21
+ }
22
+
23
+ export function generate(attrs?: Attributes, options?: Options): Certificate;
24
+ export function generate(
25
+ attrs: Attributes,
26
+ options: Options,
27
+ callback: (err: Error, certificate: Certificate) => void
28
+ ): void;
29
+ }
package/src/sites.tsx CHANGED
@@ -1,34 +1,35 @@
1
+ import assert from "node:assert";
1
2
  import { readdir, readFile, stat } from "node:fs/promises";
2
3
  import * as path from "node:path";
3
4
  import ignore from "ignore";
4
5
  import xxhash from "xxhash-wasm";
5
6
  import {
6
- createNamespace,
7
- listNamespaceKeys,
8
- listNamespaces,
9
- putBulkKeyValue,
7
+ createKVNamespace,
8
+ listKVNamespaceKeys,
9
+ listKVNamespaces,
10
+ putKVBulkKeyValue,
11
+ deleteKVBulkKeyValue,
10
12
  } from "./kv";
13
+ import { logger } from "./logger";
11
14
  import type { Config } from "./config";
15
+ import type { KeyValue } from "./kv";
12
16
  import type { XXHashAPI } from "xxhash-wasm";
13
17
 
14
18
  /** Paths to always ignore. */
15
- const ALWAYS_IGNORE = ["node_modules"];
16
- const HIDDEN_FILES_TO_INCLUDE = [
19
+ const ALWAYS_IGNORE = new Set(["node_modules"]);
20
+ const HIDDEN_FILES_TO_INCLUDE = new Set([
17
21
  ".well-known", // See https://datatracker.ietf.org/doc/html/rfc8615
18
- ];
22
+ ]);
19
23
 
20
24
  async function* getFilesInFolder(dirPath: string): AsyncIterable<string> {
21
25
  const files = await readdir(dirPath, { withFileTypes: true });
22
26
  for (const file of files) {
23
27
  // Skip files that we never want to process.
24
- if (ALWAYS_IGNORE.some((p) => file.name === p)) {
28
+ if (ALWAYS_IGNORE.has(file.name)) {
25
29
  continue;
26
30
  }
27
31
  // Skip hidden files (starting with .) except for some special ones
28
- if (
29
- file.name.startsWith(".") &&
30
- !HIDDEN_FILES_TO_INCLUDE.some((p) => file.name === p)
31
- ) {
32
+ if (file.name.startsWith(".") && !HIDDEN_FILES_TO_INCLUDE.has(file.name)) {
32
33
  continue;
33
34
  }
34
35
  // TODO: follow symlinks??
@@ -75,15 +76,15 @@ async function createKVNamespaceIfNotAlreadyExisting(
75
76
  ) {
76
77
  // check if it already exists
77
78
  // TODO: this is super inefficient, should be made better
78
- const namespaces = await listNamespaces(accountId);
79
+ const namespaces = await listKVNamespaces(accountId);
79
80
  const found = namespaces.find((x) => x.title === title);
80
81
  if (found) {
81
82
  return { created: false, id: found.id };
82
83
  }
83
84
 
84
85
  // else we make the namespace
85
- const id = await createNamespace(accountId, title);
86
- console.log(`🌀 Created namespace for Workers Site "${title}"`);
86
+ const id = await createKVNamespace(accountId, title);
87
+ logger.log(`🌀 Created namespace for Workers Site "${title}"`);
87
88
 
88
89
  return {
89
90
  created: true,
@@ -103,10 +104,11 @@ async function createKVNamespaceIfNotAlreadyExisting(
103
104
  * asset in the KV namespace.
104
105
  */
105
106
  export async function syncAssets(
106
- accountId: string,
107
+ accountId: string | undefined,
107
108
  scriptName: string,
108
109
  siteAssets: AssetPaths | undefined,
109
- preview: boolean
110
+ preview: boolean,
111
+ dryRun: boolean | undefined
110
112
  ): Promise<{
111
113
  manifest: { [filePath: string]: string } | undefined;
112
114
  namespace: string | undefined;
@@ -115,59 +117,88 @@ export async function syncAssets(
115
117
  return { manifest: undefined, namespace: undefined };
116
118
  }
117
119
 
120
+ if (dryRun) {
121
+ logger.log("(Note: doing a dry run, not uploading or deleting anything.)");
122
+ return { manifest: undefined, namespace: undefined };
123
+ }
124
+ assert(accountId, "Missing accountId");
125
+
118
126
  const title = `__${scriptName}-workers_sites_assets${
119
127
  preview ? "_preview" : ""
120
128
  }`;
129
+
121
130
  const { id: namespace } = await createKVNamespaceIfNotAlreadyExisting(
122
131
  title,
123
132
  accountId
124
133
  );
125
134
 
126
135
  // let's get all the keys in this namespace
127
- const result = await listNamespaceKeys(accountId, namespace);
128
- const keys = new Set(result.map((x) => x.name));
136
+ const namespaceKeysResponse = await listKVNamespaceKeys(accountId, namespace);
137
+ const namespaceKeys = new Set(namespaceKeysResponse.map((x) => x.name));
129
138
 
130
139
  const manifest: Record<string, string> = {};
131
- const upload: {
132
- key: string;
133
- value: string;
134
- base64: boolean;
135
- }[] = [];
140
+ const toUpload: KeyValue[] = [];
136
141
 
137
142
  const include = createPatternMatcher(siteAssets.includePatterns, false);
138
143
  const exclude = createPatternMatcher(siteAssets.excludePatterns, true);
139
144
  const hasher = await xxhash();
140
145
 
141
- // TODO: this can be more efficient by parallelising
142
- for await (const file of getFilesInFolder(siteAssets.baseDirectory)) {
143
- if (!include(file)) {
146
+ const assetDirectory = path.join(
147
+ siteAssets.baseDirectory,
148
+ siteAssets.assetDirectory
149
+ );
150
+ for await (const absAssetFile of getFilesInFolder(assetDirectory)) {
151
+ const assetFile = path.relative(siteAssets.baseDirectory, absAssetFile);
152
+ if (!include(assetFile)) {
144
153
  continue;
145
154
  }
146
- if (exclude(file)) {
155
+ if (exclude(assetFile)) {
147
156
  continue;
148
157
  }
149
158
 
150
- await validateAssetSize(file);
151
- console.log(`reading ${file}...`);
152
- const content = await readFile(file, "base64");
159
+ await validateAssetSize(absAssetFile, assetFile);
160
+ logger.log(`Reading ${assetFile}...`);
161
+ const content = await readFile(absAssetFile, "base64");
153
162
 
154
- const assetKey = await hashAsset(hasher, file, content);
163
+ const assetKey = hashAsset(hasher, assetFile, content);
155
164
  validateAssetKey(assetKey);
156
165
 
157
166
  // now put each of the files into kv
158
- if (!keys.has(assetKey)) {
159
- console.log(`uploading as ${assetKey}...`);
160
- upload.push({
167
+ if (!namespaceKeys.has(assetKey)) {
168
+ logger.log(`Uploading as ${assetKey}...`);
169
+ toUpload.push({
161
170
  key: assetKey,
162
171
  value: content,
163
172
  base64: true,
164
173
  });
165
174
  } else {
166
- console.log(`skipping - already uploaded`);
175
+ logger.log(`Skipping - already uploaded.`);
167
176
  }
168
- manifest[path.relative(siteAssets.baseDirectory, file)] = assetKey;
177
+
178
+ // remove the key from the set so we know what we've already uploaded
179
+ namespaceKeys.delete(assetKey);
180
+
181
+ // prevent causing different manifest keys on windows
182
+ const maifestKey = urlSafe(
183
+ path.relative(siteAssets.assetDirectory, absAssetFile)
184
+ );
185
+ manifest[maifestKey] = assetKey;
169
186
  }
170
- await putBulkKeyValue(accountId, namespace, JSON.stringify(upload));
187
+
188
+ // keys now contains all the files we're deleting
189
+ for (const key of namespaceKeys) {
190
+ logger.log(`Deleting ${key} from the asset store...`);
191
+ }
192
+
193
+ await Promise.all([
194
+ // upload all the new assets
195
+ putKVBulkKeyValue(accountId, namespace, toUpload),
196
+ // delete all the unused assets
197
+ deleteKVBulkKeyValue(accountId, namespace, Array.from(namespaceKeys)),
198
+ ]);
199
+
200
+ logger.log("↗️ Done syncing assets");
201
+
171
202
  return { manifest, namespace };
172
203
  }
173
204
 
@@ -183,11 +214,14 @@ function createPatternMatcher(
183
214
  }
184
215
  }
185
216
 
186
- async function validateAssetSize(filePath: string) {
187
- const { size } = await stat(filePath);
217
+ async function validateAssetSize(
218
+ absFilePath: string,
219
+ relativeFilePath: string
220
+ ) {
221
+ const { size } = await stat(absFilePath);
188
222
  if (size > 25 * 1024 * 1024) {
189
223
  throw new Error(
190
- `File ${filePath} is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits`
224
+ `File ${relativeFilePath} is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits`
191
225
  );
192
226
  }
193
227
  }
@@ -213,8 +247,23 @@ function urlSafe(filePath: string): string {
213
247
  * Information about the assets that should be uploaded
214
248
  */
215
249
  export interface AssetPaths {
250
+ /**
251
+ * Absolute path to the root of the project.
252
+ *
253
+ * This is the directory containing wrangler.toml or cwd if no config.
254
+ */
216
255
  baseDirectory: string;
256
+ /**
257
+ * The path to the assets directory, relative to the `baseDirectory`.
258
+ */
259
+ assetDirectory: string;
260
+ /**
261
+ * An array of patterns that match files that should be uploaded.
262
+ */
217
263
  includePatterns: string[];
264
+ /**
265
+ * An array of patterns that match files that should not be uploaded.
266
+ */
218
267
  excludePatterns: string[];
219
268
  }
220
269
 
@@ -227,13 +276,18 @@ export interface AssetPaths {
227
276
  */
228
277
  export function getAssetPaths(
229
278
  config: Config,
230
- baseDirectory = config.site?.bucket,
279
+ assetDirectory = config.site?.bucket,
231
280
  includePatterns = config.site?.include ?? [],
232
281
  excludePatterns = config.site?.exclude ?? []
233
- ): undefined | AssetPaths {
234
- return baseDirectory
282
+ ): AssetPaths | undefined {
283
+ const baseDirectory = path.resolve(
284
+ path.dirname(config.configPath ?? "wrangler.toml")
285
+ );
286
+
287
+ return assetDirectory
235
288
  ? {
236
289
  baseDirectory,
290
+ assetDirectory,
237
291
  includePatterns,
238
292
  excludePatterns,
239
293
  }
package/src/tail/index.ts CHANGED
@@ -161,7 +161,7 @@ export type TailEventMessage = {
161
161
  * Any logs sent out by the worker
162
162
  */
163
163
  logs: {
164
- message: unknown;
164
+ message: unknown[];
165
165
  level: string; // TODO: make this a union of possible values
166
166
  timestamp: number;
167
167
  }[];
@@ -179,7 +179,7 @@ export type TailEventMessage = {
179
179
  * Until workers-types exposes individual types for export, we'll have
180
180
  * to just re-define these types ourselves.
181
181
  */
182
- event: RequestEvent | ScheduledEvent;
182
+ event: RequestEvent | ScheduledEvent | undefined | null;
183
183
  };
184
184
 
185
185
  /**
@@ -1,3 +1,4 @@
1
+ import { logger } from "../logger";
1
2
  import type { RequestEvent, ScheduledEvent, TailEventMessage } from ".";
2
3
  import type { Outcome } from "./filters";
3
4
  import type WebSocket from "ws";
@@ -12,25 +13,29 @@ export function prettyPrintLogs(data: WebSocket.RawData): void {
12
13
  ).toLocaleString();
13
14
  const outcome = prettifyOutcome(eventMessage.outcome);
14
15
 
15
- console.log(`"${cronPattern}" @ ${datetime} - ${outcome}`);
16
+ logger.log(`"${cronPattern}" @ ${datetime} - ${outcome}`);
16
17
  } else {
17
- const requestMethod = eventMessage.event.request.method.toUpperCase();
18
- const url = eventMessage.event.request.url;
18
+ const requestMethod = eventMessage.event?.request.method.toUpperCase();
19
+ const url = eventMessage.event?.request.url;
19
20
  const outcome = prettifyOutcome(eventMessage.outcome);
20
21
  const datetime = new Date(eventMessage.eventTimestamp).toLocaleString();
21
22
 
22
- console.log(`${requestMethod} ${url} - ${outcome} @ ${datetime}`);
23
+ logger.log(
24
+ url
25
+ ? `${requestMethod} ${url} - ${outcome} @ ${datetime}`
26
+ : `[missing request] - ${outcome} @ ${datetime}`
27
+ );
23
28
  }
24
29
 
25
30
  if (eventMessage.logs.length > 0) {
26
31
  eventMessage.logs.forEach(({ level, message }) => {
27
- console.log(` (${level}) ${message}`);
32
+ logger.log(` (${level})`, ...message);
28
33
  });
29
34
  }
30
35
 
31
36
  if (eventMessage.exceptions.length > 0) {
32
37
  eventMessage.exceptions.forEach(({ name, message }) => {
33
- console.error(` ${name}: ${message}`);
38
+ logger.error(` ${name}:`, message);
34
39
  });
35
40
  }
36
41
  }
@@ -40,9 +45,9 @@ export function jsonPrintLogs(data: WebSocket.RawData): void {
40
45
  }
41
46
 
42
47
  function isScheduledEvent(
43
- event: RequestEvent | ScheduledEvent
48
+ event: RequestEvent | ScheduledEvent | undefined | null
44
49
  ): event is ScheduledEvent {
45
- return "cron" in event;
50
+ return Boolean(event && "cron" in event);
46
51
  }
47
52
 
48
53
  function prettifyOutcome(outcome: Outcome): string {
@@ -0,0 +1,19 @@
1
+ import chalk from "chalk";
2
+ import checkForUpdate from "update-check";
3
+ import pkg from "../package.json";
4
+
5
+ export async function updateCheck(): Promise<string> {
6
+ let update = null;
7
+ try {
8
+ // default cache for update check is 1 day
9
+ update = await checkForUpdate(pkg, {
10
+ distTag: pkg.version.startsWith("0.0.0") ? "beta" : "latest",
11
+ });
12
+ } catch (err) {
13
+ // ignore error
14
+ }
15
+
16
+ if (update) return `(update available ${chalk.green(update.latest)})`;
17
+
18
+ return "";
19
+ }