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.
- package/README.md +25 -14
- package/bin/wrangler.js +13 -1
- package/miniflare-dist/index.mjs +4886 -0
- package/package.json +33 -25
- package/pages/functions/buildPlugin.ts +92 -0
- package/pages/functions/buildWorker.ts +80 -4
- package/pages/functions/filepath-routing.test.ts +136 -25
- package/pages/functions/filepath-routing.ts +14 -3
- package/pages/functions/routes.ts +6 -1
- package/pages/functions/template-plugin.ts +146 -0
- package/pages/functions/template-worker.ts +21 -8
- package/src/__tests__/config-cache.test.ts +36 -0
- package/src/__tests__/configuration.test.ts +1179 -271
- package/src/__tests__/dev.test.tsx +382 -25
- package/src/__tests__/guess-worker-format.test.ts +28 -1
- package/src/__tests__/helpers/mock-account-id.ts +4 -0
- package/src/__tests__/helpers/mock-cfetch.ts +5 -1
- package/src/__tests__/helpers/mock-console.ts +20 -3
- package/src/__tests__/helpers/mock-dialogs.ts +26 -1
- package/src/__tests__/helpers/mock-http-server.ts +46 -0
- package/src/__tests__/helpers/mock-istty.ts +27 -0
- package/src/__tests__/helpers/mock-kv.ts +9 -16
- package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
- package/src/__tests__/helpers/run-in-tmp.ts +10 -0
- package/src/__tests__/helpers/write-worker-source.ts +31 -0
- package/src/__tests__/https-options.test.ts +5 -2
- package/src/__tests__/index.test.ts +27 -769
- package/src/__tests__/init.test.ts +1516 -0
- package/src/__tests__/jest.setup.ts +4 -4
- package/src/__tests__/kv.test.ts +172 -126
- package/src/__tests__/logger.test.ts +115 -0
- package/src/__tests__/pages.test.ts +311 -26
- package/src/__tests__/publish.test.ts +2946 -524
- package/src/__tests__/r2.test.ts +26 -10
- package/src/__tests__/route.test.ts +4 -4
- package/src/__tests__/secret.test.ts +55 -34
- package/src/__tests__/tail.test.ts +146 -20
- package/src/__tests__/user.test.ts +131 -0
- package/src/__tests__/whoami.test.tsx +26 -7
- package/src/bundle.ts +30 -3
- package/src/cfetch/internal.ts +9 -7
- package/src/cli.ts +5 -11
- package/src/config/README.md +7 -7
- package/src/config/config.ts +33 -78
- package/src/config/environment.ts +93 -4
- package/src/config/index.ts +15 -4
- package/src/config/validation-helpers.ts +202 -15
- package/src/config/validation.ts +530 -133
- package/src/config-cache.ts +45 -0
- package/src/create-worker-preview.ts +22 -13
- package/src/create-worker-upload-form.ts +32 -9
- package/src/dev/dev-vars.ts +36 -0
- package/src/dev/dev.tsx +136 -75
- package/src/dev/local.tsx +196 -125
- package/src/dev/remote.tsx +25 -13
- package/src/dev/use-esbuild.ts +23 -5
- package/src/dialogs.tsx +16 -6
- package/src/entry.ts +119 -11
- package/src/environment-variables.ts +3 -1
- package/src/errors.ts +11 -0
- package/src/https-options.ts +3 -2
- package/src/index.tsx +739 -400
- package/src/inspect.ts +17 -11
- package/src/intl-polyfill.d.ts +139 -0
- package/src/{kv/index.tsx → kv.ts} +8 -28
- package/src/logger.ts +66 -0
- package/src/miniflare-cli/README.md +30 -0
- package/src/miniflare-cli/enum-keys.ts +17 -0
- package/src/miniflare-cli/index.ts +41 -0
- package/src/miniflare-cli/request-context.ts +40 -0
- package/src/module-collection.ts +63 -58
- package/src/open-in-browser.ts +5 -4
- package/src/package-manager.ts +9 -8
- package/src/pages.tsx +883 -62
- package/src/parse.ts +1 -1
- package/src/proxy.ts +32 -21
- package/src/publish.ts +276 -161
- package/src/sites.tsx +88 -33
- package/src/tail/index.ts +2 -2
- package/src/tail/printing.ts +13 -8
- package/src/update-check.ts +19 -0
- package/src/user.tsx +231 -103
- package/src/whoami.tsx +2 -1
- package/src/worker.ts +15 -6
- package/templates/gitignore +171 -0
- package/templates/static-asset-facade.js +1 -1
- package/wrangler-dist/cli.js +22087 -49340
- package/src/__tests__/dev2.test.tsx +0 -152
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/__tests__/logout.test.ts +0 -50
- package/src/__tests__/sentry.test.ts +0 -146
- package/src/reporting.ts +0 -131
- 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.
|
|
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
|
-
|
|
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
|
|
129
|
-
const
|
|
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
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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(
|
|
153
|
+
if (exclude(assetFile)) {
|
|
144
154
|
continue;
|
|
145
155
|
}
|
|
146
156
|
|
|
147
|
-
await validateAssetSize(
|
|
148
|
-
|
|
149
|
-
const content = await readFile(
|
|
157
|
+
await validateAssetSize(absAssetFile, assetFile);
|
|
158
|
+
logger.log(`Reading ${assetFile}...`);
|
|
159
|
+
const content = await readFile(absAssetFile, "base64");
|
|
150
160
|
|
|
151
|
-
const assetKey =
|
|
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 (!
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
173
|
+
logger.log(`Skipping - already uploaded.`);
|
|
164
174
|
}
|
|
165
|
-
|
|
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
|
-
|
|
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(
|
|
184
|
-
|
|
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 ${
|
|
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
|
-
|
|
277
|
+
assetDirectory = config.site?.bucket,
|
|
228
278
|
includePatterns = config.site?.include ?? [],
|
|
229
279
|
excludePatterns = config.site?.exclude ?? []
|
|
230
|
-
):
|
|
231
|
-
|
|
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
|
/**
|
package/src/tail/printing.ts
CHANGED
|
@@ -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
|
-
|
|
16
|
+
logger.log(`"${cronPattern}" @ ${datetime} - ${outcome}`);
|
|
16
17
|
} else {
|
|
17
|
-
const requestMethod = eventMessage.event
|
|
18
|
-
const url = eventMessage.event
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|