@hyperbridge/simplex 0.17.1 → 0.18.0

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.
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/config/error-reporting.ts
21
+ var error_reporting_exports = {};
22
+ __export(error_reporting_exports, {
23
+ ERROR_REPORTING_ENV: () => ERROR_REPORTING_ENV,
24
+ errorReportingDisabled: () => errorReportingDisabled,
25
+ scrubBreadcrumb: () => scrubBreadcrumb,
26
+ scrubEvent: () => scrubEvent,
27
+ scrubText: () => scrubText,
28
+ sentryIngestOrigin: () => sentryIngestOrigin
29
+ });
30
+ module.exports = __toCommonJS(error_reporting_exports);
31
+ var import_english = require("@scure/bip39/wordlists/english");
32
+ var ERROR_REPORTING_ENV = "SIMPLEX_ERROR_REPORTING";
33
+ function errorReportingDisabled(value) {
34
+ return ["off", "0", "false", "no"].includes((value ?? "").trim().toLowerCase());
35
+ }
36
+ function sentryIngestOrigin(dsn) {
37
+ try {
38
+ const url = new URL(dsn);
39
+ return url.protocol === "https:" ? url.origin : void 0;
40
+ } catch {
41
+ return void 0;
42
+ }
43
+ }
44
+ var MNEMONIC_WORDS = new Set(import_english.wordlist);
45
+ var MNEMONIC_MIN_WORDS = 12;
46
+ var REDACTED = "[redacted]";
47
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "local"]);
48
+ var URL_PATTERN = /\b(?:https?|wss?|simplex|file):\/\/[^\s"'<>`]+/i;
49
+ var FILE_PATH_PATTERN = /(?<=["'])(?:[a-z]:[\\/]|\\\\|\/)[^"'<>`\r\n]+|(?:[a-z]:[\\/]|\\\\|(?<![\w/])\/)[^\s"'<>`]+/;
50
+ var LOCATION_PATTERN = new RegExp(`${URL_PATTERN.source}|${FILE_PATH_PATTERN.source}`, "gi");
51
+ var WORD_RUN_PATTERN = /\b[a-z]+(?:[\s"',\[\]]+[a-z]+){11,}\b/gi;
52
+ var LONG_TOKEN_PATTERN = /[A-Za-z0-9+/_=-]{32,}/g;
53
+ var ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/;
54
+ function scrubUrl(raw) {
55
+ try {
56
+ const url = new URL(raw);
57
+ if (url.protocol === "file:") return REDACTED;
58
+ const path = LOOPBACK_HOSTS.has(url.hostname) ? url.pathname : url.pathname === "/" ? "" : `/${REDACTED}`;
59
+ return `${url.protocol}//${url.host}${path}${url.search || url.hash ? `?${REDACTED}` : ""}`;
60
+ } catch {
61
+ return REDACTED;
62
+ }
63
+ }
64
+ function containsMnemonic(run) {
65
+ let consecutive = 0;
66
+ for (const word of run.toLowerCase().split(/[\s"',\[\]]+/)) {
67
+ consecutive = MNEMONIC_WORDS.has(word) ? consecutive + 1 : 0;
68
+ if (consecutive >= MNEMONIC_MIN_WORDS) return true;
69
+ }
70
+ return false;
71
+ }
72
+ function scrubText(text) {
73
+ const withoutUrls = text.replace(
74
+ LOCATION_PATTERN,
75
+ (location) => URL_PATTERN.test(location) ? scrubUrl(location) : REDACTED
76
+ );
77
+ const withoutMnemonics = withoutUrls.replace(WORD_RUN_PATTERN, (run) => containsMnemonic(run) ? REDACTED : run);
78
+ return withoutMnemonics.replace(LONG_TOKEN_PATTERN, (token) => ADDRESS_PATTERN.test(token) ? token : REDACTED);
79
+ }
80
+ function scrubBreadcrumb(breadcrumb) {
81
+ if (breadcrumb.message) breadcrumb.message = scrubText(breadcrumb.message);
82
+ if (breadcrumb.data) breadcrumb.data = scrubDeep(breadcrumb.data);
83
+ return breadcrumb;
84
+ }
85
+ function scrubEvent(event) {
86
+ if (event.message) event.message = scrubText(event.message);
87
+ if (event.logentry) event.logentry = scrubDeep(event.logentry);
88
+ for (const exception of event.exception?.values ?? []) {
89
+ if (exception.value) exception.value = scrubText(exception.value);
90
+ }
91
+ for (const breadcrumb of event.breadcrumbs ?? []) scrubBreadcrumb(breadcrumb);
92
+ if (event.extra) event.extra = scrubDeep(event.extra);
93
+ if (event.request) {
94
+ if (event.request.url) event.request.url = scrubText(event.request.url);
95
+ delete event.request.query_string;
96
+ delete event.request.data;
97
+ delete event.request.cookies;
98
+ const userAgent = event.request.headers?.["User-Agent"];
99
+ event.request.headers = userAgent ? { "User-Agent": userAgent } : void 0;
100
+ }
101
+ return event;
102
+ }
103
+ function scrubDeep(value, seen = /* @__PURE__ */ new WeakSet()) {
104
+ if (typeof value === "string") return scrubText(value);
105
+ if (!value || typeof value !== "object") return value;
106
+ if (seen.has(value)) return REDACTED;
107
+ seen.add(value);
108
+ if (Array.isArray(value)) {
109
+ if (containsMnemonic(value.map((item) => typeof item === "string" ? item : "").join(" "))) return REDACTED;
110
+ return value.map((item) => scrubDeep(item, seen));
111
+ }
112
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, scrubDeep(item, seen)]));
113
+ }
114
+ // Annotate the CommonJS export names for ESM import in node:
115
+ 0 && (module.exports = {
116
+ ERROR_REPORTING_ENV,
117
+ errorReportingDisabled,
118
+ scrubBreadcrumb,
119
+ scrubEvent,
120
+ scrubText,
121
+ sentryIngestOrigin
122
+ });
123
+ //# sourceMappingURL=error-reporting.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/error-reporting.ts"],"sourcesContent":["import { wordlist } from \"@scure/bip39/wordlists/english\"\n\n/**\n * Error reporting shared by the web UI and the desktop shell. Builds only report\n * when CI baked a Sentry DSN into them; operators opt out with\n * `SIMPLEX_ERROR_REPORTING=off`. Everything here is browser-safe.\n */\nexport const ERROR_REPORTING_ENV = \"SIMPLEX_ERROR_REPORTING\"\n\nexport function errorReportingDisabled(value: string | undefined): boolean {\n\treturn [\"off\", \"0\", \"false\", \"no\"].includes((value ?? \"\").trim().toLowerCase())\n}\n\n/** The ingest origin a renderer CSP must allow for this DSN, or undefined for a malformed one. */\nexport function sentryIngestOrigin(dsn: string): string | undefined {\n\ttry {\n\t\tconst url = new URL(dsn)\n\t\treturn url.protocol === \"https:\" ? url.origin : undefined\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nconst MNEMONIC_WORDS = new Set(wordlist)\nconst MNEMONIC_MIN_WORDS = 12\nconst REDACTED = \"[redacted]\"\nconst LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\", \"local\"])\nconst URL_PATTERN = /\\b(?:https?|wss?|simplex|file):\\/\\/[^\\s\"'<>`]+/i\n// Quoted paths may contain spaces. Match URLs first so local API routes remain readable.\nconst FILE_PATH_PATTERN = /(?<=[\"'])(?:[a-z]:[\\\\/]|\\\\\\\\|\\/)[^\"'<>`\\r\\n]+|(?:[a-z]:[\\\\/]|\\\\\\\\|(?<![\\w/])\\/)[^\\s\"'<>`]+/\nconst LOCATION_PATTERN = new RegExp(`${URL_PATTERN.source}|${FILE_PATH_PATTERN.source}`, \"gi\")\n// Also catches phrases rendered as JSON word arrays or comma-separated words.\nconst WORD_RUN_PATTERN = /\\b[a-z]+(?:[\\s\"',\\[\\]]+[a-z]+){11,}\\b/gi\n// Private keys, signatures, API tokens, base58/base64 secrets and long file paths\n// (which name the operator's home directory). A 0x address stays readable.\nconst LONG_TOKEN_PATTERN = /[A-Za-z0-9+/_=-]{32,}/g\nconst ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/\n\nfunction scrubUrl(raw: string): string {\n\ttry {\n\t\tconst url = new URL(raw)\n\t\tif (url.protocol === \"file:\") return REDACTED\n\t\t// RPC providers carry API keys in the path or query; local solver routes do not.\n\t\tconst path = LOOPBACK_HOSTS.has(url.hostname) ? url.pathname : url.pathname === \"/\" ? \"\" : `/${REDACTED}`\n\t\treturn `${url.protocol}//${url.host}${path}${url.search || url.hash ? `?${REDACTED}` : \"\"}`\n\t} catch {\n\t\treturn REDACTED\n\t}\n}\n\nfunction containsMnemonic(run: string): boolean {\n\tlet consecutive = 0\n\tfor (const word of run.toLowerCase().split(/[\\s\"',\\[\\]]+/)) {\n\t\tconsecutive = MNEMONIC_WORDS.has(word) ? consecutive + 1 : 0\n\t\tif (consecutive >= MNEMONIC_MIN_WORDS) return true\n\t}\n\treturn false\n}\n\n/** Removes recovery phrases, key material and provider URLs from one string. */\nexport function scrubText(text: string): string {\n\tconst withoutUrls = text.replace(LOCATION_PATTERN, (location) =>\n\t\tURL_PATTERN.test(location) ? scrubUrl(location) : REDACTED,\n\t)\n\tconst withoutMnemonics = withoutUrls.replace(WORD_RUN_PATTERN, (run) => (containsMnemonic(run) ? REDACTED : run))\n\treturn withoutMnemonics.replace(LONG_TOKEN_PATTERN, (token) => (ADDRESS_PATTERN.test(token) ? token : REDACTED))\n}\n\ninterface ScrubbableBreadcrumb {\n\tmessage?: string\n\tdata?: Record<string, unknown>\n}\n\ninterface ScrubbableEvent {\n\tmessage?: string\n\tlogentry?: { message?: string; params?: unknown[] }\n\texception?: { values?: Array<{ value?: string }> }\n\tbreadcrumbs?: ScrubbableBreadcrumb[]\n\textra?: Record<string, unknown>\n\trequest?: {\n\t\turl?: string\n\t\tquery_string?: unknown\n\t\tdata?: unknown\n\t\tcookies?: unknown\n\t\theaders?: Record<string, string>\n\t}\n}\n\n/** Scrubs a breadcrumb's message and data in place. */\nexport function scrubBreadcrumb<T extends ScrubbableBreadcrumb>(breadcrumb: T): T {\n\tif (breadcrumb.message) breadcrumb.message = scrubText(breadcrumb.message)\n\tif (breadcrumb.data) breadcrumb.data = scrubDeep(breadcrumb.data) as Record<string, unknown>\n\treturn breadcrumb\n}\n\n/**\n * Scrubs the free-text parts of an event in place. Identifiers (event and\n * trace ids), release names and stack frames stay intact so Sentry can group\n * the event and resolve uploaded source maps.\n */\nexport function scrubEvent<T extends ScrubbableEvent>(event: T): T {\n\tif (event.message) event.message = scrubText(event.message)\n\tif (event.logentry) event.logentry = scrubDeep(event.logentry) as ScrubbableEvent[\"logentry\"]\n\tfor (const exception of event.exception?.values ?? []) {\n\t\tif (exception.value) exception.value = scrubText(exception.value)\n\t}\n\tfor (const breadcrumb of event.breadcrumbs ?? []) scrubBreadcrumb(breadcrumb)\n\tif (event.extra) event.extra = scrubDeep(event.extra) as Record<string, unknown>\n\tif (event.request) {\n\t\tif (event.request.url) event.request.url = scrubText(event.request.url)\n\t\tdelete event.request.query_string\n\t\tdelete event.request.data\n\t\tdelete event.request.cookies\n\t\t// Sentry derives the browser and OS from the user agent; no other header is needed.\n\t\tconst userAgent = event.request.headers?.[\"User-Agent\"]\n\t\tevent.request.headers = userAgent ? { \"User-Agent\": userAgent } : undefined\n\t}\n\treturn event\n}\n\nfunction scrubDeep(value: unknown, seen = new WeakSet<object>()): unknown {\n\tif (typeof value === \"string\") return scrubText(value)\n\tif (!value || typeof value !== \"object\") return value\n\tif (seen.has(value)) return REDACTED\n\tseen.add(value)\n\tif (Array.isArray(value)) {\n\t\tif (containsMnemonic(value.map((item) => (typeof item === \"string\" ? item : \"\")).join(\" \"))) return REDACTED\n\t\treturn value.map((item) => scrubDeep(item, seen))\n\t}\n\treturn Object.fromEntries(Object.entries(value).map(([key, item]) => [key, scrubDeep(item, seen)]))\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Error reporting shared by the web UI and the desktop shell. Builds only report
3
+ * when CI baked a Sentry DSN into them; operators opt out with
4
+ * `SIMPLEX_ERROR_REPORTING=off`. Everything here is browser-safe.
5
+ */
6
+ declare const ERROR_REPORTING_ENV = "SIMPLEX_ERROR_REPORTING";
7
+ declare function errorReportingDisabled(value: string | undefined): boolean;
8
+ /** The ingest origin a renderer CSP must allow for this DSN, or undefined for a malformed one. */
9
+ declare function sentryIngestOrigin(dsn: string): string | undefined;
10
+ /** Removes recovery phrases, key material and provider URLs from one string. */
11
+ declare function scrubText(text: string): string;
12
+ interface ScrubbableBreadcrumb {
13
+ message?: string;
14
+ data?: Record<string, unknown>;
15
+ }
16
+ interface ScrubbableEvent {
17
+ message?: string;
18
+ logentry?: {
19
+ message?: string;
20
+ params?: unknown[];
21
+ };
22
+ exception?: {
23
+ values?: Array<{
24
+ value?: string;
25
+ }>;
26
+ };
27
+ breadcrumbs?: ScrubbableBreadcrumb[];
28
+ extra?: Record<string, unknown>;
29
+ request?: {
30
+ url?: string;
31
+ query_string?: unknown;
32
+ data?: unknown;
33
+ cookies?: unknown;
34
+ headers?: Record<string, string>;
35
+ };
36
+ }
37
+ /** Scrubs a breadcrumb's message and data in place. */
38
+ declare function scrubBreadcrumb<T extends ScrubbableBreadcrumb>(breadcrumb: T): T;
39
+ /**
40
+ * Scrubs the free-text parts of an event in place. Identifiers (event and
41
+ * trace ids), release names and stack frames stay intact so Sentry can group
42
+ * the event and resolve uploaded source maps.
43
+ */
44
+ declare function scrubEvent<T extends ScrubbableEvent>(event: T): T;
45
+
46
+ export { ERROR_REPORTING_ENV, errorReportingDisabled, scrubBreadcrumb, scrubEvent, scrubText, sentryIngestOrigin };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Error reporting shared by the web UI and the desktop shell. Builds only report
3
+ * when CI baked a Sentry DSN into them; operators opt out with
4
+ * `SIMPLEX_ERROR_REPORTING=off`. Everything here is browser-safe.
5
+ */
6
+ declare const ERROR_REPORTING_ENV = "SIMPLEX_ERROR_REPORTING";
7
+ declare function errorReportingDisabled(value: string | undefined): boolean;
8
+ /** The ingest origin a renderer CSP must allow for this DSN, or undefined for a malformed one. */
9
+ declare function sentryIngestOrigin(dsn: string): string | undefined;
10
+ /** Removes recovery phrases, key material and provider URLs from one string. */
11
+ declare function scrubText(text: string): string;
12
+ interface ScrubbableBreadcrumb {
13
+ message?: string;
14
+ data?: Record<string, unknown>;
15
+ }
16
+ interface ScrubbableEvent {
17
+ message?: string;
18
+ logentry?: {
19
+ message?: string;
20
+ params?: unknown[];
21
+ };
22
+ exception?: {
23
+ values?: Array<{
24
+ value?: string;
25
+ }>;
26
+ };
27
+ breadcrumbs?: ScrubbableBreadcrumb[];
28
+ extra?: Record<string, unknown>;
29
+ request?: {
30
+ url?: string;
31
+ query_string?: unknown;
32
+ data?: unknown;
33
+ cookies?: unknown;
34
+ headers?: Record<string, string>;
35
+ };
36
+ }
37
+ /** Scrubs a breadcrumb's message and data in place. */
38
+ declare function scrubBreadcrumb<T extends ScrubbableBreadcrumb>(breadcrumb: T): T;
39
+ /**
40
+ * Scrubs the free-text parts of an event in place. Identifiers (event and
41
+ * trace ids), release names and stack frames stay intact so Sentry can group
42
+ * the event and resolve uploaded source maps.
43
+ */
44
+ declare function scrubEvent<T extends ScrubbableEvent>(event: T): T;
45
+
46
+ export { ERROR_REPORTING_ENV, errorReportingDisabled, scrubBreadcrumb, scrubEvent, scrubText, sentryIngestOrigin };
@@ -0,0 +1,95 @@
1
+ import { createRequire } from 'module'; const require = createRequire(import.meta.url);
2
+
3
+ // src/config/error-reporting.ts
4
+ import { wordlist } from "@scure/bip39/wordlists/english";
5
+ var ERROR_REPORTING_ENV = "SIMPLEX_ERROR_REPORTING";
6
+ function errorReportingDisabled(value) {
7
+ return ["off", "0", "false", "no"].includes((value ?? "").trim().toLowerCase());
8
+ }
9
+ function sentryIngestOrigin(dsn) {
10
+ try {
11
+ const url = new URL(dsn);
12
+ return url.protocol === "https:" ? url.origin : void 0;
13
+ } catch {
14
+ return void 0;
15
+ }
16
+ }
17
+ var MNEMONIC_WORDS = new Set(wordlist);
18
+ var MNEMONIC_MIN_WORDS = 12;
19
+ var REDACTED = "[redacted]";
20
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "local"]);
21
+ var URL_PATTERN = /\b(?:https?|wss?|simplex|file):\/\/[^\s"'<>`]+/i;
22
+ var FILE_PATH_PATTERN = /(?<=["'])(?:[a-z]:[\\/]|\\\\|\/)[^"'<>`\r\n]+|(?:[a-z]:[\\/]|\\\\|(?<![\w/])\/)[^\s"'<>`]+/;
23
+ var LOCATION_PATTERN = new RegExp(`${URL_PATTERN.source}|${FILE_PATH_PATTERN.source}`, "gi");
24
+ var WORD_RUN_PATTERN = /\b[a-z]+(?:[\s"',\[\]]+[a-z]+){11,}\b/gi;
25
+ var LONG_TOKEN_PATTERN = /[A-Za-z0-9+/_=-]{32,}/g;
26
+ var ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/;
27
+ function scrubUrl(raw) {
28
+ try {
29
+ const url = new URL(raw);
30
+ if (url.protocol === "file:") return REDACTED;
31
+ const path = LOOPBACK_HOSTS.has(url.hostname) ? url.pathname : url.pathname === "/" ? "" : `/${REDACTED}`;
32
+ return `${url.protocol}//${url.host}${path}${url.search || url.hash ? `?${REDACTED}` : ""}`;
33
+ } catch {
34
+ return REDACTED;
35
+ }
36
+ }
37
+ function containsMnemonic(run) {
38
+ let consecutive = 0;
39
+ for (const word of run.toLowerCase().split(/[\s"',\[\]]+/)) {
40
+ consecutive = MNEMONIC_WORDS.has(word) ? consecutive + 1 : 0;
41
+ if (consecutive >= MNEMONIC_MIN_WORDS) return true;
42
+ }
43
+ return false;
44
+ }
45
+ function scrubText(text) {
46
+ const withoutUrls = text.replace(
47
+ LOCATION_PATTERN,
48
+ (location) => URL_PATTERN.test(location) ? scrubUrl(location) : REDACTED
49
+ );
50
+ const withoutMnemonics = withoutUrls.replace(WORD_RUN_PATTERN, (run) => containsMnemonic(run) ? REDACTED : run);
51
+ return withoutMnemonics.replace(LONG_TOKEN_PATTERN, (token) => ADDRESS_PATTERN.test(token) ? token : REDACTED);
52
+ }
53
+ function scrubBreadcrumb(breadcrumb) {
54
+ if (breadcrumb.message) breadcrumb.message = scrubText(breadcrumb.message);
55
+ if (breadcrumb.data) breadcrumb.data = scrubDeep(breadcrumb.data);
56
+ return breadcrumb;
57
+ }
58
+ function scrubEvent(event) {
59
+ if (event.message) event.message = scrubText(event.message);
60
+ if (event.logentry) event.logentry = scrubDeep(event.logentry);
61
+ for (const exception of event.exception?.values ?? []) {
62
+ if (exception.value) exception.value = scrubText(exception.value);
63
+ }
64
+ for (const breadcrumb of event.breadcrumbs ?? []) scrubBreadcrumb(breadcrumb);
65
+ if (event.extra) event.extra = scrubDeep(event.extra);
66
+ if (event.request) {
67
+ if (event.request.url) event.request.url = scrubText(event.request.url);
68
+ delete event.request.query_string;
69
+ delete event.request.data;
70
+ delete event.request.cookies;
71
+ const userAgent = event.request.headers?.["User-Agent"];
72
+ event.request.headers = userAgent ? { "User-Agent": userAgent } : void 0;
73
+ }
74
+ return event;
75
+ }
76
+ function scrubDeep(value, seen = /* @__PURE__ */ new WeakSet()) {
77
+ if (typeof value === "string") return scrubText(value);
78
+ if (!value || typeof value !== "object") return value;
79
+ if (seen.has(value)) return REDACTED;
80
+ seen.add(value);
81
+ if (Array.isArray(value)) {
82
+ if (containsMnemonic(value.map((item) => typeof item === "string" ? item : "").join(" "))) return REDACTED;
83
+ return value.map((item) => scrubDeep(item, seen));
84
+ }
85
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, scrubDeep(item, seen)]));
86
+ }
87
+ export {
88
+ ERROR_REPORTING_ENV,
89
+ errorReportingDisabled,
90
+ scrubBreadcrumb,
91
+ scrubEvent,
92
+ scrubText,
93
+ sentryIngestOrigin
94
+ };
95
+ //# sourceMappingURL=error-reporting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/error-reporting.ts"],"sourcesContent":["import { wordlist } from \"@scure/bip39/wordlists/english\"\n\n/**\n * Error reporting shared by the web UI and the desktop shell. Builds only report\n * when CI baked a Sentry DSN into them; operators opt out with\n * `SIMPLEX_ERROR_REPORTING=off`. Everything here is browser-safe.\n */\nexport const ERROR_REPORTING_ENV = \"SIMPLEX_ERROR_REPORTING\"\n\nexport function errorReportingDisabled(value: string | undefined): boolean {\n\treturn [\"off\", \"0\", \"false\", \"no\"].includes((value ?? \"\").trim().toLowerCase())\n}\n\n/** The ingest origin a renderer CSP must allow for this DSN, or undefined for a malformed one. */\nexport function sentryIngestOrigin(dsn: string): string | undefined {\n\ttry {\n\t\tconst url = new URL(dsn)\n\t\treturn url.protocol === \"https:\" ? url.origin : undefined\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nconst MNEMONIC_WORDS = new Set(wordlist)\nconst MNEMONIC_MIN_WORDS = 12\nconst REDACTED = \"[redacted]\"\nconst LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\", \"local\"])\nconst URL_PATTERN = /\\b(?:https?|wss?|simplex|file):\\/\\/[^\\s\"'<>`]+/i\n// Quoted paths may contain spaces. Match URLs first so local API routes remain readable.\nconst FILE_PATH_PATTERN = /(?<=[\"'])(?:[a-z]:[\\\\/]|\\\\\\\\|\\/)[^\"'<>`\\r\\n]+|(?:[a-z]:[\\\\/]|\\\\\\\\|(?<![\\w/])\\/)[^\\s\"'<>`]+/\nconst LOCATION_PATTERN = new RegExp(`${URL_PATTERN.source}|${FILE_PATH_PATTERN.source}`, \"gi\")\n// Also catches phrases rendered as JSON word arrays or comma-separated words.\nconst WORD_RUN_PATTERN = /\\b[a-z]+(?:[\\s\"',\\[\\]]+[a-z]+){11,}\\b/gi\n// Private keys, signatures, API tokens, base58/base64 secrets and long file paths\n// (which name the operator's home directory). A 0x address stays readable.\nconst LONG_TOKEN_PATTERN = /[A-Za-z0-9+/_=-]{32,}/g\nconst ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/\n\nfunction scrubUrl(raw: string): string {\n\ttry {\n\t\tconst url = new URL(raw)\n\t\tif (url.protocol === \"file:\") return REDACTED\n\t\t// RPC providers carry API keys in the path or query; local solver routes do not.\n\t\tconst path = LOOPBACK_HOSTS.has(url.hostname) ? url.pathname : url.pathname === \"/\" ? \"\" : `/${REDACTED}`\n\t\treturn `${url.protocol}//${url.host}${path}${url.search || url.hash ? `?${REDACTED}` : \"\"}`\n\t} catch {\n\t\treturn REDACTED\n\t}\n}\n\nfunction containsMnemonic(run: string): boolean {\n\tlet consecutive = 0\n\tfor (const word of run.toLowerCase().split(/[\\s\"',\\[\\]]+/)) {\n\t\tconsecutive = MNEMONIC_WORDS.has(word) ? consecutive + 1 : 0\n\t\tif (consecutive >= MNEMONIC_MIN_WORDS) return true\n\t}\n\treturn false\n}\n\n/** Removes recovery phrases, key material and provider URLs from one string. */\nexport function scrubText(text: string): string {\n\tconst withoutUrls = text.replace(LOCATION_PATTERN, (location) =>\n\t\tURL_PATTERN.test(location) ? scrubUrl(location) : REDACTED,\n\t)\n\tconst withoutMnemonics = withoutUrls.replace(WORD_RUN_PATTERN, (run) => (containsMnemonic(run) ? REDACTED : run))\n\treturn withoutMnemonics.replace(LONG_TOKEN_PATTERN, (token) => (ADDRESS_PATTERN.test(token) ? token : REDACTED))\n}\n\ninterface ScrubbableBreadcrumb {\n\tmessage?: string\n\tdata?: Record<string, unknown>\n}\n\ninterface ScrubbableEvent {\n\tmessage?: string\n\tlogentry?: { message?: string; params?: unknown[] }\n\texception?: { values?: Array<{ value?: string }> }\n\tbreadcrumbs?: ScrubbableBreadcrumb[]\n\textra?: Record<string, unknown>\n\trequest?: {\n\t\turl?: string\n\t\tquery_string?: unknown\n\t\tdata?: unknown\n\t\tcookies?: unknown\n\t\theaders?: Record<string, string>\n\t}\n}\n\n/** Scrubs a breadcrumb's message and data in place. */\nexport function scrubBreadcrumb<T extends ScrubbableBreadcrumb>(breadcrumb: T): T {\n\tif (breadcrumb.message) breadcrumb.message = scrubText(breadcrumb.message)\n\tif (breadcrumb.data) breadcrumb.data = scrubDeep(breadcrumb.data) as Record<string, unknown>\n\treturn breadcrumb\n}\n\n/**\n * Scrubs the free-text parts of an event in place. Identifiers (event and\n * trace ids), release names and stack frames stay intact so Sentry can group\n * the event and resolve uploaded source maps.\n */\nexport function scrubEvent<T extends ScrubbableEvent>(event: T): T {\n\tif (event.message) event.message = scrubText(event.message)\n\tif (event.logentry) event.logentry = scrubDeep(event.logentry) as ScrubbableEvent[\"logentry\"]\n\tfor (const exception of event.exception?.values ?? []) {\n\t\tif (exception.value) exception.value = scrubText(exception.value)\n\t}\n\tfor (const breadcrumb of event.breadcrumbs ?? []) scrubBreadcrumb(breadcrumb)\n\tif (event.extra) event.extra = scrubDeep(event.extra) as Record<string, unknown>\n\tif (event.request) {\n\t\tif (event.request.url) event.request.url = scrubText(event.request.url)\n\t\tdelete event.request.query_string\n\t\tdelete event.request.data\n\t\tdelete event.request.cookies\n\t\t// Sentry derives the browser and OS from the user agent; no other header is needed.\n\t\tconst userAgent = event.request.headers?.[\"User-Agent\"]\n\t\tevent.request.headers = userAgent ? { \"User-Agent\": userAgent } : undefined\n\t}\n\treturn event\n}\n\nfunction scrubDeep(value: unknown, seen = new WeakSet<object>()): unknown {\n\tif (typeof value === \"string\") return scrubText(value)\n\tif (!value || typeof value !== \"object\") return value\n\tif (seen.has(value)) return REDACTED\n\tseen.add(value)\n\tif (Array.isArray(value)) {\n\t\tif (containsMnemonic(value.map((item) => (typeof item === \"string\" ? item : \"\")).join(\" \"))) return REDACTED\n\t\treturn value.map((item) => scrubDeep(item, seen))\n\t}\n\treturn Object.fromEntries(Object.entries(value).map(([key, item]) => [key, scrubDeep(item, seen)]))\n}\n"],"mappings":"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","names":[]}