@mushi-mushi/core 1.19.0 → 1.19.2
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 +229 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.js +214 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1536,6 +1536,8 @@ interface MushiCrossAppReport {
|
|
|
1536
1536
|
project_id: string | null;
|
|
1537
1537
|
app_name: string | null;
|
|
1538
1538
|
app_slug: string | null;
|
|
1539
|
+
/** Hostname from latest SDK heartbeat — used for favicon in cross-app list. */
|
|
1540
|
+
app_domain?: string | null;
|
|
1539
1541
|
}
|
|
1540
1542
|
/** A single entry in the global tester leaderboard. */
|
|
1541
1543
|
interface MushiLeaderboardEntry {
|
|
@@ -1557,6 +1559,22 @@ interface MushiTesterReputation {
|
|
|
1557
1559
|
total_points: number;
|
|
1558
1560
|
}
|
|
1559
1561
|
|
|
1562
|
+
/** Header carrying the signed end-user identity token (verified server-side). */
|
|
1563
|
+
declare const MUSHI_USER_TOKEN_HEADER = "X-Mushi-User-Token";
|
|
1564
|
+
/** Project scope header — paired with the API key on every SDK request. */
|
|
1565
|
+
declare const MUSHI_PROJECT_HEADER = "X-Mushi-Project";
|
|
1566
|
+
/** Build-time SDK package identity — recorded on every authenticated heartbeat. */
|
|
1567
|
+
declare const MUSHI_SDK_PACKAGE_HEADER = "X-Mushi-SDK-Package";
|
|
1568
|
+
declare const MUSHI_SDK_VERSION_HEADER = "X-Mushi-SDK-Version";
|
|
1569
|
+
/** Build the standard authenticated SDK ingest headers (shared by api-client + offline flush). */
|
|
1570
|
+
declare function buildSdkIngestHeaders(opts: {
|
|
1571
|
+
apiKey: string;
|
|
1572
|
+
projectId: string;
|
|
1573
|
+
sdkPackage?: string;
|
|
1574
|
+
sdkVersion?: string;
|
|
1575
|
+
userToken?: string | null;
|
|
1576
|
+
internalKind?: MushiInternalRequestKind;
|
|
1577
|
+
}): Record<string, string>;
|
|
1560
1578
|
interface ApiClientOptions {
|
|
1561
1579
|
projectId: string;
|
|
1562
1580
|
apiKey: string;
|
|
@@ -2148,4 +2166,50 @@ declare function buildIdentityClaims(input: {
|
|
|
2148
2166
|
*/
|
|
2149
2167
|
declare function parseIdentityToken(token: string): MushiIdentityClaims | null;
|
|
2150
2168
|
|
|
2151
|
-
|
|
2169
|
+
/**
|
|
2170
|
+
* Shared favicon resolution helpers for admin console and SDK widget.
|
|
2171
|
+
*/
|
|
2172
|
+
interface ProjectFaviconSource {
|
|
2173
|
+
project_id: string;
|
|
2174
|
+
project_name: string;
|
|
2175
|
+
project_slug: string;
|
|
2176
|
+
/** Operator override — wins over inferred domain when set. */
|
|
2177
|
+
icon_url?: string | null;
|
|
2178
|
+
/** e.g. https://kensaur.us — from SDK heartbeat or API key last_seen_origin */
|
|
2179
|
+
sdk_origin?: string | null;
|
|
2180
|
+
/** Connected GitHub/GitLab repo — used when SDK has not heartbeated yet. */
|
|
2181
|
+
repo_url?: string | null;
|
|
2182
|
+
}
|
|
2183
|
+
declare function isUntrustedFaviconUrl(url: string): boolean;
|
|
2184
|
+
/** Per-domain favicon URLs — first-party assets only (no CDN globes). */
|
|
2185
|
+
declare function faviconUrlCandidates(domain: string, _size?: number): string[];
|
|
2186
|
+
/** Ordered unique domains to try for a project (most specific first). */
|
|
2187
|
+
declare function resolveProjectFaviconDomains(source: ProjectFaviconSource): string[];
|
|
2188
|
+
/** Flat URL list across all candidate domains for a project. */
|
|
2189
|
+
declare function projectFaviconUrlCandidates(source: ProjectFaviconSource, size?: number): string[];
|
|
2190
|
+
declare function originToDomain(origin: string): string | null;
|
|
2191
|
+
/** Derive a production hostname from a connected repo URL when possible. */
|
|
2192
|
+
declare function githubRepoDomainHint(repoUrl: string | null | undefined): string | null;
|
|
2193
|
+
/** Best single domain — first entry from {@link resolveProjectFaviconDomains}. */
|
|
2194
|
+
declare function resolveProjectDomain(source: ProjectFaviconSource): string | null;
|
|
2195
|
+
/**
|
|
2196
|
+
* Google's favicon CDN returns HTTP 200 with a gray globe when it has no icon.
|
|
2197
|
+
* Treat low-saturation loaded images as "miss" so callers try the next URL or initials.
|
|
2198
|
+
*/
|
|
2199
|
+
declare function isLikelyGenericFavicon(img: HTMLImageElement): boolean;
|
|
2200
|
+
/** Two-letter initials for favicon fallback chips. */
|
|
2201
|
+
declare function projectInitials(name: string): string;
|
|
2202
|
+
/** Stable theme index (0–4) for initials chip coloring. */
|
|
2203
|
+
declare function projectInitialsThemeIndex(projectId: string): number;
|
|
2204
|
+
/**
|
|
2205
|
+
* Read the host page favicon href (web SDK only).
|
|
2206
|
+
*
|
|
2207
|
+
* Security: the host page declares this value, so it could be a `data:`,
|
|
2208
|
+
* `blob:`, or `javascript:` URL. Since the result is rendered into an
|
|
2209
|
+
* `<img src>` inside the widget, only same-trust http(s) (and protocol-relative)
|
|
2210
|
+
* URLs are returned; anything else falls back to `null` so the widget renders
|
|
2211
|
+
* its default mark instead of an unvalidated, host-controlled resource.
|
|
2212
|
+
*/
|
|
2213
|
+
declare function readPageFaviconHref(): string | null;
|
|
2214
|
+
|
|
2215
|
+
export { type ApiClientOptions, type BreadcrumbBuffer, type BreadcrumbBufferOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MAX_REPORT_PAYLOAD_BYTES, MAX_SCREENSHOT_DATA_URL_BYTES, MUSHI_COLORS_DARK, MUSHI_COLORS_LIGHT, MUSHI_COPY, MUSHI_GEOMETRY, MUSHI_IDENTITY_TOKEN_PREFIX, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, MUSHI_MOTION, MUSHI_PROJECT_HEADER, MUSHI_RADIUS, MUSHI_SDK_PACKAGE_HEADER, MUSHI_SDK_VERSION_HEADER, MUSHI_SPACING, MUSHI_TYPE, MUSHI_USER_TOKEN_HEADER, MUSHI_Z, type MushiActivityEvent, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiAssistantConfig, type MushiAssistantReply, type MushiAssistantStep, type MushiBannerConfig, type MushiBannerLink, type MushiBetaChangelogEntry, type MushiBetaModeConfig, type MushiBreadcrumb, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiCaptureExceptionOptions, type MushiColorPalette, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiCrossAppReport, type MushiCustomCategory, type MushiDiagnosticsResult, type MushiDiscoverInventoryConfig, type MushiDiscoveryEventPayload, type MushiEnvironment, type MushiEventHandler, type MushiEventType, type MushiHallOfFameEntry, type MushiIdentityClaims, type MushiIntegrationsConfig, type MushiInternalRequestKind, type MushiLeaderboardEntry, type MushiNetworkEntry, type MushiOfflineConfig, type MushiOnDeviceClassifier, type MushiOnDeviceClassifierInput, type MushiOnDeviceClassifierResult, type MushiPageContext, type MushiPerformanceMetrics, type MushiPreFilterConfig, type MushiPreset, type MushiPrivacyConfig, type MushiProactiveConfig, type MushiRegion, type MushiReport, type MushiReportBuilder, type MushiReportCategory, type MushiReportStatus, type MushiReporterComment, type MushiReporterReport, type MushiReputationResult, type MushiRewardsConfig, type MushiRuntimeSdkConfig, type MushiSDKInstance, type MushiSdkVersionInfo, type MushiSelectedElement, type MushiSentryConfig, type MushiSentryContext, type MushiTesterReputation, type MushiThemeMode, type MushiTierResult, type MushiTimelineEntry, type MushiTimelineKind, type MushiTokenSnapshot, type MushiTracePropagationConfig, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type NormalisedException, type OfflineQueue, type PayloadGuardResult, type PiiScrubberConfig, type PreFilterResult, type ProjectFaviconSource, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, type ResolvedEnvConfig, buildIdentityClaims, buildSdkIngestHeaders, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, faviconUrlCandidates, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, githubRepoDomainHint, isLikelyGenericFavicon, isUntrustedFaviconUrl, mushiPalette, mushiTokens, newUuid, noopLogger, normaliseThrown, originToDomain, parseIdentityToken, projectFaviconUrlCandidates, projectInitials, projectInitialsThemeIndex, readPageFaviconHref, resolveEnvConfig, resolveProjectDomain, resolveProjectFaviconDomains, resolveRegionEndpoint, scrubPii };
|
package/dist/index.d.ts
CHANGED
|
@@ -1536,6 +1536,8 @@ interface MushiCrossAppReport {
|
|
|
1536
1536
|
project_id: string | null;
|
|
1537
1537
|
app_name: string | null;
|
|
1538
1538
|
app_slug: string | null;
|
|
1539
|
+
/** Hostname from latest SDK heartbeat — used for favicon in cross-app list. */
|
|
1540
|
+
app_domain?: string | null;
|
|
1539
1541
|
}
|
|
1540
1542
|
/** A single entry in the global tester leaderboard. */
|
|
1541
1543
|
interface MushiLeaderboardEntry {
|
|
@@ -1557,6 +1559,22 @@ interface MushiTesterReputation {
|
|
|
1557
1559
|
total_points: number;
|
|
1558
1560
|
}
|
|
1559
1561
|
|
|
1562
|
+
/** Header carrying the signed end-user identity token (verified server-side). */
|
|
1563
|
+
declare const MUSHI_USER_TOKEN_HEADER = "X-Mushi-User-Token";
|
|
1564
|
+
/** Project scope header — paired with the API key on every SDK request. */
|
|
1565
|
+
declare const MUSHI_PROJECT_HEADER = "X-Mushi-Project";
|
|
1566
|
+
/** Build-time SDK package identity — recorded on every authenticated heartbeat. */
|
|
1567
|
+
declare const MUSHI_SDK_PACKAGE_HEADER = "X-Mushi-SDK-Package";
|
|
1568
|
+
declare const MUSHI_SDK_VERSION_HEADER = "X-Mushi-SDK-Version";
|
|
1569
|
+
/** Build the standard authenticated SDK ingest headers (shared by api-client + offline flush). */
|
|
1570
|
+
declare function buildSdkIngestHeaders(opts: {
|
|
1571
|
+
apiKey: string;
|
|
1572
|
+
projectId: string;
|
|
1573
|
+
sdkPackage?: string;
|
|
1574
|
+
sdkVersion?: string;
|
|
1575
|
+
userToken?: string | null;
|
|
1576
|
+
internalKind?: MushiInternalRequestKind;
|
|
1577
|
+
}): Record<string, string>;
|
|
1560
1578
|
interface ApiClientOptions {
|
|
1561
1579
|
projectId: string;
|
|
1562
1580
|
apiKey: string;
|
|
@@ -2148,4 +2166,50 @@ declare function buildIdentityClaims(input: {
|
|
|
2148
2166
|
*/
|
|
2149
2167
|
declare function parseIdentityToken(token: string): MushiIdentityClaims | null;
|
|
2150
2168
|
|
|
2151
|
-
|
|
2169
|
+
/**
|
|
2170
|
+
* Shared favicon resolution helpers for admin console and SDK widget.
|
|
2171
|
+
*/
|
|
2172
|
+
interface ProjectFaviconSource {
|
|
2173
|
+
project_id: string;
|
|
2174
|
+
project_name: string;
|
|
2175
|
+
project_slug: string;
|
|
2176
|
+
/** Operator override — wins over inferred domain when set. */
|
|
2177
|
+
icon_url?: string | null;
|
|
2178
|
+
/** e.g. https://kensaur.us — from SDK heartbeat or API key last_seen_origin */
|
|
2179
|
+
sdk_origin?: string | null;
|
|
2180
|
+
/** Connected GitHub/GitLab repo — used when SDK has not heartbeated yet. */
|
|
2181
|
+
repo_url?: string | null;
|
|
2182
|
+
}
|
|
2183
|
+
declare function isUntrustedFaviconUrl(url: string): boolean;
|
|
2184
|
+
/** Per-domain favicon URLs — first-party assets only (no CDN globes). */
|
|
2185
|
+
declare function faviconUrlCandidates(domain: string, _size?: number): string[];
|
|
2186
|
+
/** Ordered unique domains to try for a project (most specific first). */
|
|
2187
|
+
declare function resolveProjectFaviconDomains(source: ProjectFaviconSource): string[];
|
|
2188
|
+
/** Flat URL list across all candidate domains for a project. */
|
|
2189
|
+
declare function projectFaviconUrlCandidates(source: ProjectFaviconSource, size?: number): string[];
|
|
2190
|
+
declare function originToDomain(origin: string): string | null;
|
|
2191
|
+
/** Derive a production hostname from a connected repo URL when possible. */
|
|
2192
|
+
declare function githubRepoDomainHint(repoUrl: string | null | undefined): string | null;
|
|
2193
|
+
/** Best single domain — first entry from {@link resolveProjectFaviconDomains}. */
|
|
2194
|
+
declare function resolveProjectDomain(source: ProjectFaviconSource): string | null;
|
|
2195
|
+
/**
|
|
2196
|
+
* Google's favicon CDN returns HTTP 200 with a gray globe when it has no icon.
|
|
2197
|
+
* Treat low-saturation loaded images as "miss" so callers try the next URL or initials.
|
|
2198
|
+
*/
|
|
2199
|
+
declare function isLikelyGenericFavicon(img: HTMLImageElement): boolean;
|
|
2200
|
+
/** Two-letter initials for favicon fallback chips. */
|
|
2201
|
+
declare function projectInitials(name: string): string;
|
|
2202
|
+
/** Stable theme index (0–4) for initials chip coloring. */
|
|
2203
|
+
declare function projectInitialsThemeIndex(projectId: string): number;
|
|
2204
|
+
/**
|
|
2205
|
+
* Read the host page favicon href (web SDK only).
|
|
2206
|
+
*
|
|
2207
|
+
* Security: the host page declares this value, so it could be a `data:`,
|
|
2208
|
+
* `blob:`, or `javascript:` URL. Since the result is rendered into an
|
|
2209
|
+
* `<img src>` inside the widget, only same-trust http(s) (and protocol-relative)
|
|
2210
|
+
* URLs are returned; anything else falls back to `null` so the widget renders
|
|
2211
|
+
* its default mark instead of an unvalidated, host-controlled resource.
|
|
2212
|
+
*/
|
|
2213
|
+
declare function readPageFaviconHref(): string | null;
|
|
2214
|
+
|
|
2215
|
+
export { type ApiClientOptions, type BreadcrumbBuffer, type BreadcrumbBufferOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MAX_REPORT_PAYLOAD_BYTES, MAX_SCREENSHOT_DATA_URL_BYTES, MUSHI_COLORS_DARK, MUSHI_COLORS_LIGHT, MUSHI_COPY, MUSHI_GEOMETRY, MUSHI_IDENTITY_TOKEN_PREFIX, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, MUSHI_MOTION, MUSHI_PROJECT_HEADER, MUSHI_RADIUS, MUSHI_SDK_PACKAGE_HEADER, MUSHI_SDK_VERSION_HEADER, MUSHI_SPACING, MUSHI_TYPE, MUSHI_USER_TOKEN_HEADER, MUSHI_Z, type MushiActivityEvent, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiAssistantConfig, type MushiAssistantReply, type MushiAssistantStep, type MushiBannerConfig, type MushiBannerLink, type MushiBetaChangelogEntry, type MushiBetaModeConfig, type MushiBreadcrumb, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiCaptureExceptionOptions, type MushiColorPalette, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiCrossAppReport, type MushiCustomCategory, type MushiDiagnosticsResult, type MushiDiscoverInventoryConfig, type MushiDiscoveryEventPayload, type MushiEnvironment, type MushiEventHandler, type MushiEventType, type MushiHallOfFameEntry, type MushiIdentityClaims, type MushiIntegrationsConfig, type MushiInternalRequestKind, type MushiLeaderboardEntry, type MushiNetworkEntry, type MushiOfflineConfig, type MushiOnDeviceClassifier, type MushiOnDeviceClassifierInput, type MushiOnDeviceClassifierResult, type MushiPageContext, type MushiPerformanceMetrics, type MushiPreFilterConfig, type MushiPreset, type MushiPrivacyConfig, type MushiProactiveConfig, type MushiRegion, type MushiReport, type MushiReportBuilder, type MushiReportCategory, type MushiReportStatus, type MushiReporterComment, type MushiReporterReport, type MushiReputationResult, type MushiRewardsConfig, type MushiRuntimeSdkConfig, type MushiSDKInstance, type MushiSdkVersionInfo, type MushiSelectedElement, type MushiSentryConfig, type MushiSentryContext, type MushiTesterReputation, type MushiThemeMode, type MushiTierResult, type MushiTimelineEntry, type MushiTimelineKind, type MushiTokenSnapshot, type MushiTracePropagationConfig, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type NormalisedException, type OfflineQueue, type PayloadGuardResult, type PiiScrubberConfig, type PreFilterResult, type ProjectFaviconSource, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, type ResolvedEnvConfig, buildIdentityClaims, buildSdkIngestHeaders, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, faviconUrlCandidates, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, githubRepoDomainHint, isLikelyGenericFavicon, isUntrustedFaviconUrl, mushiPalette, mushiTokens, newUuid, noopLogger, normaliseThrown, originToDomain, parseIdentityToken, projectFaviconUrlCandidates, projectInitials, projectInitialsThemeIndex, readPageFaviconHref, resolveEnvConfig, resolveProjectDomain, resolveProjectFaviconDomains, resolveRegionEndpoint, scrubPii };
|
package/dist/index.js
CHANGED
|
@@ -41,9 +41,23 @@ function formatBytes(n) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// src/api-client.ts
|
|
44
|
+
var _credWarningEmitted = false;
|
|
44
45
|
var MUSHI_USER_TOKEN_HEADER = "X-Mushi-User-Token";
|
|
46
|
+
var MUSHI_PROJECT_HEADER = "X-Mushi-Project";
|
|
45
47
|
var MUSHI_SDK_PACKAGE_HEADER = "X-Mushi-SDK-Package";
|
|
46
48
|
var MUSHI_SDK_VERSION_HEADER = "X-Mushi-SDK-Version";
|
|
49
|
+
function buildSdkIngestHeaders(opts) {
|
|
50
|
+
const headers = {
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
"X-Mushi-Api-Key": opts.apiKey,
|
|
53
|
+
[MUSHI_PROJECT_HEADER]: opts.projectId
|
|
54
|
+
};
|
|
55
|
+
if (opts.sdkPackage) headers[MUSHI_SDK_PACKAGE_HEADER] = opts.sdkPackage;
|
|
56
|
+
if (opts.sdkVersion) headers[MUSHI_SDK_VERSION_HEADER] = opts.sdkVersion;
|
|
57
|
+
if (opts.userToken) headers[MUSHI_USER_TOKEN_HEADER] = opts.userToken;
|
|
58
|
+
if (opts.internalKind) headers[MUSHI_INTERNAL_HEADER] = opts.internalKind;
|
|
59
|
+
return headers;
|
|
60
|
+
}
|
|
47
61
|
var DEFAULT_API_ENDPOINT = "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api";
|
|
48
62
|
var MUSHI_INTERNAL_HEADER = "X-Mushi-Internal";
|
|
49
63
|
var MUSHI_INTERNAL_INIT_MARKER = "__mushiInternal";
|
|
@@ -70,13 +84,14 @@ function createApiClient(options) {
|
|
|
70
84
|
const response = await fetch(url, {
|
|
71
85
|
method,
|
|
72
86
|
headers: {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
87
|
+
...buildSdkIngestHeaders({
|
|
88
|
+
apiKey,
|
|
89
|
+
projectId,
|
|
90
|
+
sdkPackage,
|
|
91
|
+
sdkVersion,
|
|
92
|
+
userToken,
|
|
93
|
+
internalKind
|
|
94
|
+
}),
|
|
80
95
|
...extraHeaders
|
|
81
96
|
},
|
|
82
97
|
body: body ? JSON.stringify(body) : void 0,
|
|
@@ -96,6 +111,13 @@ function createApiClient(options) {
|
|
|
96
111
|
}
|
|
97
112
|
if (!response.ok) {
|
|
98
113
|
const errorBody = await response.json().catch(() => ({}));
|
|
114
|
+
if ((response.status === 401 || response.status === 403) && !_credWarningEmitted) {
|
|
115
|
+
_credWarningEmitted = true;
|
|
116
|
+
const where = baseUrl === DEFAULT_API_ENDPOINT ? "Get the correct values at: https://kensaur.us/mushi-mushi/admin/projects" : "Get the correct values from your Mushi console's Projects page.";
|
|
117
|
+
console.error(
|
|
118
|
+
`[Mushi] Credentials rejected (HTTP ${response.status}). Check your Project ID and API key scope (must be "report:write"). ` + where
|
|
119
|
+
);
|
|
120
|
+
}
|
|
99
121
|
if (response.status >= 500 && retries > 0) {
|
|
100
122
|
await sleep(getBackoffDelay(maxRetries - retries));
|
|
101
123
|
return request(method, path, body, retries - 1, internalKind, extraHeaders);
|
|
@@ -1804,6 +1826,190 @@ function parseIdentityToken(token) {
|
|
|
1804
1826
|
}
|
|
1805
1827
|
}
|
|
1806
1828
|
|
|
1807
|
-
|
|
1829
|
+
// src/favicon.ts
|
|
1830
|
+
var MULTI_TENANT_PARENT_HOSTS = /* @__PURE__ */ new Set(["kensaur.us", "www.kensaur.us"]);
|
|
1831
|
+
var SLUG_DOMAIN_HINTS = {
|
|
1832
|
+
"glot-it": "glot.it",
|
|
1833
|
+
glotit: "glot.it",
|
|
1834
|
+
"solo-boss-cloud": "sbc-front.vercel.app",
|
|
1835
|
+
"mushi-mushi": "mushimushi.dev"
|
|
1836
|
+
};
|
|
1837
|
+
var SLUG_ICON_URL_HINTS = {
|
|
1838
|
+
"mushi-mushi": "https://kensaur.us/mushi-mushi/admin/favicon.svg",
|
|
1839
|
+
"solo-boss-cloud": "https://sbc-front.vercel.app/favicon.svg",
|
|
1840
|
+
"glot-it": "https://kensaur.us/glot-it/apple-touch-icon.png",
|
|
1841
|
+
glotit: "https://kensaur.us/glot-it/apple-touch-icon.png"
|
|
1842
|
+
};
|
|
1843
|
+
var UNTRUSTED_FAVICON_HOSTS = [
|
|
1844
|
+
"www.google.com",
|
|
1845
|
+
"icons.duckduckgo.com",
|
|
1846
|
+
"icon.horse"
|
|
1847
|
+
];
|
|
1848
|
+
function isUntrustedFaviconUrl(url) {
|
|
1849
|
+
try {
|
|
1850
|
+
const host = new URL(url).hostname.toLowerCase();
|
|
1851
|
+
return UNTRUSTED_FAVICON_HOSTS.some((h) => host === h || host.endsWith(`.${h}`));
|
|
1852
|
+
} catch {
|
|
1853
|
+
return false;
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
function faviconUrlCandidates(domain, _size = 32) {
|
|
1857
|
+
return [
|
|
1858
|
+
`https://${domain}/favicon.svg`,
|
|
1859
|
+
`https://${domain}/favicon.ico`,
|
|
1860
|
+
`https://${domain}/favicon-32x32.png`,
|
|
1861
|
+
`https://${domain}/apple-touch-icon.png`
|
|
1862
|
+
];
|
|
1863
|
+
}
|
|
1864
|
+
function resolveProjectFaviconDomains(source) {
|
|
1865
|
+
const out = [];
|
|
1866
|
+
const add = (d) => {
|
|
1867
|
+
const n = d?.trim().toLowerCase();
|
|
1868
|
+
if (!n || out.includes(n)) return;
|
|
1869
|
+
out.push(n);
|
|
1870
|
+
};
|
|
1871
|
+
add(githubRepoDomainHint(source.repo_url));
|
|
1872
|
+
add(SLUG_DOMAIN_HINTS[source.project_slug.toLowerCase()]);
|
|
1873
|
+
const fromOrigin = source.sdk_origin ? originToDomain(source.sdk_origin) : null;
|
|
1874
|
+
if (fromOrigin && !MULTI_TENANT_PARENT_HOSTS.has(fromOrigin)) {
|
|
1875
|
+
add(fromOrigin);
|
|
1876
|
+
}
|
|
1877
|
+
return out;
|
|
1878
|
+
}
|
|
1879
|
+
function projectFaviconUrlCandidates(source, size = 32) {
|
|
1880
|
+
if (source.icon_url?.trim()) return [source.icon_url.trim()];
|
|
1881
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1882
|
+
const urls = [];
|
|
1883
|
+
const push = (url) => {
|
|
1884
|
+
if (!url || seen.has(url) || isUntrustedFaviconUrl(url)) return;
|
|
1885
|
+
seen.add(url);
|
|
1886
|
+
urls.push(url);
|
|
1887
|
+
};
|
|
1888
|
+
const slugHint = SLUG_ICON_URL_HINTS[source.project_slug.toLowerCase()];
|
|
1889
|
+
if (slugHint) push(slugHint);
|
|
1890
|
+
for (const domain of resolveProjectFaviconDomains(source)) {
|
|
1891
|
+
for (const url of faviconUrlCandidates(domain, size)) {
|
|
1892
|
+
push(url);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
return urls;
|
|
1896
|
+
}
|
|
1897
|
+
function originToDomain(origin) {
|
|
1898
|
+
const trimmed = origin.trim();
|
|
1899
|
+
if (!trimmed) return null;
|
|
1900
|
+
if (/^(capacitor|file|content|app):/i.test(trimmed)) return null;
|
|
1901
|
+
try {
|
|
1902
|
+
const url = new URL(trimmed.includes("://") ? trimmed : `https://${trimmed}`);
|
|
1903
|
+
const host = url.hostname.toLowerCase();
|
|
1904
|
+
if (host === "localhost" || host === "127.0.0.1" || host.endsWith(".local")) {
|
|
1905
|
+
return null;
|
|
1906
|
+
}
|
|
1907
|
+
return host;
|
|
1908
|
+
} catch {
|
|
1909
|
+
return null;
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
function githubRepoDomainHint(repoUrl) {
|
|
1913
|
+
if (!repoUrl?.trim()) return null;
|
|
1914
|
+
try {
|
|
1915
|
+
const u = new URL(repoUrl.trim());
|
|
1916
|
+
const host = u.hostname.toLowerCase();
|
|
1917
|
+
const parts = u.pathname.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
|
|
1918
|
+
if (parts.length < 2) return null;
|
|
1919
|
+
const repo = parts[1].replace(/\.git$/i, "").toLowerCase();
|
|
1920
|
+
if (/^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(repo)) return repo;
|
|
1921
|
+
if (host === "github.com" && repo.endsWith(".github.io")) return repo;
|
|
1922
|
+
if (host !== "github.com" && host !== "www.github.com") return host;
|
|
1923
|
+
return null;
|
|
1924
|
+
} catch {
|
|
1925
|
+
return null;
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
function resolveProjectDomain(source) {
|
|
1929
|
+
return resolveProjectFaviconDomains(source)[0] ?? null;
|
|
1930
|
+
}
|
|
1931
|
+
function isLikelyGenericFavicon(img) {
|
|
1932
|
+
if (typeof document === "undefined") return false;
|
|
1933
|
+
if (!img.complete || img.naturalWidth === 0 || img.naturalHeight === 0) return false;
|
|
1934
|
+
try {
|
|
1935
|
+
const canvas = document.createElement("canvas");
|
|
1936
|
+
const w = img.naturalWidth;
|
|
1937
|
+
const h = img.naturalHeight;
|
|
1938
|
+
canvas.width = w;
|
|
1939
|
+
canvas.height = h;
|
|
1940
|
+
const ctx = canvas.getContext("2d");
|
|
1941
|
+
if (!ctx) return false;
|
|
1942
|
+
ctx.drawImage(img, 0, 0);
|
|
1943
|
+
const { data } = ctx.getImageData(0, 0, w, h);
|
|
1944
|
+
let opaque = 0;
|
|
1945
|
+
let satSum = 0;
|
|
1946
|
+
let maxDiff = 0;
|
|
1947
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
1948
|
+
const r = data[i];
|
|
1949
|
+
const g = data[i + 1];
|
|
1950
|
+
const b = data[i + 2];
|
|
1951
|
+
const a = data[i + 3];
|
|
1952
|
+
if (a < 128) continue;
|
|
1953
|
+
opaque++;
|
|
1954
|
+
const max = Math.max(r, g, b);
|
|
1955
|
+
const min = Math.min(r, g, b);
|
|
1956
|
+
const diff = max - min;
|
|
1957
|
+
maxDiff = Math.max(maxDiff, diff);
|
|
1958
|
+
satSum += diff;
|
|
1959
|
+
}
|
|
1960
|
+
if (opaque === 0) return true;
|
|
1961
|
+
const avgSat = satSum / opaque;
|
|
1962
|
+
return avgSat < 14 && maxDiff < 24;
|
|
1963
|
+
} catch {
|
|
1964
|
+
return isUntrustedFaviconUrl(img.src);
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
function projectInitials(name) {
|
|
1968
|
+
const parts = name.trim().split(/[\s._-]+/).filter(Boolean);
|
|
1969
|
+
if (parts.length >= 2) {
|
|
1970
|
+
return `${parts[0][0] ?? ""}${parts[1][0] ?? ""}`.toUpperCase();
|
|
1971
|
+
}
|
|
1972
|
+
const compact = name.replace(/[^a-zA-Z0-9]/g, "");
|
|
1973
|
+
return (compact.slice(0, 2) || name.slice(0, 2) || "?").toUpperCase();
|
|
1974
|
+
}
|
|
1975
|
+
var INITIALS_CHIP_THEMES = [
|
|
1976
|
+
"info",
|
|
1977
|
+
"brand",
|
|
1978
|
+
"warn",
|
|
1979
|
+
"ok",
|
|
1980
|
+
"accent"
|
|
1981
|
+
];
|
|
1982
|
+
function projectInitialsThemeIndex(projectId) {
|
|
1983
|
+
let hash = 0;
|
|
1984
|
+
for (let i = 0; i < projectId.length; i++) {
|
|
1985
|
+
hash = (hash + projectId.charCodeAt(i)) % INITIALS_CHIP_THEMES.length;
|
|
1986
|
+
}
|
|
1987
|
+
return hash;
|
|
1988
|
+
}
|
|
1989
|
+
function readPageFaviconHref() {
|
|
1990
|
+
if (typeof document === "undefined") return null;
|
|
1991
|
+
const selectors = [
|
|
1992
|
+
'link[rel="icon"]',
|
|
1993
|
+
'link[rel="shortcut icon"]',
|
|
1994
|
+
'link[rel="apple-touch-icon"]'
|
|
1995
|
+
];
|
|
1996
|
+
for (const sel of selectors) {
|
|
1997
|
+
const link = document.querySelector(sel);
|
|
1998
|
+
const href = link?.href;
|
|
1999
|
+
if (href && isSafeFaviconHref(href)) return href;
|
|
2000
|
+
}
|
|
2001
|
+
return null;
|
|
2002
|
+
}
|
|
2003
|
+
function isSafeFaviconHref(href) {
|
|
2004
|
+
try {
|
|
2005
|
+
const base = typeof location !== "undefined" ? location.href : void 0;
|
|
2006
|
+
const protocol = new URL(href, base).protocol;
|
|
2007
|
+
return protocol === "http:" || protocol === "https:";
|
|
2008
|
+
} catch {
|
|
2009
|
+
return false;
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
export { DEFAULT_API_ENDPOINT, MAX_REPORT_PAYLOAD_BYTES, MAX_SCREENSHOT_DATA_URL_BYTES, MUSHI_COLORS_DARK, MUSHI_COLORS_LIGHT, MUSHI_COPY, MUSHI_GEOMETRY, MUSHI_IDENTITY_TOKEN_PREFIX, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, MUSHI_MOTION, MUSHI_PROJECT_HEADER, MUSHI_RADIUS, MUSHI_SDK_PACKAGE_HEADER, MUSHI_SDK_VERSION_HEADER, MUSHI_SPACING, MUSHI_TYPE, MUSHI_USER_TOKEN_HEADER, MUSHI_Z, REGION_ENDPOINTS, buildIdentityClaims, buildSdkIngestHeaders, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, faviconUrlCandidates, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, githubRepoDomainHint, isLikelyGenericFavicon, isUntrustedFaviconUrl, mushiPalette, mushiTokens, newUuid, noopLogger, normaliseThrown, originToDomain, parseIdentityToken, projectFaviconUrlCandidates, projectInitials, projectInitialsThemeIndex, readPageFaviconHref, resolveEnvConfig, resolveProjectDomain, resolveProjectFaviconDomains, resolveRegionEndpoint, scrubPii };
|
|
1808
2014
|
//# sourceMappingURL=index.js.map
|
|
1809
2015
|
//# sourceMappingURL=index.js.map
|