@zyui/error-monitor-sdk 0.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.
@@ -0,0 +1,6 @@
1
+ import { CaptureOptions, DeviceInfo, InitConfig, User } from "./types";
2
+ export declare function init(config: InitConfig): void;
3
+ export declare function setCustomContext(ctx: Record<string, unknown>): void;
4
+ export declare function setUser(user: User): void;
5
+ export declare function setDevice(device: DeviceInfo): void;
6
+ export declare function captureError(error: unknown, options?: CaptureOptions): Promise<void>;
package/dist/client.js ADDED
@@ -0,0 +1,102 @@
1
+ import { send } from "./transport";
2
+ import { getRouteInfo } from "./utils/route";
3
+ const state = {
4
+ context: {},
5
+ };
6
+ const throttleMap = new Map();
7
+ function genId() {
8
+ const buf = typeof crypto !== "undefined" && "randomUUID" in crypto
9
+ ? crypto.randomUUID()
10
+ : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
11
+ return buf;
12
+ }
13
+ function normalizeError(e) {
14
+ if (e instanceof Error) {
15
+ return {
16
+ message: e.message || String(e),
17
+ name: e.name,
18
+ type: e.name,
19
+ stack: e.stack,
20
+ };
21
+ }
22
+ if (typeof e === "string") {
23
+ return {
24
+ message: e,
25
+ type: "String",
26
+ };
27
+ }
28
+ try {
29
+ return {
30
+ message: JSON.stringify(e),
31
+ type: Array.isArray(e) ? "Array" : typeof e,
32
+ };
33
+ }
34
+ catch {
35
+ return {
36
+ message: String(e),
37
+ type: typeof e,
38
+ };
39
+ }
40
+ }
41
+ export function init(config) {
42
+ state.config = config;
43
+ }
44
+ export function setCustomContext(ctx) {
45
+ state.context = { ...state.context, ...ctx };
46
+ }
47
+ export function setUser(user) {
48
+ state.user = { ...state.user, ...user };
49
+ }
50
+ export function setDevice(device) {
51
+ state.device = { ...state.device, ...device };
52
+ }
53
+ function makeKey(details) {
54
+ const m = (details.message || "").toLowerCase();
55
+ const s = (details.stack || details.type || details.name || "").toLowerCase();
56
+ return `${m}|${s}`;
57
+ }
58
+ function buildPayload(details, options) {
59
+ const level = (options === null || options === void 0 ? void 0 : options.level) || "error";
60
+ const tags = options === null || options === void 0 ? void 0 : options.tags;
61
+ const extra = options === null || options === void 0 ? void 0 : options.extra;
62
+ const cfg = state.config;
63
+ const route = getRouteInfo();
64
+ const mergedContext = Object.keys(route).length
65
+ ? { ...state.context, route }
66
+ : state.context;
67
+ return {
68
+ id: genId(),
69
+ ts: Date.now(),
70
+ level,
71
+ environment: cfg === null || cfg === void 0 ? void 0 : cfg.environment,
72
+ appKey: cfg === null || cfg === void 0 ? void 0 : cfg.appKey,
73
+ error: details,
74
+ user: state.user,
75
+ device: state.device,
76
+ context: mergedContext,
77
+ tags,
78
+ extra,
79
+ };
80
+ }
81
+ export async function captureError(error, options) {
82
+ var _a;
83
+ const details = normalizeError(error);
84
+ const cfg = state.config;
85
+ const throttleMs = (_a = cfg === null || cfg === void 0 ? void 0 : cfg.throttleIntervalMs) !== null && _a !== void 0 ? _a : 0;
86
+ if (throttleMs > 0) {
87
+ const key = makeKey(details);
88
+ const now = Date.now();
89
+ const last = throttleMap.get(key);
90
+ if (last && now - last < throttleMs)
91
+ return;
92
+ throttleMap.set(key, now);
93
+ }
94
+ const payload = buildPayload(details, options);
95
+ if (!cfg || !cfg.endpoint)
96
+ return;
97
+ const finalPayload = cfg.beforeSend ? cfg.beforeSend(payload) : payload;
98
+ if (!finalPayload)
99
+ return;
100
+ const transport = cfg.transport || send;
101
+ await transport(finalPayload, cfg);
102
+ }
@@ -0,0 +1,4 @@
1
+ export { init, captureError, setCustomContext, setUser, setDevice, } from "./client";
2
+ export { getWebInfo } from "./utils/web";
3
+ export { getRouteInfo } from "./utils/route";
4
+ export type { InitConfig, User, DeviceInfo, ErrorPayload, Level, CaptureOptions, Transport, } from "./types";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { init, captureError, setCustomContext, setUser, setDevice, } from "./client";
2
+ export { getWebInfo } from "./utils/web";
3
+ export { getRouteInfo } from "./utils/route";
@@ -0,0 +1,2 @@
1
+ import { ErrorPayload, InitConfig } from "./types";
2
+ export declare function send(payload: ErrorPayload, config: InitConfig): Promise<void>;
@@ -0,0 +1,55 @@
1
+ function mergeHeaders(a, b) {
2
+ const r = {};
3
+ if (a)
4
+ Object.keys(a).forEach((k) => {
5
+ r[k] = a[k];
6
+ });
7
+ if (b)
8
+ Object.keys(b).forEach((k) => {
9
+ r[k] = b[k];
10
+ });
11
+ return r;
12
+ }
13
+ export async function send(payload, config) {
14
+ var _a;
15
+ const controller = typeof AbortController !== "undefined" ? new AbortController() : undefined;
16
+ const timeout = (_a = config.timeoutMs) !== null && _a !== void 0 ? _a : 8000;
17
+ let timer;
18
+ if (controller) {
19
+ timer = setTimeout(() => {
20
+ try {
21
+ controller.abort();
22
+ }
23
+ catch { }
24
+ }, timeout);
25
+ }
26
+ const headers = mergeHeaders({ "Content-Type": "application/json" }, config.headers);
27
+ const url = config.endpoint;
28
+ const body = JSON.stringify(payload);
29
+ try {
30
+ if (typeof fetch === "function") {
31
+ await fetch(url, {
32
+ method: "POST",
33
+ headers,
34
+ body,
35
+ signal: controller ? controller.signal : undefined,
36
+ });
37
+ return;
38
+ }
39
+ if (typeof XMLHttpRequest !== "undefined") {
40
+ await new Promise((resolve, reject) => {
41
+ const xhr = new XMLHttpRequest();
42
+ xhr.open("POST", url, true);
43
+ Object.keys(headers).forEach((k) => xhr.setRequestHeader(k, headers[k]));
44
+ xhr.onload = () => resolve();
45
+ xhr.onerror = () => reject(new Error("XMLHttpRequest error"));
46
+ xhr.send(body);
47
+ });
48
+ return;
49
+ }
50
+ }
51
+ finally {
52
+ if (timer)
53
+ clearTimeout(timer);
54
+ }
55
+ }
@@ -0,0 +1,68 @@
1
+ export type Level = "debug" | "info" | "warn" | "error" | "fatal";
2
+ export type Transport = (payload: ErrorPayload, config: InitConfig) => Promise<void>;
3
+ export interface InitConfig {
4
+ endpoint: string;
5
+ appKey?: string;
6
+ environment?: string;
7
+ headers?: Record<string, string>;
8
+ transport?: Transport;
9
+ beforeSend?: (payload: ErrorPayload) => ErrorPayload | null;
10
+ timeoutMs?: number;
11
+ throttleIntervalMs?: number;
12
+ }
13
+ export interface User {
14
+ id?: string;
15
+ name?: string;
16
+ email?: string;
17
+ phone?: string;
18
+ username?: string;
19
+ avatarUrl?: string;
20
+ [k: string]: unknown;
21
+ }
22
+ export interface DeviceInfo {
23
+ os?: string;
24
+ osVersion?: string;
25
+ model?: string;
26
+ brand?: string;
27
+ screen?: {
28
+ width?: number;
29
+ height?: number;
30
+ scale?: number;
31
+ dpi?: number;
32
+ };
33
+ viewport?: {
34
+ width?: number;
35
+ height?: number;
36
+ };
37
+ language?: string;
38
+ timezone?: string;
39
+ online?: boolean;
40
+ platform?: string;
41
+ userAgent?: string;
42
+ referrer?: string;
43
+ [k: string]: unknown;
44
+ }
45
+ export interface ErrorDetails {
46
+ message: string;
47
+ name?: string;
48
+ type?: string;
49
+ stack?: string;
50
+ }
51
+ export interface CaptureOptions {
52
+ level?: Level;
53
+ tags?: Record<string, string>;
54
+ extra?: Record<string, unknown>;
55
+ }
56
+ export interface ErrorPayload {
57
+ id: string;
58
+ ts: number;
59
+ level: Level;
60
+ environment?: string;
61
+ appKey?: string;
62
+ error: ErrorDetails;
63
+ user?: User;
64
+ device?: DeviceInfo;
65
+ context?: Record<string, unknown>;
66
+ tags?: Record<string, string>;
67
+ extra?: Record<string, unknown>;
68
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { DeviceInfo } from "../types";
2
+ export declare function getReactNativeInfo(): DeviceInfo;
@@ -0,0 +1,25 @@
1
+ import { Dimensions, Platform, NativeModules } from "react-native";
2
+ function getLocale() {
3
+ const I18nManager = NativeModules.I18nManager;
4
+ const SettingsManager = NativeModules.SettingsManager;
5
+ const localeIOS = SettingsManager && SettingsManager.settings ? (SettingsManager.settings.AppleLocale || (SettingsManager.settings.AppleLanguages && SettingsManager.settings.AppleLanguages[0])) : undefined;
6
+ const localeAndroid = I18nManager && I18nManager.localeIdentifier ? I18nManager.localeIdentifier : undefined;
7
+ return localeIOS || localeAndroid;
8
+ }
9
+ export function getReactNativeInfo() {
10
+ const win = Dimensions.get("window");
11
+ const scale = win.scale || 1;
12
+ const os = Platform.OS;
13
+ const version = typeof Platform.Version === "string" ? Platform.Version : String(Platform.Version);
14
+ return {
15
+ os,
16
+ osVersion: version,
17
+ language: getLocale(),
18
+ screen: {
19
+ width: win.width,
20
+ height: win.height,
21
+ scale
22
+ },
23
+ platform: "ReactNative"
24
+ };
25
+ }
@@ -0,0 +1,8 @@
1
+ export interface RouteInfo {
2
+ url?: string;
3
+ origin?: string;
4
+ pathname?: string;
5
+ search?: string;
6
+ hash?: string;
7
+ }
8
+ export declare function getRouteInfo(): RouteInfo;
@@ -0,0 +1,12 @@
1
+ export function getRouteInfo() {
2
+ const loc = typeof location !== "undefined" ? location : undefined;
3
+ if (!loc)
4
+ return {};
5
+ return {
6
+ url: loc.href,
7
+ origin: loc.origin,
8
+ pathname: loc.pathname,
9
+ search: loc.search,
10
+ hash: loc.hash
11
+ };
12
+ }
@@ -0,0 +1,2 @@
1
+ import { DeviceInfo } from "../types";
2
+ export declare function getWebInfo(): DeviceInfo;
@@ -0,0 +1,29 @@
1
+ export function getWebInfo() {
2
+ const nav = typeof navigator !== "undefined" ? navigator : undefined;
3
+ const win = typeof window !== "undefined" ? window : undefined;
4
+ const doc = typeof document !== "undefined" ? document : undefined;
5
+ const screen = win === null || win === void 0 ? void 0 : win.screen;
6
+ const viewportWidth = win ? win.innerWidth : undefined;
7
+ const viewportHeight = win ? win.innerHeight : undefined;
8
+ const scale = win ? win.devicePixelRatio || 1 : undefined;
9
+ const tz = typeof Intl !== "undefined"
10
+ ? Intl.DateTimeFormat().resolvedOptions().timeZone
11
+ : undefined;
12
+ return {
13
+ userAgent: nav === null || nav === void 0 ? void 0 : nav.userAgent,
14
+ language: nav === null || nav === void 0 ? void 0 : nav.language,
15
+ platform: nav === null || nav === void 0 ? void 0 : nav.platform,
16
+ timezone: tz,
17
+ online: nav ? nav.onLine : undefined,
18
+ referrer: doc === null || doc === void 0 ? void 0 : doc.referrer,
19
+ screen: {
20
+ width: screen === null || screen === void 0 ? void 0 : screen.width,
21
+ height: screen === null || screen === void 0 ? void 0 : screen.height,
22
+ scale,
23
+ },
24
+ viewport: {
25
+ width: viewportWidth,
26
+ height: viewportHeight,
27
+ },
28
+ };
29
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@zyui/error-monitor-sdk",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./react-native": {
14
+ "types": "./dist/utils/reactNative.d.ts",
15
+ "default": "./dist/utils/reactNative.js"
16
+ },
17
+ "./web": {
18
+ "types": "./dist/utils/web.d.ts",
19
+ "default": "./dist/utils/web.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsc -p tsconfig.json",
27
+ "typecheck": "tsc -p tsconfig.json --noEmit",
28
+ "example": "npm run build && node examples/dev-server.js",
29
+ "changeset": "changeset",
30
+ "version-packages": "changeset version",
31
+ "release": "npm run build && changeset publish"
32
+ },
33
+ "engines": {
34
+ "node": ">=16"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "devDependencies": {
40
+ "typescript": "^5.6.3",
41
+ "@changesets/cli": "^2.27.7"
42
+ }
43
+ }