@tiledev/sdk-app-monitoring 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 ADDED
@@ -0,0 +1,96 @@
1
+ # @tiledev/sdk-app-monitoring
2
+
3
+ Crash reporting (Sentry), session replay (LogRocket), one `captureError` / `captureWarning` seam
4
+ and a root `ErrorBoundary` for TilePacket apps.
5
+
6
+ - **iOS/Android release builds:** Sentry and LogRocket, each on when its config is present.
7
+ - **Development builds and web** (including the Tile preview): the console only. A dev build shows
8
+ the error on screen already, and neither native module has a web build.
9
+ - **Never takes the app down.** A missing module, an empty DSN or a throwing SDK is a warning and
10
+ that tool stays off. Every call is safe before init, and callers never guard.
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ npm i @tiledev/sdk-app-monitoring
16
+ # optional, native only: install the tools you use
17
+ npx expo install @sentry/react-native @logrocket/react-native
18
+ ```
19
+
20
+ Both are optional peers. Without them the calls still work, as console logging. Sentry's Expo
21
+ config plugin (`@sentry/react-native/expo` in `app.json`) is still the app's to add for source maps.
22
+
23
+ ## Use
24
+
25
+ ```tsx
26
+ import { initMonitoring, withMonitoring, ErrorBoundary } from '@tiledev/sdk-app-monitoring';
27
+
28
+ // At module scope in App.tsx, before any provider's module runs: Sentry only sees what happens after.
29
+ initMonitoring({
30
+ sentry: { dsn, environment: 'ambermarie-production', tags: { app: 'ambermarie' } },
31
+ logRocket: { appId: 'org/project' },
32
+ });
33
+
34
+ function App() {
35
+ return (
36
+ <ErrorBoundary colors={{ background, text, muted, primary, onPrimary }}>
37
+ {/* providers, navigation */}
38
+ </ErrorBoundary>
39
+ );
40
+ }
41
+
42
+ // OUTERMOST wrap, outside an OTA updater: a crash in anything it doesn't wrap isn't reported.
43
+ export default withMonitoring(TileUpdater.wrap(options)(App));
44
+ ```
45
+
46
+ ## API
47
+
48
+ | Call | What it does |
49
+ | --- | --- |
50
+ | `initMonitoring(config)` | Starts what the config turns on. Once; later calls are ignored. Returns `{ sentry, logRocket }`. |
51
+ | `withMonitoring(App)` | Sentry's touch and navigation instrumentation around the root. `App` itself when Sentry is off. |
52
+ | `captureError(error, context?)` | Something failed: `console.error`, and Sentry. A non-`Error` is wrapped in one. |
53
+ | `captureWarning(warning, context?)` | Something the app handled that a developer should know: `console.warn`, and Sentry at warning level (never reads as a crash). |
54
+ | `identifyUser(uid, traits?)` | The signed-in customer: traits on the replay (null/undefined dropped), **only the id** on Sentry. |
55
+ | `resetUser()` | Sign-out: a fresh replay session with the same config, and no Sentry user. |
56
+ | `getMonitoringStatus()` | `{ sentry, logRocket }`: which tools are on. |
57
+ | `<ErrorBoundary>` | The outermost render catch: reports through `captureError`, shows a "Try again" screen. |
58
+
59
+ ### Config
60
+
61
+ | Field | Default | |
62
+ | --- | --- | --- |
63
+ | `sentry.dsn` | | Empty or absent: Sentry off. |
64
+ | `sentry.environment`, `sentry.tracesSampleRate` | Sentry's | |
65
+ | `sentry.sendDefaultPii` | `false` | On, Sentry attaches IP addresses and request headers to every event. |
66
+ | `sentry.tags` | | On every event. In a project shared by several apps, what a dashboard filters by. |
67
+ | `sentry.options` | | Anything else `Sentry.init` takes. |
68
+ | `logRocket.appId` | | Empty or absent: LogRocket off. |
69
+ | `logRocket.options` | | LogRocket's init options (redaction, network capture). |
70
+ | `*.enabled` | | `false` turns that tool off whatever the key. |
71
+ | `enableInDev` | `false` | Report from development builds too. |
72
+ | `linkReplayToCrashes` | `true` | Put the LogRocket session URL on every Sentry event (context `logrocket`), so a crash links to its replay. |
73
+
74
+ ### ErrorBoundary props
75
+
76
+ `colors` (`background`, `text`, `muted`, `primary`, `onPrimary`), `title`, `message`, `retryLabel`,
77
+ `showDetails` (default: development builds only), `onError(error, info)` (called after reporting;
78
+ a throwing listener can't break the screen), and `fallback({ error, componentStack, reset })` to
79
+ replace the screen entirely. It usually sits above the app's theme, so colours come in as a prop.
80
+
81
+ ## How the platforms split
82
+
83
+ `index.ts` imports `./monitoring`. Metro resolves that to `monitoring.native.ts` on iOS/Android,
84
+ which requires Sentry and LogRocket lazily, each inside a `try` so Metro treats them as optional,
85
+ and to `monitoring.ts` everywhere else, which has no tools. `npm test` checks both: the web closure
86
+ must never require either module, and the native requires must stay inside a `try`.
87
+
88
+ ## Develop
89
+
90
+ ```sh
91
+ npm test # build, the publish gate, the monitoring core against fake tools, ErrorBoundary in jsdom
92
+ ```
93
+
94
+ The dev dependencies match the apps' stack (Expo 54: React Native 0.81.5, React 19.1), so the
95
+ build type-checks against real react-native types. The ErrorBoundary test swaps react-native for
96
+ plain DOM elements at runtime, since React Native itself can't run in Node.
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The outermost render catch. A render error a boundary catches never reaches Sentry's global
3
+ * handler, so this reports it through `captureError` and shows a "Try again" screen instead of a
4
+ * blank app.
5
+ *
6
+ * It usually sits above the app's theme, so it takes its colours as a prop rather than reading a
7
+ * theme context.
8
+ */
9
+ import { Component, type ErrorInfo, type ReactNode } from 'react';
10
+ export interface ErrorBoundaryColors {
11
+ background: string;
12
+ text: string;
13
+ muted: string;
14
+ primary: string;
15
+ onPrimary: string;
16
+ }
17
+ export interface ErrorBoundaryFallbackProps {
18
+ error: Error;
19
+ componentStack: string | null;
20
+ /** Render the children again. */
21
+ reset: () => void;
22
+ }
23
+ export interface ErrorBoundaryProps {
24
+ children: ReactNode;
25
+ /** Called after the error is reported, e.g. to log an analytics event. */
26
+ onError?: (error: Error, info: ErrorInfo) => void;
27
+ /** Replace the default screen entirely. */
28
+ fallback?: (props: ErrorBoundaryFallbackProps) => ReactNode;
29
+ colors?: Partial<ErrorBoundaryColors>;
30
+ title?: string;
31
+ message?: string;
32
+ retryLabel?: string;
33
+ /** Show the error and the top of the component stack. Default: development builds only. */
34
+ showDetails?: boolean;
35
+ }
36
+ interface State {
37
+ error: Error | null;
38
+ componentStack: string | null;
39
+ }
40
+ export declare const DEFAULT_ERROR_BOUNDARY_COLORS: ErrorBoundaryColors;
41
+ export declare class ErrorBoundary extends Component<ErrorBoundaryProps, State> {
42
+ state: State;
43
+ static getDerivedStateFromError(error: Error): Partial<State>;
44
+ componentDidCatch(error: Error, info: ErrorInfo): void;
45
+ reset: () => void;
46
+ render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
47
+ }
48
+ export {};
49
+ //# sourceMappingURL=ErrorBoundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../src/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAOlE,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,KAAK,CAAC;IACb,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iCAAiC;IACjC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IAClD,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,SAAS,CAAC;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,KAAK;IACb,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,eAAO,MAAM,6BAA6B,EAAE,mBAM3C,CAAC;AAEF,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,KAAK,CAAC;IACrE,KAAK,EAAE,KAAK,CAAyC;IAErD,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAI7D,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS;IAU/C,KAAK,aAA8D;IAEnE,MAAM;CAwBP"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ErrorBoundary = exports.DEFAULT_ERROR_BOUNDARY_COLORS = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ /**
6
+ * The outermost render catch. A render error a boundary catches never reaches Sentry's global
7
+ * handler, so this reports it through `captureError` and shows a "Try again" screen instead of a
8
+ * blank app.
9
+ *
10
+ * It usually sits above the app's theme, so it takes its colours as a prop rather than reading a
11
+ * theme context.
12
+ */
13
+ const react_1 = require("react");
14
+ const react_native_1 = require("react-native");
15
+ const monitoring_1 = require("./monitoring");
16
+ exports.DEFAULT_ERROR_BOUNDARY_COLORS = {
17
+ background: '#FFFFFF',
18
+ text: '#111111',
19
+ muted: '#6B6B6B',
20
+ primary: '#111111',
21
+ onPrimary: '#FFFFFF',
22
+ };
23
+ class ErrorBoundary extends react_1.Component {
24
+ constructor() {
25
+ super(...arguments);
26
+ this.state = { error: null, componentStack: null };
27
+ this.reset = () => this.setState({ error: null, componentStack: null });
28
+ }
29
+ static getDerivedStateFromError(error) {
30
+ return { error };
31
+ }
32
+ componentDidCatch(error, info) {
33
+ var _a, _b, _c;
34
+ monitoring_1.monitoring.captureError(error, { at: 'ErrorBoundary', componentStack: info.componentStack });
35
+ this.setState({ componentStack: (_a = info.componentStack) !== null && _a !== void 0 ? _a : null });
36
+ try {
37
+ (_c = (_b = this.props).onError) === null || _c === void 0 ? void 0 : _c.call(_b, error, info);
38
+ }
39
+ catch {
40
+ // The screen must still render if the host's listener throws.
41
+ }
42
+ }
43
+ render() {
44
+ var _a, _b, _c, _d;
45
+ const { error, componentStack } = this.state;
46
+ if (!error)
47
+ return this.props.children;
48
+ if (this.props.fallback)
49
+ return this.props.fallback({ error, componentStack, reset: this.reset });
50
+ const c = { ...exports.DEFAULT_ERROR_BOUNDARY_COLORS, ...this.props.colors };
51
+ const showDetails = (_a = this.props.showDetails) !== null && _a !== void 0 ? _a : (typeof __DEV__ !== 'undefined' && !!__DEV__);
52
+ const stack = componentStack ? `\n${componentStack.trim().split('\n').slice(0, 6).join('\n')}` : '';
53
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.root, { backgroundColor: c.background }], children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.title, { color: c.text }], children: (_b = this.props.title) !== null && _b !== void 0 ? _b : 'Something went wrong' }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.message, { color: c.muted }], children: (_c = this.props.message) !== null && _c !== void 0 ? _c : 'Please try again.' }), showDetails ? ((0, jsx_runtime_1.jsxs)(react_native_1.Text, { selectable: true, style: [styles.debug, { color: c.muted }], children: [error.message, stack] })) : null, (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { accessibilityRole: "button", onPress: this.reset, style: [styles.button, { backgroundColor: c.primary }], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.buttonLabel, { color: c.onPrimary }], children: (_d = this.props.retryLabel) !== null && _d !== void 0 ? _d : 'Try again' }) })] }));
54
+ }
55
+ }
56
+ exports.ErrorBoundary = ErrorBoundary;
57
+ const styles = react_native_1.StyleSheet.create({
58
+ root: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 24 },
59
+ title: { fontSize: 18, fontWeight: '600', marginBottom: 8 },
60
+ message: { fontSize: 14, marginBottom: 24 },
61
+ debug: { fontSize: 11, marginBottom: 24, fontFamily: 'monospace' },
62
+ button: { paddingHorizontal: 28, paddingVertical: 12, borderRadius: 8 },
63
+ buttonLabel: { fontSize: 14, fontWeight: '600' },
64
+ });
65
+ //# sourceMappingURL=ErrorBoundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorBoundary.js","sourceRoot":"","sources":["../src/ErrorBoundary.tsx"],"names":[],"mappings":";;;;AAAA;;;;;;;GAOG;AACH,iCAAkE;AAClE,+CAAiE;AAEjE,6CAA0C;AAsC7B,QAAA,6BAA6B,GAAwB;IAChE,UAAU,EAAE,SAAS;IACrB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,SAAS,EAAE,SAAS;CACrB,CAAC;AAEF,MAAa,aAAc,SAAQ,iBAAoC;IAAvE;;QACE,UAAK,GAAU,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;QAgBrD,UAAK,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;IA0BrE,CAAC;IAxCC,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,iBAAiB,CAAC,KAAY,EAAE,IAAe;;QAC7C,uBAAU,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,eAAe,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;QAC7F,IAAI,CAAC,QAAQ,CAAC,EAAE,cAAc,EAAE,MAAA,IAAI,CAAC,cAAc,mCAAI,IAAI,EAAE,CAAC,CAAC;QAC/D,IAAI,CAAC;YACH,MAAA,MAAA,IAAI,CAAC,KAAK,EAAC,OAAO,mDAAG,KAAK,EAAE,IAAI,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,8DAA8D;QAChE,CAAC;IACH,CAAC;IAID,MAAM;;QACJ,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7C,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QACvC,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAElG,MAAM,CAAC,GAAG,EAAE,GAAG,qCAA6B,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QACrE,MAAM,WAAW,GAAG,MAAA,IAAI,CAAC,KAAK,CAAC,WAAW,mCAAI,CAAC,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;QAC5F,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,KAAK,cAAc,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpG,OAAO,CACL,wBAAC,mBAAI,IAAC,KAAK,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,aAC3D,uBAAC,mBAAI,IAAC,KAAK,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,YAAG,MAAA,IAAI,CAAC,KAAK,CAAC,KAAK,mCAAI,sBAAsB,GAAQ,EACnG,uBAAC,mBAAI,IAAC,KAAK,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,YAAG,MAAA,IAAI,CAAC,KAAK,CAAC,OAAO,mCAAI,mBAAmB,GAAQ,EACpG,WAAW,CAAC,CAAC,CAAC,CACb,wBAAC,mBAAI,IAAC,UAAU,QAAC,KAAK,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,aACvD,KAAK,CAAC,OAAO,EACb,KAAK,IACD,CACR,CAAC,CAAC,CAAC,IAAI,EACR,uBAAC,wBAAS,IAAC,iBAAiB,EAAC,QAAQ,EAAC,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,YAC/G,uBAAC,mBAAI,IAAC,KAAK,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,YAAG,MAAA,IAAI,CAAC,KAAK,CAAC,UAAU,mCAAI,WAAW,GAAQ,GAC9F,IACP,CACR,CAAC;IACJ,CAAC;CACF;AA3CD,sCA2CC;AAED,MAAM,MAAM,GAAG,yBAAU,CAAC,MAAM,CAAC;IAC/B,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;IAC9E,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE;IAC3D,OAAO,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE;IAC3C,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE;IAClE,MAAM,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE;IACvE,WAAW,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE;CACjD,CAAC,CAAC"}
package/dist/core.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The monitoring state machine, shared by both builds. The native build hands it loaders that
3
+ * `require` Sentry and LogRocket; the web build hands it none, so there it is the console only.
4
+ *
5
+ * Three rules hold everywhere:
6
+ * - **Loading must never take the app down.** A missing module, a bad DSN or a throwing SDK is a
7
+ * `console.warn` and that tool stays off.
8
+ * - **Callers never guard.** Every export is safe to call before `initMonitoring`, twice, or with
9
+ * the tools off: it then only logs to the console.
10
+ * - **Reporting must never be why something else fails.** Every call into a tool is wrapped.
11
+ */
12
+ import type { ComponentType } from 'react';
13
+ import type { CaptureContext, MonitoringConfig, MonitoringStatus, UserTraits } from './types';
14
+ /** The part of `@sentry/react-native` this uses. */
15
+ export interface SentryLike {
16
+ init(options: Record<string, unknown>): void;
17
+ wrap<P extends Record<string, unknown>>(component: ComponentType<P>): ComponentType<P>;
18
+ captureException(error: unknown, hint?: {
19
+ extra?: CaptureContext;
20
+ }): unknown;
21
+ captureMessage(message: string, hint?: {
22
+ level?: string;
23
+ extra?: CaptureContext;
24
+ }): unknown;
25
+ setUser(user: {
26
+ id: string;
27
+ } | null): void;
28
+ setContext?(name: string, context: Record<string, unknown> | null): void;
29
+ }
30
+ /** The part of `@logrocket/react-native` this uses. */
31
+ export interface LogRocketLike {
32
+ init(appId: string, options?: Record<string, unknown>): void;
33
+ identify(uid: string, traits?: Record<string, string | number | boolean>): void;
34
+ startNewSession?(): unknown;
35
+ startNewSessionSameConfig?(): unknown;
36
+ getSessionURL?(callback: (url: string) => void): void;
37
+ }
38
+ export interface Loaders {
39
+ sentry?: () => SentryLike;
40
+ logRocket?: () => LogRocketLike;
41
+ }
42
+ export interface Monitoring {
43
+ initMonitoring(config: MonitoringConfig): MonitoringStatus;
44
+ withMonitoring<C extends ComponentType<any>>(App: C): C;
45
+ captureError(error: unknown, context?: CaptureContext): void;
46
+ captureWarning(warning: unknown, context?: CaptureContext): void;
47
+ identifyUser(uid: string, traits?: UserTraits): void;
48
+ resetUser(): void;
49
+ getMonitoringStatus(): MonitoringStatus;
50
+ }
51
+ export interface CreateOptions {
52
+ /** Whether this is a development build. Default: the global `__DEV__`. */
53
+ isDev?: () => boolean;
54
+ console?: Pick<Console, 'error' | 'warn'>;
55
+ }
56
+ export declare function createMonitoring(loaders: Loaders, opts?: CreateOptions): Monitoring;
57
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAK,EACV,cAAc,EAEd,gBAAgB,EAChB,gBAAgB,EAEhB,UAAU,EACX,MAAM,SAAS,CAAC;AAIjB,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7C,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IACvF,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC;IAC7E,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC;IAC5F,OAAO,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,GAAG,IAAI,CAAC;IAC3C,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;CAC1E;AAED,uDAAuD;AACvD,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7D,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC;IAChF,eAAe,CAAC,IAAI,OAAO,CAAC;IAC5B,yBAAyB,CAAC,IAAI,OAAO,CAAC;IACtC,aAAa,CAAC,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;CACvD;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,MAAM,UAAU,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,aAAa,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IACzB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAAC;IAC3D,cAAc,CAAC,CAAC,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACxD,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7D,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACjE,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IACrD,SAAS,IAAI,IAAI,CAAC;IAClB,mBAAmB,IAAI,gBAAgB,CAAC;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;CAC3C;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,GAAE,aAAkB,GAAG,UAAU,CAgJvF"}
package/dist/core.js ADDED
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMonitoring = createMonitoring;
4
+ function createMonitoring(loaders, opts = {}) {
5
+ var _a, _b;
6
+ const isDev = (_a = opts.isDev) !== null && _a !== void 0 ? _a : (() => typeof __DEV__ !== 'undefined' && !!__DEV__);
7
+ const log = (_b = opts.console) !== null && _b !== void 0 ? _b : console;
8
+ let initialised = false;
9
+ let sentry = null;
10
+ let logRocket = null;
11
+ const status = () => ({ sentry: !!sentry, logRocket: !!logRocket });
12
+ const safely = (what, fn) => {
13
+ try {
14
+ fn();
15
+ }
16
+ catch (error) {
17
+ log.warn(`[monitoring] ${what} failed`, error);
18
+ }
19
+ };
20
+ function startSentry(config) {
21
+ var _a;
22
+ if (!config || config.enabled === false || !config.dsn || !loaders.sentry)
23
+ return;
24
+ try {
25
+ const mod = loaders.sentry();
26
+ mod.init({
27
+ ...config.options,
28
+ dsn: config.dsn,
29
+ ...(config.environment ? { environment: config.environment } : {}),
30
+ ...(typeof config.tracesSampleRate === 'number' ? { tracesSampleRate: config.tracesSampleRate } : {}),
31
+ sendDefaultPii: (_a = config.sendDefaultPii) !== null && _a !== void 0 ? _a : false,
32
+ ...(config.tags ? { initialScope: { tags: config.tags } } : {}),
33
+ });
34
+ sentry = mod;
35
+ }
36
+ catch (error) {
37
+ log.warn('[monitoring] Sentry init failed; continuing without crash reporting', error);
38
+ sentry = null;
39
+ }
40
+ }
41
+ function startLogRocket(config) {
42
+ if (!config || config.enabled === false || !config.appId || !loaders.logRocket)
43
+ return;
44
+ try {
45
+ const mod = loaders.logRocket();
46
+ mod.init(config.appId, config.options);
47
+ logRocket = mod;
48
+ }
49
+ catch (error) {
50
+ log.warn('[monitoring] LogRocket init failed; continuing without session replay', error);
51
+ logRocket = null;
52
+ }
53
+ }
54
+ return {
55
+ /**
56
+ * Call once, at module scope in App.tsx, before any provider's module runs: Sentry only sees
57
+ * what happens after this. Later calls are ignored. Returns which tools came up.
58
+ */
59
+ initMonitoring(config) {
60
+ if (initialised)
61
+ return status();
62
+ initialised = true;
63
+ if (isDev() && !config.enableInDev)
64
+ return status();
65
+ startSentry(config.sentry);
66
+ startLogRocket(config.logRocket);
67
+ if (sentry && logRocket && config.linkReplayToCrashes !== false) {
68
+ const s = sentry;
69
+ safely('linking the replay', () => { var _a; return (_a = logRocket === null || logRocket === void 0 ? void 0 : logRocket.getSessionURL) === null || _a === void 0 ? void 0 : _a.call(logRocket, (url) => safely('linking the replay', () => { var _a; return (_a = s.setContext) === null || _a === void 0 ? void 0 : _a.call(s, 'logrocket', { sessionURL: url }); })); });
70
+ }
71
+ return status();
72
+ },
73
+ /**
74
+ * Sentry's touch and navigation instrumentation around the root. Make it the OUTERMOST wrap
75
+ * (outside an OTA updater), or a crash in what it wraps is not reported. The app itself when
76
+ * Sentry is off.
77
+ */
78
+ withMonitoring(App) {
79
+ if (!sentry)
80
+ return App;
81
+ try {
82
+ return sentry.wrap(App);
83
+ }
84
+ catch (error) {
85
+ log.warn('[monitoring] Sentry wrap failed', error);
86
+ return App;
87
+ }
88
+ },
89
+ /** Something failed. The console for the developer, Sentry in release builds. */
90
+ captureError(error, context) {
91
+ log.error(error, context);
92
+ if (!sentry)
93
+ return;
94
+ const s = sentry;
95
+ safely('captureError', () => s.captureException(error instanceof Error ? error : new Error(String(error)), { extra: context }));
96
+ },
97
+ /**
98
+ * Something a developer should know that the app already handled. Warning level, so a
99
+ * recovered condition never reads as a crash in the feed, and no red box in development.
100
+ */
101
+ captureWarning(warning, context) {
102
+ log.warn(warning, context);
103
+ if (!sentry)
104
+ return;
105
+ const s = sentry;
106
+ safely('captureWarning', () => s.captureMessage(warning instanceof Error ? warning.message : String(warning), { level: 'warning', extra: context }));
107
+ },
108
+ /**
109
+ * The signed-in customer: their traits on the replay, so it is searchable by who it was, and
110
+ * their id (only the id) on Sentry events.
111
+ */
112
+ identifyUser(uid, traits = {}) {
113
+ if (logRocket) {
114
+ const lr = logRocket;
115
+ const clean = {};
116
+ for (const [key, value] of Object.entries(traits)) {
117
+ if (value !== null && value !== undefined)
118
+ clean[key] = value;
119
+ }
120
+ safely('LogRocket identify', () => lr.identify(uid, clean));
121
+ }
122
+ if (sentry) {
123
+ const s = sentry;
124
+ safely('Sentry setUser', () => s.setUser({ id: uid }));
125
+ }
126
+ },
127
+ /** Sign-out: a fresh replay session, so a following guest isn't recorded as the last customer. */
128
+ resetUser() {
129
+ if (logRocket) {
130
+ const lr = logRocket;
131
+ // `startNewSession()` with no app id isn't documented to keep the config; SameConfig is.
132
+ safely('LogRocket new session', () => {
133
+ var _a;
134
+ const started = lr.startNewSessionSameConfig ? lr.startNewSessionSameConfig() : (_a = lr.startNewSession) === null || _a === void 0 ? void 0 : _a.call(lr);
135
+ Promise.resolve(started).catch((error) => log.warn('[monitoring] LogRocket new session failed', error));
136
+ });
137
+ }
138
+ if (sentry) {
139
+ const s = sentry;
140
+ safely('Sentry setUser', () => s.setUser(null));
141
+ }
142
+ },
143
+ getMonitoringStatus: status,
144
+ };
145
+ }
146
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export declare const initMonitoring: (config: import("./types").MonitoringConfig) => import("./types").MonitoringStatus;
2
+ export declare const withMonitoring: <C extends import("react").ComponentType<any>>(App: C) => C;
3
+ export declare const captureError: (error: unknown, context?: import("./types").CaptureContext) => void;
4
+ export declare const captureWarning: (warning: unknown, context?: import("./types").CaptureContext) => void;
5
+ export declare const identifyUser: (uid: string, traits?: import("./types").UserTraits) => void;
6
+ export declare const resetUser: () => void;
7
+ export declare const getMonitoringStatus: () => import("./types").MonitoringStatus;
8
+ export { ErrorBoundary, DEFAULT_ERROR_BOUNDARY_COLORS, type ErrorBoundaryProps, type ErrorBoundaryColors, type ErrorBoundaryFallbackProps, } from './ErrorBoundary';
9
+ export { createMonitoring, type Monitoring, type SentryLike, type LogRocketLike, type Loaders } from './core';
10
+ export type * from './types';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,cAAc,oFAA4B,CAAC;AACxD,eAAO,MAAM,cAAc,6DAA4B,CAAC;AACxD,eAAO,MAAM,YAAY,sEAA0B,CAAC;AACpD,eAAO,MAAM,cAAc,wEAA4B,CAAC;AACxD,eAAO,MAAM,YAAY,8DAA0B,CAAC;AACpD,eAAO,MAAM,SAAS,YAAuB,CAAC;AAC9C,eAAO,MAAM,mBAAmB,0CAAiC,CAAC;AAElE,OAAO,EACL,aAAa,EACb,6BAA6B,EAC7B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,GAChC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAC9G,mBAAmB,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMonitoring = exports.DEFAULT_ERROR_BOUNDARY_COLORS = exports.ErrorBoundary = exports.getMonitoringStatus = exports.resetUser = exports.identifyUser = exports.captureWarning = exports.captureError = exports.withMonitoring = exports.initMonitoring = void 0;
4
+ /**
5
+ * `@tiledev/sdk-app-monitoring`: crash reporting, session replay and the app's error seam.
6
+ *
7
+ * Single entry point for both platforms. Metro resolves `./monitoring` to `monitoring.native.ts`
8
+ * on iOS/Android (Sentry + LogRocket, lazily required) and `monitoring.ts` everywhere else (the
9
+ * console only), because neither module has a web build and must never enter a web bundle.
10
+ *
11
+ * ```tsx
12
+ * import { initMonitoring, withMonitoring, ErrorBoundary, captureError } from '@tiledev/sdk-app-monitoring';
13
+ *
14
+ * initMonitoring({ sentry: { dsn, environment, tags: { app: 'ambermarie' } }, logRocket: { appId } });
15
+ * export default withMonitoring(App); // outermost, outside the OTA wrap
16
+ * // <ErrorBoundary colors={…}> at the root of the tree
17
+ * captureError(error, { at: 'checkout' });
18
+ * ```
19
+ *
20
+ * Off in development builds and on web unless `enableInDev` is set; every call is then the
21
+ * console only, so callers never guard.
22
+ */
23
+ const monitoring_1 = require("./monitoring");
24
+ exports.initMonitoring = monitoring_1.monitoring.initMonitoring;
25
+ exports.withMonitoring = monitoring_1.monitoring.withMonitoring;
26
+ exports.captureError = monitoring_1.monitoring.captureError;
27
+ exports.captureWarning = monitoring_1.monitoring.captureWarning;
28
+ exports.identifyUser = monitoring_1.monitoring.identifyUser;
29
+ exports.resetUser = monitoring_1.monitoring.resetUser;
30
+ exports.getMonitoringStatus = monitoring_1.monitoring.getMonitoringStatus;
31
+ var ErrorBoundary_1 = require("./ErrorBoundary");
32
+ Object.defineProperty(exports, "ErrorBoundary", { enumerable: true, get: function () { return ErrorBoundary_1.ErrorBoundary; } });
33
+ Object.defineProperty(exports, "DEFAULT_ERROR_BOUNDARY_COLORS", { enumerable: true, get: function () { return ErrorBoundary_1.DEFAULT_ERROR_BOUNDARY_COLORS; } });
34
+ var core_1 = require("./core");
35
+ Object.defineProperty(exports, "createMonitoring", { enumerable: true, get: function () { return core_1.createMonitoring; } });
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,6CAA0C;AAE7B,QAAA,cAAc,GAAG,uBAAU,CAAC,cAAc,CAAC;AAC3C,QAAA,cAAc,GAAG,uBAAU,CAAC,cAAc,CAAC;AAC3C,QAAA,YAAY,GAAG,uBAAU,CAAC,YAAY,CAAC;AACvC,QAAA,cAAc,GAAG,uBAAU,CAAC,cAAc,CAAC;AAC3C,QAAA,YAAY,GAAG,uBAAU,CAAC,YAAY,CAAC;AACvC,QAAA,SAAS,GAAG,uBAAU,CAAC,SAAS,CAAC;AACjC,QAAA,mBAAmB,GAAG,uBAAU,CAAC,mBAAmB,CAAC;AAElE,iDAMyB;AALvB,8GAAA,aAAa,OAAA;AACb,8HAAA,6BAA6B,OAAA;AAK/B,+BAA8G;AAArG,wGAAA,gBAAgB,OAAA"}
@@ -0,0 +1,2 @@
1
+ export declare const monitoring: import("./core").Monitoring;
2
+ //# sourceMappingURL=monitoring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitoring.d.ts","sourceRoot":"","sources":["../src/monitoring.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,UAAU,6BAAuB,CAAC"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.monitoring = void 0;
4
+ /**
5
+ * Web (the Tile preview and web builds): neither Sentry's nor LogRocket's React Native module has
6
+ * a web build, so they must never enter a web bundle. Every call is the console only.
7
+ */
8
+ const core_1 = require("./core");
9
+ exports.monitoring = (0, core_1.createMonitoring)({});
10
+ //# sourceMappingURL=monitoring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitoring.js","sourceRoot":"","sources":["../src/monitoring.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,iCAA0C;AAE7B,QAAA,UAAU,GAAG,IAAA,uBAAgB,EAAC,EAAE,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const monitoring: import("./core").Monitoring;
2
+ //# sourceMappingURL=monitoring.native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitoring.native.d.ts","sourceRoot":"","sources":["../src/monitoring.native.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,UAAU,6BAgBrB,CAAC"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.monitoring = void 0;
4
+ /**
5
+ * iOS/Android: Sentry and LogRocket, each required only when its config turns it on, so an app
6
+ * that installs neither still builds and runs.
7
+ *
8
+ * Each `require` sits lexically inside a `try`: that is what makes Metro treat the module as
9
+ * optional (`allowOptionalDependencies`) instead of failing the build when it isn't installed. The
10
+ * error is rethrown, and `createMonitoring` turns it into a warning with that tool off.
11
+ */
12
+ const core_1 = require("./core");
13
+ exports.monitoring = (0, core_1.createMonitoring)({
14
+ sentry: () => {
15
+ try {
16
+ return require('@sentry/react-native');
17
+ }
18
+ catch (error) {
19
+ throw error;
20
+ }
21
+ },
22
+ logRocket: () => {
23
+ var _a;
24
+ try {
25
+ const mod = require('@logrocket/react-native');
26
+ return ((_a = mod === null || mod === void 0 ? void 0 : mod.default) !== null && _a !== void 0 ? _a : mod);
27
+ }
28
+ catch (error) {
29
+ throw error;
30
+ }
31
+ },
32
+ });
33
+ //# sourceMappingURL=monitoring.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitoring.native.js","sourceRoot":"","sources":["../src/monitoring.native.ts"],"names":[],"mappings":";;;AAAA;;;;;;;GAOG;AACH,iCAA+E;AAIlE,QAAA,UAAU,GAAG,IAAA,uBAAgB,EAAC;IACzC,MAAM,EAAE,GAAG,EAAE;QACX,IAAI,CAAC;YACH,OAAO,OAAO,CAAC,sBAAsB,CAAe,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IACD,SAAS,EAAE,GAAG,EAAE;;QACd,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;YAC/C,OAAO,CAAC,MAAA,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,mCAAI,GAAG,CAAkB,CAAC;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,50 @@
1
+ export interface SentryConfig {
2
+ /** The project's DSN. Empty or absent: Sentry stays off. */
3
+ dsn?: string;
4
+ /** e.g. `ambermarie-production`. */
5
+ environment?: string;
6
+ /** 0–1. Absent: Sentry's default (no performance traces). */
7
+ tracesSampleRate?: number;
8
+ /** Off by default: on, Sentry attaches IP addresses and request headers to every event. */
9
+ sendDefaultPii?: boolean;
10
+ /**
11
+ * Tags on every event. In a project shared by several apps, `{ app: 'ambermarie' }` is what a
12
+ * dashboard filters by (Sentry still groups by stack trace across apps).
13
+ */
14
+ tags?: Record<string, string>;
15
+ /** Anything else `Sentry.init` takes, passed through as is. */
16
+ options?: Record<string, unknown>;
17
+ /** `false` turns Sentry off whatever the DSN. */
18
+ enabled?: boolean;
19
+ }
20
+ export interface LogRocketConfig {
21
+ /** `org/project`. Empty or absent: LogRocket stays off. */
22
+ appId?: string;
23
+ /** LogRocket's init options (redaction, network capture…), passed through as is. */
24
+ options?: Record<string, unknown>;
25
+ /** `false` turns LogRocket off whatever the app id. */
26
+ enabled?: boolean;
27
+ }
28
+ export interface MonitoringConfig {
29
+ sentry?: SentryConfig;
30
+ logRocket?: LogRocketConfig;
31
+ /**
32
+ * Report from development builds too. Off by default: a dev build (and the Tile preview) shows
33
+ * the error on screen already, and dev noise would bury real crashes.
34
+ */
35
+ enableInDev?: boolean;
36
+ /**
37
+ * Put the LogRocket session URL on every Sentry event (context `logrocket`), so a crash links
38
+ * to its replay. Default true; only applies when both are on.
39
+ */
40
+ linkReplayToCrashes?: boolean;
41
+ }
42
+ export interface MonitoringStatus {
43
+ sentry: boolean;
44
+ logRocket: boolean;
45
+ }
46
+ /** Customer traits for the replay. `null`/`undefined` values are dropped, never written as blanks. */
47
+ export type UserTraits = Record<string, string | number | boolean | null | undefined>;
48
+ /** Free-form detail sent with an error or warning. Keep it JSON-safe. */
49
+ export type CaptureContext = Record<string, unknown>;
50
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,4DAA4D;IAC5D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2FAA2F;IAC3F,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,sGAAsG;AACtG,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAEtF,yEAAyE;AACzE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@tiledev/sdk-app-monitoring",
3
+ "version": "0.1.0",
4
+ "description": "Crash reporting (Sentry), session replay (LogRocket), one captureError/captureWarning seam and a root ErrorBoundary for TilePacket apps. Real on iOS/Android release builds, console-only on web and in development.",
5
+ "license": "UNLICENSED",
6
+ "private": false,
7
+ "sideEffects": false,
8
+ "main": "dist/index.js",
9
+ "module": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "scripts": {
26
+ "clean": "rm -rf dist",
27
+ "build": "npm run clean && tsc",
28
+ "lint": "tsc --noEmit",
29
+ "test": "npm run build && node test/verify.js && node test/monitoring.test.js && node test/errorBoundary.test.js",
30
+ "prepublishOnly": "npm test"
31
+ },
32
+ "keywords": [
33
+ "tile",
34
+ "tilepacket",
35
+ "sentry",
36
+ "logrocket",
37
+ "crash-reporting",
38
+ "session-replay",
39
+ "error-boundary",
40
+ "react-native",
41
+ "expo"
42
+ ],
43
+ "peerDependencies": {
44
+ "@logrocket/react-native": ">=3.0.0",
45
+ "@sentry/react-native": ">=6.0.0",
46
+ "react": ">=17",
47
+ "react-native": ">=0.70"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "@logrocket/react-native": {
51
+ "optional": true
52
+ },
53
+ "@sentry/react-native": {
54
+ "optional": true
55
+ }
56
+ },
57
+ "devDependencies": {
58
+ "@types/react": "~19.1.10",
59
+ "jsdom": "^24.1.3",
60
+ "react": "19.1.0",
61
+ "react-dom": "19.1.0",
62
+ "react-native": "0.81.5",
63
+ "typescript": "^5.0.0"
64
+ }
65
+ }