@goliapkg/sentori-react-native 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.
- package/README.md +5 -0
- package/SentoriReactNative.podspec +21 -0
- package/android/build.gradle +38 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/sentori/SentoriCrashHandler.kt +213 -0
- package/android/src/main/java/com/sentori/SentoriModule.kt +39 -0
- package/android/src/test/java/com/sentori/SentoriCrashHandlerTest.kt +60 -0
- package/expo-module.config.json +9 -0
- package/ios/SentoriCrashHandler.swift +160 -0
- package/ios/SentoriModule.swift +43 -0
- package/ios/Tests/SentoriCrashHandlerTests.swift +59 -0
- package/lib/breadcrumbs.d.ts +11 -0
- package/lib/breadcrumbs.d.ts.map +1 -0
- package/lib/breadcrumbs.js +21 -0
- package/lib/breadcrumbs.js.map +1 -0
- package/lib/capture.d.ts +23 -0
- package/lib/capture.d.ts.map +1 -0
- package/lib/capture.js +91 -0
- package/lib/capture.js.map +1 -0
- package/lib/config.d.ts +12 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +10 -0
- package/lib/config.js.map +1 -0
- package/lib/error-boundary.d.ts +17 -0
- package/lib/error-boundary.d.ts.map +1 -0
- package/lib/error-boundary.js +26 -0
- package/lib/error-boundary.js.map +1 -0
- package/lib/handlers/global.d.ts +2 -0
- package/lib/handlers/global.d.ts.map +1 -0
- package/lib/handlers/global.js +29 -0
- package/lib/handlers/global.js.map +1 -0
- package/lib/handlers/network.d.ts +2 -0
- package/lib/handlers/network.d.ts.map +1 -0
- package/lib/handlers/network.js +69 -0
- package/lib/handlers/network.js.map +1 -0
- package/lib/handlers/promise.d.ts +2 -0
- package/lib/handlers/promise.d.ts.map +1 -0
- package/lib/handlers/promise.js +27 -0
- package/lib/handlers/promise.js.map +1 -0
- package/lib/index.d.ts +18 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +20 -0
- package/lib/index.js.map +1 -0
- package/lib/init.d.ts +18 -0
- package/lib/init.d.ts.map +1 -0
- package/lib/init.js +56 -0
- package/lib/init.js.map +1 -0
- package/lib/native.d.ts +23 -0
- package/lib/native.d.ts.map +1 -0
- package/lib/native.js +56 -0
- package/lib/native.js.map +1 -0
- package/lib/stack.d.ts +3 -0
- package/lib/stack.d.ts.map +1 -0
- package/lib/stack.js +69 -0
- package/lib/stack.js.map +1 -0
- package/lib/transport.d.ts +8 -0
- package/lib/transport.d.ts.map +1 -0
- package/lib/transport.js +143 -0
- package/lib/transport.js.map +1 -0
- package/lib/types.d.ts +62 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/lib/uuid.d.ts +11 -0
- package/lib/uuid.d.ts.map +1 -0
- package/lib/uuid.js +46 -0
- package/lib/uuid.js.map +1 -0
- package/package.json +66 -0
- package/src/__tests__/breadcrumbs.test.ts +44 -0
- package/src/__tests__/stack.test.ts +43 -0
- package/src/__tests__/transport.test.ts +112 -0
- package/src/__tests__/uuid.test.ts +41 -0
- package/src/breadcrumbs.ts +33 -0
- package/src/capture.ts +108 -0
- package/src/config.ts +21 -0
- package/src/error-boundary.tsx +38 -0
- package/src/handlers/global.ts +36 -0
- package/src/handlers/network.ts +70 -0
- package/src/handlers/promise.ts +38 -0
- package/src/index.ts +37 -0
- package/src/init.ts +80 -0
- package/src/native.ts +71 -0
- package/src/stack.ts +72 -0
- package/src/transport.ts +164 -0
- package/src/types.ts +63 -0
- package/src/uuid.ts +56 -0
package/lib/stack.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// V8 / Hermes (RN 0.71+):
|
|
2
|
+
// " at functionName (file:line:col)"
|
|
3
|
+
// " at file:line:col"
|
|
4
|
+
const V8_FRAME = /^\s*at\s+(?:(.+?)\s+\()?(.+?)(?::(\d+))?(?::(\d+))?\)?\s*$/;
|
|
5
|
+
// SpiderMonkey / older Hermes:
|
|
6
|
+
// "functionName@file:line:col"
|
|
7
|
+
const AT_FRAME = /^(.+?)@(.+?)(?::(\d+))?(?::(\d+))?$/;
|
|
8
|
+
export const parseStack = (stack) => {
|
|
9
|
+
if (!stack || typeof stack !== 'string')
|
|
10
|
+
return [];
|
|
11
|
+
const lines = stack.split('\n');
|
|
12
|
+
const frames = [];
|
|
13
|
+
for (const raw of lines) {
|
|
14
|
+
const line = raw.trim();
|
|
15
|
+
if (!line)
|
|
16
|
+
continue;
|
|
17
|
+
// Skip the "ErrorType: message" header line.
|
|
18
|
+
if (!line.startsWith('at ') && !line.includes('@'))
|
|
19
|
+
continue;
|
|
20
|
+
const frame = parseV8(line) ?? parseAt(line);
|
|
21
|
+
if (frame)
|
|
22
|
+
frames.push(frame);
|
|
23
|
+
}
|
|
24
|
+
return frames;
|
|
25
|
+
};
|
|
26
|
+
const parseV8 = (line) => {
|
|
27
|
+
if (!line.startsWith('at '))
|
|
28
|
+
return null;
|
|
29
|
+
const m = V8_FRAME.exec(line);
|
|
30
|
+
if (!m)
|
|
31
|
+
return null;
|
|
32
|
+
const fn = m[1] ? m[1].trim() : undefined;
|
|
33
|
+
const file = m[2] ? m[2].trim() : '<anonymous>';
|
|
34
|
+
const lineNo = m[3] ? parseInt(m[3], 10) : 0;
|
|
35
|
+
const col = m[4] ? parseInt(m[4], 10) : undefined;
|
|
36
|
+
return {
|
|
37
|
+
function: fn,
|
|
38
|
+
file,
|
|
39
|
+
line: lineNo,
|
|
40
|
+
column: col,
|
|
41
|
+
inApp: isInApp(file),
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
const parseAt = (line) => {
|
|
45
|
+
const m = AT_FRAME.exec(line);
|
|
46
|
+
if (!m)
|
|
47
|
+
return null;
|
|
48
|
+
const fn = m[1] ? m[1].trim() : undefined;
|
|
49
|
+
const file = m[2] ? m[2].trim() : '<anonymous>';
|
|
50
|
+
const lineNo = m[3] ? parseInt(m[3], 10) : 0;
|
|
51
|
+
const col = m[4] ? parseInt(m[4], 10) : undefined;
|
|
52
|
+
return {
|
|
53
|
+
function: fn,
|
|
54
|
+
file,
|
|
55
|
+
line: lineNo,
|
|
56
|
+
column: col,
|
|
57
|
+
inApp: isInApp(file),
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
const isInApp = (file) => {
|
|
61
|
+
if (!file || file === '<anonymous>')
|
|
62
|
+
return false;
|
|
63
|
+
if (file.includes('node_modules/'))
|
|
64
|
+
return false;
|
|
65
|
+
if (/^https?:\/\//.test(file))
|
|
66
|
+
return false;
|
|
67
|
+
return true;
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=stack.js.map
|
package/lib/stack.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.js","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Event } from './types';
|
|
2
|
+
export declare const enqueue: (event: Event) => void;
|
|
3
|
+
export declare const startTransport: () => void;
|
|
4
|
+
export declare const flush: () => Promise<void>;
|
|
5
|
+
export declare const drainOfflineQueue: () => Promise<void>;
|
|
6
|
+
export declare const __resetForTests: () => void;
|
|
7
|
+
export declare const __peekQueue: () => readonly Event[];
|
|
8
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAcrC,eAAO,MAAM,OAAO,GAAI,OAAO,KAAK,KAAG,IAUtC,CAAC;AAEF,eAAO,MAAM,cAAc,QAAO,IAEjC,CAAC;AAEF,eAAO,MAAM,KAAK,QAAa,OAAO,CAAC,IAAI,CAkB1C,CAAC;AA4FF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CAatD,CAAC;AAEF,eAAO,MAAM,eAAe,QAAO,IAKlC,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,SAAS,KAAK,EAAY,CAAC"}
|
package/lib/transport.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { getConfig } from './config';
|
|
2
|
+
const FLUSH_INTERVAL_MS = 5_000;
|
|
3
|
+
const BATCH_SIZE = 10;
|
|
4
|
+
const MAX_RETRY = 3;
|
|
5
|
+
const STORAGE_KEY = '@sentori/pending';
|
|
6
|
+
const MAX_PERSISTED = 1000;
|
|
7
|
+
let _queue = [];
|
|
8
|
+
let _flushTimer = null;
|
|
9
|
+
let _started = false;
|
|
10
|
+
const SDK_VERSION = '0.0.0';
|
|
11
|
+
export const enqueue = (event) => {
|
|
12
|
+
_queue.push(event);
|
|
13
|
+
if (_queue.length >= BATCH_SIZE) {
|
|
14
|
+
void flush();
|
|
15
|
+
}
|
|
16
|
+
else if (!_flushTimer) {
|
|
17
|
+
_flushTimer = setTimeout(() => {
|
|
18
|
+
_flushTimer = null;
|
|
19
|
+
void flush();
|
|
20
|
+
}, FLUSH_INTERVAL_MS);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
export const startTransport = () => {
|
|
24
|
+
_started = true;
|
|
25
|
+
};
|
|
26
|
+
export const flush = async () => {
|
|
27
|
+
if (!_started)
|
|
28
|
+
return;
|
|
29
|
+
if (_queue.length === 0)
|
|
30
|
+
return;
|
|
31
|
+
const config = getConfig();
|
|
32
|
+
if (!config)
|
|
33
|
+
return;
|
|
34
|
+
const batch = _queue.splice(0, _queue.length);
|
|
35
|
+
if (_flushTimer) {
|
|
36
|
+
clearTimeout(_flushTimer);
|
|
37
|
+
_flushTimer = null;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
await sendWithRetry(batch, config.ingestUrl, config.token);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
await persist(batch);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const sendWithRetry = async (events, ingestUrl, token) => {
|
|
47
|
+
let attempt = 0;
|
|
48
|
+
let delayMs = 1000;
|
|
49
|
+
while (true) {
|
|
50
|
+
try {
|
|
51
|
+
await sendOnce(events, ingestUrl, token);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
attempt++;
|
|
56
|
+
if (attempt >= MAX_RETRY)
|
|
57
|
+
throw e;
|
|
58
|
+
await sleep(delayMs);
|
|
59
|
+
delayMs *= 2;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const sendOnce = async (events, ingestUrl, token) => {
|
|
64
|
+
const url = events.length === 1 ? `${ingestUrl}/v1/events` : `${ingestUrl}/v1/events:batch`;
|
|
65
|
+
const body = events.length === 1 ? events[0] : { events };
|
|
66
|
+
const resp = await fetch(url, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers: {
|
|
69
|
+
'Content-Type': 'application/json',
|
|
70
|
+
Authorization: `Bearer ${token}`,
|
|
71
|
+
'Sentori-Sdk': `react-native/${SDK_VERSION}`,
|
|
72
|
+
},
|
|
73
|
+
body: JSON.stringify(body),
|
|
74
|
+
});
|
|
75
|
+
if (resp.status === 429) {
|
|
76
|
+
let retryAfterMs = 5000;
|
|
77
|
+
try {
|
|
78
|
+
const j = (await resp.json());
|
|
79
|
+
if (typeof j.retryAfterMs === 'number')
|
|
80
|
+
retryAfterMs = j.retryAfterMs;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// ignore body parse error
|
|
84
|
+
}
|
|
85
|
+
await sleep(retryAfterMs);
|
|
86
|
+
throw new Error('rate-limited');
|
|
87
|
+
}
|
|
88
|
+
if (resp.status >= 500) {
|
|
89
|
+
throw new Error(`server-${resp.status}`);
|
|
90
|
+
}
|
|
91
|
+
// 4xx other than 429 = client error, drop silently
|
|
92
|
+
};
|
|
93
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
94
|
+
const getAsyncStorage = async () => {
|
|
95
|
+
try {
|
|
96
|
+
const mod = (await import('@react-native-async-storage/async-storage'));
|
|
97
|
+
return mod.default;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const persist = async (events) => {
|
|
104
|
+
const AsyncStorage = await getAsyncStorage();
|
|
105
|
+
if (!AsyncStorage)
|
|
106
|
+
return;
|
|
107
|
+
try {
|
|
108
|
+
const existing = await AsyncStorage.getItem(STORAGE_KEY);
|
|
109
|
+
const prev = existing ? JSON.parse(existing) : [];
|
|
110
|
+
const merged = [...prev, ...events].slice(-MAX_PERSISTED);
|
|
111
|
+
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(merged));
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// best-effort
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
export const drainOfflineQueue = async () => {
|
|
118
|
+
const AsyncStorage = await getAsyncStorage();
|
|
119
|
+
if (!AsyncStorage)
|
|
120
|
+
return;
|
|
121
|
+
try {
|
|
122
|
+
const raw = await AsyncStorage.getItem(STORAGE_KEY);
|
|
123
|
+
if (!raw)
|
|
124
|
+
return;
|
|
125
|
+
await AsyncStorage.removeItem(STORAGE_KEY);
|
|
126
|
+
const events = JSON.parse(raw);
|
|
127
|
+
for (const e of events)
|
|
128
|
+
_queue.push(e);
|
|
129
|
+
await flush();
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// best-effort
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
export const __resetForTests = () => {
|
|
136
|
+
_queue = [];
|
|
137
|
+
if (_flushTimer)
|
|
138
|
+
clearTimeout(_flushTimer);
|
|
139
|
+
_flushTimer = null;
|
|
140
|
+
_started = false;
|
|
141
|
+
};
|
|
142
|
+
export const __peekQueue = () => _queue;
|
|
143
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type Platform = 'javascript' | 'ios' | 'android';
|
|
2
|
+
export type DeviceOS = 'ios' | 'android' | 'web' | 'other';
|
|
3
|
+
export type EventKind = 'error';
|
|
4
|
+
export type BreadcrumbType = 'nav' | 'net' | 'log' | 'user' | 'custom';
|
|
5
|
+
export type Event = {
|
|
6
|
+
id: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
kind: EventKind;
|
|
9
|
+
platform: Platform;
|
|
10
|
+
release: string;
|
|
11
|
+
environment: string;
|
|
12
|
+
device: Device;
|
|
13
|
+
app: App;
|
|
14
|
+
user?: User | null;
|
|
15
|
+
tags?: Tags;
|
|
16
|
+
breadcrumbs?: Breadcrumb[];
|
|
17
|
+
error: SentoriError;
|
|
18
|
+
fingerprint?: string[];
|
|
19
|
+
traceId?: string | null;
|
|
20
|
+
spanId?: string | null;
|
|
21
|
+
};
|
|
22
|
+
export type Device = {
|
|
23
|
+
os: DeviceOS;
|
|
24
|
+
osVersion: string;
|
|
25
|
+
model?: string;
|
|
26
|
+
locale?: string;
|
|
27
|
+
};
|
|
28
|
+
export type App = {
|
|
29
|
+
version: string;
|
|
30
|
+
build?: string;
|
|
31
|
+
framework?: {
|
|
32
|
+
name: string;
|
|
33
|
+
version: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export type User = {
|
|
37
|
+
id?: string;
|
|
38
|
+
anonymous?: boolean;
|
|
39
|
+
};
|
|
40
|
+
export type Tags = Record<string, string>;
|
|
41
|
+
export type SentoriError = {
|
|
42
|
+
type: string;
|
|
43
|
+
message: string;
|
|
44
|
+
stack: Frame[];
|
|
45
|
+
cause?: SentoriError | null;
|
|
46
|
+
};
|
|
47
|
+
export type Frame = {
|
|
48
|
+
function?: string;
|
|
49
|
+
file: string;
|
|
50
|
+
line: number;
|
|
51
|
+
column?: number;
|
|
52
|
+
inApp: boolean;
|
|
53
|
+
absolutePath?: string;
|
|
54
|
+
preContext?: string[];
|
|
55
|
+
postContext?: string[];
|
|
56
|
+
};
|
|
57
|
+
export type Breadcrumb = {
|
|
58
|
+
timestamp: string;
|
|
59
|
+
type: BreadcrumbType;
|
|
60
|
+
data: Record<string, unknown>;
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,KAAK,GAAG,SAAS,CAAC;AACxD,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,GAAG,OAAO,CAAC;AAC3D,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC;AAChC,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvE,MAAM,MAAM,KAAK,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,KAAK,EAAE,YAAY,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,EAAE,EAAE,QAAQ,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,GAAG,GAAG;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAExD,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE1C,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,CAAC"}
|
package/lib/types.js
ADDED
package/lib/types.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/lib/uuid.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 9562 UUID v7 generator.
|
|
3
|
+
* Layout:
|
|
4
|
+
* bytes 0-5 (48 bits) — Unix epoch milliseconds, big-endian
|
|
5
|
+
* byte 6 (high nibble) — version 7 (0x70)
|
|
6
|
+
* byte 6 (low nibble) + byte 7 — 12 random bits
|
|
7
|
+
* byte 8 (high 2 bits) — variant 10
|
|
8
|
+
* byte 8 (low 6 bits) + bytes 9-15 — 62 random bits
|
|
9
|
+
*/
|
|
10
|
+
export declare const uuidV7: () => string;
|
|
11
|
+
//# sourceMappingURL=uuid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,QAAO,MA4BzB,CAAC"}
|
package/lib/uuid.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 9562 UUID v7 generator.
|
|
3
|
+
* Layout:
|
|
4
|
+
* bytes 0-5 (48 bits) — Unix epoch milliseconds, big-endian
|
|
5
|
+
* byte 6 (high nibble) — version 7 (0x70)
|
|
6
|
+
* byte 6 (low nibble) + byte 7 — 12 random bits
|
|
7
|
+
* byte 8 (high 2 bits) — variant 10
|
|
8
|
+
* byte 8 (low 6 bits) + bytes 9-15 — 62 random bits
|
|
9
|
+
*/
|
|
10
|
+
export const uuidV7 = () => {
|
|
11
|
+
const ts = Date.now();
|
|
12
|
+
const buf = new Uint8Array(16);
|
|
13
|
+
buf[0] = Math.floor(ts / 0x10000000000) & 0xff;
|
|
14
|
+
buf[1] = Math.floor(ts / 0x100000000) & 0xff;
|
|
15
|
+
buf[2] = Math.floor(ts / 0x1000000) & 0xff;
|
|
16
|
+
buf[3] = Math.floor(ts / 0x10000) & 0xff;
|
|
17
|
+
buf[4] = Math.floor(ts / 0x100) & 0xff;
|
|
18
|
+
buf[5] = ts & 0xff;
|
|
19
|
+
fillRandom(buf.subarray(6));
|
|
20
|
+
buf[6] = (buf[6] & 0x0f) | 0x70; // version 7
|
|
21
|
+
buf[8] = (buf[8] & 0x3f) | 0x80; // variant 10xx
|
|
22
|
+
const hex = Array.from(buf, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
23
|
+
return (hex.slice(0, 8) +
|
|
24
|
+
'-' +
|
|
25
|
+
hex.slice(8, 12) +
|
|
26
|
+
'-' +
|
|
27
|
+
hex.slice(12, 16) +
|
|
28
|
+
'-' +
|
|
29
|
+
hex.slice(16, 20) +
|
|
30
|
+
'-' +
|
|
31
|
+
hex.slice(20, 32));
|
|
32
|
+
};
|
|
33
|
+
const fillRandom = (buf) => {
|
|
34
|
+
// Prefer crypto.getRandomValues (Hermes 0.74+, browsers, Node, Bun).
|
|
35
|
+
// RN apps targeting older Hermes should add `react-native-get-random-values`
|
|
36
|
+
// before importing the SDK.
|
|
37
|
+
const cryptoObj = globalThis.crypto;
|
|
38
|
+
if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
|
|
39
|
+
cryptoObj.getRandomValues(buf);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
for (let i = 0; i < buf.length; i++) {
|
|
43
|
+
buf[i] = Math.floor(Math.random() * 256);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=uuid.js.map
|
package/lib/uuid.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,GAAW,EAAE;IACjC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAE/B,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,aAAa,CAAC,GAAG,IAAI,CAAC;IAC/C,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,WAAW,CAAC,GAAG,IAAI,CAAC;IAC7C,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;IAC3C,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC;IACzC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC;IACvC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IAEnB,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAE5B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,YAAY;IAC9C,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,eAAe;IAEjD,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7E,OAAO,CACL,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QACf,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAChB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QACjB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QACjB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAClB,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,GAAe,EAAQ,EAAE;IAC3C,qEAAqE;IACrE,6EAA6E;IAC7E,4BAA4B;IAC5B,MAAM,SAAS,GACb,UAGD,CAAC,MAAM,CAAC;IACT,IAAI,SAAS,IAAI,OAAO,SAAS,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;QACjE,SAAS,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO;IACT,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@goliapkg/sentori-react-native",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Sentori SDK for React Native — JS-layer error capture, native crash handlers (iOS / Android), batched transport.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://sentori.golia.jp",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/goliajp/sentori.git",
|
|
10
|
+
"directory": "sdk/react-native"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/goliajp/sentori/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"react-native",
|
|
17
|
+
"expo",
|
|
18
|
+
"error-tracking",
|
|
19
|
+
"crash-reporting",
|
|
20
|
+
"monitoring",
|
|
21
|
+
"sentori"
|
|
22
|
+
],
|
|
23
|
+
"main": "lib/index.js",
|
|
24
|
+
"types": "lib/index.d.ts",
|
|
25
|
+
"files": [
|
|
26
|
+
"lib/",
|
|
27
|
+
"src/",
|
|
28
|
+
"android/src/",
|
|
29
|
+
"android/build.gradle",
|
|
30
|
+
"ios/",
|
|
31
|
+
"expo-module.config.json",
|
|
32
|
+
"SentoriReactNative.podspec",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -p tsconfig.json",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"test": "bun test",
|
|
39
|
+
"prepack": "bun run build"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"expo-modules-core": ">=2.0",
|
|
43
|
+
"react": ">=18",
|
|
44
|
+
"react-native": ">=0.74"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"@react-native-async-storage/async-storage": {
|
|
48
|
+
"optional": true
|
|
49
|
+
},
|
|
50
|
+
"expo-modules-core": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"optionalDependencies": {
|
|
55
|
+
"@react-native-async-storage/async-storage": ">=1.23",
|
|
56
|
+
"expo-modules-core": ">=2.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/bun": "latest",
|
|
60
|
+
"@types/react": "^19",
|
|
61
|
+
"typescript": "^5"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
addBreadcrumb,
|
|
5
|
+
getBreadcrumbs,
|
|
6
|
+
__resetForTests,
|
|
7
|
+
} from '../breadcrumbs';
|
|
8
|
+
|
|
9
|
+
describe('breadcrumbs', () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
__resetForTests();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('adds and retrieves a breadcrumb', () => {
|
|
15
|
+
addBreadcrumb({ type: 'log', data: { level: 'info', message: 'hi' } });
|
|
16
|
+
const crumbs = getBreadcrumbs();
|
|
17
|
+
expect(crumbs).toHaveLength(1);
|
|
18
|
+
expect(crumbs[0]?.type).toBe('log');
|
|
19
|
+
expect(crumbs[0]?.data.message).toBe('hi');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('auto-generates ISO 8601 timestamp when not provided', () => {
|
|
23
|
+
addBreadcrumb({ type: 'nav', data: { from: 'a', to: 'b' } });
|
|
24
|
+
const crumbs = getBreadcrumbs();
|
|
25
|
+
expect(crumbs[0]?.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('caps at 100 entries (oldest discarded)', () => {
|
|
29
|
+
for (let i = 0; i < 150; i++) {
|
|
30
|
+
addBreadcrumb({ type: 'custom', data: { i } });
|
|
31
|
+
}
|
|
32
|
+
const crumbs = getBreadcrumbs();
|
|
33
|
+
expect(crumbs).toHaveLength(100);
|
|
34
|
+
expect(crumbs[0]?.data.i).toBe(50);
|
|
35
|
+
expect(crumbs[99]?.data.i).toBe(149);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('returns a fresh copy on each getBreadcrumbs call', () => {
|
|
39
|
+
addBreadcrumb({ type: 'log', data: {} });
|
|
40
|
+
const a = getBreadcrumbs();
|
|
41
|
+
const b = getBreadcrumbs();
|
|
42
|
+
expect(a).not.toBe(b);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { parseStack } from '../stack';
|
|
4
|
+
|
|
5
|
+
describe('parseStack', () => {
|
|
6
|
+
it('returns [] for undefined', () => {
|
|
7
|
+
expect(parseStack(undefined)).toEqual([]);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('returns [] for empty string', () => {
|
|
11
|
+
expect(parseStack('')).toEqual([]);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('parses V8/Hermes-style frames', () => {
|
|
15
|
+
const stack = `TypeError: Cannot read property 'foo' of undefined
|
|
16
|
+
at handleSubmit (src/screens/Checkout.tsx:42:10)
|
|
17
|
+
at onPress (src/components/Button.tsx:15:5)`;
|
|
18
|
+
const frames = parseStack(stack);
|
|
19
|
+
expect(frames).toHaveLength(2);
|
|
20
|
+
expect(frames[0]).toEqual({
|
|
21
|
+
function: 'handleSubmit',
|
|
22
|
+
file: 'src/screens/Checkout.tsx',
|
|
23
|
+
line: 42,
|
|
24
|
+
column: 10,
|
|
25
|
+
inApp: true,
|
|
26
|
+
});
|
|
27
|
+
expect(frames[1]?.function).toBe('onPress');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('marks node_modules frames as not inApp', () => {
|
|
31
|
+
const stack = `Error
|
|
32
|
+
at vendorFn (node_modules/react-native/Libraries/Foo.js:1:1)`;
|
|
33
|
+
const frames = parseStack(stack);
|
|
34
|
+
expect(frames[0]?.inApp).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('marks http(s) urls as not inApp', () => {
|
|
38
|
+
const stack = `Error
|
|
39
|
+
at someFn (https://cdn.example.com/bundle.js:1:1)`;
|
|
40
|
+
const frames = parseStack(stack);
|
|
41
|
+
expect(frames[0]?.inApp).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describe,
|
|
3
|
+
it,
|
|
4
|
+
expect,
|
|
5
|
+
beforeEach,
|
|
6
|
+
afterEach,
|
|
7
|
+
mock,
|
|
8
|
+
} from 'bun:test';
|
|
9
|
+
|
|
10
|
+
import { setConfig, __resetForTests as resetConfig } from '../config';
|
|
11
|
+
import {
|
|
12
|
+
enqueue,
|
|
13
|
+
flush,
|
|
14
|
+
startTransport,
|
|
15
|
+
__resetForTests as resetTransport,
|
|
16
|
+
__peekQueue,
|
|
17
|
+
} from '../transport';
|
|
18
|
+
import type { Event } from '../types';
|
|
19
|
+
|
|
20
|
+
const makeEvent = (id: string): Event => ({
|
|
21
|
+
id,
|
|
22
|
+
timestamp: '2026-05-09T00:00:00.000Z',
|
|
23
|
+
kind: 'error',
|
|
24
|
+
platform: 'javascript',
|
|
25
|
+
release: 'app@1.0.0+1',
|
|
26
|
+
environment: 'test',
|
|
27
|
+
device: { os: 'ios', osVersion: '17.0' },
|
|
28
|
+
app: { version: '1.0.0' },
|
|
29
|
+
error: {
|
|
30
|
+
type: 'TypeError',
|
|
31
|
+
message: 'x',
|
|
32
|
+
stack: [{ file: 'a.ts', line: 1, inApp: true }],
|
|
33
|
+
cause: null,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const originalFetch = globalThis.fetch;
|
|
38
|
+
|
|
39
|
+
describe('transport', () => {
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
resetConfig();
|
|
42
|
+
resetTransport();
|
|
43
|
+
setConfig({
|
|
44
|
+
token: 'st_pk_test',
|
|
45
|
+
release: 'app@1.0.0+1',
|
|
46
|
+
environment: 'test',
|
|
47
|
+
ingestUrl: 'http://localhost:8080',
|
|
48
|
+
enabled: true,
|
|
49
|
+
});
|
|
50
|
+
startTransport();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
globalThis.fetch = originalFetch;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('enqueues without immediate flush below batch size', () => {
|
|
58
|
+
enqueue(makeEvent('a'));
|
|
59
|
+
expect(__peekQueue()).toHaveLength(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('flush sends to /v1/events for single event', async () => {
|
|
63
|
+
const calls: { url: string; init?: RequestInit }[] = [];
|
|
64
|
+
globalThis.fetch = mock(
|
|
65
|
+
async (url: string | URL | Request, init?: RequestInit) => {
|
|
66
|
+
calls.push({ url: String(url), init });
|
|
67
|
+
return new Response(null, { status: 202 });
|
|
68
|
+
},
|
|
69
|
+
) as typeof fetch;
|
|
70
|
+
|
|
71
|
+
enqueue(makeEvent('a'));
|
|
72
|
+
await flush();
|
|
73
|
+
|
|
74
|
+
expect(calls).toHaveLength(1);
|
|
75
|
+
expect(calls[0]?.url).toBe('http://localhost:8080/v1/events');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('flush sends to /v1/events:batch for multiple events', async () => {
|
|
79
|
+
const calls: { url: string; init?: RequestInit }[] = [];
|
|
80
|
+
globalThis.fetch = mock(
|
|
81
|
+
async (url: string | URL | Request, init?: RequestInit) => {
|
|
82
|
+
calls.push({ url: String(url), init });
|
|
83
|
+
return new Response(null, { status: 202 });
|
|
84
|
+
},
|
|
85
|
+
) as typeof fetch;
|
|
86
|
+
|
|
87
|
+
enqueue(makeEvent('a'));
|
|
88
|
+
enqueue(makeEvent('b'));
|
|
89
|
+
await flush();
|
|
90
|
+
|
|
91
|
+
expect(calls).toHaveLength(1);
|
|
92
|
+
expect(calls[0]?.url).toBe('http://localhost:8080/v1/events:batch');
|
|
93
|
+
const body = JSON.parse((calls[0]?.init?.body as string) ?? '{}');
|
|
94
|
+
expect(body.events).toHaveLength(2);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('attaches Authorization and Sentori-Sdk headers', async () => {
|
|
98
|
+
let capturedHeaders: Record<string, string> | undefined;
|
|
99
|
+
globalThis.fetch = mock(
|
|
100
|
+
async (_url: string | URL | Request, init?: RequestInit) => {
|
|
101
|
+
capturedHeaders = init?.headers as Record<string, string>;
|
|
102
|
+
return new Response(null, { status: 202 });
|
|
103
|
+
},
|
|
104
|
+
) as typeof fetch;
|
|
105
|
+
|
|
106
|
+
enqueue(makeEvent('a'));
|
|
107
|
+
await flush();
|
|
108
|
+
|
|
109
|
+
expect(capturedHeaders?.Authorization).toBe('Bearer st_pk_test');
|
|
110
|
+
expect(capturedHeaders?.['Sentori-Sdk']).toMatch(/^react-native\//);
|
|
111
|
+
});
|
|
112
|
+
});
|