wrangler 0.0.21 → 0.0.24

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 (40) hide show
  1. package/README.md +1 -1
  2. package/bin/wrangler.js +10 -1
  3. package/miniflare-dist/index.mjs +79 -0
  4. package/miniflare-dist/index.mjs.map +7 -0
  5. package/package.json +3 -4
  6. package/src/__tests__/configuration.test.ts +522 -300
  7. package/src/__tests__/dev.test.tsx +99 -3
  8. package/src/__tests__/guess-worker-format.test.ts +10 -0
  9. package/src/__tests__/index.test.ts +3 -11
  10. package/src/__tests__/kv.test.ts +16 -18
  11. package/src/__tests__/pages.test.ts +48 -10
  12. package/src/__tests__/publish.test.ts +1319 -421
  13. package/src/__tests__/r2.test.ts +4 -4
  14. package/src/__tests__/tail.test.ts +29 -4
  15. package/src/bundle.ts +15 -2
  16. package/src/config/README.md +6 -6
  17. package/src/config/config.ts +16 -77
  18. package/src/config/environment.ts +67 -1
  19. package/src/config/index.ts +13 -3
  20. package/src/config/validation-helpers.ts +105 -19
  21. package/src/config/validation.ts +272 -97
  22. package/src/dev/dev.tsx +72 -42
  23. package/src/dev/local.tsx +103 -118
  24. package/src/dev/use-esbuild.ts +4 -0
  25. package/src/entry.ts +84 -3
  26. package/src/index.tsx +137 -113
  27. package/src/intl-polyfill.d.ts +139 -0
  28. package/src/kv.ts +7 -27
  29. package/src/miniflare-cli/README.md +30 -0
  30. package/src/miniflare-cli/enum-keys.ts +17 -0
  31. package/src/miniflare-cli/index.ts +37 -0
  32. package/src/module-collection.ts +58 -53
  33. package/src/pages.tsx +26 -10
  34. package/src/publish.ts +17 -21
  35. package/src/sites.tsx +59 -52
  36. package/src/tail/printing.ts +2 -2
  37. package/src/user.tsx +12 -3
  38. package/wrangler-dist/cli.js +836 -649
  39. package/wrangler-dist/cli.js.map +2 -2
  40. package/src/__tests__/dev2.test.tsx +0 -154
package/src/sites.tsx CHANGED
@@ -4,13 +4,13 @@ import ignore from "ignore";
4
4
  import xxhash from "xxhash-wasm";
5
5
  import {
6
6
  createNamespace,
7
- getKeyValue,
8
7
  listNamespaceKeys,
9
8
  listNamespaces,
10
9
  putBulkKeyValue,
10
+ deleteBulkKeyValue,
11
11
  } from "./kv";
12
12
  import type { Config } from "./config";
13
- import type { KeyValue, NamespaceKeyInfo } from "./kv";
13
+ import type { KeyValue } from "./kv";
14
14
  import type { XXHashAPI } from "xxhash-wasm";
15
15
 
16
16
  /** Paths to always ignore. */
@@ -18,7 +18,6 @@ const ALWAYS_IGNORE = new Set(["node_modules"]);
18
18
  const HIDDEN_FILES_TO_INCLUDE = new Set([
19
19
  ".well-known", // See https://datatracker.ietf.org/doc/html/rfc8615
20
20
  ]);
21
- const FIVE_MINUTES = 60 * 5; // expressed in seconds, since that's what the api expects
22
21
 
23
22
  async function* getFilesInFolder(dirPath: string): AsyncIterable<string> {
24
23
  const files = await readdir(dirPath, { withFileTypes: true });
@@ -125,10 +124,7 @@ export async function syncAssets(
125
124
 
126
125
  // let's get all the keys in this namespace
127
126
  const result = await listNamespaceKeys(accountId, namespace);
128
- const keyMap = result.reduce<Record<string, NamespaceKeyInfo>>(
129
- (km, key) => Object.assign(km, { [key.name]: key }),
130
- {}
131
- );
127
+ const keys = new Set(result.map((x) => x.name));
132
128
 
133
129
  const manifest: Record<string, string> = {};
134
130
  const toUpload: KeyValue[] = [];
@@ -137,23 +133,28 @@ export async function syncAssets(
137
133
  const exclude = createPatternMatcher(siteAssets.excludePatterns, true);
138
134
  const hasher = await xxhash();
139
135
 
140
- for await (const file of getFilesInFolder(siteAssets.baseDirectory)) {
141
- if (!include(file)) {
136
+ const assetDirectory = path.join(
137
+ siteAssets.baseDirectory,
138
+ siteAssets.assetDirectory
139
+ );
140
+ for await (const absAssetFile of getFilesInFolder(assetDirectory)) {
141
+ const assetFile = path.relative(siteAssets.baseDirectory, absAssetFile);
142
+ if (!include(assetFile)) {
142
143
  continue;
143
144
  }
144
- if (exclude(file)) {
145
+ if (exclude(assetFile)) {
145
146
  continue;
146
147
  }
147
148
 
148
- await validateAssetSize(file);
149
- console.log(`reading ${file}...`);
150
- const content = await readFile(file, "base64");
149
+ await validateAssetSize(absAssetFile, assetFile);
150
+ console.log(`reading ${assetFile}...`);
151
+ const content = await readFile(absAssetFile, "base64");
151
152
 
152
- const assetKey = hashAsset(hasher, file, content);
153
+ const assetKey = hashAsset(hasher, assetFile, content);
153
154
  validateAssetKey(assetKey);
154
155
 
155
156
  // now put each of the files into kv
156
- if (!(assetKey in keyMap) || keyMap[assetKey].expiration) {
157
+ if (!keys.has(assetKey)) {
157
158
  console.log(`uploading as ${assetKey}...`);
158
159
  toUpload.push({
159
160
  key: assetKey,
@@ -163,43 +164,26 @@ export async function syncAssets(
163
164
  } else {
164
165
  console.log(`skipping - already uploaded`);
165
166
  }
166
- // remove the key from the set so we know we've seen it
167
- delete keyMap[assetKey];
168
- manifest[path.relative(siteAssets.baseDirectory, file)] = assetKey;
169
- }
170
167
 
171
- // `keyMap` now contains the assets that we need to expire
168
+ // remove the key from the set so we know what we've already uploaded
169
+ keys.delete(assetKey);
170
+ manifest[path.relative(siteAssets.assetDirectory, absAssetFile)] = assetKey;
171
+ }
172
172
 
173
- let toExpire: KeyValue[] = [];
174
- for (const asset of Object.values(keyMap)) {
175
- if (!asset.expiration) {
176
- console.log(`expiring unused ${asset.name}...`);
177
- toExpire.push({
178
- key: asset.name,
179
- value: "", // we'll fill all the values in one go
180
- // we use expiration_ttl, since we can't trust the time
181
- // that a deploy source might provide (eg - https://github.com/cloudflare/wrangler/issues/2224)
182
- expiration_ttl: FIVE_MINUTES,
183
- base64: true,
184
- });
185
- }
173
+ // keys now contains all the files we're deleting
174
+ for (const key of keys) {
175
+ console.log(`deleting ${key} from the asset store...`);
186
176
  }
187
177
 
188
- // TODO: batch these in groups if it causes problems
189
- toExpire = await Promise.all(
190
- toExpire.map(async (asset) => ({
191
- ...asset,
192
- // it would be great if we didn't have to do this fetch at all
193
- value: await getKeyValue(accountId, namespace, asset.key),
194
- }))
195
- );
178
+ await Promise.all([
179
+ // upload all the new assets
180
+ putBulkKeyValue(accountId, namespace, toUpload, () => {}),
181
+ // delete all the unused assets
182
+ deleteBulkKeyValue(accountId, namespace, Array.from(keys), () => {}),
183
+ ]);
184
+
185
+ console.log("↗️ Done syncing assets");
196
186
 
197
- await putBulkKeyValue(
198
- accountId,
199
- namespace,
200
- toUpload.concat(toExpire),
201
- () => {}
202
- );
203
187
  return { manifest, namespace };
204
188
  }
205
189
 
@@ -215,11 +199,14 @@ function createPatternMatcher(
215
199
  }
216
200
  }
217
201
 
218
- async function validateAssetSize(filePath: string) {
219
- const { size } = await stat(filePath);
202
+ async function validateAssetSize(
203
+ absFilePath: string,
204
+ relativeFilePath: string
205
+ ) {
206
+ const { size } = await stat(absFilePath);
220
207
  if (size > 25 * 1024 * 1024) {
221
208
  throw new Error(
222
- `File ${filePath} is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits`
209
+ `File ${relativeFilePath} is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits`
223
210
  );
224
211
  }
225
212
  }
@@ -245,8 +232,23 @@ function urlSafe(filePath: string): string {
245
232
  * Information about the assets that should be uploaded
246
233
  */
247
234
  export interface AssetPaths {
235
+ /**
236
+ * Absolute path to the root of the project.
237
+ *
238
+ * This is the directory containing wrangler.toml or cwd if no config.
239
+ */
248
240
  baseDirectory: string;
241
+ /**
242
+ * The path to the assets directory, relative to the `baseDirectory`.
243
+ */
244
+ assetDirectory: string;
245
+ /**
246
+ * An array of patterns that match files that should be uploaded.
247
+ */
249
248
  includePatterns: string[];
249
+ /**
250
+ * An array of patterns that match files that should not be uploaded.
251
+ */
250
252
  excludePatterns: string[];
251
253
  }
252
254
 
@@ -259,13 +261,18 @@ export interface AssetPaths {
259
261
  */
260
262
  export function getAssetPaths(
261
263
  config: Config,
262
- baseDirectory = config.site?.bucket,
264
+ assetDirectory = config.site?.bucket,
263
265
  includePatterns = config.site?.include ?? [],
264
266
  excludePatterns = config.site?.exclude ?? []
265
267
  ): undefined | AssetPaths {
266
- return baseDirectory
268
+ const baseDirectory = path.resolve(
269
+ path.dirname(config.configPath ?? "wrangler.toml")
270
+ );
271
+
272
+ return assetDirectory
267
273
  ? {
268
274
  baseDirectory,
275
+ assetDirectory,
269
276
  includePatterns,
270
277
  excludePatterns,
271
278
  }
@@ -24,13 +24,13 @@ export function prettyPrintLogs(data: WebSocket.RawData): void {
24
24
 
25
25
  if (eventMessage.logs.length > 0) {
26
26
  eventMessage.logs.forEach(({ level, message }) => {
27
- console.log(` (${level}) ${message}`);
27
+ console.log(` (${level})`, message);
28
28
  });
29
29
  }
30
30
 
31
31
  if (eventMessage.exceptions.length > 0) {
32
32
  eventMessage.exceptions.forEach(({ name, message }) => {
33
- console.error(` ${name}: ${message}`);
33
+ console.error(` ${name}:`, message);
34
34
  });
35
35
  }
36
36
  }
package/src/user.tsx CHANGED
@@ -610,8 +610,8 @@ export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
610
610
  `?response_type=code&` +
611
611
  `client_id=${encodeURIComponent(CLIENT_ID)}&` +
612
612
  `redirect_uri=${encodeURIComponent(CALLBACK_URL)}&` +
613
- // @ts-expect-error we add offline_access manually
614
- `scope=${encodeURIComponent(scopes.concat("offline_access").join(" "))}&` +
613
+ // we add offline_access manually for every request
614
+ `scope=${encodeURIComponent([...scopes, "offline_access"].join(" "))}&` +
615
615
  `state=${stateQueryParam}&` +
616
616
  `code_challenge=${encodeURIComponent(codeChallenge)}&` +
617
617
  `code_challenge_method=S256`
@@ -864,7 +864,16 @@ export async function loginOrRefreshIfRequired(
864
864
  // If we are not interactive, we cannot ask the user to login
865
865
  return isInteractive && (await login());
866
866
  } else if (isAccessTokenExpired()) {
867
- return await refreshToken();
867
+ // We're logged in, but the refresh token seems to have expired,
868
+ // so let's try to refresh it
869
+ const didRefresh = await refreshToken();
870
+ if (didRefresh) {
871
+ // The token was refreshed, so we're done here
872
+ return true;
873
+ } else {
874
+ // If the refresh token isn't valid, then we ask the user to login again
875
+ return isInteractive && (await login());
876
+ }
868
877
  } else {
869
878
  return true;
870
879
  }