@mushi-mushi/core 0.9.0 → 1.1.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/CONTRIBUTING.md +27 -0
- package/README.md +7 -1
- package/SECURITY.md +167 -4
- package/dist/index.cjs +244 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +497 -2
- package/dist/index.d.ts +497 -2
- package/dist/index.js +243 -2
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -145,6 +145,53 @@ function createApiClient(options) {
|
|
|
145
145
|
reporterToken,
|
|
146
146
|
{ body }
|
|
147
147
|
);
|
|
148
|
+
},
|
|
149
|
+
// ─── Rewards program (P1) ──────────────────────────────────
|
|
150
|
+
async submitActivity(userId, events, opts) {
|
|
151
|
+
return request(
|
|
152
|
+
"POST",
|
|
153
|
+
"/v1/sdk/activity",
|
|
154
|
+
{
|
|
155
|
+
user_id: userId,
|
|
156
|
+
user_traits: opts?.userTraits,
|
|
157
|
+
opted_in: opts?.optedIn,
|
|
158
|
+
reporter_token_hash: opts?.reporterTokenHash,
|
|
159
|
+
// P2: JWT for monetary verification; omitted when null
|
|
160
|
+
...opts?.hostJwt ? { host_jwt: opts.hostJwt } : {},
|
|
161
|
+
events
|
|
162
|
+
},
|
|
163
|
+
1,
|
|
164
|
+
// best-effort, 1 retry
|
|
165
|
+
"discovery"
|
|
166
|
+
);
|
|
167
|
+
},
|
|
168
|
+
async getMyPoints(userId) {
|
|
169
|
+
return request(
|
|
170
|
+
"GET",
|
|
171
|
+
`/v1/sdk/me/points?userId=${encodeURIComponent(userId)}`,
|
|
172
|
+
void 0,
|
|
173
|
+
1,
|
|
174
|
+
"reporter-poll"
|
|
175
|
+
);
|
|
176
|
+
},
|
|
177
|
+
async getMyTier(userId) {
|
|
178
|
+
return request(
|
|
179
|
+
"GET",
|
|
180
|
+
`/v1/sdk/me/tier?userId=${encodeURIComponent(userId)}`,
|
|
181
|
+
void 0,
|
|
182
|
+
1,
|
|
183
|
+
"reporter-poll"
|
|
184
|
+
);
|
|
185
|
+
},
|
|
186
|
+
async getMyHistory(userId, opts) {
|
|
187
|
+
const qs = new URLSearchParams({ userId, ...opts?.limit ? { limit: String(opts.limit) } : {} });
|
|
188
|
+
return request(
|
|
189
|
+
"GET",
|
|
190
|
+
`/v1/sdk/me/history?${qs}`,
|
|
191
|
+
void 0,
|
|
192
|
+
1,
|
|
193
|
+
"reporter-poll"
|
|
194
|
+
);
|
|
148
195
|
}
|
|
149
196
|
};
|
|
150
197
|
}
|
|
@@ -783,6 +830,8 @@ function captureEnvironment() {
|
|
|
783
830
|
const nav = typeof navigator !== "undefined" ? navigator : void 0;
|
|
784
831
|
const win = typeof window !== "undefined" ? window : void 0;
|
|
785
832
|
const doc = typeof document !== "undefined" ? document : void 0;
|
|
833
|
+
const scr = typeof screen !== "undefined" ? screen : void 0;
|
|
834
|
+
void kickOffUserAgentData(nav);
|
|
786
835
|
const connection = nav && "connection" in nav ? nav.connection : void 0;
|
|
787
836
|
return {
|
|
788
837
|
userAgent: nav?.userAgent ?? "unknown",
|
|
@@ -804,7 +853,19 @@ function captureEnvironment() {
|
|
|
804
853
|
deviceMemory: nav?.deviceMemory,
|
|
805
854
|
hardwareConcurrency: nav?.hardwareConcurrency,
|
|
806
855
|
route: win?.location?.pathname,
|
|
807
|
-
nearestTestid: findNearestTestidFromActive(doc)
|
|
856
|
+
nearestTestid: findNearestTestidFromActive(doc),
|
|
857
|
+
userAgentData: captureUserAgentData(nav),
|
|
858
|
+
screen: captureScreen(scr, win),
|
|
859
|
+
prefersColorScheme: matchScheme(win),
|
|
860
|
+
prefersReducedMotion: matchMedia(win, "(prefers-reduced-motion: reduce)"),
|
|
861
|
+
prefersReducedData: matchMedia(win, "(prefers-reduced-data: reduce)"),
|
|
862
|
+
prefersContrast: matchContrast(win),
|
|
863
|
+
forcedColors: matchMedia(win, "(forced-colors: active)"),
|
|
864
|
+
online: typeof nav?.onLine === "boolean" ? nav.onLine : void 0,
|
|
865
|
+
displayMode: matchDisplayMode(win),
|
|
866
|
+
documentTitle: doc?.title?.slice(0, 200),
|
|
867
|
+
buildId: readBuildIdMeta(doc),
|
|
868
|
+
pageLoadTiming: capturePageLoadTiming(win)
|
|
808
869
|
};
|
|
809
870
|
}
|
|
810
871
|
function findNearestTestidFromActive(doc) {
|
|
@@ -819,6 +880,117 @@ function findNearestTestidFromActive(doc) {
|
|
|
819
880
|
}
|
|
820
881
|
return void 0;
|
|
821
882
|
}
|
|
883
|
+
var cachedHighEntropy = null;
|
|
884
|
+
var highEntropyKickedOff = false;
|
|
885
|
+
function kickOffUserAgentData(nav) {
|
|
886
|
+
if (highEntropyKickedOff) return;
|
|
887
|
+
const ua = nav?.userAgentData;
|
|
888
|
+
if (!ua?.getHighEntropyValues) return;
|
|
889
|
+
highEntropyKickedOff = true;
|
|
890
|
+
ua.getHighEntropyValues([
|
|
891
|
+
"architecture",
|
|
892
|
+
"bitness",
|
|
893
|
+
"model",
|
|
894
|
+
"platformVersion",
|
|
895
|
+
"uaFullVersion",
|
|
896
|
+
"fullVersionList"
|
|
897
|
+
]).then((v) => {
|
|
898
|
+
cachedHighEntropy = v;
|
|
899
|
+
}).catch(() => {
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
function pickBrand(brands) {
|
|
903
|
+
if (!brands?.length) return void 0;
|
|
904
|
+
const real = brands.filter((b) => !/not.?a.?brand/i.test(b.brand));
|
|
905
|
+
if (real.length === 0) return void 0;
|
|
906
|
+
const named = real.find((b) => !/chromium|google chrome/i.test(b.brand));
|
|
907
|
+
return named ?? real[0];
|
|
908
|
+
}
|
|
909
|
+
function captureUserAgentData(nav) {
|
|
910
|
+
const low = nav?.userAgentData;
|
|
911
|
+
if (!low && !cachedHighEntropy) return void 0;
|
|
912
|
+
const fullList = cachedHighEntropy?.fullVersionList;
|
|
913
|
+
const brand = pickBrand(fullList ?? low?.brands);
|
|
914
|
+
const out = {};
|
|
915
|
+
if (brand) {
|
|
916
|
+
out.browser = brand.brand;
|
|
917
|
+
out.browserVersion = brand.version;
|
|
918
|
+
}
|
|
919
|
+
if (low?.platform) out.os = low.platform;
|
|
920
|
+
if (cachedHighEntropy?.platformVersion) out.osVersion = cachedHighEntropy.platformVersion;
|
|
921
|
+
if (typeof low?.mobile === "boolean") out.mobile = low.mobile;
|
|
922
|
+
if (cachedHighEntropy?.model) out.model = cachedHighEntropy.model;
|
|
923
|
+
if (cachedHighEntropy?.architecture) out.architecture = cachedHighEntropy.architecture;
|
|
924
|
+
if (cachedHighEntropy?.bitness) out.bitness = cachedHighEntropy.bitness;
|
|
925
|
+
return Object.keys(out).length === 0 ? void 0 : out;
|
|
926
|
+
}
|
|
927
|
+
function captureScreen(scr, win) {
|
|
928
|
+
if (!scr && !win) return void 0;
|
|
929
|
+
const out = {};
|
|
930
|
+
if (typeof scr?.width === "number") out.width = scr.width;
|
|
931
|
+
if (typeof scr?.height === "number") out.height = scr.height;
|
|
932
|
+
if (typeof win?.devicePixelRatio === "number") out.devicePixelRatio = win.devicePixelRatio;
|
|
933
|
+
if (typeof scr?.colorDepth === "number") out.colorDepth = scr.colorDepth;
|
|
934
|
+
const orientationType = scr?.orientation?.type;
|
|
935
|
+
if (orientationType) out.orientation = orientationType;
|
|
936
|
+
return Object.keys(out).length === 0 ? void 0 : out;
|
|
937
|
+
}
|
|
938
|
+
function matchMedia(win, query) {
|
|
939
|
+
if (!win?.matchMedia) return void 0;
|
|
940
|
+
try {
|
|
941
|
+
return win.matchMedia(query).matches;
|
|
942
|
+
} catch {
|
|
943
|
+
return void 0;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
function matchScheme(win) {
|
|
947
|
+
if (!win?.matchMedia) return void 0;
|
|
948
|
+
if (matchMedia(win, "(prefers-color-scheme: dark)")) return "dark";
|
|
949
|
+
if (matchMedia(win, "(prefers-color-scheme: light)")) return "light";
|
|
950
|
+
return "no-preference";
|
|
951
|
+
}
|
|
952
|
+
function matchContrast(win) {
|
|
953
|
+
if (!win?.matchMedia) return void 0;
|
|
954
|
+
if (matchMedia(win, "(prefers-contrast: more)")) return "more";
|
|
955
|
+
if (matchMedia(win, "(prefers-contrast: less)")) return "less";
|
|
956
|
+
if (matchMedia(win, "(prefers-contrast: custom)")) return "custom";
|
|
957
|
+
return "no-preference";
|
|
958
|
+
}
|
|
959
|
+
function matchDisplayMode(win) {
|
|
960
|
+
if (!win?.matchMedia) return void 0;
|
|
961
|
+
if (matchMedia(win, "(display-mode: fullscreen)")) return "fullscreen";
|
|
962
|
+
if (matchMedia(win, "(display-mode: standalone)")) return "standalone";
|
|
963
|
+
if (matchMedia(win, "(display-mode: minimal-ui)")) return "minimal-ui";
|
|
964
|
+
if (matchMedia(win, "(display-mode: browser)")) return "browser";
|
|
965
|
+
return void 0;
|
|
966
|
+
}
|
|
967
|
+
function readBuildIdMeta(doc) {
|
|
968
|
+
if (!doc) return void 0;
|
|
969
|
+
const el = doc.querySelector?.('meta[name="mushi:build"]');
|
|
970
|
+
const v = el?.content?.trim();
|
|
971
|
+
if (!v) return void 0;
|
|
972
|
+
return v.slice(0, 64);
|
|
973
|
+
}
|
|
974
|
+
function capturePageLoadTiming(win) {
|
|
975
|
+
const perf = win?.performance;
|
|
976
|
+
if (!perf?.getEntriesByType) return void 0;
|
|
977
|
+
let entry;
|
|
978
|
+
try {
|
|
979
|
+
const entries = perf.getEntriesByType("navigation");
|
|
980
|
+
entry = entries[0];
|
|
981
|
+
} catch {
|
|
982
|
+
return void 0;
|
|
983
|
+
}
|
|
984
|
+
if (!entry) return void 0;
|
|
985
|
+
const start = entry.startTime ?? 0;
|
|
986
|
+
const out = {};
|
|
987
|
+
if (entry.domContentLoadedEventEnd > 0)
|
|
988
|
+
out.domContentLoadedMs = Math.round(entry.domContentLoadedEventEnd - start);
|
|
989
|
+
if (entry.loadEventEnd > 0) out.loadCompleteMs = Math.round(entry.loadEventEnd - start);
|
|
990
|
+
if (entry.responseStart > 0) out.timeToFirstByteMs = Math.round(entry.responseStart - start);
|
|
991
|
+
if (typeof entry.type === "string") out.navigationType = entry.type;
|
|
992
|
+
return Object.keys(out).length === 0 ? void 0 : out;
|
|
993
|
+
}
|
|
822
994
|
|
|
823
995
|
// src/reporter-token.ts
|
|
824
996
|
var STORAGE_KEY = "mushi_reporter_token";
|
|
@@ -1026,6 +1198,75 @@ function scrubPii(text, config) {
|
|
|
1026
1198
|
return createPiiScrubber(config).scrub(text);
|
|
1027
1199
|
}
|
|
1028
1200
|
|
|
1029
|
-
|
|
1201
|
+
// src/breadcrumbs.ts
|
|
1202
|
+
var DEFAULT_MAX = 50;
|
|
1203
|
+
var DEFAULT_MAX_MESSAGE = 500;
|
|
1204
|
+
function createBreadcrumbBuffer(options = {}) {
|
|
1205
|
+
const max = Math.max(1, options.max ?? DEFAULT_MAX);
|
|
1206
|
+
const maxMsg = Math.max(50, options.maxMessageLength ?? DEFAULT_MAX_MESSAGE);
|
|
1207
|
+
let entries = [];
|
|
1208
|
+
return {
|
|
1209
|
+
add(input) {
|
|
1210
|
+
const ts = typeof input.timestamp === "number" ? input.timestamp : Date.now();
|
|
1211
|
+
const message = typeof input.message === "string" && input.message.length > maxMsg ? `${input.message.slice(0, maxMsg)}\u2026` : input.message;
|
|
1212
|
+
const crumb = {
|
|
1213
|
+
timestamp: ts,
|
|
1214
|
+
category: input.category,
|
|
1215
|
+
level: input.level ?? "info",
|
|
1216
|
+
message: message ?? "",
|
|
1217
|
+
...input.data ? { data: input.data } : {}
|
|
1218
|
+
};
|
|
1219
|
+
entries.push(crumb);
|
|
1220
|
+
while (entries.length > max) entries.shift();
|
|
1221
|
+
},
|
|
1222
|
+
getAll() {
|
|
1223
|
+
return entries.slice();
|
|
1224
|
+
},
|
|
1225
|
+
clear() {
|
|
1226
|
+
entries = [];
|
|
1227
|
+
},
|
|
1228
|
+
size() {
|
|
1229
|
+
return entries.length;
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// src/exception-normaliser.ts
|
|
1235
|
+
var STACK_LIMIT = 8 * 1024;
|
|
1236
|
+
var FALLBACK_JSON_LIMIT = 1e3;
|
|
1237
|
+
function normaliseThrown(thrown) {
|
|
1238
|
+
if (thrown instanceof Error) {
|
|
1239
|
+
const name = thrown.name || "Error";
|
|
1240
|
+
const message = thrown.message || String(thrown);
|
|
1241
|
+
const stack = typeof thrown.stack === "string" && thrown.stack.length > 0 ? thrown.stack.slice(0, STACK_LIMIT) : void 0;
|
|
1242
|
+
const cause = thrown.cause;
|
|
1243
|
+
return {
|
|
1244
|
+
name,
|
|
1245
|
+
message,
|
|
1246
|
+
...stack ? { stack } : {},
|
|
1247
|
+
...cause !== void 0 ? { cause: cause instanceof Error ? cause.message : cause } : {}
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
if (typeof thrown === "string") {
|
|
1251
|
+
return { name: "Error", message: thrown };
|
|
1252
|
+
}
|
|
1253
|
+
if (thrown && typeof thrown === "object") {
|
|
1254
|
+
const obj = thrown;
|
|
1255
|
+
const name = typeof obj.name === "string" ? obj.name : "Error";
|
|
1256
|
+
const message = typeof obj.message === "string" ? obj.message : (() => {
|
|
1257
|
+
try {
|
|
1258
|
+
return JSON.stringify(obj).slice(0, FALLBACK_JSON_LIMIT);
|
|
1259
|
+
} catch {
|
|
1260
|
+
return String(obj);
|
|
1261
|
+
}
|
|
1262
|
+
})();
|
|
1263
|
+
const stack = typeof obj.stack === "string" ? obj.stack.slice(0, STACK_LIMIT) : void 0;
|
|
1264
|
+
return { name, message, ...stack ? { stack } : {} };
|
|
1265
|
+
}
|
|
1266
|
+
if (thrown === void 0) return { name: "Error", message: "unknown" };
|
|
1267
|
+
return { name: "Error", message: String(thrown) };
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
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 };
|
|
1030
1271
|
//# sourceMappingURL=index.js.map
|
|
1031
1272
|
//# sourceMappingURL=index.js.map
|