@goliapkg/sentori-react-native 0.1.2 → 0.2.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/SentoriReactNative.podspec +3 -0
- package/lib/breadcrumbs.d.ts +1 -1
- package/lib/breadcrumbs.d.ts.map +1 -1
- package/lib/breadcrumbs.js +20 -15
- package/lib/breadcrumbs.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/stack.d.ts.map +1 -1
- package/lib/stack.js +6 -68
- package/lib/stack.js.map +1 -1
- package/lib/types.d.ts +1 -61
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -1
- package/lib/uuid.d.ts +1 -10
- package/lib/uuid.d.ts.map +1 -1
- package/lib/uuid.js +2 -45
- package/lib/uuid.js.map +1 -1
- package/package.json +5 -2
- package/src/__tests__/stack.test.ts +6 -4
- package/src/breadcrumbs.ts +32 -23
- package/src/stack.ts +8 -70
- package/src/types.ts +18 -63
- package/src/uuid.ts +2 -56
|
@@ -18,4 +18,7 @@ Pod::Spec.new do |s|
|
|
|
18
18
|
s.dependency 'ExpoModulesCore'
|
|
19
19
|
|
|
20
20
|
s.source_files = 'ios/**/*.{h,m,mm,swift,hpp,cpp}'
|
|
21
|
+
# XCTest only links into test targets; including ios/Tests/** in the
|
|
22
|
+
# main pod target makes app builds fail with `no such module 'XCTest'`.
|
|
23
|
+
s.exclude_files = 'ios/Tests/**'
|
|
21
24
|
end
|
package/lib/breadcrumbs.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Breadcrumb, BreadcrumbType } from './types';
|
|
2
2
|
export type AddBreadcrumbInput = {
|
|
3
|
-
type: BreadcrumbType;
|
|
4
3
|
data: Record<string, unknown>;
|
|
5
4
|
timestamp?: string;
|
|
5
|
+
type: BreadcrumbType;
|
|
6
6
|
};
|
|
7
7
|
export declare const addBreadcrumb: (input: AddBreadcrumbInput) => void;
|
|
8
8
|
export declare const getBreadcrumbs: () => Breadcrumb[];
|
package/lib/breadcrumbs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumbs.d.ts","sourceRoot":"","sources":["../src/breadcrumbs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"breadcrumbs.d.ts","sourceRoot":"","sources":["../src/breadcrumbs.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEzD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,cAAc,CAAA;CACrB,CAAA;AAID,eAAO,MAAM,aAAa,GAAI,OAAO,kBAAkB,KAAG,IAWzD,CAAA;AAED,eAAO,MAAM,cAAc,QAAO,UAAU,EAAe,CAAA;AAE3D,eAAO,MAAM,gBAAgB,QAAO,IAGnC,CAAA;AAED,eAAO,MAAM,eAAe,QAAO,IAA0B,CAAA"}
|
package/lib/breadcrumbs.js
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Phase 21: ring buffer logic lives in @goliapkg/sentori-core. The
|
|
2
|
+
// public surface here keeps its object-form `addBreadcrumb({ type,
|
|
3
|
+
// data, timestamp? })` so existing callers don't break, and we
|
|
4
|
+
// expose `__resetForTests` for the test suite.
|
|
5
|
+
import { BreadcrumbBuffer, clearBreadcrumbs as clearCore, getBreadcrumbs as getCore, addBreadcrumb as pushCore, } from '@goliapkg/sentori-core';
|
|
6
|
+
const _shadow = new BreadcrumbBuffer();
|
|
3
7
|
export const addBreadcrumb = (input) => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
if (input.timestamp) {
|
|
9
|
+
// Caller wants a specific timestamp — pushCore stamps `now()`, so
|
|
10
|
+
// we go through a private buffer to preserve that field. This path
|
|
11
|
+
// is rarely used (most callers omit timestamp).
|
|
12
|
+
_shadow.push(input.type, input.data);
|
|
13
|
+
const last = _shadow.snapshot().at(-1);
|
|
14
|
+
if (last)
|
|
15
|
+
last.timestamp = input.timestamp;
|
|
16
|
+
return;
|
|
12
17
|
}
|
|
18
|
+
pushCore(input.type, input.data);
|
|
13
19
|
};
|
|
14
|
-
export const getBreadcrumbs = () =>
|
|
20
|
+
export const getBreadcrumbs = () => getCore();
|
|
15
21
|
export const clearBreadcrumbs = () => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export const __resetForTests = () => {
|
|
19
|
-
_buffer = [];
|
|
22
|
+
clearCore();
|
|
23
|
+
_shadow.clear();
|
|
20
24
|
};
|
|
25
|
+
export const __resetForTests = () => clearBreadcrumbs();
|
|
21
26
|
//# sourceMappingURL=breadcrumbs.js.map
|
package/lib/breadcrumbs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumbs.js","sourceRoot":"","sources":["../src/breadcrumbs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"breadcrumbs.js","sourceRoot":"","sources":["../src/breadcrumbs.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,mEAAmE;AACnE,+DAA+D;AAC/D,+CAA+C;AAC/C,OAAO,EACL,gBAAgB,EAChB,gBAAgB,IAAI,SAAS,EAC7B,cAAc,IAAI,OAAO,EACzB,aAAa,IAAI,QAAQ,GAC1B,MAAM,wBAAwB,CAAA;AAU/B,MAAM,OAAO,GAAG,IAAI,gBAAgB,EAAE,CAAA;AAEtC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAyB,EAAQ,EAAE;IAC/D,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,kEAAkE;QAClE,mEAAmE;QACnE,gDAAgD;QAChD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACpC,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QACtC,IAAI,IAAI;YAAE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAA;QAC1C,OAAM;IACR,CAAC;IACD,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;AAClC,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,GAAiB,EAAE,CAAC,OAAO,EAAE,CAAA;AAE3D,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAS,EAAE;IACzC,SAAS,EAAE,CAAA;IACX,OAAO,CAAC,KAAK,EAAE,CAAA;AACjB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,GAAS,EAAE,CAAC,gBAAgB,EAAE,CAAA"}
|
package/lib/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { ErrorBoundary } from './error-boundary';
|
|
|
2
2
|
export declare const sentori: {
|
|
3
3
|
init: (options: import("./init").InitOptions) => void;
|
|
4
4
|
addBreadcrumb: (input: import("./breadcrumbs").AddBreadcrumbInput) => void;
|
|
5
|
-
setUser: (user: import("
|
|
6
|
-
getUser: () => import("
|
|
5
|
+
setUser: (user: import("@goliapkg/sentori-core").User | null) => void;
|
|
6
|
+
getUser: () => import("@goliapkg/sentori-core").User | null;
|
|
7
7
|
captureError: (error: Error, extras?: import("./capture").CaptureExtras) => void;
|
|
8
8
|
captureException: (error: Error, extras?: import("./capture").CaptureExtras) => void;
|
|
9
9
|
ErrorBoundary: typeof ErrorBoundary;
|
package/lib/stack.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,GAAG,SAAS,KAAG,KAAK,EACrC,CAAA"}
|
package/lib/stack.js
CHANGED
|
@@ -1,69 +1,7 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
};
|
|
1
|
+
// Phase 21: regex + parse loop moved to @goliapkg/sentori-core.
|
|
2
|
+
// RN keeps the long path (no shortFilenames option) because Hermes
|
|
3
|
+
// paths are already short and native symbolication needs the
|
|
4
|
+
// absolute form.
|
|
5
|
+
import { parseStack as parseStackCore } from '@goliapkg/sentori-core';
|
|
6
|
+
export const parseStack = (stack) => parseStackCore(stack);
|
|
69
7
|
//# sourceMappingURL=stack.js.map
|
package/lib/stack.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stack.js","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stack.js","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,mEAAmE;AACnE,6DAA6D;AAC7D,iBAAiB;AACjB,OAAO,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAIrE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAyB,EAAW,EAAE,CAC/D,cAAc,CAAC,KAAK,CAAC,CAAA"}
|
package/lib/types.d.ts
CHANGED
|
@@ -1,62 +1,2 @@
|
|
|
1
|
-
export type Platform
|
|
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
|
-
};
|
|
1
|
+
export type { App, Breadcrumb, BreadcrumbType, CaptureExtras, Device, DeviceOS, Event, EventKind, Frame, Platform, SentoriError, Tags, User, } from '@goliapkg/sentori-core';
|
|
62
2
|
//# sourceMappingURL=types.d.ts.map
|
package/lib/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,GAAG,EACH,UAAU,EACV,cAAc,EACd,aAAa,EACb,MAAM,EACN,QAAQ,EACR,KAAK,EACL,SAAS,EACT,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,IAAI,EACJ,IAAI,GACL,MAAM,wBAAwB,CAAA"}
|
package/lib/types.js
CHANGED
package/lib/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,2DAA2D"}
|
package/lib/uuid.d.ts
CHANGED
|
@@ -1,11 +1,2 @@
|
|
|
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;
|
|
1
|
+
export { uuidV7 } from '@goliapkg/sentori-core';
|
|
11
2
|
//# sourceMappingURL=uuid.d.ts.map
|
package/lib/uuid.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA"}
|
package/lib/uuid.js
CHANGED
|
@@ -1,46 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
};
|
|
1
|
+
// Phase 21: moved to @goliapkg/sentori-core.
|
|
2
|
+
export { uuidV7 } from '@goliapkg/sentori-core';
|
|
46
3
|
//# sourceMappingURL=uuid.js.map
|
package/lib/uuid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goliapkg/sentori-react-native",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Sentori SDK for React Native
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Sentori SDK for React Native \u2014 JS-layer error capture, native crash handlers (iOS / Android), batched transport.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://sentori.golia.jp",
|
|
7
7
|
"repository": {
|
|
@@ -62,5 +62,8 @@
|
|
|
62
62
|
},
|
|
63
63
|
"publishConfig": {
|
|
64
64
|
"access": "public"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@goliapkg/sentori-core": "0.1.0"
|
|
65
68
|
}
|
|
66
69
|
}
|
|
@@ -17,12 +17,14 @@ describe('parseStack', () => {
|
|
|
17
17
|
at onPress (src/components/Button.tsx:15:5)`;
|
|
18
18
|
const frames = parseStack(stack);
|
|
19
19
|
expect(frames).toHaveLength(2);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
line: 42,
|
|
20
|
+
// Phase 21: core's parseStack also populates absolutePath, so we
|
|
21
|
+
// match the subset of fields rather than full equality.
|
|
22
|
+
expect(frames[0]).toMatchObject({
|
|
24
23
|
column: 10,
|
|
24
|
+
file: 'src/screens/Checkout.tsx',
|
|
25
|
+
function: 'handleSubmit',
|
|
25
26
|
inApp: true,
|
|
27
|
+
line: 42,
|
|
26
28
|
});
|
|
27
29
|
expect(frames[1]?.function).toBe('onPress');
|
|
28
30
|
});
|
package/src/breadcrumbs.ts
CHANGED
|
@@ -1,33 +1,42 @@
|
|
|
1
|
-
|
|
1
|
+
// Phase 21: ring buffer logic lives in @goliapkg/sentori-core. The
|
|
2
|
+
// public surface here keeps its object-form `addBreadcrumb({ type,
|
|
3
|
+
// data, timestamp? })` so existing callers don't break, and we
|
|
4
|
+
// expose `__resetForTests` for the test suite.
|
|
5
|
+
import {
|
|
6
|
+
BreadcrumbBuffer,
|
|
7
|
+
clearBreadcrumbs as clearCore,
|
|
8
|
+
getBreadcrumbs as getCore,
|
|
9
|
+
addBreadcrumb as pushCore,
|
|
10
|
+
} from '@goliapkg/sentori-core'
|
|
2
11
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
let _buffer: Breadcrumb[] = [];
|
|
12
|
+
import type { Breadcrumb, BreadcrumbType } from './types'
|
|
6
13
|
|
|
7
14
|
export type AddBreadcrumbInput = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
15
|
+
data: Record<string, unknown>
|
|
16
|
+
timestamp?: string
|
|
17
|
+
type: BreadcrumbType
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const _shadow = new BreadcrumbBuffer()
|
|
12
21
|
|
|
13
22
|
export const addBreadcrumb = (input: AddBreadcrumbInput): void => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
if (input.timestamp) {
|
|
24
|
+
// Caller wants a specific timestamp — pushCore stamps `now()`, so
|
|
25
|
+
// we go through a private buffer to preserve that field. This path
|
|
26
|
+
// is rarely used (most callers omit timestamp).
|
|
27
|
+
_shadow.push(input.type, input.data)
|
|
28
|
+
const last = _shadow.snapshot().at(-1)
|
|
29
|
+
if (last) last.timestamp = input.timestamp
|
|
30
|
+
return
|
|
22
31
|
}
|
|
23
|
-
|
|
32
|
+
pushCore(input.type, input.data)
|
|
33
|
+
}
|
|
24
34
|
|
|
25
|
-
export const getBreadcrumbs = (): Breadcrumb[] =>
|
|
35
|
+
export const getBreadcrumbs = (): Breadcrumb[] => getCore()
|
|
26
36
|
|
|
27
37
|
export const clearBreadcrumbs = (): void => {
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
clearCore()
|
|
39
|
+
_shadow.clear()
|
|
40
|
+
}
|
|
30
41
|
|
|
31
|
-
export const __resetForTests = (): void =>
|
|
32
|
-
_buffer = [];
|
|
33
|
-
};
|
|
42
|
+
export const __resetForTests = (): void => clearBreadcrumbs()
|
package/src/stack.ts
CHANGED
|
@@ -1,72 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
// Phase 21: regex + parse loop moved to @goliapkg/sentori-core.
|
|
2
|
+
// RN keeps the long path (no shortFilenames option) because Hermes
|
|
3
|
+
// paths are already short and native symbolication needs the
|
|
4
|
+
// absolute form.
|
|
5
|
+
import { parseStack as parseStackCore } from '@goliapkg/sentori-core'
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
// " at functionName (file:line:col)"
|
|
5
|
-
// " at file:line:col"
|
|
6
|
-
const V8_FRAME = /^\s*at\s+(?:(.+?)\s+\()?(.+?)(?::(\d+))?(?::(\d+))?\)?\s*$/;
|
|
7
|
+
import type { Frame } from './types'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const AT_FRAME = /^(.+?)@(.+?)(?::(\d+))?(?::(\d+))?$/;
|
|
11
|
-
|
|
12
|
-
export const parseStack = (stack: string | undefined): Frame[] => {
|
|
13
|
-
if (!stack || typeof stack !== 'string') return [];
|
|
14
|
-
const lines = stack.split('\n');
|
|
15
|
-
const frames: Frame[] = [];
|
|
16
|
-
|
|
17
|
-
for (const raw of lines) {
|
|
18
|
-
const line = raw.trim();
|
|
19
|
-
if (!line) continue;
|
|
20
|
-
// Skip the "ErrorType: message" header line.
|
|
21
|
-
if (!line.startsWith('at ') && !line.includes('@')) continue;
|
|
22
|
-
|
|
23
|
-
const frame = parseV8(line) ?? parseAt(line);
|
|
24
|
-
if (frame) frames.push(frame);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return frames;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const parseV8 = (line: string): Frame | null => {
|
|
31
|
-
if (!line.startsWith('at ')) return null;
|
|
32
|
-
const m = V8_FRAME.exec(line);
|
|
33
|
-
if (!m) return null;
|
|
34
|
-
|
|
35
|
-
const fn = m[1] ? m[1].trim() : undefined;
|
|
36
|
-
const file = m[2] ? m[2].trim() : '<anonymous>';
|
|
37
|
-
const lineNo = m[3] ? parseInt(m[3], 10) : 0;
|
|
38
|
-
const col = m[4] ? parseInt(m[4], 10) : undefined;
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
function: fn,
|
|
42
|
-
file,
|
|
43
|
-
line: lineNo,
|
|
44
|
-
column: col,
|
|
45
|
-
inApp: isInApp(file),
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const parseAt = (line: string): Frame | null => {
|
|
50
|
-
const m = AT_FRAME.exec(line);
|
|
51
|
-
if (!m) return null;
|
|
52
|
-
|
|
53
|
-
const fn = m[1] ? m[1].trim() : undefined;
|
|
54
|
-
const file = m[2] ? m[2].trim() : '<anonymous>';
|
|
55
|
-
const lineNo = m[3] ? parseInt(m[3], 10) : 0;
|
|
56
|
-
const col = m[4] ? parseInt(m[4], 10) : undefined;
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
function: fn,
|
|
60
|
-
file,
|
|
61
|
-
line: lineNo,
|
|
62
|
-
column: col,
|
|
63
|
-
inApp: isInApp(file),
|
|
64
|
-
};
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const isInApp = (file: string): boolean => {
|
|
68
|
-
if (!file || file === '<anonymous>') return false;
|
|
69
|
-
if (file.includes('node_modules/')) return false;
|
|
70
|
-
if (/^https?:\/\//.test(file)) return false;
|
|
71
|
-
return true;
|
|
72
|
-
};
|
|
9
|
+
export const parseStack = (stack: string | undefined): Frame[] =>
|
|
10
|
+
parseStackCore(stack)
|
package/src/types.ts
CHANGED
|
@@ -1,63 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export type
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
fingerprint?: string[];
|
|
20
|
-
traceId?: string | null;
|
|
21
|
-
spanId?: string | null;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type Device = {
|
|
25
|
-
os: DeviceOS;
|
|
26
|
-
osVersion: string;
|
|
27
|
-
model?: string;
|
|
28
|
-
locale?: string;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export type App = {
|
|
32
|
-
version: string;
|
|
33
|
-
build?: string;
|
|
34
|
-
framework?: { name: string; version: string };
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export type User = { id?: string; anonymous?: boolean };
|
|
38
|
-
|
|
39
|
-
export type Tags = Record<string, string>;
|
|
40
|
-
|
|
41
|
-
export type SentoriError = {
|
|
42
|
-
type: string;
|
|
43
|
-
message: string;
|
|
44
|
-
stack: Frame[];
|
|
45
|
-
cause?: SentoriError | null;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export type Frame = {
|
|
49
|
-
function?: string;
|
|
50
|
-
file: string;
|
|
51
|
-
line: number;
|
|
52
|
-
column?: number;
|
|
53
|
-
inApp: boolean;
|
|
54
|
-
absolutePath?: string;
|
|
55
|
-
preContext?: string[];
|
|
56
|
-
postContext?: string[];
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export type Breadcrumb = {
|
|
60
|
-
timestamp: string;
|
|
61
|
-
type: BreadcrumbType;
|
|
62
|
-
data: Record<string, unknown>;
|
|
63
|
-
};
|
|
1
|
+
// Phase 21: wire-format types moved to @goliapkg/sentori-core. Re-
|
|
2
|
+
// exported here so existing relative imports keep working.
|
|
3
|
+
|
|
4
|
+
export type {
|
|
5
|
+
App,
|
|
6
|
+
Breadcrumb,
|
|
7
|
+
BreadcrumbType,
|
|
8
|
+
CaptureExtras,
|
|
9
|
+
Device,
|
|
10
|
+
DeviceOS,
|
|
11
|
+
Event,
|
|
12
|
+
EventKind,
|
|
13
|
+
Frame,
|
|
14
|
+
Platform,
|
|
15
|
+
SentoriError,
|
|
16
|
+
Tags,
|
|
17
|
+
User,
|
|
18
|
+
} from '@goliapkg/sentori-core'
|
package/src/uuid.ts
CHANGED
|
@@ -1,56 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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 = (): string => {
|
|
11
|
-
const ts = Date.now();
|
|
12
|
-
const buf = new Uint8Array(16);
|
|
13
|
-
|
|
14
|
-
buf[0] = Math.floor(ts / 0x10000000000) & 0xff;
|
|
15
|
-
buf[1] = Math.floor(ts / 0x100000000) & 0xff;
|
|
16
|
-
buf[2] = Math.floor(ts / 0x1000000) & 0xff;
|
|
17
|
-
buf[3] = Math.floor(ts / 0x10000) & 0xff;
|
|
18
|
-
buf[4] = Math.floor(ts / 0x100) & 0xff;
|
|
19
|
-
buf[5] = ts & 0xff;
|
|
20
|
-
|
|
21
|
-
fillRandom(buf.subarray(6));
|
|
22
|
-
|
|
23
|
-
buf[6] = (buf[6]! & 0x0f) | 0x70; // version 7
|
|
24
|
-
buf[8] = (buf[8]! & 0x3f) | 0x80; // variant 10xx
|
|
25
|
-
|
|
26
|
-
const hex = Array.from(buf, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
27
|
-
return (
|
|
28
|
-
hex.slice(0, 8) +
|
|
29
|
-
'-' +
|
|
30
|
-
hex.slice(8, 12) +
|
|
31
|
-
'-' +
|
|
32
|
-
hex.slice(12, 16) +
|
|
33
|
-
'-' +
|
|
34
|
-
hex.slice(16, 20) +
|
|
35
|
-
'-' +
|
|
36
|
-
hex.slice(20, 32)
|
|
37
|
-
);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const fillRandom = (buf: Uint8Array): void => {
|
|
41
|
-
// Prefer crypto.getRandomValues (Hermes 0.74+, browsers, Node, Bun).
|
|
42
|
-
// RN apps targeting older Hermes should add `react-native-get-random-values`
|
|
43
|
-
// before importing the SDK.
|
|
44
|
-
const cryptoObj = (
|
|
45
|
-
globalThis as {
|
|
46
|
-
crypto?: { getRandomValues?: (b: Uint8Array) => Uint8Array };
|
|
47
|
-
}
|
|
48
|
-
).crypto;
|
|
49
|
-
if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
|
|
50
|
-
cryptoObj.getRandomValues(buf);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
for (let i = 0; i < buf.length; i++) {
|
|
54
|
-
buf[i] = Math.floor(Math.random() * 256);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
1
|
+
// Phase 21: moved to @goliapkg/sentori-core.
|
|
2
|
+
export { uuidV7 } from '@goliapkg/sentori-core'
|