@mushi-mushi/core 1.7.2 → 1.8.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.
package/dist/index.d.cts CHANGED
@@ -117,6 +117,27 @@ interface MushiWidgetConfig {
117
117
  featureRequestDescription?: string;
118
118
  /** Minimum description character count before the submit button enables. */
119
119
  minDescriptionLength?: number;
120
+ /**
121
+ * CSS selectors of host-app elements that the widget trigger and panel must
122
+ * never visually overlap. At render time the widget queries each selector,
123
+ * measures the union bounding rect, and nudges `--mushi-top` / `--mushi-bottom`
124
+ * so the panel clears every avoided element by at least 8px.
125
+ *
126
+ * Typical use: avoid a sticky mobile header or a fixed sign-in CTA.
127
+ *
128
+ * @example
129
+ * avoidSelectors: ['[data-mobile-header]', '#sign-in-cta']
130
+ */
131
+ avoidSelectors?: string[];
132
+ }
133
+ /** Optional flat link in the rich banner action row (admin-console BetaBanner style). */
134
+ interface MushiBannerLink {
135
+ /** Link label shown in the action row. */
136
+ label: string;
137
+ /** External URL — opens in a new tab when set. */
138
+ href?: string;
139
+ /** When `href` is absent, opens the widget in feature-request mode. */
140
+ featureRequest?: boolean;
120
141
  }
121
142
  /**
122
143
  * Configuration for the `trigger: 'banner'` header-strip launcher.
@@ -136,12 +157,25 @@ interface MushiBannerConfig {
136
157
  variant?: 'neon' | 'brand' | 'subtle';
137
158
  /** 'top' pins the banner below any existing sticky headers; 'bottom' pins above bottom navs. Defaults to 'top'. */
138
159
  position?: 'top' | 'bottom';
139
- /** Override the call-to-action text in the banner. Defaults to 'Report a bug'. */
160
+ /**
161
+ * Body copy on the strip — the lime "Beta" announcement line users see in
162
+ * the Mushi admin console. When set, the banner switches to the rich layout
163
+ * (pill + message + flat text actions) instead of button-only CTAs.
164
+ */
165
+ message?: string;
166
+ /**
167
+ * Pill label shown before `message` (e.g. "Beta"). Defaults to `"Beta"` when
168
+ * `message` is set. Pass `false` to hide the pill.
169
+ */
170
+ label?: string | false;
171
+ /** Override the call-to-action text in the banner. Defaults to '🐛 Report a bug'. */
140
172
  bugCta?: string;
141
173
  /** Show a "✨ Request a feature" button alongside the bug button. Defaults to true. */
142
174
  featureCta?: boolean;
143
175
  /** Override the feature-request button label. */
144
176
  featureCtaLabel?: string;
177
+ /** Extra flat links after the bug/feature CTAs (e.g. "My submissions"). */
178
+ links?: MushiBannerLink[];
145
179
  /** CSS z-index of the banner element. Defaults to the widget's configured zIndex. */
146
180
  zIndex?: number;
147
181
  }
@@ -336,6 +370,18 @@ interface MushiCooldownConfig {
336
370
  maxProactivePerSession?: number;
337
371
  dismissCooldownHours?: number;
338
372
  suppressAfterDismissals?: number;
373
+ /**
374
+ * Cross-reload re-show cooldown, in minutes (default 30; `0` disables).
375
+ *
376
+ * The `dismissCooldownHours` window only starts after a clean dismissal is
377
+ * recorded (widget `onClose`). A page reload or crash tears down the JS
378
+ * context before that, so on a broken/reloading page the proactive panel
379
+ * would otherwise re-open on every load. When a prompt is shown the SDK
380
+ * persists a timestamp; a fresh session (new JS context) suppresses prompts
381
+ * shown within this window. Within a live session the per-session limit
382
+ * governs instead, so this never blocks a legitimate second trigger.
383
+ */
384
+ reshowCooldownMinutes?: number;
339
385
  }
340
386
  interface MushiPreFilterConfig {
341
387
  enabled?: boolean;
@@ -833,6 +879,34 @@ interface MushiDiagnosticsResult {
833
879
  captureScreenshotAvailable: boolean;
834
880
  captureNetworkIntercepting: boolean;
835
881
  sdkVersion: string;
882
+ /**
883
+ * True when the widget host element has `pointer-events: none` and
884
+ * zero width/height — i.e. it cannot act as a touch blocker over host UI.
885
+ * False when the SDK has not yet mounted, or when a consumer has overridden
886
+ * the host styles without restoring the pass-through contract.
887
+ */
888
+ widgetHostPointerSafe: boolean;
889
+ /**
890
+ * Bounding rect of the widget host element (`offsetWidth × offsetHeight`).
891
+ * Should be `{ width: 0, height: 0 }` for a healthy SDK — the host is
892
+ * zero-sized with `overflow: visible` so the shadow internals extend
893
+ * outward without creating a hit-test surface. `null` when not mounted.
894
+ */
895
+ widgetHostBounds: {
896
+ width: number;
897
+ height: number;
898
+ } | null;
899
+ /**
900
+ * True when the widget is currently suppressed: `hideOnSelector` matched an
901
+ * element, `hideOnRoutes` matched the current pathname, or `hide()` was
902
+ * called. A suppressed widget renders nothing and removes the body nudge.
903
+ */
904
+ widgetSuppressed: boolean;
905
+ /**
906
+ * True when `trigger: 'banner'` is active and the banner is currently
907
+ * rendered in the shadow DOM (not dismissed, not suppressed).
908
+ */
909
+ bannerRendered: boolean;
836
910
  }
837
911
  interface MushiSDKInstance {
838
912
  /**
@@ -1204,6 +1278,27 @@ declare function captureEnvironment(): MushiEnvironment;
1204
1278
 
1205
1279
  declare function getReporterToken(): string;
1206
1280
 
1281
+ /**
1282
+ * RFC-4122 v4 UUID generator with a capability ladder so it works in every
1283
+ * runtime a Mushi SDK ships to:
1284
+ *
1285
+ * 1. `crypto.randomUUID()` — browsers (secure context), Node 19+, Deno
1286
+ * 2. `crypto.getRandomValues()` — older browsers, RN with a crypto polyfill
1287
+ * 3. `Math.random()` — Hermes / JSC without a crypto polyfill
1288
+ *
1289
+ * Why this exists: report ids are written to the `reports.id uuid` column on
1290
+ * the backend. SDKs that minted ad-hoc ids (`rn-<ts>-<rand>`, `mushi_<hex>`)
1291
+ * failed the Postgres uuid cast (22P02) and the report was silently dropped
1292
+ * (Sentry MUSHI-MUSHI-SERVER-D). Emitting a well-formed UUID at the source
1293
+ * keeps the value valid end-to-end.
1294
+ *
1295
+ * A report id is an identifier, not a secret. The `Math.random` fallback only
1296
+ * affects collision odds — negligible at our volume, and the backend dedupes
1297
+ * on the `reports` primary key regardless — so we never throw just because a
1298
+ * runtime lacks a CSPRNG.
1299
+ */
1300
+ declare function newUuid(): string;
1301
+
1207
1302
  /**
1208
1303
  * §3c — stable device fingerprint hash.
1209
1304
  *
@@ -1402,4 +1497,4 @@ declare function createLogger(options: LoggerOptions): Logger;
1402
1497
  */
1403
1498
  declare const noopLogger: Logger;
1404
1499
 
1405
- export { type ApiClientOptions, type BreadcrumbBuffer, type BreadcrumbBufferOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, type MushiActivityEvent, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiBannerConfig, type MushiBetaChangelogEntry, type MushiBetaModeConfig, type MushiBreadcrumb, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiCaptureExceptionOptions, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiDiagnosticsResult, type MushiDiscoverInventoryConfig, type MushiDiscoveryEventPayload, type MushiEnvironment, type MushiEventHandler, type MushiEventType, type MushiIntegrationsConfig, type MushiInternalRequestKind, type MushiNetworkEntry, type MushiOfflineConfig, type MushiOnDeviceClassifier, type MushiOnDeviceClassifierInput, type MushiOnDeviceClassifierResult, 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 MushiTierResult, type MushiTimelineEntry, type MushiTimelineKind, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type NormalisedException, type OfflineQueue, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, captureEnvironment, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, noopLogger, normaliseThrown, resolveRegionEndpoint, scrubPii };
1500
+ export { type ApiClientOptions, type BreadcrumbBuffer, type BreadcrumbBufferOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, type MushiActivityEvent, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiBannerConfig, type MushiBannerLink, type MushiBetaChangelogEntry, type MushiBetaModeConfig, type MushiBreadcrumb, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiCaptureExceptionOptions, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiDiagnosticsResult, type MushiDiscoverInventoryConfig, type MushiDiscoveryEventPayload, type MushiEnvironment, type MushiEventHandler, type MushiEventType, type MushiIntegrationsConfig, type MushiInternalRequestKind, type MushiNetworkEntry, type MushiOfflineConfig, type MushiOnDeviceClassifier, type MushiOnDeviceClassifierInput, type MushiOnDeviceClassifierResult, 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 MushiTierResult, type MushiTimelineEntry, type MushiTimelineKind, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type NormalisedException, type OfflineQueue, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, captureEnvironment, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, newUuid, noopLogger, normaliseThrown, resolveRegionEndpoint, scrubPii };
package/dist/index.d.ts CHANGED
@@ -117,6 +117,27 @@ interface MushiWidgetConfig {
117
117
  featureRequestDescription?: string;
118
118
  /** Minimum description character count before the submit button enables. */
119
119
  minDescriptionLength?: number;
120
+ /**
121
+ * CSS selectors of host-app elements that the widget trigger and panel must
122
+ * never visually overlap. At render time the widget queries each selector,
123
+ * measures the union bounding rect, and nudges `--mushi-top` / `--mushi-bottom`
124
+ * so the panel clears every avoided element by at least 8px.
125
+ *
126
+ * Typical use: avoid a sticky mobile header or a fixed sign-in CTA.
127
+ *
128
+ * @example
129
+ * avoidSelectors: ['[data-mobile-header]', '#sign-in-cta']
130
+ */
131
+ avoidSelectors?: string[];
132
+ }
133
+ /** Optional flat link in the rich banner action row (admin-console BetaBanner style). */
134
+ interface MushiBannerLink {
135
+ /** Link label shown in the action row. */
136
+ label: string;
137
+ /** External URL — opens in a new tab when set. */
138
+ href?: string;
139
+ /** When `href` is absent, opens the widget in feature-request mode. */
140
+ featureRequest?: boolean;
120
141
  }
121
142
  /**
122
143
  * Configuration for the `trigger: 'banner'` header-strip launcher.
@@ -136,12 +157,25 @@ interface MushiBannerConfig {
136
157
  variant?: 'neon' | 'brand' | 'subtle';
137
158
  /** 'top' pins the banner below any existing sticky headers; 'bottom' pins above bottom navs. Defaults to 'top'. */
138
159
  position?: 'top' | 'bottom';
139
- /** Override the call-to-action text in the banner. Defaults to 'Report a bug'. */
160
+ /**
161
+ * Body copy on the strip — the lime "Beta" announcement line users see in
162
+ * the Mushi admin console. When set, the banner switches to the rich layout
163
+ * (pill + message + flat text actions) instead of button-only CTAs.
164
+ */
165
+ message?: string;
166
+ /**
167
+ * Pill label shown before `message` (e.g. "Beta"). Defaults to `"Beta"` when
168
+ * `message` is set. Pass `false` to hide the pill.
169
+ */
170
+ label?: string | false;
171
+ /** Override the call-to-action text in the banner. Defaults to '🐛 Report a bug'. */
140
172
  bugCta?: string;
141
173
  /** Show a "✨ Request a feature" button alongside the bug button. Defaults to true. */
142
174
  featureCta?: boolean;
143
175
  /** Override the feature-request button label. */
144
176
  featureCtaLabel?: string;
177
+ /** Extra flat links after the bug/feature CTAs (e.g. "My submissions"). */
178
+ links?: MushiBannerLink[];
145
179
  /** CSS z-index of the banner element. Defaults to the widget's configured zIndex. */
146
180
  zIndex?: number;
147
181
  }
@@ -336,6 +370,18 @@ interface MushiCooldownConfig {
336
370
  maxProactivePerSession?: number;
337
371
  dismissCooldownHours?: number;
338
372
  suppressAfterDismissals?: number;
373
+ /**
374
+ * Cross-reload re-show cooldown, in minutes (default 30; `0` disables).
375
+ *
376
+ * The `dismissCooldownHours` window only starts after a clean dismissal is
377
+ * recorded (widget `onClose`). A page reload or crash tears down the JS
378
+ * context before that, so on a broken/reloading page the proactive panel
379
+ * would otherwise re-open on every load. When a prompt is shown the SDK
380
+ * persists a timestamp; a fresh session (new JS context) suppresses prompts
381
+ * shown within this window. Within a live session the per-session limit
382
+ * governs instead, so this never blocks a legitimate second trigger.
383
+ */
384
+ reshowCooldownMinutes?: number;
339
385
  }
340
386
  interface MushiPreFilterConfig {
341
387
  enabled?: boolean;
@@ -833,6 +879,34 @@ interface MushiDiagnosticsResult {
833
879
  captureScreenshotAvailable: boolean;
834
880
  captureNetworkIntercepting: boolean;
835
881
  sdkVersion: string;
882
+ /**
883
+ * True when the widget host element has `pointer-events: none` and
884
+ * zero width/height — i.e. it cannot act as a touch blocker over host UI.
885
+ * False when the SDK has not yet mounted, or when a consumer has overridden
886
+ * the host styles without restoring the pass-through contract.
887
+ */
888
+ widgetHostPointerSafe: boolean;
889
+ /**
890
+ * Bounding rect of the widget host element (`offsetWidth × offsetHeight`).
891
+ * Should be `{ width: 0, height: 0 }` for a healthy SDK — the host is
892
+ * zero-sized with `overflow: visible` so the shadow internals extend
893
+ * outward without creating a hit-test surface. `null` when not mounted.
894
+ */
895
+ widgetHostBounds: {
896
+ width: number;
897
+ height: number;
898
+ } | null;
899
+ /**
900
+ * True when the widget is currently suppressed: `hideOnSelector` matched an
901
+ * element, `hideOnRoutes` matched the current pathname, or `hide()` was
902
+ * called. A suppressed widget renders nothing and removes the body nudge.
903
+ */
904
+ widgetSuppressed: boolean;
905
+ /**
906
+ * True when `trigger: 'banner'` is active and the banner is currently
907
+ * rendered in the shadow DOM (not dismissed, not suppressed).
908
+ */
909
+ bannerRendered: boolean;
836
910
  }
837
911
  interface MushiSDKInstance {
838
912
  /**
@@ -1204,6 +1278,27 @@ declare function captureEnvironment(): MushiEnvironment;
1204
1278
 
1205
1279
  declare function getReporterToken(): string;
1206
1280
 
1281
+ /**
1282
+ * RFC-4122 v4 UUID generator with a capability ladder so it works in every
1283
+ * runtime a Mushi SDK ships to:
1284
+ *
1285
+ * 1. `crypto.randomUUID()` — browsers (secure context), Node 19+, Deno
1286
+ * 2. `crypto.getRandomValues()` — older browsers, RN with a crypto polyfill
1287
+ * 3. `Math.random()` — Hermes / JSC without a crypto polyfill
1288
+ *
1289
+ * Why this exists: report ids are written to the `reports.id uuid` column on
1290
+ * the backend. SDKs that minted ad-hoc ids (`rn-<ts>-<rand>`, `mushi_<hex>`)
1291
+ * failed the Postgres uuid cast (22P02) and the report was silently dropped
1292
+ * (Sentry MUSHI-MUSHI-SERVER-D). Emitting a well-formed UUID at the source
1293
+ * keeps the value valid end-to-end.
1294
+ *
1295
+ * A report id is an identifier, not a secret. The `Math.random` fallback only
1296
+ * affects collision odds — negligible at our volume, and the backend dedupes
1297
+ * on the `reports` primary key regardless — so we never throw just because a
1298
+ * runtime lacks a CSPRNG.
1299
+ */
1300
+ declare function newUuid(): string;
1301
+
1207
1302
  /**
1208
1303
  * §3c — stable device fingerprint hash.
1209
1304
  *
@@ -1402,4 +1497,4 @@ declare function createLogger(options: LoggerOptions): Logger;
1402
1497
  */
1403
1498
  declare const noopLogger: Logger;
1404
1499
 
1405
- export { type ApiClientOptions, type BreadcrumbBuffer, type BreadcrumbBufferOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, type MushiActivityEvent, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiBannerConfig, type MushiBetaChangelogEntry, type MushiBetaModeConfig, type MushiBreadcrumb, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiCaptureExceptionOptions, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiDiagnosticsResult, type MushiDiscoverInventoryConfig, type MushiDiscoveryEventPayload, type MushiEnvironment, type MushiEventHandler, type MushiEventType, type MushiIntegrationsConfig, type MushiInternalRequestKind, type MushiNetworkEntry, type MushiOfflineConfig, type MushiOnDeviceClassifier, type MushiOnDeviceClassifierInput, type MushiOnDeviceClassifierResult, 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 MushiTierResult, type MushiTimelineEntry, type MushiTimelineKind, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type NormalisedException, type OfflineQueue, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, captureEnvironment, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, noopLogger, normaliseThrown, resolveRegionEndpoint, scrubPii };
1500
+ export { type ApiClientOptions, type BreadcrumbBuffer, type BreadcrumbBufferOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, type MushiActivityEvent, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiBannerConfig, type MushiBannerLink, type MushiBetaChangelogEntry, type MushiBetaModeConfig, type MushiBreadcrumb, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiCaptureExceptionOptions, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiDiagnosticsResult, type MushiDiscoverInventoryConfig, type MushiDiscoveryEventPayload, type MushiEnvironment, type MushiEventHandler, type MushiEventType, type MushiIntegrationsConfig, type MushiInternalRequestKind, type MushiNetworkEntry, type MushiOfflineConfig, type MushiOnDeviceClassifier, type MushiOnDeviceClassifierInput, type MushiOnDeviceClassifierResult, 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 MushiTierResult, type MushiTimelineEntry, type MushiTimelineKind, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type NormalisedException, type OfflineQueue, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, captureEnvironment, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, newUuid, noopLogger, normaliseThrown, resolveRegionEndpoint, scrubPii };
package/dist/index.js CHANGED
@@ -1035,6 +1035,26 @@ function generateToken() {
1035
1035
  return `mushi_${hex}`;
1036
1036
  }
1037
1037
 
1038
+ // src/uuid.ts
1039
+ function newUuid() {
1040
+ const c = typeof crypto !== "undefined" ? crypto : void 0;
1041
+ if (c?.randomUUID) {
1042
+ return c.randomUUID();
1043
+ }
1044
+ const bytes = new Uint8Array(16);
1045
+ if (c?.getRandomValues) {
1046
+ c.getRandomValues(bytes);
1047
+ } else {
1048
+ for (let i = 0; i < bytes.length; i++) {
1049
+ bytes[i] = Math.floor(Math.random() * 256);
1050
+ }
1051
+ }
1052
+ bytes[6] = bytes[6] & 15 | 64;
1053
+ bytes[8] = bytes[8] & 63 | 128;
1054
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
1055
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
1056
+ }
1057
+
1038
1058
  // src/fingerprint.ts
1039
1059
  var CACHE_KEY = "mushi_fingerprint_hash";
1040
1060
  function collectInputs() {
@@ -1278,6 +1298,6 @@ function normaliseThrown(thrown) {
1278
1298
  return { name: "Error", message: String(thrown) };
1279
1299
  }
1280
1300
 
1281
- export { DEFAULT_API_ENDPOINT, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, REGION_ENDPOINTS, captureEnvironment, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, noopLogger, normaliseThrown, resolveRegionEndpoint, scrubPii };
1301
+ export { DEFAULT_API_ENDPOINT, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, REGION_ENDPOINTS, captureEnvironment, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, newUuid, noopLogger, normaliseThrown, resolveRegionEndpoint, scrubPii };
1282
1302
  //# sourceMappingURL=index.js.map
1283
1303
  //# sourceMappingURL=index.js.map