@mushi-mushi/core 0.8.0 → 0.9.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
@@ -97,6 +97,69 @@ interface MushiCaptureConfig {
97
97
  screenshot?: 'on-report' | 'auto' | 'off';
98
98
  elementSelector?: boolean;
99
99
  replay?: 'sentry' | 'rrweb' | 'lite' | 'off';
100
+ /**
101
+ * Mushi Mushi v2.1 (whitepaper §6 hybrid mode): passive inventory
102
+ * discovery. When enabled the SDK observes navigations and emits a
103
+ * tiny payload — `(route, title, testids[], outbound api paths[],
104
+ * dom_summary[≤200 chars])` — back to `/v1/sdk/discovery`. The
105
+ * server aggregates this over a 30-day rolling window and the
106
+ * admin can ask Claude to propose an `inventory.yaml` from it.
107
+ *
108
+ * Default: off. Recommended: enable in dev/preview/staging from the
109
+ * day you install the SDK so the proposer has data when you go to
110
+ * generate your first inventory.
111
+ *
112
+ * What is sent (per navigation, throttled to ≤1/route/min):
113
+ * - `location.pathname` template-normalized (`/practice/abc-123`
114
+ * → `/practice/[id]` via uuid/numeric/hex heuristics + an
115
+ * optional framework hint if the host app sets
116
+ * `discoverInventory.routeTemplates`)
117
+ * - `document.title`
118
+ * - All `[data-testid]` values currently in the DOM
119
+ * - Recent fetch/XHR paths the existing network capturer saw
120
+ * - The first text run of `<h1>` / `<title>` / `<main>` truncated
121
+ * to 200 chars (helps Claude name stories well)
122
+ * - Sanitized query-param keys (key names only, never values)
123
+ * - A SHA-256 of `userId || sessionId` so the server can dedupe
124
+ * distinct users without ever seeing identity
125
+ *
126
+ * Nothing else. No DOM beyond the summary, no query values, no PII.
127
+ */
128
+ discoverInventory?: boolean | MushiDiscoverInventoryConfig;
129
+ }
130
+ /**
131
+ * Fine-grained controls for `discoverInventory`. Defaults are tuned
132
+ * to be quiet enough for production but dense enough for the proposer
133
+ * to produce useful drafts.
134
+ */
135
+ interface MushiDiscoverInventoryConfig {
136
+ enabled?: boolean;
137
+ /**
138
+ * Minimum gap between two emissions for the same route (ms). Defaults
139
+ * to 60_000. Set to a larger value for high-traffic SPAs to keep the
140
+ * ingest volume manageable.
141
+ */
142
+ throttleMs?: number;
143
+ /**
144
+ * Static route templates the host framework knows about. When set,
145
+ * a visit to `/practice/abc-123` matches `/practice/[id]` and gets
146
+ * normalized to that template. Without this we fall back to a
147
+ * heuristic (uuid / numeric / 24-char-hex segments collapse to
148
+ * `[id]`). Provide this when you have a static manifest — Next.js'
149
+ * `next.config` route export, React Router's route config, etc.
150
+ */
151
+ routeTemplates?: string[];
152
+ /**
153
+ * Override the SHA-256 user-id-hash input. Defaults to
154
+ * `mushi.userId || sessionId`. Set to `null` to opt out of distinct-
155
+ * user counting entirely.
156
+ */
157
+ userIdSource?: 'auto' | 'session-only' | 'none';
158
+ /**
159
+ * If false, the DOM summary (≤200 chars from h1/title/main) is not
160
+ * captured. Default true.
161
+ */
162
+ captureDomSummary?: boolean;
100
163
  }
101
164
  interface MushiPrivacyConfig {
102
165
  /** DOM nodes to visually mask in screenshots before upload. */
@@ -256,6 +319,23 @@ interface MushiEnvironment {
256
319
  };
257
320
  deviceMemory?: number;
258
321
  hardwareConcurrency?: number;
322
+ /**
323
+ * v2 inventory hints (whitepaper §4.7).
324
+ *
325
+ * `route` — the bare pathname (no query / hash). Pinned to the
326
+ * inventory's `Page.path` so the Triage LLM can shortcut from a
327
+ * freeform "the streak counter is broken" report to the right page.
328
+ *
329
+ * `nearestTestid` — the closest ancestor of the active element with
330
+ * a `data-testid`, captured at widget-open time. This pins the
331
+ * report to one Action node when more than one page has the same
332
+ * page path (e.g. a shared "Buy Pro" CTA on landing + dashboard).
333
+ *
334
+ * Both are best-effort; freeform reports with no active element will
335
+ * have `route` only.
336
+ */
337
+ route?: string;
338
+ nearestTestid?: string;
259
339
  }
260
340
  interface MushiConsoleEntry {
261
341
  level: 'log' | 'warn' | 'error' | 'info' | 'debug';
@@ -294,6 +374,18 @@ interface MushiSelectedElement {
294
374
  width: number;
295
375
  height: number;
296
376
  };
377
+ /**
378
+ * `data-testid` of the closest ancestor that has one. Mushi v2 uses this
379
+ * to map a report → Action node in the bidirectional graph (whitepaper §4.7).
380
+ * Falls back to `undefined` when no ancestor declares a testid — the v2
381
+ * Triage LLM still classifies these reports, just without the inventory
382
+ * grounding shortcut.
383
+ */
384
+ nearestTestid?: string;
385
+ /** Path of the page the user reported from (`window.location.pathname`).
386
+ * Combined with `nearestTestid` it pins a report to one Action even when
387
+ * the same testid exists on multiple pages. */
388
+ route?: string;
297
389
  }
298
390
  type MushiTimelineKind = 'route' | 'click' | 'request' | 'log' | 'screen';
299
391
  interface MushiTimelineEntry {
@@ -391,6 +483,16 @@ interface MushiApiClient {
391
483
  }>>;
392
484
  getSdkConfig(): Promise<MushiApiResponse<MushiRuntimeSdkConfig>>;
393
485
  getLatestSdkVersion(packageName: string): Promise<MushiApiResponse<MushiSdkVersionInfo>>;
486
+ /**
487
+ * Mushi v2.1: ship a single passive-discovery observation (route +
488
+ * testids + outbound APIs + DOM summary). Best-effort fire-and-forget;
489
+ * the caller should not block on the response. The server rate-limits
490
+ * per (project, route) to keep ingest cheap, so it's fine for clients
491
+ * to over-emit on the throttle window — the server picks the freshest.
492
+ */
493
+ postDiscoveryEvent(event: MushiDiscoveryEventPayload): Promise<MushiApiResponse<{
494
+ accepted: boolean;
495
+ }>>;
394
496
  listReporterReports(reporterToken: string): Promise<MushiApiResponse<{
395
497
  reports: MushiReporterReport[];
396
498
  }>>;
@@ -401,6 +503,22 @@ interface MushiApiClient {
401
503
  comment: MushiReporterComment;
402
504
  }>>;
403
505
  }
506
+ /**
507
+ * Wire shape of a single discovery event sent by the SDK to
508
+ * `POST /v1/sdk/discovery`. Mirrored server-side in
509
+ * `_shared/schemas.ts::discoveryEventSchema`.
510
+ */
511
+ interface MushiDiscoveryEventPayload {
512
+ route: string;
513
+ page_title?: string | null;
514
+ dom_summary?: string | null;
515
+ testids: string[];
516
+ network_paths: string[];
517
+ query_param_keys: string[];
518
+ user_id_hash?: string | null;
519
+ sdk_version?: string;
520
+ observed_at: string;
521
+ }
404
522
  interface MushiApiResponse<T> {
405
523
  ok: boolean;
406
524
  data?: T;
@@ -462,7 +580,7 @@ interface ApiClientOptions {
462
580
  declare const DEFAULT_API_ENDPOINT = "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api";
463
581
  declare const MUSHI_INTERNAL_HEADER = "X-Mushi-Internal";
464
582
  declare const MUSHI_INTERNAL_INIT_MARKER = "__mushiInternal";
465
- type MushiInternalRequestKind = 'sdk-config' | 'report-submit' | 'report-status' | 'reporter-poll' | 'diagnose';
583
+ type MushiInternalRequestKind = 'sdk-config' | 'report-submit' | 'report-status' | 'reporter-poll' | 'diagnose' | 'discovery';
466
584
  declare function createApiClient(options: ApiClientOptions): MushiApiClient;
467
585
 
468
586
  /**
@@ -654,4 +772,4 @@ declare function createLogger(options: LoggerOptions): Logger;
654
772
  */
655
773
  declare const noopLogger: Logger;
656
774
 
657
- export { type ApiClientOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiDiagnosticsResult, 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 MushiRewardsConfig, type MushiRuntimeSdkConfig, type MushiSDKInstance, type MushiSdkVersionInfo, type MushiSelectedElement, type MushiSentryConfig, type MushiTimelineEntry, type MushiTimelineKind, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type OfflineQueue, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, captureEnvironment, createApiClient, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, noopLogger, resolveRegionEndpoint, scrubPii };
775
+ export { type ApiClientOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiCaptureConfig, type MushiCaptureEventInput, 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 MushiRewardsConfig, type MushiRuntimeSdkConfig, type MushiSDKInstance, type MushiSdkVersionInfo, type MushiSelectedElement, type MushiSentryConfig, type MushiTimelineEntry, type MushiTimelineKind, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type OfflineQueue, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, captureEnvironment, createApiClient, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, noopLogger, resolveRegionEndpoint, scrubPii };
package/dist/index.d.ts CHANGED
@@ -97,6 +97,69 @@ interface MushiCaptureConfig {
97
97
  screenshot?: 'on-report' | 'auto' | 'off';
98
98
  elementSelector?: boolean;
99
99
  replay?: 'sentry' | 'rrweb' | 'lite' | 'off';
100
+ /**
101
+ * Mushi Mushi v2.1 (whitepaper §6 hybrid mode): passive inventory
102
+ * discovery. When enabled the SDK observes navigations and emits a
103
+ * tiny payload — `(route, title, testids[], outbound api paths[],
104
+ * dom_summary[≤200 chars])` — back to `/v1/sdk/discovery`. The
105
+ * server aggregates this over a 30-day rolling window and the
106
+ * admin can ask Claude to propose an `inventory.yaml` from it.
107
+ *
108
+ * Default: off. Recommended: enable in dev/preview/staging from the
109
+ * day you install the SDK so the proposer has data when you go to
110
+ * generate your first inventory.
111
+ *
112
+ * What is sent (per navigation, throttled to ≤1/route/min):
113
+ * - `location.pathname` template-normalized (`/practice/abc-123`
114
+ * → `/practice/[id]` via uuid/numeric/hex heuristics + an
115
+ * optional framework hint if the host app sets
116
+ * `discoverInventory.routeTemplates`)
117
+ * - `document.title`
118
+ * - All `[data-testid]` values currently in the DOM
119
+ * - Recent fetch/XHR paths the existing network capturer saw
120
+ * - The first text run of `<h1>` / `<title>` / `<main>` truncated
121
+ * to 200 chars (helps Claude name stories well)
122
+ * - Sanitized query-param keys (key names only, never values)
123
+ * - A SHA-256 of `userId || sessionId` so the server can dedupe
124
+ * distinct users without ever seeing identity
125
+ *
126
+ * Nothing else. No DOM beyond the summary, no query values, no PII.
127
+ */
128
+ discoverInventory?: boolean | MushiDiscoverInventoryConfig;
129
+ }
130
+ /**
131
+ * Fine-grained controls for `discoverInventory`. Defaults are tuned
132
+ * to be quiet enough for production but dense enough for the proposer
133
+ * to produce useful drafts.
134
+ */
135
+ interface MushiDiscoverInventoryConfig {
136
+ enabled?: boolean;
137
+ /**
138
+ * Minimum gap between two emissions for the same route (ms). Defaults
139
+ * to 60_000. Set to a larger value for high-traffic SPAs to keep the
140
+ * ingest volume manageable.
141
+ */
142
+ throttleMs?: number;
143
+ /**
144
+ * Static route templates the host framework knows about. When set,
145
+ * a visit to `/practice/abc-123` matches `/practice/[id]` and gets
146
+ * normalized to that template. Without this we fall back to a
147
+ * heuristic (uuid / numeric / 24-char-hex segments collapse to
148
+ * `[id]`). Provide this when you have a static manifest — Next.js'
149
+ * `next.config` route export, React Router's route config, etc.
150
+ */
151
+ routeTemplates?: string[];
152
+ /**
153
+ * Override the SHA-256 user-id-hash input. Defaults to
154
+ * `mushi.userId || sessionId`. Set to `null` to opt out of distinct-
155
+ * user counting entirely.
156
+ */
157
+ userIdSource?: 'auto' | 'session-only' | 'none';
158
+ /**
159
+ * If false, the DOM summary (≤200 chars from h1/title/main) is not
160
+ * captured. Default true.
161
+ */
162
+ captureDomSummary?: boolean;
100
163
  }
101
164
  interface MushiPrivacyConfig {
102
165
  /** DOM nodes to visually mask in screenshots before upload. */
@@ -256,6 +319,23 @@ interface MushiEnvironment {
256
319
  };
257
320
  deviceMemory?: number;
258
321
  hardwareConcurrency?: number;
322
+ /**
323
+ * v2 inventory hints (whitepaper §4.7).
324
+ *
325
+ * `route` — the bare pathname (no query / hash). Pinned to the
326
+ * inventory's `Page.path` so the Triage LLM can shortcut from a
327
+ * freeform "the streak counter is broken" report to the right page.
328
+ *
329
+ * `nearestTestid` — the closest ancestor of the active element with
330
+ * a `data-testid`, captured at widget-open time. This pins the
331
+ * report to one Action node when more than one page has the same
332
+ * page path (e.g. a shared "Buy Pro" CTA on landing + dashboard).
333
+ *
334
+ * Both are best-effort; freeform reports with no active element will
335
+ * have `route` only.
336
+ */
337
+ route?: string;
338
+ nearestTestid?: string;
259
339
  }
260
340
  interface MushiConsoleEntry {
261
341
  level: 'log' | 'warn' | 'error' | 'info' | 'debug';
@@ -294,6 +374,18 @@ interface MushiSelectedElement {
294
374
  width: number;
295
375
  height: number;
296
376
  };
377
+ /**
378
+ * `data-testid` of the closest ancestor that has one. Mushi v2 uses this
379
+ * to map a report → Action node in the bidirectional graph (whitepaper §4.7).
380
+ * Falls back to `undefined` when no ancestor declares a testid — the v2
381
+ * Triage LLM still classifies these reports, just without the inventory
382
+ * grounding shortcut.
383
+ */
384
+ nearestTestid?: string;
385
+ /** Path of the page the user reported from (`window.location.pathname`).
386
+ * Combined with `nearestTestid` it pins a report to one Action even when
387
+ * the same testid exists on multiple pages. */
388
+ route?: string;
297
389
  }
298
390
  type MushiTimelineKind = 'route' | 'click' | 'request' | 'log' | 'screen';
299
391
  interface MushiTimelineEntry {
@@ -391,6 +483,16 @@ interface MushiApiClient {
391
483
  }>>;
392
484
  getSdkConfig(): Promise<MushiApiResponse<MushiRuntimeSdkConfig>>;
393
485
  getLatestSdkVersion(packageName: string): Promise<MushiApiResponse<MushiSdkVersionInfo>>;
486
+ /**
487
+ * Mushi v2.1: ship a single passive-discovery observation (route +
488
+ * testids + outbound APIs + DOM summary). Best-effort fire-and-forget;
489
+ * the caller should not block on the response. The server rate-limits
490
+ * per (project, route) to keep ingest cheap, so it's fine for clients
491
+ * to over-emit on the throttle window — the server picks the freshest.
492
+ */
493
+ postDiscoveryEvent(event: MushiDiscoveryEventPayload): Promise<MushiApiResponse<{
494
+ accepted: boolean;
495
+ }>>;
394
496
  listReporterReports(reporterToken: string): Promise<MushiApiResponse<{
395
497
  reports: MushiReporterReport[];
396
498
  }>>;
@@ -401,6 +503,22 @@ interface MushiApiClient {
401
503
  comment: MushiReporterComment;
402
504
  }>>;
403
505
  }
506
+ /**
507
+ * Wire shape of a single discovery event sent by the SDK to
508
+ * `POST /v1/sdk/discovery`. Mirrored server-side in
509
+ * `_shared/schemas.ts::discoveryEventSchema`.
510
+ */
511
+ interface MushiDiscoveryEventPayload {
512
+ route: string;
513
+ page_title?: string | null;
514
+ dom_summary?: string | null;
515
+ testids: string[];
516
+ network_paths: string[];
517
+ query_param_keys: string[];
518
+ user_id_hash?: string | null;
519
+ sdk_version?: string;
520
+ observed_at: string;
521
+ }
404
522
  interface MushiApiResponse<T> {
405
523
  ok: boolean;
406
524
  data?: T;
@@ -462,7 +580,7 @@ interface ApiClientOptions {
462
580
  declare const DEFAULT_API_ENDPOINT = "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api";
463
581
  declare const MUSHI_INTERNAL_HEADER = "X-Mushi-Internal";
464
582
  declare const MUSHI_INTERNAL_INIT_MARKER = "__mushiInternal";
465
- type MushiInternalRequestKind = 'sdk-config' | 'report-submit' | 'report-status' | 'reporter-poll' | 'diagnose';
583
+ type MushiInternalRequestKind = 'sdk-config' | 'report-submit' | 'report-status' | 'reporter-poll' | 'diagnose' | 'discovery';
466
584
  declare function createApiClient(options: ApiClientOptions): MushiApiClient;
467
585
 
468
586
  /**
@@ -654,4 +772,4 @@ declare function createLogger(options: LoggerOptions): Logger;
654
772
  */
655
773
  declare const noopLogger: Logger;
656
774
 
657
- export { type ApiClientOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiDiagnosticsResult, 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 MushiRewardsConfig, type MushiRuntimeSdkConfig, type MushiSDKInstance, type MushiSdkVersionInfo, type MushiSelectedElement, type MushiSentryConfig, type MushiTimelineEntry, type MushiTimelineKind, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type OfflineQueue, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, captureEnvironment, createApiClient, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, noopLogger, resolveRegionEndpoint, scrubPii };
775
+ export { type ApiClientOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiCaptureConfig, type MushiCaptureEventInput, 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 MushiRewardsConfig, type MushiRuntimeSdkConfig, type MushiSDKInstance, type MushiSdkVersionInfo, type MushiSelectedElement, type MushiSentryConfig, type MushiTimelineEntry, type MushiTimelineKind, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type OfflineQueue, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, captureEnvironment, createApiClient, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, noopLogger, resolveRegionEndpoint, scrubPii };
package/dist/index.js CHANGED
@@ -119,6 +119,15 @@ function createApiClient(options) {
119
119
  const query = new URLSearchParams({ package: packageName }).toString();
120
120
  return request("GET", `/v1/sdk/latest-version?${query}`, void 0, maxRetries, "sdk-config");
121
121
  },
122
+ async postDiscoveryEvent(event) {
123
+ return request(
124
+ "POST",
125
+ "/v1/sdk/discovery",
126
+ event,
127
+ 1,
128
+ "discovery"
129
+ );
130
+ },
122
131
  async listReporterReports(reporterToken) {
123
132
  return requestForReporter("GET", "/v1/reporter/reports", reporterToken);
124
133
  },
@@ -793,9 +802,23 @@ function captureEnvironment() {
793
802
  rtt: connection.rtt
794
803
  } : void 0,
795
804
  deviceMemory: nav?.deviceMemory,
796
- hardwareConcurrency: nav?.hardwareConcurrency
805
+ hardwareConcurrency: nav?.hardwareConcurrency,
806
+ route: win?.location?.pathname,
807
+ nearestTestid: findNearestTestidFromActive(doc)
797
808
  };
798
809
  }
810
+ function findNearestTestidFromActive(doc) {
811
+ if (!doc) return void 0;
812
+ let cur = doc.activeElement ?? null;
813
+ let hops = 0;
814
+ while (cur && hops < 20) {
815
+ const tid = cur.getAttribute?.("data-testid");
816
+ if (tid) return tid;
817
+ cur = cur.parentElement;
818
+ hops++;
819
+ }
820
+ return void 0;
821
+ }
799
822
 
800
823
  // src/reporter-token.ts
801
824
  var STORAGE_KEY = "mushi_reporter_token";