@mushi-mushi/core 1.18.0 → 1.19.1
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/CONTRIBUTING.md +12 -0
- package/README.md +1 -0
- package/dist/index.cjs +260 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -1
- package/dist/index.d.ts +71 -1
- package/dist/index.js +245 -48
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CONTRIBUTING.md
CHANGED
|
@@ -27,12 +27,24 @@ pnpm build
|
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
pnpm dev # Start all dev servers (admin on :6464)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| Surface | URL |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| Admin console | http://localhost:6464 |
|
|
35
|
+
| Docs site | http://localhost:3000 |
|
|
36
|
+
| CLI console override | `MUSHI_CONSOLE_URL=http://localhost:6464` |
|
|
37
|
+
|
|
38
|
+
```bash
|
|
30
39
|
pnpm test # Run Vitest across all packages
|
|
31
40
|
pnpm typecheck # TypeScript checks
|
|
32
41
|
pnpm lint # ESLint
|
|
33
42
|
pnpm format # Prettier
|
|
43
|
+
pnpm smoke:cli-setup # Playwright: onboarding ?setup=cli + success panel (skips headless if unsigned)
|
|
34
44
|
```
|
|
35
45
|
|
|
46
|
+
Set `CLI_SETUP_SMOKE_STRICT=1` to fail the smoke script when Playwright is not signed in (CI gate).
|
|
47
|
+
|
|
36
48
|
Ad-hoc screenshots captured during UI reviews can live temporarily at the repo
|
|
37
49
|
root, but root-level `*.png` files are intentionally ignored. Canonical
|
|
38
50
|
screenshots that should be versioned belong under `docs/screenshots/`.
|
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ Core types, API client, and utilities for the Mushi Mushi SDK.
|
|
|
38
38
|
| `MushiCaptureExceptionOptions` | 1.0+ — options for `Mushi.captureException(err, opts)`: `level`, `tags`, `extras`, `category`, `userIntent` overrides for the structured report. |
|
|
39
39
|
| `NormalisedException` | 1.0+ — return type of `normaliseThrown(err)` (`{ name, message, stack?, cause? }`); used internally by `captureException` and exposed for adapters that want to ship their own thin wrappers. |
|
|
40
40
|
| `MushiBannerLink` | 1.8+ — flat action on the rich banner layout: `{ label, href? }` opens an external URL in a new tab, `{ label, featureRequest: true }` opens the widget in feature-request mode. Consumed via `MushiBannerConfig.links`. |
|
|
41
|
+
| `screenshotSensitiveHint` (on `MushiWidgetConfig`) | 1.19+ — privacy caption under the screenshot preview: `true` = localized default, `string` = custom copy, `false` = hide caption (preview + Remove remain). Console-configurable via `GET /v1/sdk/config`. See [`docs/SDK_SCREENSHOT_PREVIEW.md`](../../docs/SDK_SCREENSHOT_PREVIEW.md). |
|
|
41
42
|
|
|
42
43
|
Constants: `MUSHI_INTERNAL_HEADER` (`'X-Mushi-Internal'`),
|
|
43
44
|
`MUSHI_INTERNAL_INIT_MARKER`, and the `MushiInternalRequestKind` literal union
|
package/dist/index.cjs
CHANGED
|
@@ -43,9 +43,23 @@ function formatBytes(n) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// src/api-client.ts
|
|
46
|
+
var _credWarningEmitted = false;
|
|
46
47
|
var MUSHI_USER_TOKEN_HEADER = "X-Mushi-User-Token";
|
|
48
|
+
var MUSHI_PROJECT_HEADER = "X-Mushi-Project";
|
|
47
49
|
var MUSHI_SDK_PACKAGE_HEADER = "X-Mushi-SDK-Package";
|
|
48
50
|
var MUSHI_SDK_VERSION_HEADER = "X-Mushi-SDK-Version";
|
|
51
|
+
function buildSdkIngestHeaders(opts) {
|
|
52
|
+
const headers = {
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
"X-Mushi-Api-Key": opts.apiKey,
|
|
55
|
+
[MUSHI_PROJECT_HEADER]: opts.projectId
|
|
56
|
+
};
|
|
57
|
+
if (opts.sdkPackage) headers[MUSHI_SDK_PACKAGE_HEADER] = opts.sdkPackage;
|
|
58
|
+
if (opts.sdkVersion) headers[MUSHI_SDK_VERSION_HEADER] = opts.sdkVersion;
|
|
59
|
+
if (opts.userToken) headers[MUSHI_USER_TOKEN_HEADER] = opts.userToken;
|
|
60
|
+
if (opts.internalKind) headers[MUSHI_INTERNAL_HEADER] = opts.internalKind;
|
|
61
|
+
return headers;
|
|
62
|
+
}
|
|
49
63
|
var DEFAULT_API_ENDPOINT = "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api";
|
|
50
64
|
var MUSHI_INTERNAL_HEADER = "X-Mushi-Internal";
|
|
51
65
|
var MUSHI_INTERNAL_INIT_MARKER = "__mushiInternal";
|
|
@@ -72,13 +86,14 @@ function createApiClient(options) {
|
|
|
72
86
|
const response = await fetch(url, {
|
|
73
87
|
method,
|
|
74
88
|
headers: {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
...buildSdkIngestHeaders({
|
|
90
|
+
apiKey,
|
|
91
|
+
projectId,
|
|
92
|
+
sdkPackage,
|
|
93
|
+
sdkVersion,
|
|
94
|
+
userToken,
|
|
95
|
+
internalKind
|
|
96
|
+
}),
|
|
82
97
|
...extraHeaders
|
|
83
98
|
},
|
|
84
99
|
body: body ? JSON.stringify(body) : void 0,
|
|
@@ -98,6 +113,12 @@ function createApiClient(options) {
|
|
|
98
113
|
}
|
|
99
114
|
if (!response.ok) {
|
|
100
115
|
const errorBody = await response.json().catch(() => ({}));
|
|
116
|
+
if ((response.status === 401 || response.status === 403) && !_credWarningEmitted) {
|
|
117
|
+
_credWarningEmitted = true;
|
|
118
|
+
console.error(
|
|
119
|
+
`[Mushi] Credentials rejected (HTTP ${response.status}). Check your Project ID and API key scope (must be "report:write"). Get the correct values at: https://kensaur.us/mushi-mushi/admin/projects`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
101
122
|
if (response.status >= 500 && retries > 0) {
|
|
102
123
|
await sleep(getBackoffDelay(maxRetries - retries));
|
|
103
124
|
return request(method, path, body, retries - 1, internalKind, extraHeaders);
|
|
@@ -1000,6 +1021,30 @@ function createOfflineQueue(config = {}) {
|
|
|
1000
1021
|
lsWrite(rows);
|
|
1001
1022
|
}
|
|
1002
1023
|
}
|
|
1024
|
+
async function removeRow(backend, rowId) {
|
|
1025
|
+
try {
|
|
1026
|
+
if (backend === "indexeddb") await idbDelete(rowId);
|
|
1027
|
+
else lsDelete(rowId);
|
|
1028
|
+
} catch (err) {
|
|
1029
|
+
queueLog.debug("Offline queue: row removal failed (will age out)", {
|
|
1030
|
+
id: rowId,
|
|
1031
|
+
err: String(err)
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
async function persistRow(backend, row) {
|
|
1036
|
+
try {
|
|
1037
|
+
if (backend === "indexeddb") await idbPutRow(row);
|
|
1038
|
+
else lsUpdateRow(row);
|
|
1039
|
+
return true;
|
|
1040
|
+
} catch (err) {
|
|
1041
|
+
queueLog.debug("Offline queue: row persist failed", {
|
|
1042
|
+
id: row.id,
|
|
1043
|
+
err: String(err)
|
|
1044
|
+
});
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1003
1048
|
async function enqueue(report) {
|
|
1004
1049
|
if (!enabled) return;
|
|
1005
1050
|
const currentSize = await size();
|
|
@@ -1045,12 +1090,7 @@ function createOfflineQueue(config = {}) {
|
|
|
1045
1090
|
for (const row of rows) {
|
|
1046
1091
|
if (isExpired(row, now)) {
|
|
1047
1092
|
const rowId = row.id;
|
|
1048
|
-
|
|
1049
|
-
if (backend === "indexeddb") await idbDelete(rowId);
|
|
1050
|
-
else lsDelete(rowId);
|
|
1051
|
-
} catch {
|
|
1052
|
-
lsDelete(rowId);
|
|
1053
|
-
}
|
|
1093
|
+
await removeRow(backend, rowId);
|
|
1054
1094
|
queueLog.debug("Offline queue: evicting stale report", { id: rowId });
|
|
1055
1095
|
} else {
|
|
1056
1096
|
fresh.push(row);
|
|
@@ -1065,23 +1105,13 @@ function createOfflineQueue(config = {}) {
|
|
|
1065
1105
|
const rowId = row.id;
|
|
1066
1106
|
const report = await unwrapForSend(row);
|
|
1067
1107
|
if (!report) {
|
|
1068
|
-
|
|
1069
|
-
if (backend === "indexeddb") await idbDelete(rowId);
|
|
1070
|
-
else lsDelete(rowId);
|
|
1071
|
-
} catch {
|
|
1072
|
-
lsDelete(rowId);
|
|
1073
|
-
}
|
|
1108
|
+
await removeRow(backend, rowId);
|
|
1074
1109
|
failed++;
|
|
1075
1110
|
continue;
|
|
1076
1111
|
}
|
|
1077
1112
|
const result = await client.submitReport(report);
|
|
1078
1113
|
if (result.ok) {
|
|
1079
|
-
|
|
1080
|
-
if (backend === "indexeddb") await idbDelete(rowId);
|
|
1081
|
-
else lsDelete(rowId);
|
|
1082
|
-
} catch {
|
|
1083
|
-
lsDelete(rowId);
|
|
1084
|
-
}
|
|
1114
|
+
await removeRow(backend, rowId);
|
|
1085
1115
|
sent++;
|
|
1086
1116
|
} else {
|
|
1087
1117
|
const permanent = result.error?.code === "HTTP_400" || result.error?.code === "HTTP_413" || result.error?.code === "HTTP_422" || result.error?.code === "INGEST_ERROR" || result.error?.code === "VALIDATION_ERROR" || // A payload that exceeds the size guard will never shrink on its own;
|
|
@@ -1093,21 +1123,11 @@ function createOfflineQueue(config = {}) {
|
|
|
1093
1123
|
);
|
|
1094
1124
|
const transient = !permanent && (result.error?.code === "NETWORK_ERROR" || result.error?.code === "HTTP_403" || result.error?.code === "HTTP_429" || result.error?.code === "HTTP_502" || result.error?.code === "HTTP_503" || result.error?.code === "HTTP_504" || typeof result.error?.code === "string" && result.error.code.startsWith("HTTP_5"));
|
|
1095
1125
|
if (permanent) {
|
|
1096
|
-
|
|
1097
|
-
if (backend === "indexeddb") await idbDelete(rowId);
|
|
1098
|
-
else lsDelete(rowId);
|
|
1099
|
-
} catch {
|
|
1100
|
-
lsDelete(rowId);
|
|
1101
|
-
}
|
|
1126
|
+
await removeRow(backend, rowId);
|
|
1102
1127
|
} else if (transient) {
|
|
1103
1128
|
const nextAttempts = rowAttempts(row) + 1;
|
|
1104
1129
|
if (nextAttempts >= MAX_DELIVERY_ATTEMPTS) {
|
|
1105
|
-
|
|
1106
|
-
if (backend === "indexeddb") await idbDelete(rowId);
|
|
1107
|
-
else lsDelete(rowId);
|
|
1108
|
-
} catch {
|
|
1109
|
-
lsDelete(rowId);
|
|
1110
|
-
}
|
|
1130
|
+
await removeRow(backend, rowId);
|
|
1111
1131
|
queueLog.warn("Offline queue: giving up on report after repeated failures", {
|
|
1112
1132
|
id: rowId,
|
|
1113
1133
|
attempts: nextAttempts,
|
|
@@ -1115,17 +1135,20 @@ function createOfflineQueue(config = {}) {
|
|
|
1115
1135
|
});
|
|
1116
1136
|
} else {
|
|
1117
1137
|
row.attempts = nextAttempts;
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1138
|
+
const persisted = await persistRow(backend, row);
|
|
1139
|
+
if (persisted) {
|
|
1140
|
+
queueLog.debug("Offline queue: transient failure, will retry", {
|
|
1141
|
+
id: rowId,
|
|
1142
|
+
attempts: nextAttempts,
|
|
1143
|
+
code: result.error?.code
|
|
1144
|
+
});
|
|
1145
|
+
} else {
|
|
1146
|
+
await removeRow(backend, rowId);
|
|
1147
|
+
queueLog.warn("Offline queue: dropping report (attempt counter unpersistable)", {
|
|
1148
|
+
id: rowId,
|
|
1149
|
+
code: result.error?.code
|
|
1150
|
+
});
|
|
1123
1151
|
}
|
|
1124
|
-
queueLog.debug("Offline queue: transient failure, will retry", {
|
|
1125
|
-
id: rowId,
|
|
1126
|
-
attempts: nextAttempts,
|
|
1127
|
-
code: result.error?.code
|
|
1128
|
-
});
|
|
1129
1152
|
}
|
|
1130
1153
|
}
|
|
1131
1154
|
failed++;
|
|
@@ -1804,6 +1827,180 @@ function parseIdentityToken(token) {
|
|
|
1804
1827
|
}
|
|
1805
1828
|
}
|
|
1806
1829
|
|
|
1830
|
+
// src/favicon.ts
|
|
1831
|
+
var MULTI_TENANT_PARENT_HOSTS = /* @__PURE__ */ new Set(["kensaur.us", "www.kensaur.us"]);
|
|
1832
|
+
var SLUG_DOMAIN_HINTS = {
|
|
1833
|
+
"glot-it": "glot.it",
|
|
1834
|
+
glotit: "glot.it",
|
|
1835
|
+
"solo-boss-cloud": "sbc-front.vercel.app",
|
|
1836
|
+
"mushi-mushi": "mushimushi.dev"
|
|
1837
|
+
};
|
|
1838
|
+
var SLUG_ICON_URL_HINTS = {
|
|
1839
|
+
"mushi-mushi": "https://kensaur.us/mushi-mushi/admin/favicon.svg",
|
|
1840
|
+
"solo-boss-cloud": "https://sbc-front.vercel.app/favicon.svg",
|
|
1841
|
+
"glot-it": "https://kensaur.us/glot-it/apple-touch-icon.png",
|
|
1842
|
+
glotit: "https://kensaur.us/glot-it/apple-touch-icon.png"
|
|
1843
|
+
};
|
|
1844
|
+
var UNTRUSTED_FAVICON_HOSTS = [
|
|
1845
|
+
"www.google.com",
|
|
1846
|
+
"icons.duckduckgo.com",
|
|
1847
|
+
"icon.horse"
|
|
1848
|
+
];
|
|
1849
|
+
function isUntrustedFaviconUrl(url) {
|
|
1850
|
+
try {
|
|
1851
|
+
const host = new URL(url).hostname.toLowerCase();
|
|
1852
|
+
return UNTRUSTED_FAVICON_HOSTS.some((h) => host === h || host.endsWith(`.${h}`));
|
|
1853
|
+
} catch {
|
|
1854
|
+
return false;
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
function faviconUrlCandidates(domain, _size = 32) {
|
|
1858
|
+
return [
|
|
1859
|
+
`https://${domain}/favicon.svg`,
|
|
1860
|
+
`https://${domain}/favicon.ico`,
|
|
1861
|
+
`https://${domain}/favicon-32x32.png`,
|
|
1862
|
+
`https://${domain}/apple-touch-icon.png`
|
|
1863
|
+
];
|
|
1864
|
+
}
|
|
1865
|
+
function resolveProjectFaviconDomains(source) {
|
|
1866
|
+
const out = [];
|
|
1867
|
+
const add = (d) => {
|
|
1868
|
+
const n = d?.trim().toLowerCase();
|
|
1869
|
+
if (!n || out.includes(n)) return;
|
|
1870
|
+
out.push(n);
|
|
1871
|
+
};
|
|
1872
|
+
add(githubRepoDomainHint(source.repo_url));
|
|
1873
|
+
add(SLUG_DOMAIN_HINTS[source.project_slug.toLowerCase()]);
|
|
1874
|
+
const fromOrigin = source.sdk_origin ? originToDomain(source.sdk_origin) : null;
|
|
1875
|
+
if (fromOrigin && !MULTI_TENANT_PARENT_HOSTS.has(fromOrigin)) {
|
|
1876
|
+
add(fromOrigin);
|
|
1877
|
+
}
|
|
1878
|
+
return out;
|
|
1879
|
+
}
|
|
1880
|
+
function projectFaviconUrlCandidates(source, size = 32) {
|
|
1881
|
+
if (source.icon_url?.trim()) return [source.icon_url.trim()];
|
|
1882
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1883
|
+
const urls = [];
|
|
1884
|
+
const push = (url) => {
|
|
1885
|
+
if (!url || seen.has(url) || isUntrustedFaviconUrl(url)) return;
|
|
1886
|
+
seen.add(url);
|
|
1887
|
+
urls.push(url);
|
|
1888
|
+
};
|
|
1889
|
+
const slugHint = SLUG_ICON_URL_HINTS[source.project_slug.toLowerCase()];
|
|
1890
|
+
if (slugHint) push(slugHint);
|
|
1891
|
+
for (const domain of resolveProjectFaviconDomains(source)) {
|
|
1892
|
+
for (const url of faviconUrlCandidates(domain, size)) {
|
|
1893
|
+
push(url);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
return urls;
|
|
1897
|
+
}
|
|
1898
|
+
function originToDomain(origin) {
|
|
1899
|
+
const trimmed = origin.trim();
|
|
1900
|
+
if (!trimmed) return null;
|
|
1901
|
+
if (/^(capacitor|file|content|app):/i.test(trimmed)) return null;
|
|
1902
|
+
try {
|
|
1903
|
+
const url = new URL(trimmed.includes("://") ? trimmed : `https://${trimmed}`);
|
|
1904
|
+
const host = url.hostname.toLowerCase();
|
|
1905
|
+
if (host === "localhost" || host === "127.0.0.1" || host.endsWith(".local")) {
|
|
1906
|
+
return null;
|
|
1907
|
+
}
|
|
1908
|
+
return host;
|
|
1909
|
+
} catch {
|
|
1910
|
+
return null;
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
function githubRepoDomainHint(repoUrl) {
|
|
1914
|
+
if (!repoUrl?.trim()) return null;
|
|
1915
|
+
try {
|
|
1916
|
+
const u = new URL(repoUrl.trim());
|
|
1917
|
+
const host = u.hostname.toLowerCase();
|
|
1918
|
+
const parts = u.pathname.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
|
|
1919
|
+
if (parts.length < 2) return null;
|
|
1920
|
+
const repo = parts[1].replace(/\.git$/i, "").toLowerCase();
|
|
1921
|
+
if (/^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(repo)) return repo;
|
|
1922
|
+
if (host === "github.com" && repo.endsWith(".github.io")) return repo;
|
|
1923
|
+
if (host !== "github.com" && host !== "www.github.com") return host;
|
|
1924
|
+
return null;
|
|
1925
|
+
} catch {
|
|
1926
|
+
return null;
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
function resolveProjectDomain(source) {
|
|
1930
|
+
return resolveProjectFaviconDomains(source)[0] ?? null;
|
|
1931
|
+
}
|
|
1932
|
+
function isLikelyGenericFavicon(img) {
|
|
1933
|
+
if (typeof document === "undefined") return false;
|
|
1934
|
+
if (!img.complete || img.naturalWidth === 0 || img.naturalHeight === 0) return false;
|
|
1935
|
+
try {
|
|
1936
|
+
const canvas = document.createElement("canvas");
|
|
1937
|
+
const w = img.naturalWidth;
|
|
1938
|
+
const h = img.naturalHeight;
|
|
1939
|
+
canvas.width = w;
|
|
1940
|
+
canvas.height = h;
|
|
1941
|
+
const ctx = canvas.getContext("2d");
|
|
1942
|
+
if (!ctx) return false;
|
|
1943
|
+
ctx.drawImage(img, 0, 0);
|
|
1944
|
+
const { data } = ctx.getImageData(0, 0, w, h);
|
|
1945
|
+
let opaque = 0;
|
|
1946
|
+
let satSum = 0;
|
|
1947
|
+
let maxDiff = 0;
|
|
1948
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
1949
|
+
const r = data[i];
|
|
1950
|
+
const g = data[i + 1];
|
|
1951
|
+
const b = data[i + 2];
|
|
1952
|
+
const a = data[i + 3];
|
|
1953
|
+
if (a < 128) continue;
|
|
1954
|
+
opaque++;
|
|
1955
|
+
const max = Math.max(r, g, b);
|
|
1956
|
+
const min = Math.min(r, g, b);
|
|
1957
|
+
const diff = max - min;
|
|
1958
|
+
maxDiff = Math.max(maxDiff, diff);
|
|
1959
|
+
satSum += diff;
|
|
1960
|
+
}
|
|
1961
|
+
if (opaque === 0) return true;
|
|
1962
|
+
const avgSat = satSum / opaque;
|
|
1963
|
+
return avgSat < 14 && maxDiff < 24;
|
|
1964
|
+
} catch {
|
|
1965
|
+
return isUntrustedFaviconUrl(img.src);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
function projectInitials(name) {
|
|
1969
|
+
const parts = name.trim().split(/[\s._-]+/).filter(Boolean);
|
|
1970
|
+
if (parts.length >= 2) {
|
|
1971
|
+
return `${parts[0][0] ?? ""}${parts[1][0] ?? ""}`.toUpperCase();
|
|
1972
|
+
}
|
|
1973
|
+
const compact = name.replace(/[^a-zA-Z0-9]/g, "");
|
|
1974
|
+
return (compact.slice(0, 2) || name.slice(0, 2) || "?").toUpperCase();
|
|
1975
|
+
}
|
|
1976
|
+
var INITIALS_CHIP_THEMES = [
|
|
1977
|
+
"info",
|
|
1978
|
+
"brand",
|
|
1979
|
+
"warn",
|
|
1980
|
+
"ok",
|
|
1981
|
+
"accent"
|
|
1982
|
+
];
|
|
1983
|
+
function projectInitialsThemeIndex(projectId) {
|
|
1984
|
+
let hash = 0;
|
|
1985
|
+
for (let i = 0; i < projectId.length; i++) {
|
|
1986
|
+
hash = (hash + projectId.charCodeAt(i)) % INITIALS_CHIP_THEMES.length;
|
|
1987
|
+
}
|
|
1988
|
+
return hash;
|
|
1989
|
+
}
|
|
1990
|
+
function readPageFaviconHref() {
|
|
1991
|
+
if (typeof document === "undefined") return null;
|
|
1992
|
+
const selectors = [
|
|
1993
|
+
'link[rel="icon"]',
|
|
1994
|
+
'link[rel="shortcut icon"]',
|
|
1995
|
+
'link[rel="apple-touch-icon"]'
|
|
1996
|
+
];
|
|
1997
|
+
for (const sel of selectors) {
|
|
1998
|
+
const link = document.querySelector(sel);
|
|
1999
|
+
if (link?.href) return link.href;
|
|
2000
|
+
}
|
|
2001
|
+
return null;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
1807
2004
|
exports.DEFAULT_API_ENDPOINT = DEFAULT_API_ENDPOINT;
|
|
1808
2005
|
exports.MAX_REPORT_PAYLOAD_BYTES = MAX_REPORT_PAYLOAD_BYTES;
|
|
1809
2006
|
exports.MAX_SCREENSHOT_DATA_URL_BYTES = MAX_SCREENSHOT_DATA_URL_BYTES;
|
|
@@ -1815,12 +2012,17 @@ exports.MUSHI_IDENTITY_TOKEN_PREFIX = MUSHI_IDENTITY_TOKEN_PREFIX;
|
|
|
1815
2012
|
exports.MUSHI_INTERNAL_HEADER = MUSHI_INTERNAL_HEADER;
|
|
1816
2013
|
exports.MUSHI_INTERNAL_INIT_MARKER = MUSHI_INTERNAL_INIT_MARKER;
|
|
1817
2014
|
exports.MUSHI_MOTION = MUSHI_MOTION;
|
|
2015
|
+
exports.MUSHI_PROJECT_HEADER = MUSHI_PROJECT_HEADER;
|
|
1818
2016
|
exports.MUSHI_RADIUS = MUSHI_RADIUS;
|
|
2017
|
+
exports.MUSHI_SDK_PACKAGE_HEADER = MUSHI_SDK_PACKAGE_HEADER;
|
|
2018
|
+
exports.MUSHI_SDK_VERSION_HEADER = MUSHI_SDK_VERSION_HEADER;
|
|
1819
2019
|
exports.MUSHI_SPACING = MUSHI_SPACING;
|
|
1820
2020
|
exports.MUSHI_TYPE = MUSHI_TYPE;
|
|
2021
|
+
exports.MUSHI_USER_TOKEN_HEADER = MUSHI_USER_TOKEN_HEADER;
|
|
1821
2022
|
exports.MUSHI_Z = MUSHI_Z;
|
|
1822
2023
|
exports.REGION_ENDPOINTS = REGION_ENDPOINTS;
|
|
1823
2024
|
exports.buildIdentityClaims = buildIdentityClaims;
|
|
2025
|
+
exports.buildSdkIngestHeaders = buildSdkIngestHeaders;
|
|
1824
2026
|
exports.captureEnvironment = captureEnvironment;
|
|
1825
2027
|
exports.checkReportPayloadSize = checkReportPayloadSize;
|
|
1826
2028
|
exports.createApiClient = createApiClient;
|
|
@@ -1831,17 +2033,28 @@ exports.createPiiScrubber = createPiiScrubber;
|
|
|
1831
2033
|
exports.createPreFilter = createPreFilter;
|
|
1832
2034
|
exports.createRateLimiter = createRateLimiter;
|
|
1833
2035
|
exports.estimateJsonBytes = estimateJsonBytes;
|
|
2036
|
+
exports.faviconUrlCandidates = faviconUrlCandidates;
|
|
1834
2037
|
exports.formatBytes = formatBytes;
|
|
1835
2038
|
exports.getDeviceFingerprintHash = getDeviceFingerprintHash;
|
|
1836
2039
|
exports.getReporterToken = getReporterToken;
|
|
1837
2040
|
exports.getSessionId = getSessionId;
|
|
2041
|
+
exports.githubRepoDomainHint = githubRepoDomainHint;
|
|
2042
|
+
exports.isLikelyGenericFavicon = isLikelyGenericFavicon;
|
|
2043
|
+
exports.isUntrustedFaviconUrl = isUntrustedFaviconUrl;
|
|
1838
2044
|
exports.mushiPalette = mushiPalette;
|
|
1839
2045
|
exports.mushiTokens = mushiTokens;
|
|
1840
2046
|
exports.newUuid = newUuid;
|
|
1841
2047
|
exports.noopLogger = noopLogger;
|
|
1842
2048
|
exports.normaliseThrown = normaliseThrown;
|
|
2049
|
+
exports.originToDomain = originToDomain;
|
|
1843
2050
|
exports.parseIdentityToken = parseIdentityToken;
|
|
2051
|
+
exports.projectFaviconUrlCandidates = projectFaviconUrlCandidates;
|
|
2052
|
+
exports.projectInitials = projectInitials;
|
|
2053
|
+
exports.projectInitialsThemeIndex = projectInitialsThemeIndex;
|
|
2054
|
+
exports.readPageFaviconHref = readPageFaviconHref;
|
|
1844
2055
|
exports.resolveEnvConfig = resolveEnvConfig;
|
|
2056
|
+
exports.resolveProjectDomain = resolveProjectDomain;
|
|
2057
|
+
exports.resolveProjectFaviconDomains = resolveProjectFaviconDomains;
|
|
1845
2058
|
exports.resolveRegionEndpoint = resolveRegionEndpoint;
|
|
1846
2059
|
exports.scrubPii = scrubPii;
|
|
1847
2060
|
//# sourceMappingURL=index.cjs.map
|