@smarthivelabs-devs/observe-expo 0.0.0-stage → 26.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/LICENSE +21 -0
- package/README.md +103 -2
- package/app.plugin.cjs +1 -0
- package/dist/chunk-HDKJGFST.js +362 -0
- package/dist/chunk-HDKJGFST.js.map +1 -0
- package/dist/index.cjs +399 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +130 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.cjs +99 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +18 -0
- package/dist/plugin.d.ts +18 -0
- package/dist/plugin.js +74 -0
- package/dist/plugin.js.map +1 -0
- package/dist/react.cjs +414 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +18 -0
- package/dist/react.d.ts +18 -0
- package/dist/react.js +24 -0
- package/dist/react.js.map +1 -0
- package/package.json +79 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Smart Hive Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,104 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @smarthivelabs-devs/observe-expo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Crashes, sessions, and traces for Expo and React Native. Failed ingest never throws. Events persist offline and flush when the network returns.
|
|
4
|
+
|
|
5
|
+
Use a **public** DSN (`pk_`) from SmartObserve project settings.
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
https://<pk_public_key>@observe.smarthivelabs.dev/<project-slug>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Native traffic uses origin `smartobserve-expo://native` — you do not add that in project settings. If the same app also runs in a browser, allow that HTTPS origin separately (or use `@smarthivelabs-devs/observe-web` on web).
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @smarthivelabs-devs/observe-expo
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Device, offline queue, and network helpers ship with the package. The app already has `expo`, `react`, and `react-native`.
|
|
20
|
+
|
|
21
|
+
## Start
|
|
22
|
+
|
|
23
|
+
Call `init` once at the root of the app.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { SmartObserve } from "@smarthivelabs-devs/observe-expo";
|
|
27
|
+
|
|
28
|
+
SmartObserve.init({ dsn: process.env.EXPO_PUBLIC_SMARTOBSERVE_DSN });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Release and dist default to the native app version and build number. The global JS handler, AppState lifecycle, a session (`active` / `exited` / `crashed`), device/app context, and `fetch` are wired automatically.
|
|
32
|
+
|
|
33
|
+
## What you send
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
SmartObserve.captureException(err);
|
|
37
|
+
SmartObserve.captureMessage("paywall shown", "info");
|
|
38
|
+
SmartObserve.log.warn("slow sync", { table: "orders" });
|
|
39
|
+
|
|
40
|
+
SmartObserve.setUser({ id: user.id });
|
|
41
|
+
SmartObserve.setTag("workspace", workspace.slug);
|
|
42
|
+
SmartObserve.trackScreen("Checkout", { step: "pay" });
|
|
43
|
+
|
|
44
|
+
await SmartObserve.startSpan({ name: "sync", operation: "db" }, async (span) => {
|
|
45
|
+
span.setAttribute("rows", count);
|
|
46
|
+
return sync();
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`trackScreen` is a navigation breadcrumb. `beforeSend` can drop or edit events. Tokens and passwords are redacted; add keys with `redactKeys`. Email / IP stay off the user unless `sendDefaultPii: true`.
|
|
51
|
+
|
|
52
|
+
Disable automations with `trackNetwork: false` or `trackLifecycle: false`. Outgoing `fetch` never gets `traceparent` unless you allow hosts you own:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
SmartObserve.init({
|
|
56
|
+
dsn: process.env.EXPO_PUBLIC_SMARTOBSERVE_DSN,
|
|
57
|
+
allowTracePropagationTargets: ["https://api.yourapp.com", /^https:\/\/.*\.yourapp\.com$/],
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## React Native UI
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { SmartObserveErrorBoundary } from "@smarthivelabs-devs/observe-expo/react";
|
|
65
|
+
|
|
66
|
+
<SmartObserveErrorBoundary fallback={<Text>Something broke.</Text>}>
|
|
67
|
+
<App />
|
|
68
|
+
</SmartObserveErrorBoundary>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Config plugin
|
|
72
|
+
|
|
73
|
+
Writes DSN into `extra` and can upload source maps during the iOS prebuild step.
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"expo": {
|
|
78
|
+
"plugins": ["@smarthivelabs-devs/observe-expo"]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
["@smarthivelabs-devs/observe-expo", { dsn: process.env.EXPO_PUBLIC_SMARTOBSERVE_DSN }]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
In CI set `SMARTOBSERVE_AUTH_TOKEN` to a **release token** from project settings (`SMARTOBSERVE_RELEASE_TOKEN` also works). Fail the build without a token with `{ "requireUpload": true }` in plugin options.
|
|
88
|
+
|
|
89
|
+
## Options
|
|
90
|
+
|
|
91
|
+
| Option | Default | Notes |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| `dsn` | — | Public key DSN |
|
|
94
|
+
| `environment` | `production` | Shown on issues |
|
|
95
|
+
| `release` / `dist` | native version / build | Override for EAS |
|
|
96
|
+
| `enabled` | `true` if DSN is set | Set `false` in tests |
|
|
97
|
+
| `errorSampleRate` | `1` | `0`–`1` |
|
|
98
|
+
| `tracesSampleRate` | `0.1` | Root transactions |
|
|
99
|
+
| `sendDefaultPii` | `false` | Include email / IP |
|
|
100
|
+
| `debug` | `false` | Logs transport failures |
|
|
101
|
+
|
|
102
|
+
## API
|
|
103
|
+
|
|
104
|
+
`SmartObserve.init` · `captureException` · `captureMessage` · `log` · `setUser` · `setTag` · `setContext` · `addBreadcrumb` · `trackScreen` · `startSpan` · `beforeSend` · `flush` · `close` · `SmartObserveExpoClient`
|
package/app.plugin.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("./dist/plugin.cjs").default;
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import { AppState, Platform } from "react-native";
|
|
3
|
+
import * as Application from "expo-application";
|
|
4
|
+
import Constants from "expo-constants";
|
|
5
|
+
import * as Device from "expo-device";
|
|
6
|
+
|
|
7
|
+
// src/internal.ts
|
|
8
|
+
function id() {
|
|
9
|
+
return crypto.randomUUID();
|
|
10
|
+
}
|
|
11
|
+
function randomHex(bytes) {
|
|
12
|
+
return [...crypto.getRandomValues(new Uint8Array(bytes))].map((value) => value.toString(16).padStart(2, "0")).join("");
|
|
13
|
+
}
|
|
14
|
+
function parseDsn(value) {
|
|
15
|
+
const url = new URL(value);
|
|
16
|
+
const key = decodeURIComponent(url.username);
|
|
17
|
+
if (!/^(pk|sk)_[A-Za-z0-9_-]{16,}$/.test(key)) throw new Error("SmartObserve DSN contains an invalid project key");
|
|
18
|
+
const slug = url.pathname.replace(/^\/+|\/+$/g, "");
|
|
19
|
+
if (!slug) throw new Error("SmartObserve DSN must include a project slug");
|
|
20
|
+
return { endpoint: `${url.protocol}//${url.host}/api/v1/envelope`, key };
|
|
21
|
+
}
|
|
22
|
+
var BLOCKED = /* @__PURE__ */ new Set(["password", "passcode", "authorization", "cookie", "access_token", "refresh_token", "api_key", "secret", "private_key", "card_number", "cvv", "cvc"]);
|
|
23
|
+
function sanitize(input, extra = []) {
|
|
24
|
+
const blocked = /* @__PURE__ */ new Set([...BLOCKED, ...extra.map((value) => value.toLowerCase())]);
|
|
25
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
26
|
+
const walk = (value, depth) => {
|
|
27
|
+
if (depth > 5) return "[Truncated]";
|
|
28
|
+
if (typeof value === "string") return value.slice(0, 8192);
|
|
29
|
+
if (value === null || typeof value !== "object") return value;
|
|
30
|
+
if (seen.has(value)) return "[Circular]";
|
|
31
|
+
seen.add(value);
|
|
32
|
+
if (Array.isArray(value)) return value.slice(0, 200).map((entry) => walk(entry, depth + 1));
|
|
33
|
+
return Object.fromEntries(Object.entries(value).slice(0, 200).map(([key, entry]) => [key, blocked.has(key.toLowerCase().replace(/[\s-]+/g, "_")) ? "[Redacted]" : walk(entry, depth + 1)]));
|
|
34
|
+
};
|
|
35
|
+
return walk(input, 0);
|
|
36
|
+
}
|
|
37
|
+
function safeUrl(value) {
|
|
38
|
+
try {
|
|
39
|
+
const url = new URL(value);
|
|
40
|
+
url.username = "";
|
|
41
|
+
url.password = "";
|
|
42
|
+
for (const key of [...url.searchParams.keys()]) url.searchParams.set(key, "[Filtered]");
|
|
43
|
+
return url.toString();
|
|
44
|
+
} catch {
|
|
45
|
+
return "[Invalid URL]";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/transport.ts
|
|
50
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
51
|
+
import NetInfo from "@react-native-community/netinfo";
|
|
52
|
+
|
|
53
|
+
// src/sdk-meta.ts
|
|
54
|
+
var SDK_NAME = "@smarthivelabs-devs/observe-expo";
|
|
55
|
+
var SDK_VERSION = "26.1.0";
|
|
56
|
+
|
|
57
|
+
// src/transport.ts
|
|
58
|
+
var STORAGE_KEY = "@smartobserve/events/v1";
|
|
59
|
+
var ExpoTransport = class {
|
|
60
|
+
constructor(options) {
|
|
61
|
+
this.options = options;
|
|
62
|
+
this.timer = setInterval(() => void this.flush(), options.flushIntervalMs);
|
|
63
|
+
this.unsubscribe = NetInfo.addEventListener((state) => {
|
|
64
|
+
this.online = state.isConnected !== false;
|
|
65
|
+
if (this.online) void this.flush();
|
|
66
|
+
});
|
|
67
|
+
void this.restore();
|
|
68
|
+
}
|
|
69
|
+
options;
|
|
70
|
+
queue = [];
|
|
71
|
+
timer;
|
|
72
|
+
flushing;
|
|
73
|
+
online = true;
|
|
74
|
+
unsubscribe;
|
|
75
|
+
closed = false;
|
|
76
|
+
enqueue(event) {
|
|
77
|
+
if (this.closed) return;
|
|
78
|
+
if (this.queue.length >= this.options.maxQueueSize) this.evict();
|
|
79
|
+
this.queue.push(event);
|
|
80
|
+
void this.persist();
|
|
81
|
+
if (this.queue.length >= 50 || event.level === "fatal") void this.flush();
|
|
82
|
+
}
|
|
83
|
+
async flush() {
|
|
84
|
+
if (this.flushing || !this.queue.length || !this.online) return this.flushing;
|
|
85
|
+
const events = this.queue.splice(0, 50);
|
|
86
|
+
this.flushing = this.send(events).then(() => this.persist()).catch((error) => {
|
|
87
|
+
this.queue.unshift(...events);
|
|
88
|
+
void this.persist();
|
|
89
|
+
if (this.options.debug) console.warn("[SmartObserve] offline batch retained", error);
|
|
90
|
+
}).finally(() => {
|
|
91
|
+
this.flushing = void 0;
|
|
92
|
+
});
|
|
93
|
+
return this.flushing;
|
|
94
|
+
}
|
|
95
|
+
async close() {
|
|
96
|
+
this.closed = true;
|
|
97
|
+
if (this.timer) clearInterval(this.timer);
|
|
98
|
+
this.unsubscribe?.();
|
|
99
|
+
await this.flush();
|
|
100
|
+
}
|
|
101
|
+
async restore() {
|
|
102
|
+
try {
|
|
103
|
+
const raw = await AsyncStorage.getItem(STORAGE_KEY);
|
|
104
|
+
if (!raw) return;
|
|
105
|
+
const stored = JSON.parse(raw);
|
|
106
|
+
this.queue.push(...stored.filter((event) => Date.now() - (event._storedAt ?? event.timestamp) < 7 * 864e5).slice(-this.options.maxQueueSize));
|
|
107
|
+
await this.flush();
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (this.options.debug) console.warn("[SmartObserve] offline queue unavailable", error);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async persist() {
|
|
113
|
+
try {
|
|
114
|
+
let prepared = this.queue.map((event) => ({ ...event, _storedAt: Date.now() }));
|
|
115
|
+
let raw = JSON.stringify(prepared);
|
|
116
|
+
while (raw.length > 10 * 1024 * 1024 && this.queue.length) {
|
|
117
|
+
this.evict();
|
|
118
|
+
prepared = this.queue.map((event) => ({ ...event, _storedAt: Date.now() }));
|
|
119
|
+
raw = JSON.stringify(prepared);
|
|
120
|
+
}
|
|
121
|
+
await AsyncStorage.setItem(STORAGE_KEY, raw);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (this.options.debug) console.warn("[SmartObserve] could not persist offline queue", error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async send(events) {
|
|
127
|
+
const body = JSON.stringify({ schemaVersion: 1, envelopeId: id(), sentAt: Date.now(), sdk: { name: SDK_NAME, version: SDK_VERSION }, environment: this.options.environment, release: this.options.release, dist: this.options.dist, events });
|
|
128
|
+
let attempt = 0;
|
|
129
|
+
while (true) {
|
|
130
|
+
const response = await fetch(this.options.endpoint, { method: "POST", headers: { "content-type": "application/json", "origin": "smartobserve-expo://native", "x-smartobserve-key": this.options.key, "x-smartobserve-platform": "expo", "x-smartobserve-internal": "transport" }, body });
|
|
131
|
+
if (response.ok) return;
|
|
132
|
+
if (![408, 429].includes(response.status) && response.status < 500) return;
|
|
133
|
+
if (attempt++ >= 5) throw new Error(`HTTP ${response.status}`);
|
|
134
|
+
await new Promise((resolve) => setTimeout(resolve, Math.random() * Math.min(3e4, 2 ** attempt * 1e3)));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
evict() {
|
|
138
|
+
const index = this.queue.findIndex((event) => event.type === "span" || event.type === "performance" || event.level === "debug");
|
|
139
|
+
this.queue.splice(index < 0 ? 0 : index, 1);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/client.ts
|
|
144
|
+
var SmartObserveExpoClient = class {
|
|
145
|
+
config;
|
|
146
|
+
transport;
|
|
147
|
+
user;
|
|
148
|
+
tags = {};
|
|
149
|
+
contexts = {};
|
|
150
|
+
breadcrumbs = [];
|
|
151
|
+
trace;
|
|
152
|
+
beforeSendHook;
|
|
153
|
+
cleanups = [];
|
|
154
|
+
sessionId = id();
|
|
155
|
+
sessionStarted = Date.now();
|
|
156
|
+
constructor(input = {}) {
|
|
157
|
+
this.config = { ...input, environment: input.environment ?? "production", enabled: input.enabled ?? Boolean(input.dsn), debug: input.debug ?? false, tracesSampleRate: input.tracesSampleRate ?? input.sampleRate ?? 0.1, errorSampleRate: input.errorSampleRate ?? 1, maxBreadcrumbs: input.maxBreadcrumbs ?? 100 };
|
|
158
|
+
this.beforeSendHook = input.beforeSend;
|
|
159
|
+
if (this.config.enabled && input.dsn) {
|
|
160
|
+
const parsed = parseDsn(input.dsn);
|
|
161
|
+
this.transport = new ExpoTransport({ ...parsed, environment: this.config.environment, release: input.release ?? Application.nativeApplicationVersion ?? void 0, dist: input.dist ?? Application.nativeBuildVersion ?? void 0, maxQueueSize: input.maxQueueSize ?? 1e3, flushIntervalMs: input.flushIntervalMs ?? 5e3, debug: this.config.debug });
|
|
162
|
+
this.setup(parsed.endpoint);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
captureException(error, hint = {}) {
|
|
166
|
+
const eventId = id();
|
|
167
|
+
if (Math.random() <= this.config.errorSampleRate) {
|
|
168
|
+
const exception = serialize(error);
|
|
169
|
+
void this.capture({ ...this.base("error"), eventId, message: exception.value, level: "error", exceptions: [exception], metadata: hint });
|
|
170
|
+
}
|
|
171
|
+
return eventId;
|
|
172
|
+
}
|
|
173
|
+
captureMessage(message, level = "info", metadata) {
|
|
174
|
+
const eventId = id();
|
|
175
|
+
void this.capture({ ...this.base("log"), eventId, message, level, metadata });
|
|
176
|
+
return eventId;
|
|
177
|
+
}
|
|
178
|
+
log = { debug: (m, d) => this.captureMessage(m, "debug", d), info: (m, d) => this.captureMessage(m, "info", d), warn: (m, d) => this.captureMessage(m, "warn", d), error: (m, d) => this.captureMessage(m, "error", d) };
|
|
179
|
+
addBreadcrumb(value) {
|
|
180
|
+
this.breadcrumbs.push({ ...sanitize(value, this.config.redactKeys), timestamp: value.timestamp ?? Date.now() });
|
|
181
|
+
this.breadcrumbs.splice(0, Math.max(0, this.breadcrumbs.length - this.config.maxBreadcrumbs));
|
|
182
|
+
}
|
|
183
|
+
setUser(value) {
|
|
184
|
+
this.user = value ? sanitize(value, this.config.redactKeys) : void 0;
|
|
185
|
+
}
|
|
186
|
+
setTag(key, value) {
|
|
187
|
+
if (value === null) delete this.tags[key];
|
|
188
|
+
else this.tags[key.slice(0, 64)] = value.slice(0, 256);
|
|
189
|
+
}
|
|
190
|
+
setContext(key, value) {
|
|
191
|
+
if (value === null) delete this.contexts[key];
|
|
192
|
+
else this.contexts[key] = sanitize(value, this.config.redactKeys);
|
|
193
|
+
}
|
|
194
|
+
beforeSend(callback) {
|
|
195
|
+
this.beforeSendHook = callback;
|
|
196
|
+
}
|
|
197
|
+
startSpan(options, callback) {
|
|
198
|
+
const previous = this.trace;
|
|
199
|
+
const trace = { traceId: options.parent?.traceId ?? previous?.traceId ?? randomHex(16), spanId: randomHex(8), parentSpanId: options.parent?.spanId ?? previous?.spanId, sampled: options.sampled ?? previous?.sampled ?? Math.random() < this.config.tracesSampleRate };
|
|
200
|
+
const start = Date.now();
|
|
201
|
+
const attributes = { ...options.attributes ?? {} };
|
|
202
|
+
let status = "unknown";
|
|
203
|
+
let ended = false;
|
|
204
|
+
const span = { traceId: trace.traceId, spanId: trace.spanId, setAttribute: (key, value) => {
|
|
205
|
+
attributes[key] = value;
|
|
206
|
+
}, setStatus: (value) => {
|
|
207
|
+
status = value;
|
|
208
|
+
}, recordException: (error) => {
|
|
209
|
+
status = "error";
|
|
210
|
+
this.captureException(error);
|
|
211
|
+
}, end: () => {
|
|
212
|
+
if (!ended && trace.sampled) {
|
|
213
|
+
ended = true;
|
|
214
|
+
const isRoot = !trace.parentSpanId;
|
|
215
|
+
void this.capture({ ...this.base(isRoot ? "transaction" : "span"), name: options.name, operation: options.operation, startTimestamp: start, endTimestamp: Date.now(), status, ...isRoot ? {} : { attributes }, trace });
|
|
216
|
+
}
|
|
217
|
+
} };
|
|
218
|
+
this.trace = trace;
|
|
219
|
+
try {
|
|
220
|
+
const result = callback(span);
|
|
221
|
+
if (result && typeof result.then === "function") return result.then((value) => {
|
|
222
|
+
if (status === "unknown") status = "ok";
|
|
223
|
+
span.end();
|
|
224
|
+
return value;
|
|
225
|
+
}, (error) => {
|
|
226
|
+
span.recordException(error);
|
|
227
|
+
span.end();
|
|
228
|
+
throw error;
|
|
229
|
+
}).finally(() => {
|
|
230
|
+
this.trace = previous;
|
|
231
|
+
});
|
|
232
|
+
if (status === "unknown") status = "ok";
|
|
233
|
+
span.end();
|
|
234
|
+
return result;
|
|
235
|
+
} finally {
|
|
236
|
+
if (this.trace?.spanId === trace.spanId) this.trace = previous;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
flush() {
|
|
240
|
+
return this.transport?.flush() ?? Promise.resolve();
|
|
241
|
+
}
|
|
242
|
+
async close() {
|
|
243
|
+
for (const cleanup of this.cleanups.splice(0)) cleanup();
|
|
244
|
+
await this.transport?.close();
|
|
245
|
+
}
|
|
246
|
+
trackScreen(name, params) {
|
|
247
|
+
this.addBreadcrumb({ category: "navigation", message: name, data: params });
|
|
248
|
+
}
|
|
249
|
+
base(type) {
|
|
250
|
+
return { eventId: id(), type, timestamp: Date.now(), platform: "expo", user: applyUser(this.user, this.config.sendDefaultPii), tags: { ...this.tags }, contexts: sanitize({ ...this.contexts, device: { brand: Device.brand, model: Device.modelName, deviceType: Device.deviceType, os: Platform.OS, osVersion: Platform.Version, isDevice: Device.isDevice }, app: { id: Application.applicationId, name: Application.applicationName, version: Application.nativeApplicationVersion, build: Application.nativeBuildVersion, runtimeVersion: Constants.expoConfig?.runtimeVersion } }, this.config.redactKeys), breadcrumbs: [...this.breadcrumbs], trace: this.trace };
|
|
251
|
+
}
|
|
252
|
+
async capture(raw) {
|
|
253
|
+
if (!this.transport) return;
|
|
254
|
+
try {
|
|
255
|
+
let event = sanitize(raw, this.config.redactKeys);
|
|
256
|
+
if (this.beforeSendHook) event = await this.beforeSendHook(event);
|
|
257
|
+
if (event) this.transport.enqueue(event);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
if (this.config.debug) console.warn("[SmartObserve] event dropped", error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
setup(endpoint) {
|
|
263
|
+
const original = typeof ErrorUtils !== "undefined" ? ErrorUtils.getGlobalHandler() : void 0;
|
|
264
|
+
if (original && typeof ErrorUtils !== "undefined") {
|
|
265
|
+
ErrorUtils.setGlobalHandler((error, fatal) => {
|
|
266
|
+
this.captureException(error, { mechanism: "react-native.global", fatal });
|
|
267
|
+
if (fatal) void this.capture({ ...this.base("session"), sessionId: this.sessionId, startedAt: this.sessionStarted, endedAt: Date.now(), status: "crashed" });
|
|
268
|
+
original(error, fatal);
|
|
269
|
+
});
|
|
270
|
+
this.cleanups.push(() => ErrorUtils.setGlobalHandler(original));
|
|
271
|
+
}
|
|
272
|
+
if (this.config.trackLifecycle !== false) {
|
|
273
|
+
const sub = AppState.addEventListener("change", (state) => {
|
|
274
|
+
this.addBreadcrumb({ category: "app.lifecycle", message: state });
|
|
275
|
+
if (state === "active") void this.flush();
|
|
276
|
+
if (state === "background" || state === "inactive") void this.capture({ ...this.base("session"), sessionId: this.sessionId, startedAt: this.sessionStarted, endedAt: Date.now(), status: "exited" });
|
|
277
|
+
});
|
|
278
|
+
this.cleanups.push(() => sub.remove());
|
|
279
|
+
}
|
|
280
|
+
if (this.config.trackNetwork !== false) this.patchFetch(endpoint);
|
|
281
|
+
void this.capture({ ...this.base("session"), sessionId: this.sessionId, startedAt: this.sessionStarted, status: "active" });
|
|
282
|
+
}
|
|
283
|
+
patchFetch(endpoint) {
|
|
284
|
+
const original = globalThis.fetch;
|
|
285
|
+
const client2 = this;
|
|
286
|
+
globalThis.fetch = async function(input, init) {
|
|
287
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
288
|
+
if (url.startsWith(endpoint) || init?.headers && new Headers(init.headers).has("x-smartobserve-internal")) return original(input, init);
|
|
289
|
+
const started = Date.now();
|
|
290
|
+
const method = init?.method ?? (typeof input === "object" && "method" in input ? input.method : "GET");
|
|
291
|
+
client2.addBreadcrumb({ category: "http", message: `${method} ${safeUrl(url)}`, data: { method, url: safeUrl(url) } });
|
|
292
|
+
const headers = new Headers(init?.headers ?? (typeof input === "object" && "headers" in input ? input.headers : void 0));
|
|
293
|
+
if (matches(url, client2.config.allowTracePropagationTargets) && client2.trace) headers.set("traceparent", `00-${client2.trace.traceId}-${client2.trace.spanId}-${client2.trace.sampled ? "01" : "00"}`);
|
|
294
|
+
try {
|
|
295
|
+
const response = await original(input, { ...init, headers });
|
|
296
|
+
void client2.capture({ ...client2.base("performance"), metric: "http.client.duration", value: Date.now() - started, unit: "ms", route: safeUrl(url), metadata: { method, status: response.status } });
|
|
297
|
+
return response;
|
|
298
|
+
} catch (error) {
|
|
299
|
+
client2.captureException(error, { mechanism: "fetch", url: safeUrl(url), method });
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
this.cleanups.push(() => {
|
|
304
|
+
globalThis.fetch = original;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
function matches(value, patterns) {
|
|
309
|
+
return patterns?.some((pattern) => typeof pattern === "string" ? value.startsWith(pattern) : pattern.test(value)) ?? false;
|
|
310
|
+
}
|
|
311
|
+
function serialize(error) {
|
|
312
|
+
if (!(error instanceof Error)) return { type: typeof error, value: typeof error === "string" ? error : String(error), handled: true, mechanism: "manual" };
|
|
313
|
+
return { type: error.name, value: error.message, handled: true, mechanism: "manual", stacktrace: (error.stack ?? "").split("\n").slice(1, 101).map((line) => {
|
|
314
|
+
const match = line.match(/(?:at\s+)?(?:(.*?)\s+\()?(.+?):(\d+):(\d+)\)?$/);
|
|
315
|
+
return match ? { function: match[1] || "<anonymous>", filename: match[2], line: Number(match[3]), column: Number(match[4]), inApp: !match[2]?.includes("node_modules") } : { function: line.trim() };
|
|
316
|
+
}) };
|
|
317
|
+
}
|
|
318
|
+
function applyUser(user, sendDefaultPii) {
|
|
319
|
+
if (!user) return void 0;
|
|
320
|
+
if (sendDefaultPii) return user;
|
|
321
|
+
return { id: user.id, username: user.username };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// src/index.ts
|
|
325
|
+
var client;
|
|
326
|
+
var SmartObserve = { init(config = {}) {
|
|
327
|
+
return client ??= new SmartObserveExpoClient(config);
|
|
328
|
+
}, captureException(error, hint) {
|
|
329
|
+
return active().captureException(error, hint);
|
|
330
|
+
}, captureMessage(message, level, metadata) {
|
|
331
|
+
return active().captureMessage(message, level, metadata);
|
|
332
|
+
}, addBreadcrumb(value) {
|
|
333
|
+
active().addBreadcrumb(value);
|
|
334
|
+
}, setUser(value) {
|
|
335
|
+
active().setUser(value);
|
|
336
|
+
}, setTag(key, value) {
|
|
337
|
+
active().setTag(key, value);
|
|
338
|
+
}, setContext(key, value) {
|
|
339
|
+
active().setContext(key, value);
|
|
340
|
+
}, startSpan(options, callback) {
|
|
341
|
+
return active().startSpan(options, callback);
|
|
342
|
+
}, trackScreen(name, params) {
|
|
343
|
+
active().trackScreen(name, params);
|
|
344
|
+
}, beforeSend(callback) {
|
|
345
|
+
active().beforeSend(callback);
|
|
346
|
+
}, flush() {
|
|
347
|
+
return active().flush();
|
|
348
|
+
}, async close() {
|
|
349
|
+
await client?.close();
|
|
350
|
+
client = void 0;
|
|
351
|
+
}, get log() {
|
|
352
|
+
return active().log;
|
|
353
|
+
} };
|
|
354
|
+
function active() {
|
|
355
|
+
return client ??= new SmartObserveExpoClient({ enabled: false });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export {
|
|
359
|
+
SmartObserveExpoClient,
|
|
360
|
+
SmartObserve
|
|
361
|
+
};
|
|
362
|
+
//# sourceMappingURL=chunk-HDKJGFST.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/internal.ts","../src/transport.ts","../src/sdk-meta.ts","../src/index.ts"],"sourcesContent":["import { AppState, Platform } from \"react-native\";\nimport * as Application from \"expo-application\";\nimport Constants from \"expo-constants\";\nimport * as Device from \"expo-device\";\nimport { id, parseDsn, randomHex, safeUrl, sanitize } from \"./internal.js\";\nimport { ExpoTransport } from \"./transport.js\";\nimport type { BeforeSend, Breadcrumb, Level, ObserveEvent, ObserveSpan, ObserveUser, SmartObserveConfig, SpanOptions, TraceContext } from \"./types.js\";\n\ndeclare const ErrorUtils: { getGlobalHandler(): (error: Error, fatal?: boolean) => void; setGlobalHandler(handler: (error: Error, fatal?: boolean) => void): void } | undefined;\n\nexport class SmartObserveExpoClient {\n private config: SmartObserveConfig & { environment: string; enabled: boolean; debug: boolean; tracesSampleRate: number; errorSampleRate: number; maxBreadcrumbs: number }; private transport?: ExpoTransport; private user?: ObserveUser; private tags: Record<string, string> = {}; private contexts: Record<string, unknown> = {}; private breadcrumbs: Breadcrumb[] = []; private trace?: TraceContext; private beforeSendHook?: BeforeSend; private cleanups: Array<() => void> = []; private sessionId = id(); private sessionStarted = Date.now();\n constructor(input: SmartObserveConfig = {}) { this.config = { ...input, environment: input.environment ?? \"production\", enabled: input.enabled ?? Boolean(input.dsn), debug: input.debug ?? false, tracesSampleRate: input.tracesSampleRate ?? input.sampleRate ?? 0.1, errorSampleRate: input.errorSampleRate ?? 1, maxBreadcrumbs: input.maxBreadcrumbs ?? 100 }; this.beforeSendHook = input.beforeSend; if (this.config.enabled && input.dsn) { const parsed = parseDsn(input.dsn); this.transport = new ExpoTransport({ ...parsed, environment: this.config.environment, release: input.release ?? Application.nativeApplicationVersion ?? undefined, dist: input.dist ?? Application.nativeBuildVersion ?? undefined, maxQueueSize: input.maxQueueSize ?? 1_000, flushIntervalMs: input.flushIntervalMs ?? 5_000, debug: this.config.debug }); this.setup(parsed.endpoint); } }\n captureException(error: unknown, hint: Record<string, unknown> = {}) { const eventId = id(); if (Math.random() <= this.config.errorSampleRate) { const exception = serialize(error); void this.capture({ ...this.base(\"error\"), eventId, message: exception.value, level: \"error\", exceptions: [exception], metadata: hint }); } return eventId; }\n captureMessage(message: string, level: Level = \"info\", metadata?: Record<string, unknown>) { const eventId = id(); void this.capture({ ...this.base(\"log\"), eventId, message, level, metadata }); return eventId; }\n log = { debug: (m: string, d?: Record<string, unknown>) => this.captureMessage(m, \"debug\", d), info: (m: string, d?: Record<string, unknown>) => this.captureMessage(m, \"info\", d), warn: (m: string, d?: Record<string, unknown>) => this.captureMessage(m, \"warn\", d), error: (m: string, d?: Record<string, unknown>) => this.captureMessage(m, \"error\", d) };\n addBreadcrumb(value: Breadcrumb) { this.breadcrumbs.push({ ...sanitize(value, this.config.redactKeys), timestamp: value.timestamp ?? Date.now() }); this.breadcrumbs.splice(0, Math.max(0, this.breadcrumbs.length - this.config.maxBreadcrumbs)); }\n setUser(value: ObserveUser | null) { this.user = value ? sanitize(value, this.config.redactKeys) : undefined; }\n setTag(key: string, value: string | null) { if (value === null) delete this.tags[key]; else this.tags[key.slice(0, 64)] = value.slice(0, 256); }\n setContext(key: string, value: Record<string, unknown> | null) { if (value === null) delete this.contexts[key]; else this.contexts[key] = sanitize(value, this.config.redactKeys); }\n beforeSend(callback: BeforeSend) { this.beforeSendHook = callback; }\n startSpan<T>(options: SpanOptions, callback: (span: ObserveSpan) => T | Promise<T>): T | Promise<T> { const previous = this.trace; const trace: TraceContext = { traceId: options.parent?.traceId ?? previous?.traceId ?? randomHex(16), spanId: randomHex(8), parentSpanId: options.parent?.spanId ?? previous?.spanId, sampled: options.sampled ?? previous?.sampled ?? Math.random() < this.config.tracesSampleRate }; const start = Date.now(); const attributes = { ...(options.attributes ?? {}) }; let status: \"ok\" | \"error\" | \"cancelled\" | \"unknown\" = \"unknown\"; let ended = false; const span: ObserveSpan = { traceId: trace.traceId, spanId: trace.spanId, setAttribute: (key, value) => { attributes[key] = value; }, setStatus: (value) => { status = value; }, recordException: (error) => { status = \"error\"; this.captureException(error); }, end: () => { if (!ended && trace.sampled) { ended = true; const isRoot = !trace.parentSpanId; void this.capture({ ...this.base(isRoot ? \"transaction\" : \"span\"), name: options.name, operation: options.operation, startTimestamp: start, endTimestamp: Date.now(), status, ...(isRoot ? {} : { attributes }), trace }); } } }; this.trace = trace; try { const result = callback(span); if (result && typeof (result as Promise<T>).then === \"function\") return (result as Promise<T>).then((value) => { if (status === \"unknown\") status = \"ok\"; span.end(); return value; }, (error) => { span.recordException(error); span.end(); throw error; }).finally(() => { this.trace = previous; }); if (status === \"unknown\") status = \"ok\"; span.end(); return result; } finally { if (this.trace?.spanId === trace.spanId) this.trace = previous; } }\n flush() { return this.transport?.flush() ?? Promise.resolve(); } async close() { for (const cleanup of this.cleanups.splice(0)) cleanup(); await this.transport?.close(); }\n trackScreen(name: string, params?: Record<string, unknown>) { this.addBreadcrumb({ category: \"navigation\", message: name, data: params }); }\n private base(type: ObserveEvent[\"type\"]): ObserveEvent { return { eventId: id(), type, timestamp: Date.now(), platform: \"expo\", user: applyUser(this.user, this.config.sendDefaultPii), tags: { ...this.tags }, contexts: sanitize({ ...this.contexts, device: { brand: Device.brand, model: Device.modelName, deviceType: Device.deviceType, os: Platform.OS, osVersion: Platform.Version, isDevice: Device.isDevice }, app: { id: Application.applicationId, name: Application.applicationName, version: Application.nativeApplicationVersion, build: Application.nativeBuildVersion, runtimeVersion: Constants.expoConfig?.runtimeVersion } }, this.config.redactKeys), breadcrumbs: [...this.breadcrumbs], trace: this.trace }; }\n private async capture(raw: ObserveEvent) { if (!this.transport) return; try { let event = sanitize(raw, this.config.redactKeys); if (this.beforeSendHook) event = await this.beforeSendHook(event) as ObserveEvent; if (event) this.transport.enqueue(event); } catch (error) { if (this.config.debug) console.warn(\"[SmartObserve] event dropped\", error); } }\n private setup(endpoint: string) { const original = typeof ErrorUtils !== \"undefined\" ? ErrorUtils.getGlobalHandler() : undefined; if (original && typeof ErrorUtils !== \"undefined\") { ErrorUtils.setGlobalHandler((error, fatal) => { this.captureException(error, { mechanism: \"react-native.global\", fatal }); if (fatal) void this.capture({ ...this.base(\"session\"), sessionId: this.sessionId, startedAt: this.sessionStarted, endedAt: Date.now(), status: \"crashed\" }); original(error, fatal); }); this.cleanups.push(() => ErrorUtils.setGlobalHandler(original)); } if (this.config.trackLifecycle !== false) { const sub = AppState.addEventListener(\"change\", (state) => { this.addBreadcrumb({ category: \"app.lifecycle\", message: state }); if (state === \"active\") void this.flush(); if (state === \"background\" || state === \"inactive\") void this.capture({ ...this.base(\"session\"), sessionId: this.sessionId, startedAt: this.sessionStarted, endedAt: Date.now(), status: \"exited\" }); }); this.cleanups.push(() => sub.remove()); } if (this.config.trackNetwork !== false) this.patchFetch(endpoint); void this.capture({ ...this.base(\"session\"), sessionId: this.sessionId, startedAt: this.sessionStarted, status: \"active\" }); }\n private patchFetch(endpoint: string) { const original = globalThis.fetch; const client = this; globalThis.fetch = async function(input: RequestInfo | URL, init?: RequestInit) { const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url; if (url.startsWith(endpoint) || (init?.headers && new Headers(init.headers as HeadersInit).has(\"x-smartobserve-internal\"))) return original(input as RequestInfo, init); const started = Date.now(); const method = init?.method ?? (typeof input === \"object\" && \"method\" in input ? (input as Request).method : \"GET\"); client.addBreadcrumb({ category: \"http\", message: `${method} ${safeUrl(url)}`, data: { method, url: safeUrl(url) } }); const headers = new Headers(init?.headers ?? (typeof input === \"object\" && \"headers\" in input ? (input as Request).headers : undefined)); if (matches(url, client.config.allowTracePropagationTargets) && client.trace) headers.set(\"traceparent\", `00-${client.trace.traceId}-${client.trace.spanId}-${client.trace.sampled ? \"01\" : \"00\"}`); try { const response = await original(input as RequestInfo, { ...init, headers }); void client.capture({ ...client.base(\"performance\"), metric: \"http.client.duration\", value: Date.now() - started, unit: \"ms\", route: safeUrl(url), metadata: { method, status: response.status } }); return response; } catch (error) { client.captureException(error, { mechanism: \"fetch\", url: safeUrl(url), method }); throw error; } } as typeof fetch; this.cleanups.push(() => { globalThis.fetch = original; }); }\n}\nfunction matches(value: string, patterns?: Array<string | RegExp>) { return patterns?.some((pattern) => typeof pattern === \"string\" ? value.startsWith(pattern) : pattern.test(value)) ?? false; }\nfunction serialize(error: unknown) { if (!(error instanceof Error)) return { type: typeof error, value: typeof error === \"string\" ? error : String(error), handled: true, mechanism: \"manual\" }; return { type: error.name, value: error.message, handled: true, mechanism: \"manual\", stacktrace: (error.stack ?? \"\").split(\"\\n\").slice(1, 101).map((line) => { const match = line.match(/(?:at\\s+)?(?:(.*?)\\s+\\()?(.+?):(\\d+):(\\d+)\\)?$/); return match ? { function: match[1] || \"<anonymous>\", filename: match[2], line: Number(match[3]), column: Number(match[4]), inApp: !match[2]?.includes(\"node_modules\") } : { function: line.trim() }; }) }; }\nfunction applyUser(user: ObserveUser | undefined, sendDefaultPii?: boolean) { if (!user) return undefined; if (sendDefaultPii) return user; return { id: user.id, username: user.username }; }\n","export function id() { return crypto.randomUUID(); }\nexport function randomHex(bytes: number) { return [...crypto.getRandomValues(new Uint8Array(bytes))].map((value) => value.toString(16).padStart(2, \"0\")).join(\"\"); }\nexport function parseDsn(value: string) { const url = new URL(value); const key = decodeURIComponent(url.username); if (!/^(pk|sk)_[A-Za-z0-9_-]{16,}$/.test(key)) throw new Error(\"SmartObserve DSN contains an invalid project key\"); const slug = url.pathname.replace(/^\\/+|\\/+$/g, \"\"); if (!slug) throw new Error(\"SmartObserve DSN must include a project slug\"); return { endpoint: `${url.protocol}//${url.host}/api/v1/envelope`, key }; }\nconst BLOCKED = new Set([\"password\", \"passcode\", \"authorization\", \"cookie\", \"access_token\", \"refresh_token\", \"api_key\", \"secret\", \"private_key\", \"card_number\", \"cvv\", \"cvc\"]);\nexport function sanitize<T>(input: T, extra: string[] = []): T { const blocked = new Set([...BLOCKED, ...extra.map((value) => value.toLowerCase())]); const seen = new WeakSet<object>(); const walk = (value: unknown, depth: number): unknown => { if (depth > 5) return \"[Truncated]\"; if (typeof value === \"string\") return value.slice(0, 8_192); if (value === null || typeof value !== \"object\") return value; if (seen.has(value)) return \"[Circular]\"; seen.add(value); if (Array.isArray(value)) return value.slice(0, 200).map((entry) => walk(entry, depth + 1)); return Object.fromEntries(Object.entries(value).slice(0, 200).map(([key, entry]) => [key, blocked.has(key.toLowerCase().replace(/[\\s-]+/g, \"_\")) ? \"[Redacted]\" : walk(entry, depth + 1)])); }; return walk(input, 0) as T; }\nexport function safeUrl(value: string) { try { const url = new URL(value); url.username = \"\"; url.password = \"\"; for (const key of [...url.searchParams.keys()]) url.searchParams.set(key, \"[Filtered]\"); return url.toString(); } catch { return \"[Invalid URL]\"; } }\n","import AsyncStorage from \"@react-native-async-storage/async-storage\";\nimport NetInfo from \"@react-native-community/netinfo\";\nimport type { ObserveEvent } from \"./types.js\";\nimport { id } from \"./internal.js\";\nimport { SDK_NAME, SDK_VERSION } from \"./sdk-meta.js\";\nconst STORAGE_KEY = \"@smartobserve/events/v1\";\ninterface Options { endpoint: string; key: string; environment: string; release?: string; dist?: string; maxQueueSize: number; flushIntervalMs: number; debug: boolean }\nexport class ExpoTransport {\n private queue: ObserveEvent[] = []; private timer?: ReturnType<typeof setInterval>; private flushing?: Promise<void>; private online = true; private unsubscribe?: () => void; private closed = false;\n constructor(private readonly options: Options) { this.timer = setInterval(() => void this.flush(), options.flushIntervalMs); this.unsubscribe = NetInfo.addEventListener((state) => { this.online = state.isConnected !== false; if (this.online) void this.flush(); }); void this.restore(); }\n enqueue(event: ObserveEvent) { if (this.closed) return; if (this.queue.length >= this.options.maxQueueSize) this.evict(); this.queue.push(event); void this.persist(); if (this.queue.length >= 50 || event.level === \"fatal\") void this.flush(); }\n async flush() { if (this.flushing || !this.queue.length || !this.online) return this.flushing; const events = this.queue.splice(0, 50); this.flushing = this.send(events).then(() => this.persist()).catch((error) => { this.queue.unshift(...events); void this.persist(); if (this.options.debug) console.warn(\"[SmartObserve] offline batch retained\", error); }).finally(() => { this.flushing = undefined; }); return this.flushing; }\n async close() { this.closed = true; if (this.timer) clearInterval(this.timer); this.unsubscribe?.(); await this.flush(); }\n private async restore() { try { const raw = await AsyncStorage.getItem(STORAGE_KEY); if (!raw) return; const stored = JSON.parse(raw) as Array<ObserveEvent & { _storedAt?: number }>; this.queue.push(...stored.filter((event) => Date.now() - (event._storedAt ?? event.timestamp) < 7 * 86_400_000).slice(-this.options.maxQueueSize)); await this.flush(); } catch (error) { if (this.options.debug) console.warn(\"[SmartObserve] offline queue unavailable\", error); } }\n private async persist(): Promise<void> { try { let prepared = this.queue.map((event) => ({ ...event, _storedAt: Date.now() })); let raw = JSON.stringify(prepared); while (raw.length > 10 * 1024 * 1024 && this.queue.length) { this.evict(); prepared = this.queue.map((event) => ({ ...event, _storedAt: Date.now() })); raw = JSON.stringify(prepared); } await AsyncStorage.setItem(STORAGE_KEY, raw); } catch (error) { if (this.options.debug) console.warn(\"[SmartObserve] could not persist offline queue\", error); } }\n private async send(events: ObserveEvent[]) { const body = JSON.stringify({ schemaVersion: 1, envelopeId: id(), sentAt: Date.now(), sdk: { name: SDK_NAME, version: SDK_VERSION }, environment: this.options.environment, release: this.options.release, dist: this.options.dist, events }); let attempt = 0; while (true) { const response = await fetch(this.options.endpoint, { method: \"POST\", headers: { \"content-type\": \"application/json\", \"origin\": \"smartobserve-expo://native\", \"x-smartobserve-key\": this.options.key, \"x-smartobserve-platform\": \"expo\", \"x-smartobserve-internal\": \"transport\" }, body }); if (response.ok) return; if (![408, 429].includes(response.status) && response.status < 500) return; if (attempt++ >= 5) throw new Error(`HTTP ${response.status}`); await new Promise((resolve) => setTimeout(resolve, Math.random() * Math.min(30_000, 2 ** attempt * 1_000))); } }\n private evict() { const index = this.queue.findIndex((event) => event.type === \"span\" || event.type === \"performance\" || event.level === \"debug\"); this.queue.splice(index < 0 ? 0 : index, 1); }\n}\n","export const SDK_NAME = \"@smarthivelabs-devs/observe-expo\";\nexport const SDK_VERSION = \"26.1.0\";\n","import { SmartObserveExpoClient } from \"./client.js\";\nimport type { SmartObserveConfig } from \"./types.js\";\nlet client: SmartObserveExpoClient | undefined;\nexport const SmartObserve = { init(config: SmartObserveConfig = {}) { return client ??= new SmartObserveExpoClient(config); }, captureException(error: unknown, hint?: Record<string, unknown>) { return active().captureException(error, hint); }, captureMessage(message: string, level?: import(\"./types.js\").Level, metadata?: Record<string, unknown>) { return active().captureMessage(message, level, metadata); }, addBreadcrumb(value: import(\"./types.js\").Breadcrumb) { active().addBreadcrumb(value); }, setUser(value: import(\"./types.js\").ObserveUser | null) { active().setUser(value); }, setTag(key: string, value: string | null) { active().setTag(key, value); }, setContext(key: string, value: Record<string, unknown> | null) { active().setContext(key, value); }, startSpan<T>(options: import(\"./types.js\").SpanOptions, callback: (span: import(\"./types.js\").ObserveSpan) => T | Promise<T>) { return active().startSpan(options, callback); }, trackScreen(name: string, params?: Record<string, unknown>) { active().trackScreen(name, params); }, beforeSend(callback: import(\"./types.js\").BeforeSend) { active().beforeSend(callback); }, flush() { return active().flush(); }, async close() { await client?.close(); client = undefined; }, get log() { return active().log; } };\nfunction active() { return client ??= new SmartObserveExpoClient({ enabled: false }); }\nexport { SmartObserveExpoClient } from \"./client.js\"; export * from \"./types.js\";\n"],"mappings":"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","names":["client"]}
|