@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/dist/index.d.cts CHANGED
@@ -102,6 +102,20 @@ interface MushiWidgetConfig {
102
102
  brandFooter?: boolean;
103
103
  /** How the widget should surface SDK freshness warnings. Defaults to auto. */
104
104
  outdatedBanner?: 'auto' | 'banner' | 'console-only' | 'off';
105
+ /**
106
+ * Privacy nudge shown beside an attached screenshot preview, reminding the
107
+ * reporter to remove anything sensitive (balances, PII, tokens) before they
108
+ * submit. The widget always renders the captured image as a visible preview
109
+ * so the user can see exactly what will be sent and remove it if needed; this
110
+ * flag only controls the accompanying caption.
111
+ * - `true` (default) — show the localized default caption.
112
+ * - string — show this custom caption verbatim.
113
+ * - `false` — hide the caption (the preview + remove control still show).
114
+ *
115
+ * Settable per-host via the SDK config and remotely via the Mushi console
116
+ * runtime config (it travels in the `widget` block of `GET /v1/sdk/config`).
117
+ */
118
+ screenshotSensitiveHint?: boolean | string;
105
119
  /**
106
120
  * Beta mode: injects discreet "early access" messaging into the widget panel.
107
121
  * Shows a beta strip on the category step and a contact footer on the success
@@ -1522,6 +1536,8 @@ interface MushiCrossAppReport {
1522
1536
  project_id: string | null;
1523
1537
  app_name: string | null;
1524
1538
  app_slug: string | null;
1539
+ /** Hostname from latest SDK heartbeat — used for favicon in cross-app list. */
1540
+ app_domain?: string | null;
1525
1541
  }
1526
1542
  /** A single entry in the global tester leaderboard. */
1527
1543
  interface MushiLeaderboardEntry {
@@ -1543,6 +1559,22 @@ interface MushiTesterReputation {
1543
1559
  total_points: number;
1544
1560
  }
1545
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>;
1546
1578
  interface ApiClientOptions {
1547
1579
  projectId: string;
1548
1580
  apiKey: string;
@@ -2134,4 +2166,42 @@ declare function buildIdentityClaims(input: {
2134
2166
  */
2135
2167
  declare function parseIdentityToken(token: string): MushiIdentityClaims | null;
2136
2168
 
2137
- 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_RADIUS, MUSHI_SPACING, MUSHI_TYPE, 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, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, type ResolvedEnvConfig, buildIdentityClaims, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, mushiPalette, mushiTokens, newUuid, noopLogger, normaliseThrown, parseIdentityToken, resolveEnvConfig, resolveRegionEndpoint, scrubPii };
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
+ /** Read the host page favicon href (web SDK only). */
2205
+ declare function readPageFaviconHref(): string | null;
2206
+
2207
+ 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
@@ -102,6 +102,20 @@ interface MushiWidgetConfig {
102
102
  brandFooter?: boolean;
103
103
  /** How the widget should surface SDK freshness warnings. Defaults to auto. */
104
104
  outdatedBanner?: 'auto' | 'banner' | 'console-only' | 'off';
105
+ /**
106
+ * Privacy nudge shown beside an attached screenshot preview, reminding the
107
+ * reporter to remove anything sensitive (balances, PII, tokens) before they
108
+ * submit. The widget always renders the captured image as a visible preview
109
+ * so the user can see exactly what will be sent and remove it if needed; this
110
+ * flag only controls the accompanying caption.
111
+ * - `true` (default) — show the localized default caption.
112
+ * - string — show this custom caption verbatim.
113
+ * - `false` — hide the caption (the preview + remove control still show).
114
+ *
115
+ * Settable per-host via the SDK config and remotely via the Mushi console
116
+ * runtime config (it travels in the `widget` block of `GET /v1/sdk/config`).
117
+ */
118
+ screenshotSensitiveHint?: boolean | string;
105
119
  /**
106
120
  * Beta mode: injects discreet "early access" messaging into the widget panel.
107
121
  * Shows a beta strip on the category step and a contact footer on the success
@@ -1522,6 +1536,8 @@ interface MushiCrossAppReport {
1522
1536
  project_id: string | null;
1523
1537
  app_name: string | null;
1524
1538
  app_slug: string | null;
1539
+ /** Hostname from latest SDK heartbeat — used for favicon in cross-app list. */
1540
+ app_domain?: string | null;
1525
1541
  }
1526
1542
  /** A single entry in the global tester leaderboard. */
1527
1543
  interface MushiLeaderboardEntry {
@@ -1543,6 +1559,22 @@ interface MushiTesterReputation {
1543
1559
  total_points: number;
1544
1560
  }
1545
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>;
1546
1578
  interface ApiClientOptions {
1547
1579
  projectId: string;
1548
1580
  apiKey: string;
@@ -2134,4 +2166,42 @@ declare function buildIdentityClaims(input: {
2134
2166
  */
2135
2167
  declare function parseIdentityToken(token: string): MushiIdentityClaims | null;
2136
2168
 
2137
- 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_RADIUS, MUSHI_SPACING, MUSHI_TYPE, 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, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, type ResolvedEnvConfig, buildIdentityClaims, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, mushiPalette, mushiTokens, newUuid, noopLogger, normaliseThrown, parseIdentityToken, resolveEnvConfig, resolveRegionEndpoint, scrubPii };
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
+ /** Read the host page favicon href (web SDK only). */
2205
+ declare function readPageFaviconHref(): string | null;
2206
+
2207
+ 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
- "Content-Type": "application/json",
74
- "X-Mushi-Api-Key": apiKey,
75
- "X-Mushi-Project": projectId,
76
- ...sdkPackage ? { [MUSHI_SDK_PACKAGE_HEADER]: sdkPackage } : {},
77
- ...sdkVersion ? { [MUSHI_SDK_VERSION_HEADER]: sdkVersion } : {},
78
- ...userToken ? { [MUSHI_USER_TOKEN_HEADER]: userToken } : {},
79
- ...internalKind ? { [MUSHI_INTERNAL_HEADER]: internalKind } : {},
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,12 @@ 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
+ console.error(
117
+ `[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`
118
+ );
119
+ }
99
120
  if (response.status >= 500 && retries > 0) {
100
121
  await sleep(getBackoffDelay(maxRetries - retries));
101
122
  return request(method, path, body, retries - 1, internalKind, extraHeaders);
@@ -998,6 +1019,30 @@ function createOfflineQueue(config = {}) {
998
1019
  lsWrite(rows);
999
1020
  }
1000
1021
  }
1022
+ async function removeRow(backend, rowId) {
1023
+ try {
1024
+ if (backend === "indexeddb") await idbDelete(rowId);
1025
+ else lsDelete(rowId);
1026
+ } catch (err) {
1027
+ queueLog.debug("Offline queue: row removal failed (will age out)", {
1028
+ id: rowId,
1029
+ err: String(err)
1030
+ });
1031
+ }
1032
+ }
1033
+ async function persistRow(backend, row) {
1034
+ try {
1035
+ if (backend === "indexeddb") await idbPutRow(row);
1036
+ else lsUpdateRow(row);
1037
+ return true;
1038
+ } catch (err) {
1039
+ queueLog.debug("Offline queue: row persist failed", {
1040
+ id: row.id,
1041
+ err: String(err)
1042
+ });
1043
+ return false;
1044
+ }
1045
+ }
1001
1046
  async function enqueue(report) {
1002
1047
  if (!enabled) return;
1003
1048
  const currentSize = await size();
@@ -1043,12 +1088,7 @@ function createOfflineQueue(config = {}) {
1043
1088
  for (const row of rows) {
1044
1089
  if (isExpired(row, now)) {
1045
1090
  const rowId = row.id;
1046
- try {
1047
- if (backend === "indexeddb") await idbDelete(rowId);
1048
- else lsDelete(rowId);
1049
- } catch {
1050
- lsDelete(rowId);
1051
- }
1091
+ await removeRow(backend, rowId);
1052
1092
  queueLog.debug("Offline queue: evicting stale report", { id: rowId });
1053
1093
  } else {
1054
1094
  fresh.push(row);
@@ -1063,23 +1103,13 @@ function createOfflineQueue(config = {}) {
1063
1103
  const rowId = row.id;
1064
1104
  const report = await unwrapForSend(row);
1065
1105
  if (!report) {
1066
- try {
1067
- if (backend === "indexeddb") await idbDelete(rowId);
1068
- else lsDelete(rowId);
1069
- } catch {
1070
- lsDelete(rowId);
1071
- }
1106
+ await removeRow(backend, rowId);
1072
1107
  failed++;
1073
1108
  continue;
1074
1109
  }
1075
1110
  const result = await client.submitReport(report);
1076
1111
  if (result.ok) {
1077
- try {
1078
- if (backend === "indexeddb") await idbDelete(rowId);
1079
- else lsDelete(rowId);
1080
- } catch {
1081
- lsDelete(rowId);
1082
- }
1112
+ await removeRow(backend, rowId);
1083
1113
  sent++;
1084
1114
  } else {
1085
1115
  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;
@@ -1091,21 +1121,11 @@ function createOfflineQueue(config = {}) {
1091
1121
  );
1092
1122
  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"));
1093
1123
  if (permanent) {
1094
- try {
1095
- if (backend === "indexeddb") await idbDelete(rowId);
1096
- else lsDelete(rowId);
1097
- } catch {
1098
- lsDelete(rowId);
1099
- }
1124
+ await removeRow(backend, rowId);
1100
1125
  } else if (transient) {
1101
1126
  const nextAttempts = rowAttempts(row) + 1;
1102
1127
  if (nextAttempts >= MAX_DELIVERY_ATTEMPTS) {
1103
- try {
1104
- if (backend === "indexeddb") await idbDelete(rowId);
1105
- else lsDelete(rowId);
1106
- } catch {
1107
- lsDelete(rowId);
1108
- }
1128
+ await removeRow(backend, rowId);
1109
1129
  queueLog.warn("Offline queue: giving up on report after repeated failures", {
1110
1130
  id: rowId,
1111
1131
  attempts: nextAttempts,
@@ -1113,17 +1133,20 @@ function createOfflineQueue(config = {}) {
1113
1133
  });
1114
1134
  } else {
1115
1135
  row.attempts = nextAttempts;
1116
- try {
1117
- if (backend === "indexeddb") await idbPutRow(row);
1118
- else lsUpdateRow(row);
1119
- } catch {
1120
- lsUpdateRow(row);
1136
+ const persisted = await persistRow(backend, row);
1137
+ if (persisted) {
1138
+ queueLog.debug("Offline queue: transient failure, will retry", {
1139
+ id: rowId,
1140
+ attempts: nextAttempts,
1141
+ code: result.error?.code
1142
+ });
1143
+ } else {
1144
+ await removeRow(backend, rowId);
1145
+ queueLog.warn("Offline queue: dropping report (attempt counter unpersistable)", {
1146
+ id: rowId,
1147
+ code: result.error?.code
1148
+ });
1121
1149
  }
1122
- queueLog.debug("Offline queue: transient failure, will retry", {
1123
- id: rowId,
1124
- attempts: nextAttempts,
1125
- code: result.error?.code
1126
- });
1127
1150
  }
1128
1151
  }
1129
1152
  failed++;
@@ -1802,6 +1825,180 @@ function parseIdentityToken(token) {
1802
1825
  }
1803
1826
  }
1804
1827
 
1805
- 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_RADIUS, MUSHI_SPACING, MUSHI_TYPE, MUSHI_Z, REGION_ENDPOINTS, buildIdentityClaims, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, mushiPalette, mushiTokens, newUuid, noopLogger, normaliseThrown, parseIdentityToken, resolveEnvConfig, resolveRegionEndpoint, scrubPii };
1828
+ // src/favicon.ts
1829
+ var MULTI_TENANT_PARENT_HOSTS = /* @__PURE__ */ new Set(["kensaur.us", "www.kensaur.us"]);
1830
+ var SLUG_DOMAIN_HINTS = {
1831
+ "glot-it": "glot.it",
1832
+ glotit: "glot.it",
1833
+ "solo-boss-cloud": "sbc-front.vercel.app",
1834
+ "mushi-mushi": "mushimushi.dev"
1835
+ };
1836
+ var SLUG_ICON_URL_HINTS = {
1837
+ "mushi-mushi": "https://kensaur.us/mushi-mushi/admin/favicon.svg",
1838
+ "solo-boss-cloud": "https://sbc-front.vercel.app/favicon.svg",
1839
+ "glot-it": "https://kensaur.us/glot-it/apple-touch-icon.png",
1840
+ glotit: "https://kensaur.us/glot-it/apple-touch-icon.png"
1841
+ };
1842
+ var UNTRUSTED_FAVICON_HOSTS = [
1843
+ "www.google.com",
1844
+ "icons.duckduckgo.com",
1845
+ "icon.horse"
1846
+ ];
1847
+ function isUntrustedFaviconUrl(url) {
1848
+ try {
1849
+ const host = new URL(url).hostname.toLowerCase();
1850
+ return UNTRUSTED_FAVICON_HOSTS.some((h) => host === h || host.endsWith(`.${h}`));
1851
+ } catch {
1852
+ return false;
1853
+ }
1854
+ }
1855
+ function faviconUrlCandidates(domain, _size = 32) {
1856
+ return [
1857
+ `https://${domain}/favicon.svg`,
1858
+ `https://${domain}/favicon.ico`,
1859
+ `https://${domain}/favicon-32x32.png`,
1860
+ `https://${domain}/apple-touch-icon.png`
1861
+ ];
1862
+ }
1863
+ function resolveProjectFaviconDomains(source) {
1864
+ const out = [];
1865
+ const add = (d) => {
1866
+ const n = d?.trim().toLowerCase();
1867
+ if (!n || out.includes(n)) return;
1868
+ out.push(n);
1869
+ };
1870
+ add(githubRepoDomainHint(source.repo_url));
1871
+ add(SLUG_DOMAIN_HINTS[source.project_slug.toLowerCase()]);
1872
+ const fromOrigin = source.sdk_origin ? originToDomain(source.sdk_origin) : null;
1873
+ if (fromOrigin && !MULTI_TENANT_PARENT_HOSTS.has(fromOrigin)) {
1874
+ add(fromOrigin);
1875
+ }
1876
+ return out;
1877
+ }
1878
+ function projectFaviconUrlCandidates(source, size = 32) {
1879
+ if (source.icon_url?.trim()) return [source.icon_url.trim()];
1880
+ const seen = /* @__PURE__ */ new Set();
1881
+ const urls = [];
1882
+ const push = (url) => {
1883
+ if (!url || seen.has(url) || isUntrustedFaviconUrl(url)) return;
1884
+ seen.add(url);
1885
+ urls.push(url);
1886
+ };
1887
+ const slugHint = SLUG_ICON_URL_HINTS[source.project_slug.toLowerCase()];
1888
+ if (slugHint) push(slugHint);
1889
+ for (const domain of resolveProjectFaviconDomains(source)) {
1890
+ for (const url of faviconUrlCandidates(domain, size)) {
1891
+ push(url);
1892
+ }
1893
+ }
1894
+ return urls;
1895
+ }
1896
+ function originToDomain(origin) {
1897
+ const trimmed = origin.trim();
1898
+ if (!trimmed) return null;
1899
+ if (/^(capacitor|file|content|app):/i.test(trimmed)) return null;
1900
+ try {
1901
+ const url = new URL(trimmed.includes("://") ? trimmed : `https://${trimmed}`);
1902
+ const host = url.hostname.toLowerCase();
1903
+ if (host === "localhost" || host === "127.0.0.1" || host.endsWith(".local")) {
1904
+ return null;
1905
+ }
1906
+ return host;
1907
+ } catch {
1908
+ return null;
1909
+ }
1910
+ }
1911
+ function githubRepoDomainHint(repoUrl) {
1912
+ if (!repoUrl?.trim()) return null;
1913
+ try {
1914
+ const u = new URL(repoUrl.trim());
1915
+ const host = u.hostname.toLowerCase();
1916
+ const parts = u.pathname.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
1917
+ if (parts.length < 2) return null;
1918
+ const repo = parts[1].replace(/\.git$/i, "").toLowerCase();
1919
+ if (/^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(repo)) return repo;
1920
+ if (host === "github.com" && repo.endsWith(".github.io")) return repo;
1921
+ if (host !== "github.com" && host !== "www.github.com") return host;
1922
+ return null;
1923
+ } catch {
1924
+ return null;
1925
+ }
1926
+ }
1927
+ function resolveProjectDomain(source) {
1928
+ return resolveProjectFaviconDomains(source)[0] ?? null;
1929
+ }
1930
+ function isLikelyGenericFavicon(img) {
1931
+ if (typeof document === "undefined") return false;
1932
+ if (!img.complete || img.naturalWidth === 0 || img.naturalHeight === 0) return false;
1933
+ try {
1934
+ const canvas = document.createElement("canvas");
1935
+ const w = img.naturalWidth;
1936
+ const h = img.naturalHeight;
1937
+ canvas.width = w;
1938
+ canvas.height = h;
1939
+ const ctx = canvas.getContext("2d");
1940
+ if (!ctx) return false;
1941
+ ctx.drawImage(img, 0, 0);
1942
+ const { data } = ctx.getImageData(0, 0, w, h);
1943
+ let opaque = 0;
1944
+ let satSum = 0;
1945
+ let maxDiff = 0;
1946
+ for (let i = 0; i < data.length; i += 4) {
1947
+ const r = data[i];
1948
+ const g = data[i + 1];
1949
+ const b = data[i + 2];
1950
+ const a = data[i + 3];
1951
+ if (a < 128) continue;
1952
+ opaque++;
1953
+ const max = Math.max(r, g, b);
1954
+ const min = Math.min(r, g, b);
1955
+ const diff = max - min;
1956
+ maxDiff = Math.max(maxDiff, diff);
1957
+ satSum += diff;
1958
+ }
1959
+ if (opaque === 0) return true;
1960
+ const avgSat = satSum / opaque;
1961
+ return avgSat < 14 && maxDiff < 24;
1962
+ } catch {
1963
+ return isUntrustedFaviconUrl(img.src);
1964
+ }
1965
+ }
1966
+ function projectInitials(name) {
1967
+ const parts = name.trim().split(/[\s._-]+/).filter(Boolean);
1968
+ if (parts.length >= 2) {
1969
+ return `${parts[0][0] ?? ""}${parts[1][0] ?? ""}`.toUpperCase();
1970
+ }
1971
+ const compact = name.replace(/[^a-zA-Z0-9]/g, "");
1972
+ return (compact.slice(0, 2) || name.slice(0, 2) || "?").toUpperCase();
1973
+ }
1974
+ var INITIALS_CHIP_THEMES = [
1975
+ "info",
1976
+ "brand",
1977
+ "warn",
1978
+ "ok",
1979
+ "accent"
1980
+ ];
1981
+ function projectInitialsThemeIndex(projectId) {
1982
+ let hash = 0;
1983
+ for (let i = 0; i < projectId.length; i++) {
1984
+ hash = (hash + projectId.charCodeAt(i)) % INITIALS_CHIP_THEMES.length;
1985
+ }
1986
+ return hash;
1987
+ }
1988
+ function readPageFaviconHref() {
1989
+ if (typeof document === "undefined") return null;
1990
+ const selectors = [
1991
+ 'link[rel="icon"]',
1992
+ 'link[rel="shortcut icon"]',
1993
+ 'link[rel="apple-touch-icon"]'
1994
+ ];
1995
+ for (const sel of selectors) {
1996
+ const link = document.querySelector(sel);
1997
+ if (link?.href) return link.href;
1998
+ }
1999
+ return null;
2000
+ }
2001
+
2002
+ 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 };
1806
2003
  //# sourceMappingURL=index.js.map
1807
2004
  //# sourceMappingURL=index.js.map