@mushi-mushi/core 0.8.0 → 1.0.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.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
  },
@@ -774,6 +783,8 @@ function captureEnvironment() {
774
783
  const nav = typeof navigator !== "undefined" ? navigator : void 0;
775
784
  const win = typeof window !== "undefined" ? window : void 0;
776
785
  const doc = typeof document !== "undefined" ? document : void 0;
786
+ const scr = typeof screen !== "undefined" ? screen : void 0;
787
+ void kickOffUserAgentData(nav);
777
788
  const connection = nav && "connection" in nav ? nav.connection : void 0;
778
789
  return {
779
790
  userAgent: nav?.userAgent ?? "unknown",
@@ -793,9 +804,146 @@ function captureEnvironment() {
793
804
  rtt: connection.rtt
794
805
  } : void 0,
795
806
  deviceMemory: nav?.deviceMemory,
796
- hardwareConcurrency: nav?.hardwareConcurrency
807
+ hardwareConcurrency: nav?.hardwareConcurrency,
808
+ route: win?.location?.pathname,
809
+ nearestTestid: findNearestTestidFromActive(doc),
810
+ userAgentData: captureUserAgentData(nav),
811
+ screen: captureScreen(scr, win),
812
+ prefersColorScheme: matchScheme(win),
813
+ prefersReducedMotion: matchMedia(win, "(prefers-reduced-motion: reduce)"),
814
+ prefersReducedData: matchMedia(win, "(prefers-reduced-data: reduce)"),
815
+ prefersContrast: matchContrast(win),
816
+ forcedColors: matchMedia(win, "(forced-colors: active)"),
817
+ online: typeof nav?.onLine === "boolean" ? nav.onLine : void 0,
818
+ displayMode: matchDisplayMode(win),
819
+ documentTitle: doc?.title?.slice(0, 200),
820
+ buildId: readBuildIdMeta(doc),
821
+ pageLoadTiming: capturePageLoadTiming(win)
797
822
  };
798
823
  }
824
+ function findNearestTestidFromActive(doc) {
825
+ if (!doc) return void 0;
826
+ let cur = doc.activeElement ?? null;
827
+ let hops = 0;
828
+ while (cur && hops < 20) {
829
+ const tid = cur.getAttribute?.("data-testid");
830
+ if (tid) return tid;
831
+ cur = cur.parentElement;
832
+ hops++;
833
+ }
834
+ return void 0;
835
+ }
836
+ var cachedHighEntropy = null;
837
+ var highEntropyKickedOff = false;
838
+ function kickOffUserAgentData(nav) {
839
+ if (highEntropyKickedOff) return;
840
+ const ua = nav?.userAgentData;
841
+ if (!ua?.getHighEntropyValues) return;
842
+ highEntropyKickedOff = true;
843
+ ua.getHighEntropyValues([
844
+ "architecture",
845
+ "bitness",
846
+ "model",
847
+ "platformVersion",
848
+ "uaFullVersion",
849
+ "fullVersionList"
850
+ ]).then((v) => {
851
+ cachedHighEntropy = v;
852
+ }).catch(() => {
853
+ });
854
+ }
855
+ function pickBrand(brands) {
856
+ if (!brands?.length) return void 0;
857
+ const real = brands.filter((b) => !/not.?a.?brand/i.test(b.brand));
858
+ if (real.length === 0) return void 0;
859
+ const named = real.find((b) => !/chromium|google chrome/i.test(b.brand));
860
+ return named ?? real[0];
861
+ }
862
+ function captureUserAgentData(nav) {
863
+ const low = nav?.userAgentData;
864
+ if (!low && !cachedHighEntropy) return void 0;
865
+ const fullList = cachedHighEntropy?.fullVersionList;
866
+ const brand = pickBrand(fullList ?? low?.brands);
867
+ const out = {};
868
+ if (brand) {
869
+ out.browser = brand.brand;
870
+ out.browserVersion = brand.version;
871
+ }
872
+ if (low?.platform) out.os = low.platform;
873
+ if (cachedHighEntropy?.platformVersion) out.osVersion = cachedHighEntropy.platformVersion;
874
+ if (typeof low?.mobile === "boolean") out.mobile = low.mobile;
875
+ if (cachedHighEntropy?.model) out.model = cachedHighEntropy.model;
876
+ if (cachedHighEntropy?.architecture) out.architecture = cachedHighEntropy.architecture;
877
+ if (cachedHighEntropy?.bitness) out.bitness = cachedHighEntropy.bitness;
878
+ return Object.keys(out).length === 0 ? void 0 : out;
879
+ }
880
+ function captureScreen(scr, win) {
881
+ if (!scr && !win) return void 0;
882
+ const out = {};
883
+ if (typeof scr?.width === "number") out.width = scr.width;
884
+ if (typeof scr?.height === "number") out.height = scr.height;
885
+ if (typeof win?.devicePixelRatio === "number") out.devicePixelRatio = win.devicePixelRatio;
886
+ if (typeof scr?.colorDepth === "number") out.colorDepth = scr.colorDepth;
887
+ const orientationType = scr?.orientation?.type;
888
+ if (orientationType) out.orientation = orientationType;
889
+ return Object.keys(out).length === 0 ? void 0 : out;
890
+ }
891
+ function matchMedia(win, query) {
892
+ if (!win?.matchMedia) return void 0;
893
+ try {
894
+ return win.matchMedia(query).matches;
895
+ } catch {
896
+ return void 0;
897
+ }
898
+ }
899
+ function matchScheme(win) {
900
+ if (!win?.matchMedia) return void 0;
901
+ if (matchMedia(win, "(prefers-color-scheme: dark)")) return "dark";
902
+ if (matchMedia(win, "(prefers-color-scheme: light)")) return "light";
903
+ return "no-preference";
904
+ }
905
+ function matchContrast(win) {
906
+ if (!win?.matchMedia) return void 0;
907
+ if (matchMedia(win, "(prefers-contrast: more)")) return "more";
908
+ if (matchMedia(win, "(prefers-contrast: less)")) return "less";
909
+ if (matchMedia(win, "(prefers-contrast: custom)")) return "custom";
910
+ return "no-preference";
911
+ }
912
+ function matchDisplayMode(win) {
913
+ if (!win?.matchMedia) return void 0;
914
+ if (matchMedia(win, "(display-mode: fullscreen)")) return "fullscreen";
915
+ if (matchMedia(win, "(display-mode: standalone)")) return "standalone";
916
+ if (matchMedia(win, "(display-mode: minimal-ui)")) return "minimal-ui";
917
+ if (matchMedia(win, "(display-mode: browser)")) return "browser";
918
+ return void 0;
919
+ }
920
+ function readBuildIdMeta(doc) {
921
+ if (!doc) return void 0;
922
+ const el = doc.querySelector?.('meta[name="mushi:build"]');
923
+ const v = el?.content?.trim();
924
+ if (!v) return void 0;
925
+ return v.slice(0, 64);
926
+ }
927
+ function capturePageLoadTiming(win) {
928
+ const perf = win?.performance;
929
+ if (!perf?.getEntriesByType) return void 0;
930
+ let entry;
931
+ try {
932
+ const entries = perf.getEntriesByType("navigation");
933
+ entry = entries[0];
934
+ } catch {
935
+ return void 0;
936
+ }
937
+ if (!entry) return void 0;
938
+ const start = entry.startTime ?? 0;
939
+ const out = {};
940
+ if (entry.domContentLoadedEventEnd > 0)
941
+ out.domContentLoadedMs = Math.round(entry.domContentLoadedEventEnd - start);
942
+ if (entry.loadEventEnd > 0) out.loadCompleteMs = Math.round(entry.loadEventEnd - start);
943
+ if (entry.responseStart > 0) out.timeToFirstByteMs = Math.round(entry.responseStart - start);
944
+ if (typeof entry.type === "string") out.navigationType = entry.type;
945
+ return Object.keys(out).length === 0 ? void 0 : out;
946
+ }
799
947
 
800
948
  // src/reporter-token.ts
801
949
  var STORAGE_KEY = "mushi_reporter_token";
@@ -1003,6 +1151,75 @@ function scrubPii(text, config) {
1003
1151
  return createPiiScrubber(config).scrub(text);
1004
1152
  }
1005
1153
 
1006
- export { DEFAULT_API_ENDPOINT, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, REGION_ENDPOINTS, captureEnvironment, createApiClient, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, noopLogger, resolveRegionEndpoint, scrubPii };
1154
+ // src/breadcrumbs.ts
1155
+ var DEFAULT_MAX = 50;
1156
+ var DEFAULT_MAX_MESSAGE = 500;
1157
+ function createBreadcrumbBuffer(options = {}) {
1158
+ const max = Math.max(1, options.max ?? DEFAULT_MAX);
1159
+ const maxMsg = Math.max(50, options.maxMessageLength ?? DEFAULT_MAX_MESSAGE);
1160
+ let entries = [];
1161
+ return {
1162
+ add(input) {
1163
+ const ts = typeof input.timestamp === "number" ? input.timestamp : Date.now();
1164
+ const message = typeof input.message === "string" && input.message.length > maxMsg ? `${input.message.slice(0, maxMsg)}\u2026` : input.message;
1165
+ const crumb = {
1166
+ timestamp: ts,
1167
+ category: input.category,
1168
+ level: input.level ?? "info",
1169
+ message: message ?? "",
1170
+ ...input.data ? { data: input.data } : {}
1171
+ };
1172
+ entries.push(crumb);
1173
+ while (entries.length > max) entries.shift();
1174
+ },
1175
+ getAll() {
1176
+ return entries.slice();
1177
+ },
1178
+ clear() {
1179
+ entries = [];
1180
+ },
1181
+ size() {
1182
+ return entries.length;
1183
+ }
1184
+ };
1185
+ }
1186
+
1187
+ // src/exception-normaliser.ts
1188
+ var STACK_LIMIT = 8 * 1024;
1189
+ var FALLBACK_JSON_LIMIT = 1e3;
1190
+ function normaliseThrown(thrown) {
1191
+ if (thrown instanceof Error) {
1192
+ const name = thrown.name || "Error";
1193
+ const message = thrown.message || String(thrown);
1194
+ const stack = typeof thrown.stack === "string" && thrown.stack.length > 0 ? thrown.stack.slice(0, STACK_LIMIT) : void 0;
1195
+ const cause = thrown.cause;
1196
+ return {
1197
+ name,
1198
+ message,
1199
+ ...stack ? { stack } : {},
1200
+ ...cause !== void 0 ? { cause: cause instanceof Error ? cause.message : cause } : {}
1201
+ };
1202
+ }
1203
+ if (typeof thrown === "string") {
1204
+ return { name: "Error", message: thrown };
1205
+ }
1206
+ if (thrown && typeof thrown === "object") {
1207
+ const obj = thrown;
1208
+ const name = typeof obj.name === "string" ? obj.name : "Error";
1209
+ const message = typeof obj.message === "string" ? obj.message : (() => {
1210
+ try {
1211
+ return JSON.stringify(obj).slice(0, FALLBACK_JSON_LIMIT);
1212
+ } catch {
1213
+ return String(obj);
1214
+ }
1215
+ })();
1216
+ const stack = typeof obj.stack === "string" ? obj.stack.slice(0, STACK_LIMIT) : void 0;
1217
+ return { name, message, ...stack ? { stack } : {} };
1218
+ }
1219
+ if (thrown === void 0) return { name: "Error", message: "unknown" };
1220
+ return { name: "Error", message: String(thrown) };
1221
+ }
1222
+
1223
+ 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 };
1007
1224
  //# sourceMappingURL=index.js.map
1008
1225
  //# sourceMappingURL=index.js.map