wrangler 2.0.21 → 2.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.
- package/README.md +20 -2
- package/bin/wrangler.js +1 -1
- package/miniflare-dist/index.mjs +527 -5
- package/package.json +18 -5
- package/src/__tests__/configuration.test.ts +88 -16
- package/src/__tests__/dev.test.tsx +95 -4
- package/src/__tests__/generate.test.ts +93 -0
- package/src/__tests__/helpers/mock-cfetch.ts +54 -2
- package/src/__tests__/index.test.ts +10 -27
- package/src/__tests__/jest.setup.ts +31 -1
- package/src/__tests__/kv.test.ts +82 -61
- package/src/__tests__/metrics.test.ts +35 -0
- package/src/__tests__/publish.test.ts +573 -254
- package/src/__tests__/r2.test.ts +155 -71
- package/src/__tests__/user.test.ts +1 -0
- package/src/__tests__/validate-dev-props.test.ts +56 -0
- package/src/__tests__/version.test.ts +35 -0
- package/src/__tests__/whoami.test.tsx +60 -1
- package/src/api/dev.ts +43 -9
- package/src/bundle.ts +297 -37
- package/src/cfetch/internal.ts +34 -2
- package/src/config/config.ts +14 -2
- package/src/config/environment.ts +40 -8
- package/src/config/index.ts +13 -0
- package/src/config/validation.ts +110 -8
- package/src/create-worker-preview.ts +3 -1
- package/src/create-worker-upload-form.ts +25 -0
- package/src/dev/dev.tsx +135 -31
- package/src/dev/local.tsx +48 -20
- package/src/dev/remote.tsx +39 -12
- package/src/dev/use-esbuild.ts +25 -0
- package/src/dev/validate-dev-props.ts +31 -0
- package/src/dev-registry.tsx +157 -0
- package/src/dev.tsx +137 -65
- package/src/generate.ts +112 -14
- package/src/index.tsx +222 -7
- package/src/inspect.ts +93 -5
- package/src/metrics/index.ts +1 -0
- package/src/metrics/is-ci.ts +14 -0
- package/src/metrics/metrics-config.ts +19 -2
- package/src/metrics/metrics-dispatcher.ts +1 -0
- package/src/metrics/metrics-usage-headers.ts +24 -0
- package/src/metrics/send-event.ts +2 -2
- package/src/miniflare-cli/assets.ts +543 -0
- package/src/miniflare-cli/index.ts +36 -4
- package/src/module-collection.ts +3 -3
- package/src/pages/constants.ts +1 -0
- package/src/pages/deployments.tsx +1 -1
- package/src/pages/dev.tsx +85 -639
- package/src/pages/publish.tsx +1 -1
- package/src/pages/upload.tsx +32 -13
- package/src/publish.ts +139 -112
- package/src/r2.ts +68 -0
- package/src/user/choose-account.tsx +20 -11
- package/src/user/user.tsx +20 -2
- package/src/whoami.tsx +79 -1
- package/src/worker.ts +12 -0
- package/templates/first-party-worker-module-facade.ts +18 -0
- package/templates/format-dev-errors.ts +32 -0
- package/templates/pages-shim.ts +9 -0
- package/templates/{static-asset-facade.js → serve-static-assets.ts} +21 -7
- package/templates/service-bindings-module-facade.js +51 -0
- package/templates/service-bindings-sw-facade.js +39 -0
- package/wrangler-dist/cli.d.ts +32 -3
- package/wrangler-dist/cli.js +45257 -25209
package/src/index.tsx
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
1
2
|
import path from "node:path";
|
|
3
|
+
import * as stream from "node:stream";
|
|
2
4
|
import { StringDecoder } from "node:string_decoder";
|
|
3
5
|
import { setTimeout } from "node:timers/promises";
|
|
4
6
|
import TOML from "@iarna/toml";
|
|
@@ -45,7 +47,14 @@ import {
|
|
|
45
47
|
import { previewHandler, previewOptions } from "./preview";
|
|
46
48
|
import publish from "./publish";
|
|
47
49
|
import { pubSubCommands } from "./pubsub/pubsub-commands";
|
|
48
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
bucketAndKeyFromObjectPath,
|
|
52
|
+
createR2Bucket,
|
|
53
|
+
deleteR2Bucket,
|
|
54
|
+
getR2Object,
|
|
55
|
+
listR2Buckets,
|
|
56
|
+
putR2Object,
|
|
57
|
+
} from "./r2";
|
|
49
58
|
import { getAssetPaths, getSiteAssetPaths } from "./sites";
|
|
50
59
|
import {
|
|
51
60
|
createTail,
|
|
@@ -67,6 +76,7 @@ import { workerNamespaceCommands } from "./worker-namespace";
|
|
|
67
76
|
import type { Config } from "./config";
|
|
68
77
|
import type { KeyValue } from "./kv";
|
|
69
78
|
import type { TailCLIFilters } from "./tail";
|
|
79
|
+
import type { Readable } from "node:stream";
|
|
70
80
|
import type { RawData } from "ws";
|
|
71
81
|
import type { CommandModule } from "yargs";
|
|
72
82
|
import type Yargs from "yargs";
|
|
@@ -76,6 +86,7 @@ export type ConfigPath = string | undefined;
|
|
|
76
86
|
const resetColor = "\x1b[0m";
|
|
77
87
|
const fgGreenColor = "\x1b[32m";
|
|
78
88
|
export const DEFAULT_LOCAL_PORT = 8787;
|
|
89
|
+
export const DEFAULT_INSPECTOR_PORT = 9229;
|
|
79
90
|
|
|
80
91
|
const proxy =
|
|
81
92
|
process.env.https_proxy ||
|
|
@@ -258,11 +269,22 @@ function createCLIParser(argv: string[]) {
|
|
|
258
269
|
["*"],
|
|
259
270
|
false,
|
|
260
271
|
() => {},
|
|
261
|
-
(args) => {
|
|
272
|
+
async (args) => {
|
|
262
273
|
if (args._.length > 0) {
|
|
263
274
|
throw new CommandLineArgsError(`Unknown command: ${args._}.`);
|
|
264
275
|
} else {
|
|
265
|
-
|
|
276
|
+
// args.v will exist and be true in the case that no command is called, and the -v
|
|
277
|
+
// option is present. This is to allow for running asynchronous printWranglerBanner
|
|
278
|
+
// in the version command.
|
|
279
|
+
if (args.v) {
|
|
280
|
+
if (process.stdout.isTTY) {
|
|
281
|
+
await printWranglerBanner();
|
|
282
|
+
} else {
|
|
283
|
+
logger.log(wranglerVersion);
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
wrangler.showHelp("log");
|
|
287
|
+
}
|
|
266
288
|
}
|
|
267
289
|
}
|
|
268
290
|
);
|
|
@@ -501,10 +523,16 @@ function createCLIParser(argv: string[]) {
|
|
|
501
523
|
(args.config as ConfigPath) ||
|
|
502
524
|
(args.script && findWranglerToml(path.dirname(args.script)));
|
|
503
525
|
const config = readConfig(configPath, args);
|
|
504
|
-
await metrics.sendMetricsEvent("deploy worker script", {
|
|
505
|
-
sendMetrics: config.send_metrics,
|
|
506
|
-
});
|
|
507
526
|
const entry = await getEntry(args, config, "publish");
|
|
527
|
+
await metrics.sendMetricsEvent(
|
|
528
|
+
"deploy worker script",
|
|
529
|
+
{
|
|
530
|
+
usesTypeScript: /\.tsx?$/.test(entry.file),
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
sendMetrics: config.send_metrics,
|
|
534
|
+
}
|
|
535
|
+
);
|
|
508
536
|
|
|
509
537
|
if (args.public) {
|
|
510
538
|
throw new Error("The --public field has been renamed to --assets");
|
|
@@ -938,6 +966,7 @@ function createCLIParser(argv: string[]) {
|
|
|
938
966
|
text_blobs: {},
|
|
939
967
|
data_blobs: {},
|
|
940
968
|
worker_namespaces: [],
|
|
969
|
+
logfwdr: { schema: undefined, bindings: [] },
|
|
941
970
|
unsafe: [],
|
|
942
971
|
},
|
|
943
972
|
modules: [],
|
|
@@ -1220,7 +1249,9 @@ function createCLIParser(argv: string[]) {
|
|
|
1220
1249
|
|
|
1221
1250
|
const accountId = await requireAuth(config);
|
|
1222
1251
|
|
|
1252
|
+
logger.log(`Deleting KV namespace ${id}.`);
|
|
1223
1253
|
await deleteKVNamespace(accountId, id);
|
|
1254
|
+
logger.log(`Deleted KV namespace ${id}.`);
|
|
1224
1255
|
await metrics.sendMetricsEvent("delete kv namespace", {
|
|
1225
1256
|
sendMetrics: config.send_metrics,
|
|
1226
1257
|
});
|
|
@@ -1732,6 +1763,167 @@ function createCLIParser(argv: string[]) {
|
|
|
1732
1763
|
wrangler.command("r2", "📦 Interact with an R2 store", (r2Yargs) => {
|
|
1733
1764
|
return r2Yargs
|
|
1734
1765
|
.command(subHelp)
|
|
1766
|
+
.command("object", "Manage R2 objects", (r2ObjectYargs) => {
|
|
1767
|
+
return r2ObjectYargs
|
|
1768
|
+
.command(
|
|
1769
|
+
"get <objectPath>",
|
|
1770
|
+
"Fetch an object from an R2 bucket",
|
|
1771
|
+
(yargs) => {
|
|
1772
|
+
return yargs
|
|
1773
|
+
.positional("objectPath", {
|
|
1774
|
+
describe:
|
|
1775
|
+
"The source object path in the form of {bucket}/{key}",
|
|
1776
|
+
type: "string",
|
|
1777
|
+
})
|
|
1778
|
+
.option("file", {
|
|
1779
|
+
describe: "The destination file to create",
|
|
1780
|
+
alias: "f",
|
|
1781
|
+
conflicts: "pipe",
|
|
1782
|
+
requiresArg: true,
|
|
1783
|
+
type: "string",
|
|
1784
|
+
})
|
|
1785
|
+
.option("pipe", {
|
|
1786
|
+
describe:
|
|
1787
|
+
"Enables the file to be piped to a destination, rather than specified with the --file option",
|
|
1788
|
+
alias: "p",
|
|
1789
|
+
conflicts: "file",
|
|
1790
|
+
type: "boolean",
|
|
1791
|
+
});
|
|
1792
|
+
},
|
|
1793
|
+
async (args) => {
|
|
1794
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
1795
|
+
const accountId = await requireAuth(config);
|
|
1796
|
+
const { objectPath, pipe } = args;
|
|
1797
|
+
const { bucket, key } = bucketAndKeyFromObjectPath(objectPath);
|
|
1798
|
+
|
|
1799
|
+
let file = args.file;
|
|
1800
|
+
if (!file && !pipe) {
|
|
1801
|
+
file = key;
|
|
1802
|
+
}
|
|
1803
|
+
if (!pipe) {
|
|
1804
|
+
await printWranglerBanner();
|
|
1805
|
+
logger.log(`Downloading "${key}" from "${bucket}".`);
|
|
1806
|
+
}
|
|
1807
|
+
const input = await getR2Object(accountId, bucket, key);
|
|
1808
|
+
const output = file ? fs.createWriteStream(file) : process.stdout;
|
|
1809
|
+
await new Promise<void>((resolve, reject) => {
|
|
1810
|
+
stream.pipeline(input, output, (err: unknown) => {
|
|
1811
|
+
err ? reject(err) : resolve();
|
|
1812
|
+
});
|
|
1813
|
+
});
|
|
1814
|
+
if (!pipe) logger.log("Download complete.");
|
|
1815
|
+
}
|
|
1816
|
+
)
|
|
1817
|
+
.command(
|
|
1818
|
+
"put <objectPath>",
|
|
1819
|
+
"Create an object in an R2 bucket",
|
|
1820
|
+
(yargs) => {
|
|
1821
|
+
return yargs
|
|
1822
|
+
.positional("objectPath", {
|
|
1823
|
+
describe:
|
|
1824
|
+
"The destination object path in the form of {bucket}/{key}",
|
|
1825
|
+
type: "string",
|
|
1826
|
+
})
|
|
1827
|
+
.option("file", {
|
|
1828
|
+
describe: "The path of the file to upload",
|
|
1829
|
+
alias: "f",
|
|
1830
|
+
conflicts: "pipe",
|
|
1831
|
+
requiresArg: true,
|
|
1832
|
+
type: "string",
|
|
1833
|
+
})
|
|
1834
|
+
.option("pipe", {
|
|
1835
|
+
describe:
|
|
1836
|
+
"Enables the file to be piped in, rather than specified with the --file option",
|
|
1837
|
+
alias: "p",
|
|
1838
|
+
conflicts: "file",
|
|
1839
|
+
type: "boolean",
|
|
1840
|
+
})
|
|
1841
|
+
.option("content-type", {
|
|
1842
|
+
describe:
|
|
1843
|
+
"A standard MIME type describing the format of the object data",
|
|
1844
|
+
alias: "ct",
|
|
1845
|
+
requiresArg: true,
|
|
1846
|
+
type: "string",
|
|
1847
|
+
})
|
|
1848
|
+
.option("content-disposition", {
|
|
1849
|
+
describe:
|
|
1850
|
+
"Specifies presentational information for the object",
|
|
1851
|
+
alias: "cd",
|
|
1852
|
+
requiresArg: true,
|
|
1853
|
+
type: "string",
|
|
1854
|
+
})
|
|
1855
|
+
.option("content-encoding", {
|
|
1856
|
+
describe:
|
|
1857
|
+
"Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field",
|
|
1858
|
+
alias: "ce",
|
|
1859
|
+
requiresArg: true,
|
|
1860
|
+
type: "string",
|
|
1861
|
+
})
|
|
1862
|
+
.option("content-language", {
|
|
1863
|
+
describe: "The language the content is in",
|
|
1864
|
+
alias: "cl",
|
|
1865
|
+
requiresArg: true,
|
|
1866
|
+
type: "string",
|
|
1867
|
+
})
|
|
1868
|
+
.option("cache-control", {
|
|
1869
|
+
describe:
|
|
1870
|
+
"Specifies caching behavior along the request/reply chain",
|
|
1871
|
+
alias: "cc",
|
|
1872
|
+
requiresArg: true,
|
|
1873
|
+
type: "string",
|
|
1874
|
+
})
|
|
1875
|
+
.option("expires", {
|
|
1876
|
+
describe:
|
|
1877
|
+
"The date and time at which the object is no longer cacheable",
|
|
1878
|
+
alias: "e",
|
|
1879
|
+
requiresArg: true,
|
|
1880
|
+
type: "string",
|
|
1881
|
+
});
|
|
1882
|
+
},
|
|
1883
|
+
async (args) => {
|
|
1884
|
+
await printWranglerBanner();
|
|
1885
|
+
|
|
1886
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
1887
|
+
const accountId = await requireAuth(config);
|
|
1888
|
+
const { objectPath, file, pipe, ...options } = args;
|
|
1889
|
+
const { bucket, key } = bucketAndKeyFromObjectPath(objectPath);
|
|
1890
|
+
if (!file && !pipe) {
|
|
1891
|
+
throw new CommandLineArgsError(
|
|
1892
|
+
"Either the --file or --pipe options are required."
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1895
|
+
let object: Readable | Buffer;
|
|
1896
|
+
let objectSize: number;
|
|
1897
|
+
if (file) {
|
|
1898
|
+
object = fs.createReadStream(file);
|
|
1899
|
+
const stats = fs.statSync(file);
|
|
1900
|
+
objectSize = stats.size;
|
|
1901
|
+
} else {
|
|
1902
|
+
object = await new Promise<Buffer>((resolve, reject) => {
|
|
1903
|
+
const stdin = process.stdin;
|
|
1904
|
+
const chunks = Array<Buffer>();
|
|
1905
|
+
stdin.on("data", (chunk) => chunks.push(chunk));
|
|
1906
|
+
stdin.on("end", () => resolve(Buffer.concat(chunks)));
|
|
1907
|
+
stdin.on("error", (err) =>
|
|
1908
|
+
reject(
|
|
1909
|
+
new CommandLineArgsError(
|
|
1910
|
+
`Could not pipe. Reason: "${err.message}"`
|
|
1911
|
+
)
|
|
1912
|
+
)
|
|
1913
|
+
);
|
|
1914
|
+
});
|
|
1915
|
+
objectSize = object.byteLength;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
logger.log(`Creating object "${key}" in bucket "${bucket}".`);
|
|
1919
|
+
await putR2Object(accountId, bucket, key, object, {
|
|
1920
|
+
...options,
|
|
1921
|
+
"content-length": `${objectSize}`,
|
|
1922
|
+
});
|
|
1923
|
+
logger.log("Upload complete.");
|
|
1924
|
+
}
|
|
1925
|
+
);
|
|
1926
|
+
})
|
|
1735
1927
|
.command("bucket", "Manage R2 buckets", (r2BucketYargs) => {
|
|
1736
1928
|
r2BucketYargs.command(
|
|
1737
1929
|
"create <name>",
|
|
@@ -1903,6 +2095,29 @@ function createCLIParser(argv: string[]) {
|
|
|
1903
2095
|
}
|
|
1904
2096
|
);
|
|
1905
2097
|
|
|
2098
|
+
// This set to false to allow overwrite of default behaviour
|
|
2099
|
+
wrangler.version(false);
|
|
2100
|
+
|
|
2101
|
+
// version
|
|
2102
|
+
wrangler.command(
|
|
2103
|
+
"version",
|
|
2104
|
+
false,
|
|
2105
|
+
() => {},
|
|
2106
|
+
async () => {
|
|
2107
|
+
if (process.stdout.isTTY) {
|
|
2108
|
+
await printWranglerBanner();
|
|
2109
|
+
} else {
|
|
2110
|
+
logger.log(wranglerVersion);
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
);
|
|
2114
|
+
|
|
2115
|
+
wrangler.option("v", {
|
|
2116
|
+
describe: "Show version number",
|
|
2117
|
+
alias: "version",
|
|
2118
|
+
type: "boolean",
|
|
2119
|
+
});
|
|
2120
|
+
|
|
1906
2121
|
wrangler.option("config", {
|
|
1907
2122
|
alias: "c",
|
|
1908
2123
|
describe: "Path to .toml configuration file",
|
|
@@ -1912,7 +2127,7 @@ function createCLIParser(argv: string[]) {
|
|
|
1912
2127
|
|
|
1913
2128
|
wrangler.group(["config", "help", "version"], "Flags:");
|
|
1914
2129
|
wrangler.help().alias("h", "help");
|
|
1915
|
-
|
|
2130
|
+
|
|
1916
2131
|
wrangler.exitProcess(false);
|
|
1917
2132
|
|
|
1918
2133
|
return wrangler;
|
package/src/inspect.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
1
2
|
import assert from "node:assert";
|
|
2
3
|
import { createServer } from "node:http";
|
|
3
4
|
import os from "node:os";
|
|
@@ -5,6 +6,7 @@ import { URL } from "node:url";
|
|
|
5
6
|
|
|
6
7
|
import open from "open";
|
|
7
8
|
import { useEffect, useRef, useState } from "react";
|
|
9
|
+
import { SourceMapConsumer } from "source-map";
|
|
8
10
|
import WebSocket, { WebSocketServer } from "ws";
|
|
9
11
|
import { version } from "../package.json";
|
|
10
12
|
import { logger } from "./logger";
|
|
@@ -52,6 +54,10 @@ interface InspectorProps {
|
|
|
52
54
|
* logged to the terminal by nature of them actually running in node locally.)
|
|
53
55
|
*/
|
|
54
56
|
logToTerminal: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Sourcemap path, so that stacktraces can be interpretted
|
|
59
|
+
*/
|
|
60
|
+
sourceMapPath?: string | undefined;
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
export default function useInspector(props: InspectorProps) {
|
|
@@ -252,15 +258,96 @@ export default function useInspector(props: InspectorProps) {
|
|
|
252
258
|
* without having to open the devtools).
|
|
253
259
|
*/
|
|
254
260
|
if (props.logToTerminal) {
|
|
255
|
-
ws.addEventListener("message", (event: MessageEvent) => {
|
|
261
|
+
ws.addEventListener("message", async (event: MessageEvent) => {
|
|
256
262
|
if (typeof event.data === "string") {
|
|
257
263
|
const evt = JSON.parse(event.data);
|
|
258
264
|
if (evt.method === "Runtime.exceptionThrown") {
|
|
259
265
|
const params = evt.params as Protocol.Runtime.ExceptionThrownEvent;
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
266
|
+
|
|
267
|
+
// Parse stack trace with source map.
|
|
268
|
+
if (props.sourceMapPath) {
|
|
269
|
+
// Parse in the sourcemap
|
|
270
|
+
const mapContent = JSON.parse(
|
|
271
|
+
await readFile(props.sourceMapPath, "utf-8")
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
// Create the lines for the exception details log
|
|
275
|
+
const exceptionLines = [
|
|
276
|
+
params.exceptionDetails.exception?.description?.split("\n")[0],
|
|
277
|
+
];
|
|
278
|
+
|
|
279
|
+
await SourceMapConsumer.with(
|
|
280
|
+
mapContent,
|
|
281
|
+
null,
|
|
282
|
+
async (consumer) => {
|
|
283
|
+
// Pass each of the callframes into the consumer, and format the error
|
|
284
|
+
const stack = params.exceptionDetails.stackTrace?.callFrames;
|
|
285
|
+
|
|
286
|
+
stack?.forEach(
|
|
287
|
+
({ functionName, lineNumber, columnNumber }, i) => {
|
|
288
|
+
try {
|
|
289
|
+
if (lineNumber) {
|
|
290
|
+
// The line and column numbers in the stackTrace are zero indexed,
|
|
291
|
+
// whereas the sourcemap consumer indexes from one.
|
|
292
|
+
const pos = consumer.originalPositionFor({
|
|
293
|
+
line: lineNumber + 1,
|
|
294
|
+
column: columnNumber + 1,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Print out line which caused error:
|
|
298
|
+
if (i === 0 && pos.source && pos.line) {
|
|
299
|
+
const fileSource = consumer.sourceContentFor(
|
|
300
|
+
pos.source
|
|
301
|
+
);
|
|
302
|
+
const fileSourceLine =
|
|
303
|
+
fileSource?.split("\n")[pos.line - 1] || "";
|
|
304
|
+
exceptionLines.push(fileSourceLine.trim());
|
|
305
|
+
|
|
306
|
+
// If we have a column, we can mark the position underneath
|
|
307
|
+
if (pos.column) {
|
|
308
|
+
exceptionLines.push(
|
|
309
|
+
`${" ".repeat(
|
|
310
|
+
pos.column - fileSourceLine.search(/\S/)
|
|
311
|
+
)}^`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// From the way esbuild implements the "names" field:
|
|
317
|
+
// > To save space, the original name is only recorded when it's different from the final name.
|
|
318
|
+
// however, source-map consumer does not handle this
|
|
319
|
+
if (pos && pos.line != null) {
|
|
320
|
+
const convertedFnName =
|
|
321
|
+
pos.name || functionName || "";
|
|
322
|
+
exceptionLines.push(
|
|
323
|
+
` at ${convertedFnName} (${pos.source?.replace(
|
|
324
|
+
`${mapContent.sourceRoot}/`,
|
|
325
|
+
""
|
|
326
|
+
)}:${pos.line}:${pos.column})`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
} catch {
|
|
331
|
+
// Line failed to parse through the sourcemap consumer
|
|
332
|
+
// We should handle this better
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
// Log the parsed stacktrace
|
|
340
|
+
logger.error(
|
|
341
|
+
params.exceptionDetails.text,
|
|
342
|
+
exceptionLines.join("\n")
|
|
343
|
+
);
|
|
344
|
+
} else {
|
|
345
|
+
// We log the stacktrace to the terminal
|
|
346
|
+
logger.error(
|
|
347
|
+
params.exceptionDetails.text,
|
|
348
|
+
params.exceptionDetails.exception?.description ?? ""
|
|
349
|
+
);
|
|
350
|
+
}
|
|
264
351
|
}
|
|
265
352
|
if (evt.method === "Runtime.consoleAPICalled") {
|
|
266
353
|
logConsoleMessage(
|
|
@@ -333,6 +420,7 @@ export default function useInspector(props: InspectorProps) {
|
|
|
333
420
|
}, [
|
|
334
421
|
props.inspectorUrl,
|
|
335
422
|
props.logToTerminal,
|
|
423
|
+
props.sourceMapPath,
|
|
336
424
|
wsServer,
|
|
337
425
|
// We use a state value as a sigil to trigger a retry of the
|
|
338
426
|
// remote websocket connection. It's not used inside the effect,
|
package/src/metrics/index.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import isCI from "is-ci";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Use this object to find out if we are currently running in a continuous integration environment.
|
|
5
|
+
*
|
|
6
|
+
* The isCI constant imported above cannot be easily mocked for testing.
|
|
7
|
+
* By wrapping this up in a method on an object, it results in clean and testable code.
|
|
8
|
+
*/
|
|
9
|
+
export const CI = {
|
|
10
|
+
/** Is Wrangler currently running in a CI? */
|
|
11
|
+
isCI() {
|
|
12
|
+
return isCI;
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -5,9 +5,11 @@ import path from "node:path";
|
|
|
5
5
|
import { fetchResult } from "../cfetch";
|
|
6
6
|
import { getConfigCache, saveToConfigCache } from "../config-cache";
|
|
7
7
|
import { confirm } from "../dialogs";
|
|
8
|
+
import { getEnvironmentVariableFactory } from "../environment-variables";
|
|
8
9
|
import isInteractive from "../is-interactive";
|
|
9
10
|
import { logger } from "../logger";
|
|
10
11
|
import { getAPIToken } from "../user";
|
|
12
|
+
import { CI } from "./is-ci";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* The date that the metrics being gathered was last updated in a way that would require
|
|
@@ -21,6 +23,10 @@ import { getAPIToken } from "../user";
|
|
|
21
23
|
export const CURRENT_METRICS_DATE = new Date(2022, 6, 4);
|
|
22
24
|
export const USER_ID_CACHE_PATH = "user-id.json";
|
|
23
25
|
|
|
26
|
+
export const getWranglerSendMetricsFromEnv = getEnvironmentVariableFactory({
|
|
27
|
+
variableName: "WRANGLER_SEND_METRICS",
|
|
28
|
+
});
|
|
29
|
+
|
|
24
30
|
export interface MetricsConfigOptions {
|
|
25
31
|
/**
|
|
26
32
|
* Defines whether to send metrics to Cloudflare:
|
|
@@ -70,6 +76,17 @@ export async function getMetricsConfig({
|
|
|
70
76
|
const deviceId = getDeviceId(config);
|
|
71
77
|
const userId = await getUserId(offline);
|
|
72
78
|
|
|
79
|
+
// If the WRANGLER_SEND_METRICS environment variable has been set use that
|
|
80
|
+
// and ignore everything else.
|
|
81
|
+
const sendMetricsEnv = getWranglerSendMetricsFromEnv();
|
|
82
|
+
if (sendMetricsEnv !== undefined) {
|
|
83
|
+
return {
|
|
84
|
+
enabled: sendMetricsEnv.toLowerCase() === "true",
|
|
85
|
+
deviceId,
|
|
86
|
+
userId,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
73
90
|
// If the project is explicitly set the `send_metrics` options in `wrangler.toml`
|
|
74
91
|
// then use that and ignore any user preference.
|
|
75
92
|
if (sendMetrics !== undefined) {
|
|
@@ -89,8 +106,8 @@ export async function getMetricsConfig({
|
|
|
89
106
|
}
|
|
90
107
|
|
|
91
108
|
// We couldn't get the metrics permission from the project-level nor the user-level config.
|
|
92
|
-
// If we are not interactive then just bail out.
|
|
93
|
-
if (!isInteractive()) {
|
|
109
|
+
// If we are not interactive or in a CI build then just bail out.
|
|
110
|
+
if (!isInteractive() || CI.isCI()) {
|
|
94
111
|
return { enabled: false, deviceId, userId };
|
|
95
112
|
}
|
|
96
113
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getMetricsConfig } from "./metrics-config";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Add an additional header to publish requests if the user has opted into sending usage metrics.
|
|
5
|
+
*
|
|
6
|
+
* This allows us to estimate the number of instances of Wrangler that have opted-in
|
|
7
|
+
* without breaking our agreement not to send stuff if you have not opted-in.
|
|
8
|
+
*/
|
|
9
|
+
export async function getMetricsUsageHeaders(
|
|
10
|
+
sendMetrics: boolean | undefined
|
|
11
|
+
): Promise<Record<string, string> | undefined> {
|
|
12
|
+
const metricsEnabled = (
|
|
13
|
+
await getMetricsConfig({
|
|
14
|
+
sendMetrics,
|
|
15
|
+
})
|
|
16
|
+
).enabled;
|
|
17
|
+
if (metricsEnabled) {
|
|
18
|
+
return {
|
|
19
|
+
metricsEnabled: "true",
|
|
20
|
+
};
|
|
21
|
+
} else {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -47,8 +47,8 @@ export type EventNames =
|
|
|
47
47
|
| "rename worker namespace"
|
|
48
48
|
| "create pages project"
|
|
49
49
|
| "list pages projects"
|
|
50
|
-
| "
|
|
51
|
-
| "list pages
|
|
50
|
+
| "create pages deployment"
|
|
51
|
+
| "list pages deployments"
|
|
52
52
|
| "build pages functions"
|
|
53
53
|
| "run dev"
|
|
54
54
|
| "run pages dev";
|