wrangler 0.0.0-e95bea2 → 0.0.0-e95e5a0

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 (93) hide show
  1. package/README.md +25 -14
  2. package/bin/wrangler.js +13 -1
  3. package/miniflare-dist/index.mjs +4886 -0
  4. package/package.json +33 -25
  5. package/pages/functions/buildPlugin.ts +92 -0
  6. package/pages/functions/buildWorker.ts +80 -4
  7. package/pages/functions/filepath-routing.test.ts +136 -25
  8. package/pages/functions/filepath-routing.ts +14 -3
  9. package/pages/functions/routes.ts +6 -1
  10. package/pages/functions/template-plugin.ts +146 -0
  11. package/pages/functions/template-worker.ts +21 -8
  12. package/src/__tests__/config-cache.test.ts +36 -0
  13. package/src/__tests__/configuration.test.ts +1179 -271
  14. package/src/__tests__/dev.test.tsx +382 -25
  15. package/src/__tests__/guess-worker-format.test.ts +28 -1
  16. package/src/__tests__/helpers/mock-account-id.ts +4 -0
  17. package/src/__tests__/helpers/mock-cfetch.ts +5 -1
  18. package/src/__tests__/helpers/mock-console.ts +20 -3
  19. package/src/__tests__/helpers/mock-dialogs.ts +26 -1
  20. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  21. package/src/__tests__/helpers/mock-istty.ts +27 -0
  22. package/src/__tests__/helpers/mock-kv.ts +9 -16
  23. package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
  24. package/src/__tests__/helpers/run-in-tmp.ts +10 -0
  25. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  26. package/src/__tests__/https-options.test.ts +5 -2
  27. package/src/__tests__/index.test.ts +27 -769
  28. package/src/__tests__/init.test.ts +1516 -0
  29. package/src/__tests__/jest.setup.ts +4 -4
  30. package/src/__tests__/kv.test.ts +172 -126
  31. package/src/__tests__/logger.test.ts +115 -0
  32. package/src/__tests__/pages.test.ts +311 -26
  33. package/src/__tests__/publish.test.ts +2946 -524
  34. package/src/__tests__/r2.test.ts +26 -10
  35. package/src/__tests__/route.test.ts +4 -4
  36. package/src/__tests__/secret.test.ts +55 -34
  37. package/src/__tests__/tail.test.ts +146 -20
  38. package/src/__tests__/user.test.ts +131 -0
  39. package/src/__tests__/whoami.test.tsx +26 -7
  40. package/src/bundle.ts +30 -3
  41. package/src/cfetch/internal.ts +9 -7
  42. package/src/cli.ts +5 -11
  43. package/src/config/README.md +7 -7
  44. package/src/config/config.ts +33 -78
  45. package/src/config/environment.ts +93 -4
  46. package/src/config/index.ts +15 -4
  47. package/src/config/validation-helpers.ts +202 -15
  48. package/src/config/validation.ts +530 -133
  49. package/src/config-cache.ts +45 -0
  50. package/src/create-worker-preview.ts +22 -13
  51. package/src/create-worker-upload-form.ts +32 -9
  52. package/src/dev/dev-vars.ts +36 -0
  53. package/src/dev/dev.tsx +136 -75
  54. package/src/dev/local.tsx +196 -125
  55. package/src/dev/remote.tsx +25 -13
  56. package/src/dev/use-esbuild.ts +23 -5
  57. package/src/dialogs.tsx +16 -6
  58. package/src/entry.ts +119 -11
  59. package/src/environment-variables.ts +3 -1
  60. package/src/errors.ts +11 -0
  61. package/src/https-options.ts +3 -2
  62. package/src/index.tsx +739 -400
  63. package/src/inspect.ts +17 -11
  64. package/src/intl-polyfill.d.ts +139 -0
  65. package/src/{kv/index.tsx → kv.ts} +8 -28
  66. package/src/logger.ts +66 -0
  67. package/src/miniflare-cli/README.md +30 -0
  68. package/src/miniflare-cli/enum-keys.ts +17 -0
  69. package/src/miniflare-cli/index.ts +41 -0
  70. package/src/miniflare-cli/request-context.ts +40 -0
  71. package/src/module-collection.ts +63 -58
  72. package/src/open-in-browser.ts +5 -4
  73. package/src/package-manager.ts +9 -8
  74. package/src/pages.tsx +883 -62
  75. package/src/parse.ts +1 -1
  76. package/src/proxy.ts +32 -21
  77. package/src/publish.ts +276 -161
  78. package/src/sites.tsx +88 -33
  79. package/src/tail/index.ts +2 -2
  80. package/src/tail/printing.ts +13 -8
  81. package/src/update-check.ts +19 -0
  82. package/src/user.tsx +231 -103
  83. package/src/whoami.tsx +2 -1
  84. package/src/worker.ts +15 -6
  85. package/templates/gitignore +171 -0
  86. package/templates/static-asset-facade.js +1 -1
  87. package/wrangler-dist/cli.js +22087 -49340
  88. package/src/__tests__/dev2.test.tsx +0 -152
  89. package/src/__tests__/helpers/mock-user.ts +0 -27
  90. package/src/__tests__/logout.test.ts +0 -50
  91. package/src/__tests__/sentry.test.ts +0 -146
  92. package/src/reporting.ts +0 -131
  93. package/wrangler-dist/cli.js.map +0 -7
package/src/sites.tsx CHANGED
@@ -7,29 +7,28 @@ import {
7
7
  listNamespaceKeys,
8
8
  listNamespaces,
9
9
  putBulkKeyValue,
10
+ deleteBulkKeyValue,
10
11
  } from "./kv";
12
+ import { logger } from "./logger";
11
13
  import type { Config } from "./config";
12
14
  import type { KeyValue } from "./kv";
13
15
  import type { XXHashAPI } from "xxhash-wasm";
14
16
 
15
17
  /** Paths to always ignore. */
16
- const ALWAYS_IGNORE = ["node_modules"];
17
- const HIDDEN_FILES_TO_INCLUDE = [
18
+ const ALWAYS_IGNORE = new Set(["node_modules"]);
19
+ const HIDDEN_FILES_TO_INCLUDE = new Set([
18
20
  ".well-known", // See https://datatracker.ietf.org/doc/html/rfc8615
19
- ];
21
+ ]);
20
22
 
21
23
  async function* getFilesInFolder(dirPath: string): AsyncIterable<string> {
22
24
  const files = await readdir(dirPath, { withFileTypes: true });
23
25
  for (const file of files) {
24
26
  // Skip files that we never want to process.
25
- if (ALWAYS_IGNORE.some((p) => file.name === p)) {
27
+ if (ALWAYS_IGNORE.has(file.name)) {
26
28
  continue;
27
29
  }
28
30
  // Skip hidden files (starting with .) except for some special ones
29
- if (
30
- file.name.startsWith(".") &&
31
- !HIDDEN_FILES_TO_INCLUDE.some((p) => file.name === p)
32
- ) {
31
+ if (file.name.startsWith(".") && !HIDDEN_FILES_TO_INCLUDE.has(file.name)) {
33
32
  continue;
34
33
  }
35
34
  // TODO: follow symlinks??
@@ -84,7 +83,7 @@ async function createKVNamespaceIfNotAlreadyExisting(
84
83
 
85
84
  // else we make the namespace
86
85
  const id = await createNamespace(accountId, title);
87
- console.log(`🌀 Created namespace for Workers Site "${title}"`);
86
+ logger.log(`🌀 Created namespace for Workers Site "${title}"`);
88
87
 
89
88
  return {
90
89
  created: true,
@@ -107,7 +106,8 @@ export async function syncAssets(
107
106
  accountId: string,
108
107
  scriptName: string,
109
108
  siteAssets: AssetPaths | undefined,
110
- preview: boolean
109
+ preview: boolean,
110
+ dryRun: boolean | undefined
111
111
  ): Promise<{
112
112
  manifest: { [filePath: string]: string } | undefined;
113
113
  namespace: string | undefined;
@@ -116,55 +116,87 @@ export async function syncAssets(
116
116
  return { manifest: undefined, namespace: undefined };
117
117
  }
118
118
 
119
+ if (dryRun) {
120
+ logger.log("(Note: doing a dry run, not uploading or deleting anything.)");
121
+ return { manifest: undefined, namespace: undefined };
122
+ }
123
+
119
124
  const title = `__${scriptName}-workers_sites_assets${
120
125
  preview ? "_preview" : ""
121
126
  }`;
127
+
122
128
  const { id: namespace } = await createKVNamespaceIfNotAlreadyExisting(
123
129
  title,
124
130
  accountId
125
131
  );
126
132
 
127
133
  // let's get all the keys in this namespace
128
- const result = await listNamespaceKeys(accountId, namespace);
129
- const keys = new Set(result.map((x) => x.name));
134
+ const namespaceKeysResponse = await listNamespaceKeys(accountId, namespace);
135
+ const namespaceKeys = new Set(namespaceKeysResponse.map((x) => x.name));
130
136
 
131
137
  const manifest: Record<string, string> = {};
132
- const upload: KeyValue[] = [];
138
+ const toUpload: KeyValue[] = [];
133
139
 
134
140
  const include = createPatternMatcher(siteAssets.includePatterns, false);
135
141
  const exclude = createPatternMatcher(siteAssets.excludePatterns, true);
136
142
  const hasher = await xxhash();
137
143
 
138
- // TODO: this can be more efficient by parallelising
139
- for await (const file of getFilesInFolder(siteAssets.baseDirectory)) {
140
- if (!include(file)) {
144
+ const assetDirectory = path.join(
145
+ siteAssets.baseDirectory,
146
+ siteAssets.assetDirectory
147
+ );
148
+ for await (const absAssetFile of getFilesInFolder(assetDirectory)) {
149
+ const assetFile = path.relative(siteAssets.baseDirectory, absAssetFile);
150
+ if (!include(assetFile)) {
141
151
  continue;
142
152
  }
143
- if (exclude(file)) {
153
+ if (exclude(assetFile)) {
144
154
  continue;
145
155
  }
146
156
 
147
- await validateAssetSize(file);
148
- console.log(`reading ${file}...`);
149
- const content = await readFile(file, "base64");
157
+ await validateAssetSize(absAssetFile, assetFile);
158
+ logger.log(`Reading ${assetFile}...`);
159
+ const content = await readFile(absAssetFile, "base64");
150
160
 
151
- const assetKey = await hashAsset(hasher, file, content);
161
+ const assetKey = hashAsset(hasher, assetFile, content);
152
162
  validateAssetKey(assetKey);
153
163
 
154
164
  // now put each of the files into kv
155
- if (!keys.has(assetKey)) {
156
- console.log(`uploading as ${assetKey}...`);
157
- upload.push({
165
+ if (!namespaceKeys.has(assetKey)) {
166
+ logger.log(`Uploading as ${assetKey}...`);
167
+ toUpload.push({
158
168
  key: assetKey,
159
169
  value: content,
160
170
  base64: true,
161
171
  });
162
172
  } else {
163
- console.log(`skipping - already uploaded`);
173
+ logger.log(`Skipping - already uploaded.`);
164
174
  }
165
- manifest[path.relative(siteAssets.baseDirectory, file)] = assetKey;
175
+
176
+ // remove the key from the set so we know what we've already uploaded
177
+ namespaceKeys.delete(assetKey);
178
+ manifest[path.relative(siteAssets.assetDirectory, absAssetFile)] = assetKey;
179
+ }
180
+
181
+ // keys now contains all the files we're deleting
182
+ for (const key of namespaceKeys) {
183
+ logger.log(`Deleting ${key} from the asset store...`);
166
184
  }
167
- await putBulkKeyValue(accountId, namespace, upload, () => {});
185
+
186
+ await Promise.all([
187
+ // upload all the new assets
188
+ putBulkKeyValue(accountId, namespace, toUpload, () => {}),
189
+ // delete all the unused assets
190
+ deleteBulkKeyValue(
191
+ accountId,
192
+ namespace,
193
+ Array.from(namespaceKeys),
194
+ () => {}
195
+ ),
196
+ ]);
197
+
198
+ logger.log("↗️ Done syncing assets");
199
+
168
200
  return { manifest, namespace };
169
201
  }
170
202
 
@@ -180,11 +212,14 @@ function createPatternMatcher(
180
212
  }
181
213
  }
182
214
 
183
- async function validateAssetSize(filePath: string) {
184
- const { size } = await stat(filePath);
215
+ async function validateAssetSize(
216
+ absFilePath: string,
217
+ relativeFilePath: string
218
+ ) {
219
+ const { size } = await stat(absFilePath);
185
220
  if (size > 25 * 1024 * 1024) {
186
221
  throw new Error(
187
- `File ${filePath} is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits`
222
+ `File ${relativeFilePath} is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits`
188
223
  );
189
224
  }
190
225
  }
@@ -210,8 +245,23 @@ function urlSafe(filePath: string): string {
210
245
  * Information about the assets that should be uploaded
211
246
  */
212
247
  export interface AssetPaths {
248
+ /**
249
+ * Absolute path to the root of the project.
250
+ *
251
+ * This is the directory containing wrangler.toml or cwd if no config.
252
+ */
213
253
  baseDirectory: string;
254
+ /**
255
+ * The path to the assets directory, relative to the `baseDirectory`.
256
+ */
257
+ assetDirectory: string;
258
+ /**
259
+ * An array of patterns that match files that should be uploaded.
260
+ */
214
261
  includePatterns: string[];
262
+ /**
263
+ * An array of patterns that match files that should not be uploaded.
264
+ */
215
265
  excludePatterns: string[];
216
266
  }
217
267
 
@@ -224,13 +274,18 @@ export interface AssetPaths {
224
274
  */
225
275
  export function getAssetPaths(
226
276
  config: Config,
227
- baseDirectory = config.site?.bucket,
277
+ assetDirectory = config.site?.bucket,
228
278
  includePatterns = config.site?.include ?? [],
229
279
  excludePatterns = config.site?.exclude ?? []
230
- ): undefined | AssetPaths {
231
- return baseDirectory
280
+ ): AssetPaths | undefined {
281
+ const baseDirectory = path.resolve(
282
+ path.dirname(config.configPath ?? "wrangler.toml")
283
+ );
284
+
285
+ return assetDirectory
232
286
  ? {
233
287
  baseDirectory,
288
+ assetDirectory,
234
289
  includePatterns,
235
290
  excludePatterns,
236
291
  }
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
+ }