wrangler 2.0.24 → 2.0.25
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/miniflare-dist/index.mjs +130 -16
- package/package.json +1 -1
- package/src/__tests__/configuration.test.ts +1 -1
- package/src/__tests__/dev.test.tsx +26 -4
- package/src/__tests__/helpers/mock-cfetch.ts +2 -2
- package/src/__tests__/r2.test.ts +18 -0
- package/src/__tests__/tail.test.ts +93 -39
- package/src/api/dev.ts +6 -0
- package/src/bundle.ts +3 -2
- package/src/config/config.ts +1 -1
- package/src/config/validation.ts +1 -1
- package/src/dev/dev.tsx +12 -2
- package/src/dev/local.tsx +69 -5
- package/src/dev/use-esbuild.ts +3 -0
- package/src/dev-registry.tsx +3 -0
- package/src/dev.tsx +26 -17
- package/src/index.tsx +51 -21
- package/src/inspect.ts +1 -4
- package/src/miniflare-cli/assets.ts +19 -16
- package/src/miniflare-cli/index.ts +121 -2
- package/src/pages/build.tsx +36 -28
- package/src/pages/constants.ts +3 -0
- package/src/pages/deployments.tsx +9 -9
- package/src/pages/dev.tsx +85 -27
- package/src/pages/functions/buildPlugin.ts +4 -0
- package/src/pages/functions/buildWorker.ts +4 -0
- package/src/pages/functions/routes-consolidation.test.ts +66 -0
- package/src/pages/functions/routes-consolidation.ts +29 -0
- package/src/pages/functions/routes-transformation.test.ts +271 -0
- package/src/pages/functions/routes-transformation.ts +125 -0
- package/src/pages/projects.tsx +9 -3
- package/src/pages/publish.tsx +56 -14
- package/src/pages/types.ts +9 -0
- package/src/pages/upload.tsx +6 -8
- package/src/r2.ts +13 -0
- package/src/tail/index.ts +15 -2
- package/src/tail/printing.ts +41 -3
- package/wrangler-dist/cli.d.ts +6 -0
- package/wrangler-dist/cli.js +385 -89
package/miniflare-dist/index.mjs
CHANGED
|
@@ -105,7 +105,18 @@ var require_mime = __commonJS({
|
|
|
105
105
|
});
|
|
106
106
|
|
|
107
107
|
// src/miniflare-cli/index.ts
|
|
108
|
-
import {
|
|
108
|
+
import { fetch } from "@miniflare/core";
|
|
109
|
+
import {
|
|
110
|
+
DurableObjectNamespace,
|
|
111
|
+
DurableObjectStub
|
|
112
|
+
} from "@miniflare/durable-objects";
|
|
113
|
+
import {
|
|
114
|
+
Log,
|
|
115
|
+
LogLevel,
|
|
116
|
+
Miniflare,
|
|
117
|
+
Response as MiniflareResponse,
|
|
118
|
+
Request as MiniflareRequest
|
|
119
|
+
} from "miniflare";
|
|
109
120
|
|
|
110
121
|
// ../../node_modules/yargs/lib/platform-shims/esm.mjs
|
|
111
122
|
import { notStrictEqual, strictEqual } from "assert";
|
|
@@ -4914,6 +4925,14 @@ function isYargsInstance(y) {
|
|
|
4914
4925
|
var Yargs = YargsFactory(esm_default);
|
|
4915
4926
|
var yargs_default = Yargs;
|
|
4916
4927
|
|
|
4928
|
+
// src/errors.ts
|
|
4929
|
+
var FatalError = class extends Error {
|
|
4930
|
+
constructor(message, code) {
|
|
4931
|
+
super(message);
|
|
4932
|
+
this.code = code;
|
|
4933
|
+
}
|
|
4934
|
+
};
|
|
4935
|
+
|
|
4917
4936
|
// src/miniflare-cli/assets.ts
|
|
4918
4937
|
var import_mime = __toESM(require_mime());
|
|
4919
4938
|
import { existsSync, lstatSync, readFileSync as readFileSync4 } from "node:fs";
|
|
@@ -5147,6 +5166,11 @@ async function generateAssetsFetch(directory, log) {
|
|
|
5147
5166
|
};
|
|
5148
5167
|
const generateResponse = (request) => {
|
|
5149
5168
|
const url = new URL(request.url);
|
|
5169
|
+
let assetName = url.pathname;
|
|
5170
|
+
try {
|
|
5171
|
+
assetName = decodeURIComponent(url.pathname);
|
|
5172
|
+
} catch {
|
|
5173
|
+
}
|
|
5150
5174
|
const deconstructedResponse = {
|
|
5151
5175
|
status: 200,
|
|
5152
5176
|
headers: new Headers(),
|
|
@@ -5176,7 +5200,7 @@ async function generateAssetsFetch(directory, log) {
|
|
|
5176
5200
|
return deconstructedResponse;
|
|
5177
5201
|
}
|
|
5178
5202
|
const notFound = () => {
|
|
5179
|
-
let cwd =
|
|
5203
|
+
let cwd = assetName;
|
|
5180
5204
|
while (cwd) {
|
|
5181
5205
|
cwd = cwd.slice(0, cwd.lastIndexOf("/"));
|
|
5182
5206
|
if (asset = getAsset(`${cwd}/404.html`)) {
|
|
@@ -5201,34 +5225,34 @@ async function generateAssetsFetch(directory, log) {
|
|
|
5201
5225
|
return deconstructedResponse;
|
|
5202
5226
|
};
|
|
5203
5227
|
let asset;
|
|
5204
|
-
if (
|
|
5205
|
-
if (asset = getAsset(`${
|
|
5228
|
+
if (assetName.endsWith("/")) {
|
|
5229
|
+
if (asset = getAsset(`${assetName}/index.html`)) {
|
|
5206
5230
|
deconstructedResponse.body = serveAsset(asset);
|
|
5207
5231
|
deconstructedResponse.headers.set(
|
|
5208
5232
|
"Content-Type",
|
|
5209
5233
|
(0, import_mime.getType)(asset) || "application/octet-stream"
|
|
5210
5234
|
);
|
|
5211
5235
|
return deconstructedResponse;
|
|
5212
|
-
} else if (asset = getAsset(`${
|
|
5236
|
+
} else if (asset = getAsset(`${assetName.replace(/\/$/, ".html")}`)) {
|
|
5213
5237
|
deconstructedResponse.status = 301;
|
|
5214
5238
|
deconstructedResponse.headers.set(
|
|
5215
5239
|
"Location",
|
|
5216
|
-
`${
|
|
5240
|
+
`${assetName.slice(0, -1)}${url.search}`
|
|
5217
5241
|
);
|
|
5218
5242
|
return deconstructedResponse;
|
|
5219
5243
|
}
|
|
5220
5244
|
}
|
|
5221
|
-
if (
|
|
5245
|
+
if (assetName.endsWith("/index")) {
|
|
5222
5246
|
deconstructedResponse.status = 301;
|
|
5223
5247
|
deconstructedResponse.headers.set(
|
|
5224
5248
|
"Location",
|
|
5225
|
-
`${
|
|
5249
|
+
`${assetName.slice(0, -"index".length)}${url.search}`
|
|
5226
5250
|
);
|
|
5227
5251
|
return deconstructedResponse;
|
|
5228
5252
|
}
|
|
5229
|
-
if (asset = getAsset(
|
|
5230
|
-
if (
|
|
5231
|
-
const extensionlessPath =
|
|
5253
|
+
if (asset = getAsset(assetName)) {
|
|
5254
|
+
if (assetName.endsWith(".html")) {
|
|
5255
|
+
const extensionlessPath = assetName.slice(0, -".html".length);
|
|
5232
5256
|
if (getAsset(extensionlessPath) || extensionlessPath === "/") {
|
|
5233
5257
|
deconstructedResponse.body = serveAsset(asset);
|
|
5234
5258
|
deconstructedResponse.headers.set(
|
|
@@ -5252,11 +5276,11 @@ async function generateAssetsFetch(directory, log) {
|
|
|
5252
5276
|
);
|
|
5253
5277
|
return deconstructedResponse;
|
|
5254
5278
|
}
|
|
5255
|
-
} else if (hasFileExtension(
|
|
5279
|
+
} else if (hasFileExtension(assetName)) {
|
|
5256
5280
|
notFound();
|
|
5257
5281
|
return deconstructedResponse;
|
|
5258
5282
|
}
|
|
5259
|
-
if (asset = getAsset(`${
|
|
5283
|
+
if (asset = getAsset(`${assetName}.html`)) {
|
|
5260
5284
|
deconstructedResponse.body = serveAsset(asset);
|
|
5261
5285
|
deconstructedResponse.headers.set(
|
|
5262
5286
|
"Content-Type",
|
|
@@ -5264,11 +5288,11 @@ async function generateAssetsFetch(directory, log) {
|
|
|
5264
5288
|
);
|
|
5265
5289
|
return deconstructedResponse;
|
|
5266
5290
|
}
|
|
5267
|
-
if (asset = getAsset(`${
|
|
5291
|
+
if (asset = getAsset(`${assetName}/index.html`)) {
|
|
5268
5292
|
deconstructedResponse.status = 301;
|
|
5269
5293
|
deconstructedResponse.headers.set(
|
|
5270
5294
|
"Location",
|
|
5271
|
-
`${
|
|
5295
|
+
`${assetName}/${url.search}`
|
|
5272
5296
|
);
|
|
5273
5297
|
return deconstructedResponse;
|
|
5274
5298
|
} else {
|
|
@@ -5369,7 +5393,40 @@ async function main() {
|
|
|
5369
5393
|
if (logLevel > LogLevel.INFO) {
|
|
5370
5394
|
console.log("OPTIONS:\n", JSON.stringify(config, null, 2));
|
|
5371
5395
|
}
|
|
5396
|
+
config.bindings = {
|
|
5397
|
+
...config.bindings,
|
|
5398
|
+
...Object.fromEntries(
|
|
5399
|
+
Object.entries(
|
|
5400
|
+
config.externalDurableObjects
|
|
5401
|
+
).map(([binding, { name, host, port }]) => {
|
|
5402
|
+
const factory = () => {
|
|
5403
|
+
throw new FatalError(
|
|
5404
|
+
"An external Durable Object instance's state has somehow been attempted to be accessed.",
|
|
5405
|
+
1
|
|
5406
|
+
);
|
|
5407
|
+
};
|
|
5408
|
+
const namespace = new DurableObjectNamespace(name, factory);
|
|
5409
|
+
namespace.get = (id) => {
|
|
5410
|
+
const stub = new DurableObjectStub(factory, id);
|
|
5411
|
+
stub.fetch = (...reqArgs) => {
|
|
5412
|
+
const url = `http://${host}${port ? `:${port}` : ""}`;
|
|
5413
|
+
const request = new MiniflareRequest(
|
|
5414
|
+
url,
|
|
5415
|
+
new MiniflareRequest(...reqArgs)
|
|
5416
|
+
);
|
|
5417
|
+
request.headers.set("x-miniflare-durable-object-name", name);
|
|
5418
|
+
request.headers.set("x-miniflare-durable-object-id", id.toString());
|
|
5419
|
+
return fetch(request);
|
|
5420
|
+
};
|
|
5421
|
+
return stub;
|
|
5422
|
+
};
|
|
5423
|
+
return [binding, namespace];
|
|
5424
|
+
})
|
|
5425
|
+
)
|
|
5426
|
+
};
|
|
5372
5427
|
let mf;
|
|
5428
|
+
let durableObjectsMf = void 0;
|
|
5429
|
+
let durableObjectsMfPort = void 0;
|
|
5373
5430
|
try {
|
|
5374
5431
|
if (args._[1]) {
|
|
5375
5432
|
const opts = JSON.parse(
|
|
@@ -5393,11 +5450,68 @@ async function main() {
|
|
|
5393
5450
|
mf = new Miniflare(config);
|
|
5394
5451
|
await mf.startServer();
|
|
5395
5452
|
await mf.startScheduler();
|
|
5396
|
-
|
|
5453
|
+
const internalDurableObjectClassNames = Object.values(
|
|
5454
|
+
config.durableObjects
|
|
5455
|
+
);
|
|
5456
|
+
if (internalDurableObjectClassNames.length > 0) {
|
|
5457
|
+
durableObjectsMf = new Miniflare({
|
|
5458
|
+
host: config.host,
|
|
5459
|
+
port: 0,
|
|
5460
|
+
script: `
|
|
5461
|
+
export default {
|
|
5462
|
+
fetch(request, env) {
|
|
5463
|
+
return env.DO.fetch(request)
|
|
5464
|
+
}
|
|
5465
|
+
}`,
|
|
5466
|
+
serviceBindings: {
|
|
5467
|
+
DO: async (request) => {
|
|
5468
|
+
request = new MiniflareRequest(request);
|
|
5469
|
+
const name = request.headers.get("x-miniflare-durable-object-name");
|
|
5470
|
+
const idString = request.headers.get(
|
|
5471
|
+
"x-miniflare-durable-object-id"
|
|
5472
|
+
);
|
|
5473
|
+
request.headers.delete("x-miniflare-durable-object-name");
|
|
5474
|
+
request.headers.delete("x-miniflare-durable-object-id");
|
|
5475
|
+
if (!name || !idString) {
|
|
5476
|
+
return new MiniflareResponse(
|
|
5477
|
+
"[durable-object-proxy-err] Missing `x-miniflare-durable-object-name` or `x-miniflare-durable-object-id` headers.",
|
|
5478
|
+
{ status: 400 }
|
|
5479
|
+
);
|
|
5480
|
+
}
|
|
5481
|
+
const namespace = await mf?.getDurableObjectNamespace(name);
|
|
5482
|
+
const id = namespace?.idFromString(idString);
|
|
5483
|
+
if (!id) {
|
|
5484
|
+
return new MiniflareResponse(
|
|
5485
|
+
"[durable-object-proxy-err] Could not generate an ID. Possibly due to a mismatched DO name and ID?",
|
|
5486
|
+
{ status: 500 }
|
|
5487
|
+
);
|
|
5488
|
+
}
|
|
5489
|
+
const stub = namespace?.get(id);
|
|
5490
|
+
if (!stub) {
|
|
5491
|
+
return new MiniflareResponse(
|
|
5492
|
+
"[durable-object-proxy-err] Could not generate a stub. Possibly due to a mismatched DO name and ID?",
|
|
5493
|
+
{ status: 500 }
|
|
5494
|
+
);
|
|
5495
|
+
}
|
|
5496
|
+
return stub.fetch(request);
|
|
5497
|
+
}
|
|
5498
|
+
},
|
|
5499
|
+
modules: true
|
|
5500
|
+
});
|
|
5501
|
+
const server = await durableObjectsMf.startServer();
|
|
5502
|
+
durableObjectsMfPort = server.address().port;
|
|
5503
|
+
}
|
|
5504
|
+
process.send && process.send(
|
|
5505
|
+
JSON.stringify({
|
|
5506
|
+
ready: true,
|
|
5507
|
+
durableObjectsPort: durableObjectsMfPort
|
|
5508
|
+
})
|
|
5509
|
+
);
|
|
5397
5510
|
} catch (e) {
|
|
5398
5511
|
mf?.log.error(e);
|
|
5399
5512
|
process.exitCode = 1;
|
|
5400
5513
|
await mf?.dispose();
|
|
5514
|
+
await durableObjectsMf?.dispose();
|
|
5401
5515
|
}
|
|
5402
5516
|
}
|
|
5403
5517
|
await main();
|
package/package.json
CHANGED
|
@@ -26,7 +26,7 @@ describe("normalizeAndValidateConfig()", () => {
|
|
|
26
26
|
compatibility_flags: [],
|
|
27
27
|
configPath: undefined,
|
|
28
28
|
dev: {
|
|
29
|
-
ip: "
|
|
29
|
+
ip: "0.0.0.0",
|
|
30
30
|
local_protocol: "http",
|
|
31
31
|
port: undefined, // the default of 8787 is set at runtime
|
|
32
32
|
upstream_protocol: "https",
|
|
@@ -68,6 +68,28 @@ describe("wrangler dev", () => {
|
|
|
68
68
|
});
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
+
describe("usage-model", () => {
|
|
72
|
+
it("should read wrangler.toml's usage_model", async () => {
|
|
73
|
+
writeWranglerToml({
|
|
74
|
+
main: "index.js",
|
|
75
|
+
usage_model: "unbound",
|
|
76
|
+
});
|
|
77
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
78
|
+
await runWrangler("dev");
|
|
79
|
+
expect((Dev as jest.Mock).mock.calls[0][0].usageModel).toEqual("unbound");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should read wrangler.toml's usage_model in local mode", async () => {
|
|
83
|
+
writeWranglerToml({
|
|
84
|
+
main: "index.js",
|
|
85
|
+
usage_model: "unbound",
|
|
86
|
+
});
|
|
87
|
+
fs.writeFileSync("index.js", `export default {};`);
|
|
88
|
+
await runWrangler("dev --local");
|
|
89
|
+
expect((Dev as jest.Mock).mock.calls[0][0].usageModel).toEqual("unbound");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
71
93
|
describe("entry-points", () => {
|
|
72
94
|
it("should error if there is no entry-point specified", async () => {
|
|
73
95
|
writeWranglerToml();
|
|
@@ -630,13 +652,13 @@ describe("wrangler dev", () => {
|
|
|
630
652
|
});
|
|
631
653
|
|
|
632
654
|
describe("ip", () => {
|
|
633
|
-
it("should default ip to
|
|
655
|
+
it("should default ip to 0.0.0.0", async () => {
|
|
634
656
|
writeWranglerToml({
|
|
635
657
|
main: "index.js",
|
|
636
658
|
});
|
|
637
659
|
fs.writeFileSync("index.js", `export default {};`);
|
|
638
660
|
await runWrangler("dev");
|
|
639
|
-
expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("
|
|
661
|
+
expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("0.0.0.0");
|
|
640
662
|
expect(std.out).toMatchInlineSnapshot(`""`);
|
|
641
663
|
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
642
664
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
@@ -849,7 +871,7 @@ describe("wrangler dev", () => {
|
|
|
849
871
|
});
|
|
850
872
|
fs.writeFileSync("index.js", `export default {};`);
|
|
851
873
|
await runWrangler("dev");
|
|
852
|
-
expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("
|
|
874
|
+
expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("0.0.0.0");
|
|
853
875
|
expect(std.out).toMatchInlineSnapshot(`
|
|
854
876
|
"Your worker has access to the following bindings:
|
|
855
877
|
- Durable Objects:
|
|
@@ -983,7 +1005,7 @@ describe("wrangler dev", () => {
|
|
|
983
1005
|
--compatibility-date Date to use for compatibility checks [string]
|
|
984
1006
|
--compatibility-flags, --compatibility-flag Flags to use for compatibility checks [array]
|
|
985
1007
|
--latest Use the latest version of the worker runtime [boolean] [default: true]
|
|
986
|
-
--ip IP address to listen on
|
|
1008
|
+
--ip IP address to listen on [string] [default: \\"0.0.0.0\\"]
|
|
987
1009
|
--port Port to listen on [number]
|
|
988
1010
|
--inspector-port Port for devtools to connect to [number]
|
|
989
1011
|
--routes, --route Routes to upload [array]
|
|
@@ -217,7 +217,7 @@ export async function mockFetchR2Objects(
|
|
|
217
217
|
bodyInit: {
|
|
218
218
|
body: BodyInit | Readable;
|
|
219
219
|
headers: HeadersInit | undefined;
|
|
220
|
-
method: "PUT" | "GET";
|
|
220
|
+
method: "PUT" | "GET" | "DELETE";
|
|
221
221
|
}
|
|
222
222
|
): Promise<Response> {
|
|
223
223
|
/**
|
|
@@ -234,7 +234,7 @@ export async function mockFetchR2Objects(
|
|
|
234
234
|
|
|
235
235
|
return new Response(value);
|
|
236
236
|
}
|
|
237
|
-
throw new Error(`no
|
|
237
|
+
throw new Error(`no mock found for \`r2 object\` - ${resource}`);
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
/**
|
package/src/__tests__/r2.test.ts
CHANGED
|
@@ -290,6 +290,24 @@ describe("wrangler", () => {
|
|
|
290
290
|
Upload complete."
|
|
291
291
|
`);
|
|
292
292
|
});
|
|
293
|
+
|
|
294
|
+
it("should delete R2 object from bucket", async () => {
|
|
295
|
+
setMockFetchR2Objects({
|
|
296
|
+
accountId: "some-account-id",
|
|
297
|
+
bucketName: "bucketName-object-test",
|
|
298
|
+
objectName: "wormhole-img.png",
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
await runWrangler(
|
|
302
|
+
`r2 object delete bucketName-object-test/wormhole-img.png`
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
expect(std.out).toMatchInlineSnapshot(`
|
|
306
|
+
"Deleting object \\"wormhole-img.png\\" from bucket \\"bucketName-object-test\\".
|
|
307
|
+
Delete complete."
|
|
308
|
+
`);
|
|
309
|
+
});
|
|
310
|
+
|
|
293
311
|
it("should not allow `--pipe` & `--file` to run together", async () => {
|
|
294
312
|
fs.writeFileSync("wormhole-img.png", "passageway");
|
|
295
313
|
setMockFetchR2Objects({
|
|
@@ -6,7 +6,12 @@ import { mockConsoleMethods } from "./helpers/mock-console";
|
|
|
6
6
|
import { useMockIsTTY } from "./helpers/mock-istty";
|
|
7
7
|
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
8
8
|
import { runWrangler } from "./helpers/run-wrangler";
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
TailEventMessage,
|
|
11
|
+
RequestEvent,
|
|
12
|
+
ScheduledEvent,
|
|
13
|
+
AlarmEvent,
|
|
14
|
+
} from "../tail";
|
|
10
15
|
import type WebSocket from "ws";
|
|
11
16
|
|
|
12
17
|
describe("tail", () => {
|
|
@@ -251,6 +256,18 @@ describe("tail", () => {
|
|
|
251
256
|
expect(std.out).toMatch(deserializeToJson(serializedMessage));
|
|
252
257
|
});
|
|
253
258
|
|
|
259
|
+
it("logs alarm messages in json format", async () => {
|
|
260
|
+
const api = mockWebsocketAPIs();
|
|
261
|
+
await runWrangler("tail test-worker --format json");
|
|
262
|
+
|
|
263
|
+
const event = generateMockAlarmEvent();
|
|
264
|
+
const message = generateMockEventMessage({ event });
|
|
265
|
+
const serializedMessage = serialize(message);
|
|
266
|
+
|
|
267
|
+
api.ws.send(serializedMessage);
|
|
268
|
+
expect(std.out).toMatch(deserializeToJson(serializedMessage));
|
|
269
|
+
});
|
|
270
|
+
|
|
254
271
|
it("logs request messages in pretty format", async () => {
|
|
255
272
|
const api = mockWebsocketAPIs();
|
|
256
273
|
await runWrangler("tail test-worker --format pretty");
|
|
@@ -271,10 +288,10 @@ describe("tail", () => {
|
|
|
271
288
|
"[mock expiration date]"
|
|
272
289
|
)
|
|
273
290
|
).toMatchInlineSnapshot(`
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
291
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
292
|
+
Connected to test-worker, waiting for logs...
|
|
293
|
+
GET https://example.org/ - Ok @ [mock event timestamp]"
|
|
294
|
+
`);
|
|
278
295
|
});
|
|
279
296
|
|
|
280
297
|
it("logs scheduled messages in pretty format", async () => {
|
|
@@ -297,35 +314,61 @@ describe("tail", () => {
|
|
|
297
314
|
"[mock expiration date]"
|
|
298
315
|
)
|
|
299
316
|
).toMatchInlineSnapshot(`
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
317
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
318
|
+
Connected to test-worker, waiting for logs...
|
|
319
|
+
\\"* * * * *\\" @ [mock timestamp string] - Ok"
|
|
320
|
+
`);
|
|
304
321
|
});
|
|
305
322
|
|
|
306
|
-
it("
|
|
323
|
+
it("logs alarm messages in pretty format", async () => {
|
|
307
324
|
const api = mockWebsocketAPIs();
|
|
308
325
|
await runWrangler("tail test-worker --format pretty");
|
|
309
326
|
|
|
310
|
-
const
|
|
327
|
+
const event = generateMockAlarmEvent();
|
|
328
|
+
const message = generateMockEventMessage({ event });
|
|
311
329
|
const serializedMessage = serialize(message);
|
|
312
330
|
|
|
313
331
|
api.ws.send(serializedMessage);
|
|
314
332
|
expect(
|
|
315
333
|
std.out
|
|
316
334
|
.replace(
|
|
317
|
-
new Date(
|
|
318
|
-
"[mock
|
|
335
|
+
new Date(mockEventScheduledTime).toLocaleString(),
|
|
336
|
+
"[mock scheduled time]"
|
|
319
337
|
)
|
|
320
338
|
.replace(
|
|
321
339
|
mockTailExpiration.toLocaleString(),
|
|
322
340
|
"[mock expiration date]"
|
|
323
341
|
)
|
|
324
342
|
).toMatchInlineSnapshot(`
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
343
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
344
|
+
Connected to test-worker, waiting for logs...
|
|
345
|
+
Alarm @ [mock scheduled time] - Ok"
|
|
346
|
+
`);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("should not crash when the tail message has a void event", async () => {
|
|
350
|
+
const api = mockWebsocketAPIs();
|
|
351
|
+
await runWrangler("tail test-worker --format pretty");
|
|
352
|
+
|
|
353
|
+
const message = generateMockEventMessage({ event: null });
|
|
354
|
+
const serializedMessage = serialize(message);
|
|
355
|
+
|
|
356
|
+
api.ws.send(serializedMessage);
|
|
357
|
+
expect(
|
|
358
|
+
std.out
|
|
359
|
+
.replace(
|
|
360
|
+
mockTailExpiration.toLocaleString(),
|
|
361
|
+
"[mock expiration date]"
|
|
362
|
+
)
|
|
363
|
+
.replace(
|
|
364
|
+
new Date(mockEventTimestamp).toLocaleString(),
|
|
365
|
+
"[mock timestamp string]"
|
|
366
|
+
)
|
|
367
|
+
).toMatchInlineSnapshot(`
|
|
368
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
369
|
+
Connected to test-worker, waiting for logs...
|
|
370
|
+
Unknown Event - Ok @ [mock timestamp string]"
|
|
371
|
+
`);
|
|
329
372
|
});
|
|
330
373
|
|
|
331
374
|
it("defaults to logging in pretty format when the output is a TTY", async () => {
|
|
@@ -349,10 +392,10 @@ describe("tail", () => {
|
|
|
349
392
|
"[mock expiration date]"
|
|
350
393
|
)
|
|
351
394
|
).toMatchInlineSnapshot(`
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
395
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
396
|
+
Connected to test-worker, waiting for logs...
|
|
397
|
+
GET https://example.org/ - Ok @ [mock event timestamp]"
|
|
398
|
+
`);
|
|
356
399
|
});
|
|
357
400
|
|
|
358
401
|
it("defaults to logging in json format when the output is not a TTY", async () => {
|
|
@@ -405,21 +448,21 @@ describe("tail", () => {
|
|
|
405
448
|
"[mock expiration date]"
|
|
406
449
|
)
|
|
407
450
|
).toMatchInlineSnapshot(`
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
451
|
+
"Successfully created tail, expires at [mock expiration date]
|
|
452
|
+
Connected to test-worker, waiting for logs...
|
|
453
|
+
GET https://example.org/ - Ok @ [mock event timestamp]
|
|
454
|
+
(log) some string
|
|
455
|
+
(log) { complex: 'object' }
|
|
456
|
+
(error) 1234"
|
|
457
|
+
`);
|
|
415
458
|
expect(std.err).toMatchInlineSnapshot(`
|
|
416
|
-
|
|
459
|
+
"[31mX [41;31m[[41;97mERROR[41;31m][0m [1m Error: some error[0m
|
|
417
460
|
|
|
418
461
|
|
|
419
|
-
|
|
462
|
+
[31mX [41;31m[[41;97mERROR[41;31m][0m [1m Error: { complex: 'error' }[0m
|
|
420
463
|
|
|
421
|
-
|
|
422
|
-
|
|
464
|
+
"
|
|
465
|
+
`);
|
|
423
466
|
expect(std.warn).toMatchInlineSnapshot(`""`);
|
|
424
467
|
});
|
|
425
468
|
});
|
|
@@ -437,8 +480,8 @@ describe("tail", () => {
|
|
|
437
480
|
* @returns the same type we expect when deserializing in wrangler
|
|
438
481
|
*/
|
|
439
482
|
function serialize(message: TailEventMessage): WebSocket.RawData {
|
|
440
|
-
if (
|
|
441
|
-
// `ScheduledEvent`s work just fine
|
|
483
|
+
if (!isRequest(message.event)) {
|
|
484
|
+
// `ScheduledEvent`s and `TailEvent`s work just fine
|
|
442
485
|
const stringified = JSON.stringify(message);
|
|
443
486
|
return Buffer.from(stringified, "utf-8");
|
|
444
487
|
} else {
|
|
@@ -470,12 +513,12 @@ function serialize(message: TailEventMessage): WebSocket.RawData {
|
|
|
470
513
|
* Small helper to disambiguate the event types possible in a `TailEventMessage`
|
|
471
514
|
*
|
|
472
515
|
* @param event A TailEvent
|
|
473
|
-
* @returns
|
|
516
|
+
* @returns true if `event` is a RequestEvent
|
|
474
517
|
*/
|
|
475
|
-
function
|
|
476
|
-
event: ScheduledEvent | RequestEvent | undefined | null
|
|
477
|
-
): event is
|
|
478
|
-
return Boolean(event && "
|
|
518
|
+
function isRequest(
|
|
519
|
+
event: ScheduledEvent | RequestEvent | AlarmEvent | undefined | null
|
|
520
|
+
): event is RequestEvent {
|
|
521
|
+
return Boolean(event && "request" in event);
|
|
479
522
|
}
|
|
480
523
|
|
|
481
524
|
/**
|
|
@@ -559,6 +602,11 @@ const mockTailExpiration = new Date(3005, 1);
|
|
|
559
602
|
*/
|
|
560
603
|
const mockEventTimestamp = 1645454470467;
|
|
561
604
|
|
|
605
|
+
/**
|
|
606
|
+
* Default value for event time ISO strings
|
|
607
|
+
*/
|
|
608
|
+
const mockEventScheduledTime = new Date(mockEventTimestamp).toISOString();
|
|
609
|
+
|
|
562
610
|
/**
|
|
563
611
|
* Mock out the API hit during Tail deletion
|
|
564
612
|
*
|
|
@@ -696,3 +744,9 @@ function generateMockScheduledEvent(
|
|
|
696
744
|
scheduledTime: opts?.scheduledTime || mockEventTimestamp,
|
|
697
745
|
};
|
|
698
746
|
}
|
|
747
|
+
|
|
748
|
+
function generateMockAlarmEvent(opts?: Partial<AlarmEvent>): AlarmEvent {
|
|
749
|
+
return {
|
|
750
|
+
scheduledTime: opts?.scheduledTime || mockEventScheduledTime,
|
|
751
|
+
};
|
|
752
|
+
}
|
package/src/api/dev.ts
CHANGED
|
@@ -8,6 +8,7 @@ interface DevOptions {
|
|
|
8
8
|
env?: string;
|
|
9
9
|
ip?: string;
|
|
10
10
|
port?: number;
|
|
11
|
+
inspectorPort?: number;
|
|
11
12
|
localProtocol?: "http" | "https";
|
|
12
13
|
assets?: string;
|
|
13
14
|
site?: string;
|
|
@@ -32,6 +33,11 @@ interface DevOptions {
|
|
|
32
33
|
script_name?: string | undefined;
|
|
33
34
|
environment?: string | undefined;
|
|
34
35
|
}[];
|
|
36
|
+
r2?: {
|
|
37
|
+
binding: string;
|
|
38
|
+
bucket_name: string;
|
|
39
|
+
preview_bucket_name?: string;
|
|
40
|
+
}[];
|
|
35
41
|
showInteractiveDevSession?: boolean;
|
|
36
42
|
logLevel?: "none" | "error" | "log" | "warn" | "debug";
|
|
37
43
|
logPrefix?: string;
|
package/src/bundle.ts
CHANGED
|
@@ -192,8 +192,9 @@ export async function bundleWorker(
|
|
|
192
192
|
format: entry.format === "modules" ? "esm" : "iife",
|
|
193
193
|
target: "es2020",
|
|
194
194
|
sourcemap: true,
|
|
195
|
-
//
|
|
196
|
-
|
|
195
|
+
// Include a reference to the output folder in the sourcemap.
|
|
196
|
+
// This is omitted by default, but we need it to properly resolve source paths in error output.
|
|
197
|
+
sourceRoot: destination,
|
|
197
198
|
minify,
|
|
198
199
|
metafile: true,
|
|
199
200
|
conditions: ["worker", "browser"],
|
package/src/config/config.ts
CHANGED