@catdoes/watch 1.2.0 → 2.0.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 +126 -128
- package/dist/expo-router.d.mts +15 -0
- package/dist/expo-router.d.ts +15 -0
- package/dist/expo-router.js +1 -0
- package/dist/expo-router.mjs +1 -0
- package/dist/index.d.mts +69 -315
- package/dist/index.d.ts +69 -315
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/react.d.mts +2 -30
- package/dist/react.d.ts +2 -30
- package/dist/react.js +1 -1
- package/dist/react.mjs +1 -1
- package/package.json +48 -13
package/dist/index.d.ts
CHANGED
|
@@ -1,431 +1,202 @@
|
|
|
1
1
|
export { WatchErrorBoundary, WatchErrorBoundaryProps, withWatchErrorBoundary } from './react.js';
|
|
2
2
|
import 'react';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* and other data used by the Watch SDK.
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* Minimal async key-value storage used to persist the event queue across
|
|
12
|
-
* launches. Compatible with `@react-native-async-storage/async-storage`'s
|
|
13
|
-
* default export and `window.localStorage`-style wrappers.
|
|
14
|
-
*
|
|
15
|
-
* The SDK never imports a storage module itself — dynamic `require()` of
|
|
16
|
-
* optional dependencies is reported as a fatal error by Metro in release
|
|
17
|
-
* builds. Pass an implementation explicitly:
|
|
18
|
-
*
|
|
19
|
-
* ```ts
|
|
20
|
-
* import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
21
|
-
* initCatDoesWatch({ apiKey, storage: AsyncStorage });
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
interface WatchStorage {
|
|
25
|
-
getItem(key: string): Promise<string | null>;
|
|
26
|
-
setItem(key: string, value: string): Promise<void>;
|
|
27
|
-
removeItem(key: string): Promise<void>;
|
|
4
|
+
interface QueueStore {
|
|
5
|
+
read(): string | null;
|
|
6
|
+
write(value: string): void;
|
|
7
|
+
remove(): void;
|
|
28
8
|
}
|
|
29
|
-
|
|
30
|
-
* Configuration options for initializing the Watch client.
|
|
31
|
-
*/
|
|
9
|
+
|
|
32
10
|
interface WatchConfig {
|
|
33
|
-
/**
|
|
34
|
-
* The API key for authenticating with CatDoes Watch.
|
|
35
|
-
* Format: cd_watch_xxxxx
|
|
36
|
-
*/
|
|
37
11
|
apiKey: string;
|
|
38
|
-
/**
|
|
39
|
-
* The endpoint URL for the ingestion API.
|
|
40
|
-
* @default "https://app.catdoes.com/api/watch/ingest"
|
|
41
|
-
*/
|
|
42
12
|
endpoint?: string;
|
|
43
|
-
/**
|
|
44
|
-
* The environment to report errors for.
|
|
45
|
-
* Auto-detected from __DEV__ if not specified.
|
|
46
|
-
* @default Auto-detected
|
|
47
|
-
*/
|
|
48
13
|
environment?: "development" | "production";
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
* This can be noisy and is disabled by default.
|
|
52
|
-
* @default false
|
|
53
|
-
*/
|
|
14
|
+
debug?: boolean;
|
|
15
|
+
installGlobalHandlers?: boolean;
|
|
54
16
|
captureConsoleErrors?: boolean;
|
|
55
|
-
|
|
56
|
-
* Maximum number of breadcrumbs to store.
|
|
57
|
-
* @default 20
|
|
58
|
-
*/
|
|
17
|
+
captureHttpBreadcrumbs?: boolean;
|
|
59
18
|
maxBreadcrumbs?: number;
|
|
60
|
-
/**
|
|
61
|
-
* Maximum number of events to buffer before flushing.
|
|
62
|
-
* @default 10
|
|
63
|
-
*/
|
|
64
19
|
maxBufferSize?: number;
|
|
65
|
-
/**
|
|
66
|
-
* Interval in milliseconds between automatic flushes.
|
|
67
|
-
* @default 5000
|
|
68
|
-
*/
|
|
69
20
|
flushInterval?: number;
|
|
70
|
-
|
|
71
|
-
* Callback invoked before sending an event.
|
|
72
|
-
* Return null to drop the event, or modify and return it.
|
|
73
|
-
*/
|
|
21
|
+
fatalFlushTimeoutMs?: number;
|
|
74
22
|
beforeSend?: (event: WatchEvent) => WatchEvent | null;
|
|
75
|
-
/**
|
|
76
|
-
* Enable debug logging to console.
|
|
77
|
-
* @default false
|
|
78
|
-
*/
|
|
79
|
-
debug?: boolean;
|
|
80
|
-
/**
|
|
81
|
-
* Initial context to attach to all events.
|
|
82
|
-
*/
|
|
83
23
|
initialContext?: Record<string, unknown>;
|
|
84
|
-
/**
|
|
85
|
-
* Time window in milliseconds to consider errors as duplicates.
|
|
86
|
-
* Errors with the same key occurring within this window will be deduplicated.
|
|
87
|
-
* @default 5000
|
|
88
|
-
*/
|
|
89
24
|
dedupWindowMs?: number;
|
|
90
|
-
/**
|
|
91
|
-
* Maximum number of recent error keys to keep in memory for deduplication.
|
|
92
|
-
* When exceeded, the oldest keys will be evicted.
|
|
93
|
-
* @default 500
|
|
94
|
-
*/
|
|
95
25
|
dedupMaxEntries?: number;
|
|
96
|
-
|
|
97
|
-
* Storage implementation used to persist queued events across launches
|
|
98
|
-
* (e.g. AsyncStorage). Persistence is disabled when omitted.
|
|
99
|
-
*/
|
|
100
|
-
storage?: WatchStorage;
|
|
26
|
+
storage?: QueueStore;
|
|
101
27
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Required configuration with defaults applied.
|
|
104
|
-
*/
|
|
105
28
|
interface WatchConfigResolved {
|
|
106
29
|
apiKey: string;
|
|
107
30
|
endpoint: string;
|
|
108
31
|
environment: "development" | "production";
|
|
32
|
+
debug: boolean;
|
|
33
|
+
installGlobalHandlers: boolean;
|
|
109
34
|
captureConsoleErrors: boolean;
|
|
35
|
+
captureHttpBreadcrumbs: boolean;
|
|
110
36
|
maxBreadcrumbs: number;
|
|
111
37
|
maxBufferSize: number;
|
|
112
38
|
flushInterval: number;
|
|
39
|
+
fatalFlushTimeoutMs: number;
|
|
113
40
|
beforeSend: (event: WatchEvent) => WatchEvent | null;
|
|
114
|
-
debug: boolean;
|
|
115
41
|
dedupWindowMs: number;
|
|
116
42
|
dedupMaxEntries: number;
|
|
117
|
-
storage
|
|
43
|
+
storage?: QueueStore;
|
|
118
44
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Device and environment information collected automatically.
|
|
121
|
-
*/
|
|
122
45
|
interface DeviceInfo {
|
|
123
|
-
deviceModel?: string;
|
|
124
|
-
deviceName?: string;
|
|
125
|
-
deviceType?: string;
|
|
126
46
|
brand?: string;
|
|
127
47
|
manufacturer?: string;
|
|
128
48
|
modelName?: string;
|
|
49
|
+
modelId?: string;
|
|
50
|
+
designName?: string;
|
|
51
|
+
productName?: string;
|
|
52
|
+
deviceName?: string;
|
|
53
|
+
deviceType?: "unknown" | "phone" | "tablet" | "desktop" | "tv";
|
|
54
|
+
deviceYearClass?: number;
|
|
55
|
+
memoryTotal?: number;
|
|
56
|
+
supportedCpuArchitectures?: string[];
|
|
129
57
|
isDevice?: boolean;
|
|
130
|
-
isEmulator?: boolean;
|
|
131
|
-
isTablet?: boolean;
|
|
132
58
|
osName?: string;
|
|
133
59
|
osVersion?: string;
|
|
134
60
|
osBuildId?: string;
|
|
61
|
+
osInternalBuildId?: string;
|
|
62
|
+
osBuildFingerprint?: string;
|
|
135
63
|
platformApiLevel?: number;
|
|
64
|
+
appName?: string;
|
|
65
|
+
appSlug?: string;
|
|
136
66
|
appVersion?: string;
|
|
137
67
|
appBuildNumber?: string;
|
|
138
|
-
appName?: string;
|
|
139
68
|
bundleId?: string;
|
|
69
|
+
expoSdkVersion?: string;
|
|
140
70
|
runtimeVersion?: string;
|
|
71
|
+
expoRuntimeVersion?: string;
|
|
141
72
|
expoVersion?: string;
|
|
142
|
-
|
|
143
|
-
|
|
73
|
+
executionEnvironment?: string;
|
|
74
|
+
jsEngine?: "hermes" | "jsc" | "unknown";
|
|
144
75
|
screenWidth?: number;
|
|
145
76
|
screenHeight?: number;
|
|
146
77
|
screenScale?: number;
|
|
78
|
+
fontScale?: number;
|
|
147
79
|
locale?: string;
|
|
148
80
|
timezone?: string;
|
|
149
|
-
networkType?: string;
|
|
150
|
-
isConnected?: boolean;
|
|
151
81
|
browserName?: string;
|
|
152
82
|
browserVersion?: string;
|
|
153
83
|
userAgent?: string;
|
|
154
84
|
}
|
|
155
|
-
/**
|
|
156
|
-
* A breadcrumb representing an action or event before an error.
|
|
157
|
-
*/
|
|
158
85
|
interface Breadcrumb {
|
|
159
|
-
/**
|
|
160
|
-
* The type of breadcrumb.
|
|
161
|
-
*/
|
|
162
86
|
type: "navigation" | "ui" | "http" | "console" | "custom";
|
|
163
|
-
/**
|
|
164
|
-
* A human-readable message describing the breadcrumb.
|
|
165
|
-
*/
|
|
166
87
|
message: string;
|
|
167
|
-
/**
|
|
168
|
-
* ISO 8601 timestamp of when the breadcrumb was created.
|
|
169
|
-
*/
|
|
170
88
|
timestamp: string;
|
|
171
|
-
/**
|
|
172
|
-
* Additional data associated with the breadcrumb.
|
|
173
|
-
*/
|
|
174
89
|
data?: Record<string, unknown>;
|
|
175
90
|
}
|
|
176
|
-
/**
|
|
177
|
-
* An error event to be sent to CatDoes Watch.
|
|
178
|
-
*/
|
|
179
91
|
interface WatchEvent {
|
|
180
|
-
|
|
181
|
-
* The error message.
|
|
182
|
-
*/
|
|
92
|
+
eventId?: string;
|
|
183
93
|
message: string;
|
|
184
|
-
/**
|
|
185
|
-
* The stack trace of the error.
|
|
186
|
-
*/
|
|
187
94
|
stack?: string;
|
|
188
|
-
/**
|
|
189
|
-
* React component stack trace.
|
|
190
|
-
*/
|
|
191
95
|
componentStack?: string;
|
|
192
|
-
/**
|
|
193
|
-
* The filename where the error occurred.
|
|
194
|
-
*/
|
|
195
96
|
filename?: string;
|
|
196
|
-
/**
|
|
197
|
-
* The line number where the error occurred.
|
|
198
|
-
*/
|
|
199
97
|
lineno?: number;
|
|
200
|
-
/**
|
|
201
|
-
* The column number where the error occurred.
|
|
202
|
-
*/
|
|
203
98
|
colno?: number;
|
|
204
|
-
/**
|
|
205
|
-
* ISO 8601 timestamp of when the error occurred.
|
|
206
|
-
*/
|
|
207
99
|
timestamp: string;
|
|
208
|
-
/**
|
|
209
|
-
* The environment where the error occurred.
|
|
210
|
-
*/
|
|
211
100
|
environment: "development" | "production";
|
|
212
|
-
/**
|
|
213
|
-
* The platform where the error occurred.
|
|
214
|
-
*/
|
|
215
101
|
platform: "ios" | "android" | "web";
|
|
216
|
-
/**
|
|
217
|
-
* A unique identifier for the current session.
|
|
218
|
-
*/
|
|
219
102
|
sessionId: string;
|
|
220
|
-
/**
|
|
221
|
-
* Device and environment information.
|
|
222
|
-
*/
|
|
223
103
|
deviceInfo?: DeviceInfo;
|
|
224
|
-
/**
|
|
225
|
-
* Additional context data.
|
|
226
|
-
*/
|
|
227
104
|
extra?: Record<string, unknown>;
|
|
228
|
-
/**
|
|
229
|
-
* Breadcrumbs leading up to the error.
|
|
230
|
-
*/
|
|
231
105
|
breadcrumbs?: Breadcrumb[];
|
|
232
|
-
/**
|
|
233
|
-
* SDK version for debugging. Helps correlate reports across SDK releases.
|
|
234
|
-
*/
|
|
235
106
|
sdkVersion?: string;
|
|
236
107
|
}
|
|
237
|
-
/**
|
|
238
|
-
* Input for adding a breadcrumb (timestamp is auto-generated).
|
|
239
|
-
*/
|
|
240
108
|
type BreadcrumbInput = Omit<Breadcrumb, "timestamp">;
|
|
241
|
-
/**
|
|
242
|
-
* Response from the ingestion API.
|
|
243
|
-
*/
|
|
244
109
|
interface IngestResponse {
|
|
245
110
|
accepted?: number;
|
|
246
111
|
filtered?: boolean;
|
|
247
112
|
error?: string;
|
|
248
113
|
retryAfter?: number;
|
|
249
114
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
115
|
+
interface LastFlush {
|
|
116
|
+
at: string;
|
|
117
|
+
status: number | null;
|
|
118
|
+
accepted: number | null;
|
|
119
|
+
error?: string;
|
|
120
|
+
}
|
|
121
|
+
interface TransportStats {
|
|
122
|
+
disabledReason: "auth" | "failures" | null;
|
|
123
|
+
backoffUntil: number | null;
|
|
124
|
+
consecutiveFailures: number;
|
|
125
|
+
lastFlush: LastFlush | null;
|
|
126
|
+
}
|
|
127
|
+
interface WatchStats {
|
|
128
|
+
queueSize: number;
|
|
129
|
+
sessionId: string;
|
|
130
|
+
transport: TransportStats;
|
|
131
|
+
}
|
|
261
132
|
|
|
262
133
|
interface FlushOptions {
|
|
263
|
-
/**
|
|
264
|
-
* Hint browsers to allow the request to outlive the page lifecycle.
|
|
265
|
-
*/
|
|
266
134
|
keepalive?: boolean;
|
|
267
135
|
}
|
|
268
136
|
|
|
269
|
-
/**
|
|
270
|
-
* CatDoes Watch SDK - Main Client
|
|
271
|
-
*
|
|
272
|
-
* The primary interface for the CatDoes Watch error tracking SDK.
|
|
273
|
-
* Implements a singleton pattern for ease of use.
|
|
274
|
-
*/
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* The main CatDoes Watch client class.
|
|
278
|
-
*/
|
|
279
137
|
declare class WatchClient {
|
|
280
|
-
private
|
|
281
|
-
private
|
|
282
|
-
private
|
|
283
|
-
private breadcrumbs;
|
|
138
|
+
private readonly config;
|
|
139
|
+
private readonly transport;
|
|
140
|
+
private readonly breadcrumbs;
|
|
284
141
|
private context;
|
|
285
142
|
private user;
|
|
286
|
-
private
|
|
287
|
-
private recentErrors;
|
|
143
|
+
private readonly recentErrors;
|
|
288
144
|
private recentErrorsCleanupTimer;
|
|
289
145
|
private constructor();
|
|
290
|
-
/**
|
|
291
|
-
* Initializes the Watch client with the given configuration.
|
|
292
|
-
*/
|
|
293
146
|
static init(config: WatchConfig): WatchClient;
|
|
294
|
-
/**
|
|
295
|
-
* Gets the existing Watch client instance, or null if not initialized.
|
|
296
|
-
*/
|
|
297
147
|
static getInstance(): WatchClient | null;
|
|
298
|
-
|
|
299
|
-
* Captures an error and sends it to CatDoes Watch.
|
|
300
|
-
*/
|
|
301
|
-
captureError(error: Error, extra?: Record<string, unknown>): void;
|
|
302
|
-
/**
|
|
303
|
-
* Captures a message as an error.
|
|
304
|
-
*/
|
|
148
|
+
captureError(error: unknown, extra?: Record<string, unknown>): void;
|
|
305
149
|
captureMessage(message: string, level?: "info" | "warning" | "error"): void;
|
|
306
|
-
/**
|
|
307
|
-
* Adds a breadcrumb to the trail.
|
|
308
|
-
*/
|
|
309
150
|
addBreadcrumb(breadcrumb: BreadcrumbInput): void;
|
|
310
|
-
/**
|
|
311
|
-
* Sets a context value that will be attached to all future events.
|
|
312
|
-
*/
|
|
313
151
|
setContext(key: string, value: unknown): void;
|
|
314
|
-
/**
|
|
315
|
-
* Clears a context value.
|
|
316
|
-
*/
|
|
317
152
|
clearContext(key: string): void;
|
|
318
|
-
/**
|
|
319
|
-
* Sets user information to attach to events.
|
|
320
|
-
*/
|
|
321
153
|
setUser(user: {
|
|
322
154
|
id?: string;
|
|
323
155
|
[key: string]: unknown;
|
|
324
156
|
} | null): void;
|
|
325
|
-
/**
|
|
326
|
-
* Flushes all queued events immediately.
|
|
327
|
-
*/
|
|
328
157
|
flush(options?: FlushOptions): Promise<void>;
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
158
|
+
flushWithTimeout(timeoutMs: number): Promise<void>;
|
|
159
|
+
commit(): void;
|
|
160
|
+
getStats(): WatchStats;
|
|
332
161
|
getConfig(): Readonly<WatchConfigResolved>;
|
|
333
|
-
/**
|
|
334
|
-
* Checks if the client is initialized and ready to capture events.
|
|
335
|
-
*/
|
|
336
162
|
get initialized(): boolean;
|
|
337
163
|
private buildEvent;
|
|
338
|
-
private shouldCapture;
|
|
339
164
|
private ensureStack;
|
|
340
165
|
private getErrorKey;
|
|
341
|
-
private markErrorAsSeen;
|
|
342
166
|
private hasSeenErrorRecently;
|
|
167
|
+
private markErrorAsSeen;
|
|
343
168
|
private scheduleRecentErrorsCleanup;
|
|
344
|
-
private pruneRecentErrors;
|
|
345
169
|
}
|
|
346
|
-
/**
|
|
347
|
-
* Static interface for convenience methods.
|
|
348
|
-
*/
|
|
349
170
|
declare const Watch: {
|
|
350
171
|
init(config: WatchConfig): WatchClient;
|
|
351
172
|
getInstance(): WatchClient | null;
|
|
352
|
-
captureError(error:
|
|
173
|
+
captureError(error: unknown, extra?: Record<string, unknown>): void;
|
|
353
174
|
captureMessage(message: string, level?: "info" | "warning" | "error"): void;
|
|
354
175
|
addBreadcrumb(breadcrumb: BreadcrumbInput): void;
|
|
355
176
|
setContext(key: string, value: unknown): void;
|
|
177
|
+
clearContext(key: string): void;
|
|
356
178
|
setUser(user: {
|
|
357
179
|
id?: string;
|
|
358
180
|
[key: string]: unknown;
|
|
359
181
|
} | null): void;
|
|
360
182
|
flush(options?: FlushOptions): Promise<void>;
|
|
183
|
+
flushWithTimeout(timeoutMs: number): Promise<void>;
|
|
184
|
+
getStats(): WatchStats | null;
|
|
361
185
|
};
|
|
362
186
|
|
|
363
|
-
/**
|
|
364
|
-
* CatDoes Watch SDK - Global Error Handlers
|
|
365
|
-
*
|
|
366
|
-
* Sets up global error handlers to automatically capture unhandled errors.
|
|
367
|
-
* Supports both web (window.onerror) and React Native (ErrorUtils).
|
|
368
|
-
*/
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Sets up global error handlers for the given Watch client.
|
|
372
|
-
*/
|
|
373
187
|
declare function setupGlobalHandlers(client: WatchClient): void;
|
|
374
|
-
/**
|
|
375
|
-
* Sets up console.error interception (optional, can be noisy).
|
|
376
|
-
*/
|
|
377
|
-
declare function setupConsoleErrorCapture(client: WatchClient): void;
|
|
378
|
-
/**
|
|
379
|
-
* Removes all installed global handlers.
|
|
380
|
-
*/
|
|
381
188
|
declare function removeGlobalHandlers(): void;
|
|
382
189
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
* Generates and manages a unique session ID for the current app session.
|
|
387
|
-
* The session ID is used to group errors from the same user session.
|
|
388
|
-
*/
|
|
389
|
-
/**
|
|
390
|
-
* Gets the current session ID, generating one if it doesn't exist.
|
|
391
|
-
* The session ID persists for the lifetime of the app process.
|
|
392
|
-
*/
|
|
190
|
+
declare function installHttpBreadcrumbs(client: WatchClient): void;
|
|
191
|
+
declare function removeHttpBreadcrumbs(): void;
|
|
192
|
+
|
|
393
193
|
declare function getSessionId(): string;
|
|
394
|
-
/**
|
|
395
|
-
* Resets the session ID, forcing a new one to be generated.
|
|
396
|
-
* This can be called when a user logs out or the app wants to start fresh.
|
|
397
|
-
*/
|
|
398
194
|
declare function resetSession(): void;
|
|
399
|
-
/**
|
|
400
|
-
* Sets a specific session ID (useful for testing or migration).
|
|
401
|
-
*/
|
|
402
195
|
declare function setSessionId(sessionId: string): void;
|
|
403
196
|
|
|
404
|
-
/**
|
|
405
|
-
* CatDoes Watch SDK - Context Collection
|
|
406
|
-
*
|
|
407
|
-
* Collects device and environment information to attach to error events.
|
|
408
|
-
* Uses Expo and React Native APIs where available.
|
|
409
|
-
*/
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Gets the current platform: 'ios', 'android', or 'web'.
|
|
413
|
-
*/
|
|
414
197
|
declare function getPlatform(): "ios" | "android" | "web";
|
|
415
|
-
/**
|
|
416
|
-
* Gets the current environment based on __DEV__ flag.
|
|
417
|
-
*/
|
|
418
198
|
declare function getEnvironment(): "development" | "production";
|
|
419
|
-
|
|
420
|
-
* Collects device and environment information.
|
|
421
|
-
* Only includes fields that are in the server's allowlist.
|
|
422
|
-
*/
|
|
423
|
-
declare function collectDeviceInfo(): DeviceInfo;
|
|
424
|
-
declare function getCachedDeviceInfo(): DeviceInfo;
|
|
425
|
-
/**
|
|
426
|
-
* Clears the cached device info, forcing re-collection on next call.
|
|
427
|
-
*/
|
|
428
|
-
declare function clearDeviceInfoCache(): void;
|
|
199
|
+
declare function getDeviceInfo(): DeviceInfo;
|
|
429
200
|
|
|
430
201
|
/**
|
|
431
202
|
* CatDoes Watch SDK - Breadcrumb Management
|
|
@@ -484,28 +255,11 @@ declare function createConsoleBreadcrumb(level: "log" | "warn" | "error" | "info
|
|
|
484
255
|
*/
|
|
485
256
|
declare function createCustomBreadcrumb(message: string, data?: Record<string, unknown>): BreadcrumbInput;
|
|
486
257
|
|
|
487
|
-
/**
|
|
488
|
-
* CatDoes Watch SDK - Symbolication Helpers
|
|
489
|
-
*
|
|
490
|
-
* Utilities for processing stack traces and file paths.
|
|
491
|
-
*/
|
|
492
|
-
/**
|
|
493
|
-
* Produces a readable file path from a Metro/URL-style file reference.
|
|
494
|
-
* - Strips query params
|
|
495
|
-
* - Prefers repo-relative paths like app/... or src/...
|
|
496
|
-
* - Falls back to URL pathname
|
|
497
|
-
*/
|
|
498
|
-
declare function deriveReadableFile(file: string): string;
|
|
499
|
-
/**
|
|
500
|
-
* Checks if a derived filename is usable (not a noisy bundle/node_modules path)
|
|
501
|
-
*/
|
|
502
|
-
declare function isUsableFilename(filename: string): boolean;
|
|
503
|
-
|
|
504
258
|
/**
|
|
505
259
|
* CatDoes Watch SDK - Version
|
|
506
260
|
*
|
|
507
261
|
* Keep this value updated when making SDK changes.
|
|
508
262
|
*/
|
|
509
|
-
declare const SDK_VERSION = "
|
|
263
|
+
declare const SDK_VERSION = "2.0.0";
|
|
510
264
|
|
|
511
|
-
export { type Breadcrumb, type BreadcrumbInput, BreadcrumbManager, type DeviceInfo, type IngestResponse, SDK_VERSION, Watch, WatchClient, type WatchConfig, type WatchConfigResolved, type WatchEvent, type
|
|
265
|
+
export { type Breadcrumb, type BreadcrumbInput, BreadcrumbManager, type DeviceInfo, type IngestResponse, type QueueStore, SDK_VERSION, Watch, WatchClient, type WatchConfig, type WatchConfigResolved, type WatchEvent, type WatchStats, createConsoleBreadcrumb, createCustomBreadcrumb, createHttpBreadcrumb, createNavigationBreadcrumb, createUIBreadcrumb, getDeviceInfo, getEnvironment, getPlatform, getSessionId, installHttpBreadcrumbs, removeGlobalHandlers, removeHttpBreadcrumbs, resetSession, setSessionId, setupGlobalHandlers };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var t=require("react-native"),e=require("react"),s=require("react/jsx-runtime");function i(t){return t&&t.t?t:{default:t}}var n=i(e);function r(t){return"object"==typeof t&&null!==t}function o(t){return!!r(t)&&"string"==typeof t.message&&"string"==typeof t.timestamp}var h=class{constructor(t){if(this.queue=[],this.flushTimer=null,this.backoffMs=0,this.backoffUntil=0,this.flushPromise=null,this.consecutiveFailures=0,this.disabledUntil=0,this.disabledReason=null,this.persistRequestedVersion=0,this.persistCompletedVersion=0,this.persistInFlight=null,this.config=t,this.storage=t.storage??null,this.maxConsecutiveFailures=t.maxConsecutiveFailures??50,this.failuresCooldownMs=t.failuresCooldownMs??9e5,this.disableOnAuthError=t.disableOnAuthError??!0,!t.apiKey)return this.config.debug&&console.warn("[CatDoes Watch] No API key; transport disabled."),this.disabledReason="auth",this.disabledUntil=1/0,this.apiKeyHash="",this.storageKey="",void(this.hydratePromise=Promise.resolve());this.apiKeyHash=function(t){let e=5381;for(let s=0;s<t.length;s++)e=(e<<5)+e^t.charCodeAt(s);return(e>>>0).toString(36)}(t.apiKey),this.storageKey=`@catdoes_watch_queue_v1_${this.apiKeyHash}`,this.hydratePromise=this.hydrateQueue().catch(t=>{this.config.debug&&console.debug("[CatDoes Watch] Failed to hydrate queue:",t)}).finally(()=>{this.requestPersist(),this.queue.length>0&&this.scheduleFlush(0)})}send(t){if(this.config.apiKey)if(this.isDisabled()){if(this.config.debug){const t=this.disabledUntil===1/0?"permanently":`for ${Math.max(0,this.disabledUntil-Date.now())}ms`;console.warn(`[CatDoes Watch] Transport disabled (${this.disabledReason}), dropping event ${t}`)}}else this.queue.push(t),this.trimQueue(),this.requestPersist(),this.config.debug&&console.debug("[CatDoes Watch] Event queued:",t.message),this.queue.length>=this.config.maxBufferSize?this.flush():this.scheduleFlush()}scheduleFlush(t=this.config.flushInterval){this.flushTimer||(this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flush()},Math.max(0,t)))}async flush(t){return this.flushPromise?(await this.flushPromise,this.queue.length>0&&!this.isDisabled()&&Date.now()>=this.backoffUntil?this.flush(t):void 0):(this.flushTimer&&(clearTimeout(this.flushTimer),this.flushTimer=null),0!==this.queue.length?(this.flushPromise=this.doFlushWithLock(t).finally(()=>{this.flushPromise=null}),this.flushPromise):void 0)}async doFlushWithLock(t){if(0===this.queue.length)return;if(this.isDisabled()){if(this.config.debug){const t=this.disabledUntil===1/0?"permanently":`for ${Math.max(0,this.disabledUntil-Date.now())}ms`;console.warn(`[CatDoes Watch] Transport disabled (${this.disabledReason}), skipping flush ${t}`)}if(this.disabledUntil!==1/0){const t=Math.max(0,this.disabledUntil-Date.now());this.scheduleFlush(t)}return}if(Date.now()<this.backoffUntil){if(this.config.debug){const t=this.backoffUntil-Date.now();console.debug(`[CatDoes Watch] In backoff, waiting ${t}ms`)}return void this.scheduleFlush(this.backoffUntil-Date.now())}const e=Math.max(1,this.config.maxBufferSize),s=t?.keepalive?Math.min(e,10):e,i=this.queue.slice(0,s);try{await this.doFlush(i,t),this.backoffMs=0,this.consecutiveFailures=0,this.queue=this.queue.slice(i.length),this.requestPersist(),this.queue.length>0&&this.scheduleFlush(0)}catch(t){this.config.debug&&console.debug("[CatDoes Watch] Flush failed, events retained for retry:",t),this.isDisabled()||this.scheduleFlush()}}async doFlush(t,e){if(0===t.length)return;this.config.debug&&console.debug(`[CatDoes Watch] Sending ${t.length} event(s)`);const s=JSON.stringify({events:t});try{const t={method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:s};e?.keepalive&&(t.keepalive=!0);const i=await fetch(this.config.endpoint,t);if(401===i.status||403===i.status)throw this.config.debug&&console.warn("[CatDoes Watch] Invalid API key. Clearing queue and disabling."),this.queue=[],this.requestPersist(),this.disableOnAuthError&&this.disable("auth",1/0),this.config.onTransportError?.("auth",{status:i.status}),new Error("Invalid API key");if(429===i.status){const t=i.headers.get("Retry-After"),e=t?1e3*parseInt(t,10):this.calculateBackoff();throw this.backoffMs=e,this.backoffUntil=Date.now()+e,this.config.debug&&console.debug(`[CatDoes Watch] Rate limited, backing off ${e}ms`),new Error("Rate limited")}if(!i.ok)throw this.applyBackoff(),new Error(`HTTP ${i.status}`);const n=await i.json();this.config.debug&&console.debug(`[CatDoes Watch] Sent successfully, accepted: ${n.accepted}`)}catch(t){throw t instanceof Error&&"Rate limited"===t.message||this.applyBackoff(),t}}applyBackoff(){this.consecutiveFailures++,this.backoffMs=this.calculateBackoff(),this.backoffUntil=Date.now()+this.backoffMs,this.config.debug&&console.debug(`[CatDoes Watch] Failure #${this.consecutiveFailures}, backing off ${this.backoffMs}ms`),this.consecutiveFailures>=this.maxConsecutiveFailures&&(this.config.debug&&console.warn("[CatDoes Watch] Max consecutive failures reached. Clearing queue."),this.queue=[],this.requestPersist(),this.disable("failures",this.failuresCooldownMs),this.config.onTransportError?.("failures"))}calculateBackoff(){const t=1e3*Math.pow(2,this.consecutiveFailures);return Math.min(t,6e4)}isDisabled(){return null!==this.disabledReason&&Date.now()<this.disabledUntil}disable(t,e){this.disabledReason=t,this.disabledUntil=e===1/0?1/0:Date.now()+Math.max(0,e)}get queueSize(){return this.queue.length}clear(){this.queue=[],this.requestPersist(),this.flushTimer&&(clearTimeout(this.flushTimer),this.flushTimer=null)}shutdown(){this.clear()}trimQueue(){this.queue.length<=200||(this.queue=this.queue.slice(-200))}requestPersist(){this.storage&&(this.persistRequestedVersion++,this.persistInFlight||(this.persistInFlight=this.persistUntilStable().catch(t=>{this.config.debug&&console.debug("[CatDoes Watch] Failed to persist queue:",t)}).finally(()=>{this.persistInFlight=null})))}async persistUntilStable(){if(this.storage)for(await this.hydratePromise.catch(()=>{});this.persistCompletedVersion<this.persistRequestedVersion;){const t=this.persistRequestedVersion;await this.persistSnapshot(),this.persistCompletedVersion=t}}async persistSnapshot(){const t=this.storage;if(t)try{if(0===this.queue.length)return void await t.removeItem(this.storageKey);const e={v:1,savedAt:(new Date).toISOString(),apiKeyHash:this.apiKeyHash,events:this.queue};await t.setItem(this.storageKey,JSON.stringify(e))}catch(t){this.config.debug&&console.debug("[CatDoes Watch] Queue persistence failed:",t)}}async hydrateQueue(){const t=this.storage;if(!t)return;let e=null;try{e=await t.getItem(this.storageKey)}catch{return}if(e)try{const s=JSON.parse(e);let i=[];if(Array.isArray(s))i=s.filter(o);else if(r(s)&&1===s.v&&Array.isArray(s.events)){const e=s.apiKeyHash;if(e&&e!==this.apiKeyHash){try{await t.removeItem(this.storageKey)}catch{}return}i=s.events.filter(o)}if(0===i.length)return void await t.removeItem(this.storageKey);this.queue=[...i,...this.queue],this.trimQueue(),this.config.debug&&console.debug(`[CatDoes Watch] Hydrated ${i.length} queued event(s)`)}catch{try{await t.removeItem(this.storageKey)}catch{}}}},a=null;function c(){return a||(a=`sess_${Date.now()}_${function(){const t="abcdefghijklmnopqrstuvwxyz0123456789";if("undefined"!=typeof crypto&&crypto.getRandomValues){const e=new Uint8Array(8);crypto.getRandomValues(e);let s="";for(let i=0;i<8;i++)s+=t.charAt(e[i]%36);return s}let e="";for(let s=0;s<8;s++)e+=t.charAt(Math.floor(36*Math.random()));return e}()}`),a}function l(t){try{const e=globalThis.expo;return e?.modules?.[t]??null}catch{return null}}function u(){const e=t.Platform.OS;return"ios"===e?"ios":"android"===e?"android":"web"}function d(){return"undefined"!=typeof __DEV__&&__DEV__?"development":"production"}function f(){const e=l("ExpoDevice"),s=l("ExponentConstants"),i=l("ExpoLocalization"),n=u(),r={};try{const{width:e,height:s,scale:i}=t.Dimensions.get("window");r.screenWidth=Math.round(e),r.screenHeight=Math.round(s),r.screenScale=i}catch{}if(r.osName=t.Platform.OS,t.Platform.Version&&(r.osVersion=String(t.Platform.Version)),e&&"web"!==n)try{e.brand&&(r.brand=e.brand),e.manufacturer&&(r.manufacturer=e.manufacturer),e.modelName&&(r.modelName=e.modelName),e.deviceName&&(r.deviceName=e.deviceName),e.osName&&(r.osName=e.osName),e.osVersion&&(r.osVersion=e.osVersion),e.osBuildId&&(r.osBuildId=e.osBuildId),e.platformApiLevel&&(r.platformApiLevel=e.platformApiLevel),"boolean"==typeof e.isDevice&&(r.isDevice=e.isDevice)}catch{}if(s)try{let t=null;"string"==typeof s.manifest?t=JSON.parse(s.manifest):s.manifest&&"object"==typeof s.manifest&&(t=s.manifest),t?.version&&(r.appVersion=t.version),s.expoVersion&&(r.expoVersion=s.expoVersion),t?.name&&(r.appName=t.name),"ios"===n&&t?.ios?.bundleIdentifier?r.bundleId=t.ios.bundleIdentifier:"android"===n&&t?.android?.package&&(r.bundleId=t.android.package)}catch{}if(i)try{const t=i.getLocales?.()?.[0]?.languageTag;t&&(r.locale=t);const e=i.getCalendars?.()?.[0]?.timeZone;e&&(r.timezone=e)}catch{}if("web"===n&&"undefined"!=typeof window)try{const t=window.navigator?.userAgent;if(t){r.userAgent=t.slice(0,500);const e=t.match(/(Chrome|Firefox|Safari|Edge|Opera)\/(\d+(\.\d+)?)/);e&&(r.browserName=e[1],r.browserVersion=e[2])}}catch{}return r}var p=null;function y(){return p||(p=f()),p}var w=class{constructor(t=20){this.breadcrumbs=[],this.maxBreadcrumbs=t}add(t){const e={...t,timestamp:(new Date).toISOString()};this.breadcrumbs.push(e),this.breadcrumbs.length>this.maxBreadcrumbs&&(this.breadcrumbs=this.breadcrumbs.slice(-this.maxBreadcrumbs))}getAll(){return[...this.breadcrumbs]}clear(){this.breadcrumbs=[]}get count(){return this.breadcrumbs.length}setMaxBreadcrumbs(t){this.maxBreadcrumbs=t,this.breadcrumbs.length>t&&(this.breadcrumbs=this.breadcrumbs.slice(-t))}},m="1.2.0";function g(t){const e=function(t){if(!t)return t;let e=t;const s=e.indexOf("?");s>=0&&(e=e.slice(0,s));const i=e.indexOf("&");for(i>=0&&(e=e.slice(0,i));e.endsWith("/")&&e.length>1;)e=e.slice(0,-1);return e}(t),s=["/app/","/src/","/components/","/screens/"];for(const t of s){const s=e.indexOf(t);if(s>=0)return b(e.slice(s+1))}try{const t=new URL(e).pathname.replace(/^\//,"");if(t)return t}catch{}return b(e)}function v(t){return!(!t||t.includes("node_modules")||/\.bundle(\/|$|:)/.test(t)||/bundle(\.js|\.map)$/.test(t)||t.startsWith("[native code]")||t.startsWith("native "))}function b(t){if(!t)return t;if(/^[a-z]+:\/\//i.test(t))return t;let e=t.replace(/\/{2,}/g,"/");return e.startsWith("./")&&(e=e.slice(2)),e}var C=class _WatchClient{constructor(t){this.context={},this.user=null,this.isInitialized=!1,this.recentErrors=new Map,this.recentErrorsCleanupTimer=null,this.config={apiKey:t.apiKey,endpoint:t.endpoint||"https://app.catdoes.com/api/watch/ingest",environment:t.environment||d(),captureConsoleErrors:t.captureConsoleErrors||!1,maxBreadcrumbs:t.maxBreadcrumbs||20,maxBufferSize:t.maxBufferSize||10,flushInterval:t.flushInterval||5e3,beforeSend:t.beforeSend||(t=>t),debug:t.debug??!1,dedupWindowMs:t.dedupWindowMs??5e3,dedupMaxEntries:t.dedupMaxEntries??500,storage:t.storage??null},this.transport=new h({endpoint:this.config.endpoint,apiKey:this.config.apiKey,maxBufferSize:this.config.maxBufferSize,flushInterval:this.config.flushInterval,debug:this.config.debug,storage:this.config.storage}),this.breadcrumbs=new w(this.config.maxBreadcrumbs);try{y()}catch{}t.initialContext&&(this.context={...t.initialContext}),this.isInitialized=!0,this.config.debug&&console.debug("[CatDoes Watch] Initialized with config:",{endpoint:this.config.endpoint,environment:this.config.environment,apiKey:this.config.apiKey.slice(0,12)+"..."})}static init(t){return t.apiKey||"undefined"!=typeof __DEV__&&__DEV__&&console.warn("[CatDoes Watch] No API key provided. Errors will not be reported."),_WatchClient.instance?(t.debug||_WatchClient.instance.config.debug)&&console.debug("[CatDoes Watch] Already initialized. Returning existing instance."):_WatchClient.instance=new _WatchClient(t),_WatchClient.instance}static getInstance(){return _WatchClient.instance}captureError(t,e){if(!this.shouldCapture())return;let s;try{throw new Error("__CALL_SITE_CAPTURE__")}catch(t){s=t.stack}const i=this.ensureStack(t,s),n=this.getErrorKey(i);if(this.hasSeenErrorRecently(n))return void(this.config.debug&&console.debug("[CatDoes Watch] Skipping duplicate error:",i.message));this.markErrorAsSeen(n);const r=this.buildEvent(i,e),o=this.config.beforeSend(r);o?this.transport.send(o):this.config.debug&&console.debug("[CatDoes Watch] Event dropped by beforeSend hook")}captureMessage(t,e="error"){if(!this.shouldCapture())return;const s=new Error(t);if(s.stack){const t=s.stack.split("\n");s.stack=[t[0],...t.slice(2)].join("\n")}this.captureError(s,{level:e,synthetic:!0})}addBreadcrumb(t){this.breadcrumbs.add(t)}setContext(t,e){this.context[t]=e}clearContext(t){delete this.context[t]}setUser(t){this.user=t}async flush(t){await this.transport.flush(t)}getConfig(){return this.config}get initialized(){return this.isInitialized}buildEvent(t,e){let s={};try{s=y()}catch{}const i={message:t.message||"Unknown error",stack:t.stack,timestamp:(new Date).toISOString(),environment:this.config.environment,platform:u(),sessionId:c(),deviceInfo:s,sdkVersion:m,extra:{...this.context,...e,...this.user?{user:this.user}:{}},breadcrumbs:this.breadcrumbs.getAll()};if("development"!==this.config.environment&&t.stack){let s=t.stack.match(/at\s+(?:.*?\s+\()?(.+?):(\d+):(\d+)\)?/);if(s||(s=t.stack.match(/^[^@]+@(.+?):(\d+):(\d+)$/m)),s){const t=g(s[1]);t&&v(t)&&(i.filename=t),i.lineno=parseInt(s[2],10),i.colno=parseInt(s[3],10)}void 0===i.lineno&&"number"==typeof e?.lineno&&(i.lineno=e.lineno),void 0===i.colno&&"number"==typeof e?.colno&&(i.colno=e.colno)}return i}shouldCapture(){return!!this.isInitialized&&!!this.config.apiKey}ensureStack(t,e){if(t.stack&&t.stack.length>100&&/:\d+:\d+/.test(t.stack))return t;if(e&&e.length>100&&/:\d+:\d+/.test(e)){const s=e.split("\n"),i=[];i.push(`${t.name}: ${t.message}`);let n=!1;for(const t of s){if(t.includes("__CALL_SITE_CAPTURE__")||t.includes("ensureStack")||t.includes("captureError")||t.includes("WatchClient"))continue;const e=t.trim();e&&(e.includes("@")||e.startsWith("at "))&&(i.push(e),n=!0)}if(n&&i.length>1){const e=new Error(t.message);return e.name=t.name,e.stack=i.join("\n"),Object.assign(e,t),e}}try{throw t}catch(t){const e=t;if(e.stack&&e.stack.length>100&&/:\d+:\d+/.test(e.stack))return e}return t}getErrorKey(t){const e=t.stack?.split("\n")[1]?.trim()||"";return`${t.message}::${e}`}markErrorAsSeen(t){const e=Date.now();if(this.recentErrors.has(t)&&this.recentErrors.delete(t),this.recentErrors.set(t,e),this.recentErrors.size>this.config.dedupMaxEntries){const t=this.recentErrors.keys().next().value;t&&this.recentErrors.delete(t)}this.scheduleRecentErrorsCleanup()}hasSeenErrorRecently(t){const e=this.recentErrors.get(t);if(null==e)return!1;const s=Date.now()-e<=this.config.dedupWindowMs;return s||this.recentErrors.delete(t),s}scheduleRecentErrorsCleanup(){this.recentErrorsCleanupTimer||(this.recentErrorsCleanupTimer=setTimeout(()=>{this.pruneRecentErrors(),this.recentErrorsCleanupTimer=null,this.recentErrors.size>0&&this.scheduleRecentErrorsCleanup()},this.config.dedupWindowMs))}pruneRecentErrors(){const t=Date.now(),e=this.config.dedupWindowMs;for(const[s,i]of this.recentErrors.entries())t-i>e&&this.recentErrors.delete(s)}};C.instance=null;var D=C,E={init:t=>D.init(t),getInstance:()=>D.getInstance(),captureError(t,e){D.getInstance()?.captureError(t,e)},captureMessage(t,e="error"){D.getInstance()?.captureMessage(t,e)},addBreadcrumb(t){D.getInstance()?.addBreadcrumb(t)},setContext(t,e){D.getInstance()?.setContext(t,e)},setUser(t){D.getInstance()?.setUser(t)},flush:t=>D.getInstance()?.flush(t)||Promise.resolve()},x=!1,_=null,W=null,$=null,S=null,k=null,I=null,M=class extends n.default.Component{constructor(t){super(t),this.resetError=()=>{this.setState({hasError:!1,error:null,errorInfo:null})},this.state={hasError:!1,error:null,errorInfo:null}}static getDerivedStateFromError(t){return{hasError:!0,error:t}}componentDidCatch(t,e){this.setState({errorInfo:e});const{captureErrors:s=!0,onError:i}=this.props;if(s){const s=D.getInstance();s&&s.captureError(t,{componentStack:e.componentStack,source:"WatchErrorBoundary"})}i&&i(t,e)}render(){const{hasError:t,error:e,errorInfo:i}=this.state,{children:n,fallback:r}=this.props;return t&&e?r?s.jsx(r,{error:e,errorInfo:i,resetError:this.resetError}):null:n}};exports.BreadcrumbManager=w,exports.SDK_VERSION=m,exports.Watch=E,exports.WatchClient=D,exports.WatchErrorBoundary=M,exports.clearDeviceInfoCache=function(){p=null},exports.collectDeviceInfo=f,exports.createConsoleBreadcrumb=function(t,e){return{type:"console",message:`[${t}] ${e}`.slice(0,200),data:{level:t}}},exports.createCustomBreadcrumb=function(t,e){return{type:"custom",message:t,data:e}},exports.createHttpBreadcrumb=function(t,e,s){return{type:"http",message:`${t} ${e}${s?` [${s}]`:""}`,data:{method:t,url:e,statusCode:s}}},exports.createNavigationBreadcrumb=function(t,e){return{type:"navigation",message:`Navigated from ${t} to ${e}`,data:{from:t,to:e}}},exports.createUIBreadcrumb=function(t,e){return{type:"ui",message:e?`${t} on ${e}`:t,data:{action:t,target:e}}},exports.deriveReadableFile=g,exports.getCachedDeviceInfo=y,exports.getEnvironment=d,exports.getPlatform=u,exports.getSessionId=c,exports.isUsableFilename=v,exports.removeGlobalHandlers=function(){if(x){if("web"===t.Platform.OS&&"undefined"!=typeof window)window.onerror=_,_=null,$&&(window.removeEventListener("beforeunload",$),$=null),S&&(window.removeEventListener("pagehide",S),S=null),k&&(window.removeEventListener("unhandledrejection",k),k=null);else{const t=global.ErrorUtils;t&&W&&(t.setGlobalHandler(W),W=null),I&&(I.remove(),I=null)}x=!1}},exports.resetSession=function(){a=null},exports.setSessionId=function(t){a=t},exports.setupConsoleErrorCapture=function(t){const e=console.error;console.error=(...s)=>{let i;e.apply(console,s);const n=s.find(t=>t instanceof Error);if(n)i=n;else{const t=s.map(t=>{if("string"==typeof t)return t;try{return JSON.stringify(t)}catch{return String(t)}}).join(" ");if(i=new Error(t),i.stack){const t=i.stack.split("\n");i.stack=[t[0],...t.slice(3)].join("\n")}}t.captureError(i,{source:"console.error",synthetic:!n})}},exports.setupGlobalHandlers=function(e){x?e.getConfig().debug&&console.debug("[CatDoes Watch] Global handlers already installed"):("web"===t.Platform.OS?function(t){"undefined"!=typeof window&&(_=window.onerror,window.onerror=(e,s,i,n,r)=>{_&&_(e,s,i,n,r);const o=r||new Error("string"==typeof e?e:"Unknown error");return!r&&s&&(o.filename=s,o.lineno=i,o.colno=n),t.captureError(o,{source:"global.onerror",filename:s,lineno:i,colno:n}),!1},k=e=>{if(e.defaultPrevented)return;const s=e.reason instanceof Error?e.reason:new Error(String(e.reason??"Unhandled Promise rejection"));t.captureError(s,{source:"global.unhandledrejection",isPromiseRejection:!0})},window.addEventListener("unhandledrejection",k),$=()=>{t.flush({keepalive:!0}).catch(()=>{})},S=()=>{t.flush({keepalive:!0}).catch(()=>{})},window.addEventListener("beforeunload",$),window.addEventListener("pagehide",S))}(e):function(e){try{I=t.AppState.addEventListener("change",t=>{"background"!==t&&"inactive"!==t||e.flush().catch(()=>{})})}catch{}const s=global.ErrorUtils;s?(W=s.getGlobalHandler(),s.setGlobalHandler((t,s)=>{e.captureError(t,{source:"ErrorUtils.globalHandler",isFatal:s}),s&&e.flush().catch(()=>{}),W&&W(t,s)})):e.getConfig().debug&&console.debug("[CatDoes Watch] ErrorUtils not available")}(e),x=!0,e.getConfig().debug&&console.debug("[CatDoes Watch] Global handlers installed"))},exports.withWatchErrorBoundary=function(t,e){const i=t.displayName||t.name||"Component",n=i=>s.jsx(M,{...e,children:s.jsx(t,{...i})});return n.displayName=`withWatchErrorBoundary(${i})`,n};
|
|
1
|
+
var t=require("expo-constants"),e=require("expo-device"),r=require("react-native"),i=require("react-native/Libraries/Core/ExceptionsManager"),n=require("expo-modules-core"),s=require("expo-file-system"),o=require("react"),a=require("react/jsx-runtime");function h(t){return t&&t.t?t:{default:t}}function u(t){if(t&&t.t)return t;var e=Object.create(null);return t&&Object.keys(t).forEach(function(r){if("default"!==r){var i=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,i.get?i:{enumerable:!0,get:function(){return t[r]}})}}),e.default=t,Object.freeze(e)}var c=h(t),l=u(e),d=h(i),f=h(o),p=class{constructor(t=20){this.breadcrumbs=[],this.maxBreadcrumbs=t}add(t){const e={...t,timestamp:(new Date).toISOString()};this.breadcrumbs.push(e),this.breadcrumbs.length>this.maxBreadcrumbs&&(this.breadcrumbs=this.breadcrumbs.slice(-this.maxBreadcrumbs))}getAll(){return[...this.breadcrumbs]}clear(){this.breadcrumbs=[]}get count(){return this.breadcrumbs.length}setMaxBreadcrumbs(t){this.maxBreadcrumbs=t,this.breadcrumbs.length>t&&(this.breadcrumbs=this.breadcrumbs.slice(-t))}};function y(){return"ios"===r.Platform.OS?"ios":"android"===r.Platform.OS?"android":"web"}function m(){return"undefined"!=typeof __DEV__&&__DEV__?"development":"production"}function v(t,e,r){try{const i=r();null!=i&&(t[e]=i)}catch{}}var w=null;function g(){return w||(w=function(){const t={},e=y();try{const e=r.Dimensions.get("window");t.screenWidth=e.width,t.screenHeight=e.height,t.screenScale=e.scale,t.fontScale=e.fontScale}catch{}v(t,"osName",()=>String(r.Platform.OS)),v(t,"osVersion",()=>null===r.Platform.Version||void 0===r.Platform.Version?void 0:String(r.Platform.Version)),"web"!==e&&(v(t,"brand",()=>l.brand??void 0),v(t,"manufacturer",()=>l.manufacturer??void 0),v(t,"modelName",()=>l.modelName??void 0),v(t,"modelId",()=>null===l.modelId||void 0===l.modelId?void 0:String(l.modelId)),v(t,"designName",()=>l.designName??void 0),v(t,"productName",()=>l.productName??void 0),v(t,"deviceName",()=>l.deviceName??void 0),v(t,"deviceType",()=>function(t){if(null!=t)return["unknown","phone","tablet","desktop","tv"][t]}(l.deviceType)),v(t,"deviceYearClass",()=>l.deviceYearClass??void 0),v(t,"memoryTotal",()=>l.totalMemory??void 0),v(t,"supportedCpuArchitectures",()=>l.supportedCpuArchitectures??void 0),v(t,"osName",()=>l.osName??void 0),v(t,"osVersion",()=>l.osVersion??void 0),v(t,"osBuildId",()=>l.osBuildId??void 0),v(t,"osInternalBuildId",()=>l.osInternalBuildId??void 0),v(t,"osBuildFingerprint",()=>l.osBuildFingerprint??void 0),v(t,"platformApiLevel",()=>l.platformApiLevel??void 0),v(t,"isDevice",()=>l.isDevice)),v(t,"appName",()=>c.default.expoConfig?.name),v(t,"appSlug",()=>c.default.expoConfig?.slug),v(t,"appVersion",()=>c.default.expoConfig?.version),v(t,"expoSdkVersion",()=>c.default.expoConfig?.sdkVersion),v(t,"runtimeVersion",()=>{const t=c.default.expoConfig?.runtimeVersion;return"string"==typeof t?t:void 0}),v(t,"executionEnvironment",()=>c.default.executionEnvironment?String(c.default.executionEnvironment):void 0),v(t,"expoRuntimeVersion",()=>c.default.expoRuntimeVersion??void 0),v(t,"expoVersion",()=>c.default.expoVersion??void 0),"ios"===e?(v(t,"bundleId",()=>c.default.expoConfig?.ios?.bundleIdentifier??void 0),v(t,"appBuildNumber",()=>c.default.expoConfig?.ios?.buildNumber??void 0)):"android"===e&&(v(t,"bundleId",()=>c.default.expoConfig?.android?.package??void 0),v(t,"appBuildNumber",()=>{const t=c.default.expoConfig?.android?.versionCode;return null==t?void 0:String(t)})),v(t,"jsEngine",()=>{if("web"===e)return"unknown";const t=globalThis.HermesInternal;return null!==t&&"object"==typeof t?"hermes":"jsc"});try{const e=Intl.DateTimeFormat().resolvedOptions();e.locale&&(t.locale=e.locale),e.timeZone&&(t.timezone=e.timeZone)}catch{}if("web"===e)try{const e=window.navigator?.userAgent;if(e){t.userAgent=e.slice(0,500);const r=e.match(/(Chrome|Firefox|Safari|Edge|Opera)\/(\d+(?:\.\d+)?)/);r&&(t.browserName=r[1],t.browserVersion=r[2])}}catch{}return t}()),w}var b=Symbol.for("@catdoes/watch/runtime-state-v2");function E(){const t=globalThis;return t[b]||(t[b]={capturedErrors:new WeakSet,handoffDepth:0,activeClient:null,clientInstance:null}),t[b]}function $(t){("object"==typeof t&&null!==t||"function"==typeof t)&&E().capturedErrors.add(t)}function _(t){return("object"==typeof t&&null!==t||"function"==typeof t)&&E().capturedErrors.has(t)}function D(t){const e=E();e.handoffDepth++;try{return t()}finally{e.handoffDepth--}}function S(){return E().handoffDepth>0}function x(t){E().activeClient=t}function k(){return E().activeClient}function T(){return E().clientInstance}var j=null,C=null;function I(t){try{const e=new URL(t);return`${e.origin}${e.pathname}`.slice(0,200)}catch{return t.split(/[?#]/,1)[0].slice(0,200)}}function M(t){if(x(t),C||"function"!=typeof globalThis.fetch)return;const e=globalThis.fetch;j=e,C=async(t,r)=>{const i=Date.now();let n="",s="GET",o=!1;try{n=function(t){return"string"==typeof t?t:"undefined"!=typeof URL&&t instanceof URL?t.href:"url"in t?t.url:String(t)}(t),s=function(t,e){return e?.method?e.method.toUpperCase():"undefined"!=typeof Request&&t instanceof Request?t.method.toUpperCase():"GET"}(t,r),o=n.startsWith(k()?.getConfig().endpoint??"")}catch{}try{const a=await e(t,r);if(!o)try{const t=I(n);k()?.addBreadcrumb({type:"http",message:`${s} ${t} ${a.status}`,data:{method:s,url:t,status:a.status,durationMs:Date.now()-i}})}catch{}return a}catch(t){if(!o)try{const e=I(n);k()?.addBreadcrumb({type:"http",message:`${s} ${e} failed`,data:{method:s,url:e,status:null,durationMs:Date.now()-i,error:t instanceof Error?t.name:"Error"}})}catch{}throw t}},globalThis.fetch=C}function N(){C&&j&&globalThis.fetch===C&&(globalThis.fetch=j),C=null,j=null}var W=!1,U=!1,A=!1,P=null,R=null;function B(t,e){const r=k();r?.getConfig().debug&&(void 0===e?console.debug(`[CatDoes Watch] ${t}`):console.debug(`[CatDoes Watch] ${t}:`,e))}function q(t){if(void 0===t)return"";if("[object Error]"===Object.prototype.toString.call(t))try{return Error.prototype.toString.call(t)}catch{return String(t)}if("string"==typeof t)return t;try{return JSON.stringify(t)}catch{return String(t)}}function V(t){return` at ${t.methodName||"<unknown>"} (${t.file||"<unknown>"}:${t.lineNumber??0}:${t.column??0})`}var F=null,O=null,z=null,L=null;function H(t){x(t),"web"===r.Platform.OS?function(t){x(t),"undefined"==typeof window||F||(F=t=>{const e=t.error??new Error(t.message||"Unknown error");k()?.captureError(e,{source:"global.error",filename:t.filename,lineno:t.lineno,colno:t.colno})},O=t=>{t.defaultPrevented||k()?.captureError(t.reason,{source:"global.unhandledrejection",isPromiseRejection:!0})},z=()=>{const t=k();t?.commit(),t?.flush({keepalive:!0}).catch(()=>{})},L=()=>{const t=k();t?.commit(),t?.flush({keepalive:!0}).catch(()=>{})},window.addEventListener("error",F),window.addEventListener("unhandledrejection",O),window.addEventListener("beforeunload",z),window.addEventListener("pagehide",L))}(t):function(t){x(t),function(){if(!W)try{const t=globalThis.ErrorUtils;if(!t)return void B("ErrorUtils not available");P=t.getGlobalHandler();const e=(t,e)=>{const r=P,i=k();if(!i)return void(r&&D(()=>r(t,e)));try{i.captureError(t,{source:"ErrorUtils.globalHandler",isFatal:e})}catch(t){B("Global handler capture failed",t)}if(e&&i.commit(),!r)return;if(!e||"development"===i.getConfig().environment){if(e)try{D(()=>r(t,e))}finally{i.flush().catch(()=>{})}else D(()=>r(t,e));return}const n=i.getConfig().fatalFlushTimeoutMs;if(n<=0)return i.flush().catch(()=>{}),void D(()=>r(t,e));i.flushWithTimeout(n).finally(()=>D(()=>r(t,e)))};t.setGlobalHandler(e),W=!0}catch(t){B("Failed to install ErrorUtils hook",t)}}(),function(){if(!U)try{const t=globalThis.HermesInternal;if(!t?.hasPromise?.()||!t.enablePromiseRejectionTracker)return;t.enablePromiseRejectionTracker({allRejections:!0,onUnhandled:(t,e)=>{const r=k();if(r)try{r.captureError(function(t){if(t instanceof Error)return t;const e=new Error(q(t));return e.name="UnhandledPromiseRejection",e}(e),{source:"HermesInternal.promiseRejectionTracker",isPromiseRejection:!0,rejectionId:t})}catch(t){B("Promise rejection capture failed",t)}if("undefined"!=typeof __DEV__&&__DEV__)try{!function(t,e){const r=`Uncaught (in promise, id: ${t})`,i=q(e),n=new Error(`${r} ${i??""}`);n.cause=e,n.stack="object"==typeof e&&null!==e&&"stack"in e?`${r} ${String(e.stack??"")}`:`${r} ${i??""}`,$(n),D(()=>d.default.handleException(n,!1))}(t,e)}catch(t){B("Promise rejection forwarding failed",t)}},onHandled:t=>{"undefined"!=typeof __DEV__&&__DEV__&&console.warn(`Promise rejection handled (id: ${t})\nThis means you can ignore any previous messages of the form "Uncaught (in promise, id: ${t})"`)}}),U=!0}catch(t){B("Failed to install promise rejection tracker",t)}}(),function(){if(!A)try{d.default.unstable_setExceptionDecorator(t=>{try{const e=k();if(e&&t.isFatal&&!S()){try{e.captureError(function(t){const e=new Error(t.originalMessage??t.message);t.name&&(e.name=t.name);const r=t.extraData?.rawStack;return"string"==typeof r?e.stack=r:Array.isArray(t.stack)&&(e.stack=[`${e.name}: ${e.message}`,...t.stack.map(V)].join("\n")),e}(t),{source:"ExceptionsManager.exceptionDecorator",isFatal:!0,componentStack:t.componentStack??void 0})}finally{e.commit()}e.flush().catch(()=>{})}}catch(t){B("Exception decorator capture failed",t)}return t}),A=!0}catch(t){B("Failed to install exception decorator",t)}}(),function(){if(!R)try{R=r.AppState.addEventListener("change",t=>{if("background"===t||"inactive"===t){const t=k();t?.commit(),t?.flush().catch(()=>{})}})}catch(t){B("Failed to install AppState flush",t)}}()}(t)}var Q=!1,G=null;function J(t){if("string"==typeof t)return t;try{return JSON.stringify(t)}catch{return String(t)}}var K=null;function Y(){const t="abcdefghijklmnopqrstuvwxyz0123456789",e=new Uint8Array(8);try{if("undefined"!=typeof crypto&&crypto.getRandomValues)return crypto.getRandomValues(e),Array.from(e,e=>t[e%36]).join("")}catch{}let r="";for(let e=0;e<8;e++)r+=t.charAt(Math.floor(36*Math.random()));return r}function X(){try{return n.uuid.v4()}catch{return`${Date.now().toString(36)}${Y()}`}}function Z(){return K||(K=`sess_${Date.now()}_${function(){try{return n.uuid.v4().replace(/-/g,"").slice(0,8)}catch{return Y()}}()}`),K}function tt(t){if(!t)return t;if(/^[a-z]+:\/\//i.test(t))return t;let e=t.replace(/\/{2,}/g,"/");return e.startsWith("./")&&(e=e.slice(2)),e}function et(t,e,r){t&&console.debug(`[CatDoes Watch] Queue ${e} failed:`,r)}var rt=class{constructor(t,e=!1){this.debug=e,this.directoryReady=!1,this.directory=new s.Directory(s.Paths.cache,"catdoes-watch"),this.file=new s.File(this.directory,`${t}.json`)}read(){try{return this.file.exists?this.file.textSync():null}catch(t){return et(this.debug,"read",t),null}}write(t){try{this.directoryReady||(this.directory.create({idempotent:!0,intermediates:!0}),this.directoryReady=!0),this.file.write(t)}catch(t){et(this.debug,"write",t)}}remove(){try{this.file.exists&&this.file.delete()}catch(t){et(this.debug,"remove",t)}}},it=class{constructor(t,e=!1){this.key=t,this.debug=e}getStorage(){try{return"undefined"==typeof window?null:window.localStorage}catch(t){return et(this.debug,"localStorage access",t),null}}read(){try{return this.getStorage()?.getItem(this.key)??null}catch(t){return et(this.debug,"read",t),null}}write(t){try{this.getStorage()?.setItem(this.key,t)}catch(t){et(this.debug,"write",t)}}remove(){try{this.getStorage()?.removeItem(this.key)}catch(t){et(this.debug,"remove",t)}}},nt=3e5;function st(t){return"object"==typeof t&&null!==t}function ot(t){return st(t)&&"string"==typeof t.message&&"string"==typeof t.timestamp}function at(t){return t instanceof Error?t.message:String(t)}function ht(t){const e=new WeakSet;return JSON.stringify(t,(t,r)=>{if("bigint"==typeof r)return r.toString();if("object"==typeof r&&null!==r){if(e.has(r))return"[Circular]";e.add(r)}return r})}var ut=class{constructor(t){if(this.config=t,this.queue=[],this.inFlight=[],this.queueBytes=0,this.inFlightBytes=0,this.flushTimer=null,this.flushTimerAt=0,this.flushPromise=null,this.dirty=!1,this.lastWriteAt=null,this.writeTimer=null,this.backoffMs=0,this.backoffUntil=0,this.consecutiveFailures=0,this.disabledUntil=0,this.disabledReason=null,this.lastFlush=null,this.maxConsecutiveFailures=t.maxConsecutiveFailures??50,this.failuresCooldownMs=t.failuresCooldownMs??9e5,this.disableOnAuthError=t.disableOnAuthError??!0,this.apiKeyHash=t.apiKey?function(t){let e=5381;for(let r=0;r<t.length;r++)e=(e<<5)+e^t.charCodeAt(r);return(e>>>0).toString(36)}(t.apiKey):"",this.store=t.apiKey?t.storage??function(t,e=!1){const i=`@catdoes_watch_queue_v2_${t}`;return"web"===r.Platform.OS?new it(i,e):new rt(i,e)}(this.apiKeyHash,t.debug):null,!t.apiKey)return this.disabledReason="auth",this.disabledUntil=1/0,void(t.debug&&console.warn("[CatDoes Watch] No API key; transport disabled."));this.hydrate(),this.queue.length>0&&this.scheduleFlush(0)}send(t){if(!this.config.apiKey||this.isDisabled())return;const e=this.createEntry(t);this.queue.push(e),this.queueBytes+=e.serialized.length,this.trimQueue(),this.persist(),this.config.debug&&console.debug("[CatDoes Watch] Event queued:",t.message),this.queue.length>=Math.max(1,this.config.maxBufferSize)?this.flush():this.scheduleFlush()}async flush(t){for(;;){if(!this.flushPromise){if(0===this.queue.length)return;this.cancelScheduledFlush(),this.flushPromise=this.flushBatch(t).finally(()=>{this.flushPromise=null})}if(!await this.flushPromise)return}}async flushWithTimeout(t){let e=null;const r=new Promise(r=>{e=setTimeout(r,Math.max(0,t))});try{await Promise.race([this.flush().catch(()=>{}),r])}finally{e&&clearTimeout(e)}}get queueSize(){return this.inFlight.length+this.queue.length}getStats(){const t=Date.now(),e="failures"===this.disabledReason&&t>=this.disabledUntil;return{disabledReason:e?null:this.disabledReason,backoffUntil:this.backoffUntil>t?this.backoffUntil:null,consecutiveFailures:e?0:this.consecutiveFailures,lastFlush:this.lastFlush?{...this.lastFlush}:null}}commit(){if(this.writeTimer&&(clearTimeout(this.writeTimer),this.writeTimer=null),this.store&&this.dirty){this.dirty=!1,this.lastWriteAt=Date.now();try{const t=Date.now(),e=this.backoffUntil>t||"failures"===this.disabledReason&&this.disabledUntil>t;if(0===this.inFlight.length&&0===this.queue.length&&!e)return void this.store.remove();this.store.write(this.serializePersistedQueue())}catch(t){this.config.debug&&console.debug("[CatDoes Watch] Queue serialization failed:",t)}}}clear(){this.queue=[],this.inFlight=[],this.queueBytes=0,this.inFlightBytes=0,this.persist(),this.commit(),this.cancelScheduledFlush()}shutdown(){this.clear()}scheduleFlush(t=this.config.flushInterval){const e=Math.max(0,t),r=Date.now()+e;this.flushTimer&&this.flushTimerAt<=r||(this.flushTimer&&clearTimeout(this.flushTimer),this.flushTimerAt=r,this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flushTimerAt=0,this.flush()},e))}cancelScheduledFlush(){this.flushTimer&&(clearTimeout(this.flushTimer),this.flushTimer=null,this.flushTimerAt=0)}async flushBatch(t){if(0===this.queue.length)return!1;if(this.isDisabled())return this.disabledUntil!==1/0&&this.scheduleFlush(this.disabledUntil-Date.now()),!1;if(Date.now()<this.backoffUntil)return this.scheduleFlush(this.backoffUntil-Date.now()),!1;const e=Math.max(1,this.config.maxBufferSize),r=t?.keepalive?Math.min(e,10):e,i=this.queue.splice(0,r),n=i.reduce((t,e)=>t+e.serialized.length,0);this.queueBytes-=n,this.inFlight=i,this.inFlightBytes=n,this.persist();const s=(new Date).toISOString();this.lastFlush={at:s,status:null,accepted:null};let o=!1;try{const e=await fetch(this.config.endpoint,this.requestInit(i,t));if(401===e.status||403===e.status)return this.lastFlush={at:s,status:e.status,accepted:null,error:"Invalid API key"},this.queue=[],this.queueBytes=0,o=!0,this.disableOnAuthError&&this.disable("auth",1/0),this.config.onTransportError?.("auth",{status:e.status}),!1;if(429===e.status){o=this.applyBackoff();const t=1e3*Number.parseInt(e.headers.get("Retry-After")??"",10);return Number.isFinite(t)&&t>this.backoffMs&&(this.backoffMs=t,this.backoffUntil=Date.now()+t),this.lastFlush={at:s,status:e.status,accepted:null,error:"Rate limited"},!1}if(!e.ok)return this.lastFlush={at:s,status:e.status,accepted:null,error:`HTTP ${e.status}`},o=this.applyBackoff(),!1;let r={};try{r=await e.json()}catch{}return this.lastFlush={at:s,status:e.status,accepted:"number"==typeof r.accepted?r.accepted:null},this.backoffMs=0,this.backoffUntil=0,this.consecutiveFailures=0,o=!0,!0}catch(t){return this.lastFlush={at:s,status:null,accepted:null,error:at(t)},o=this.applyBackoff(),!1}finally{this.inFlight===i&&(o||(this.queue=[...i,...this.queue],this.queueBytes+=n),this.inFlight=[],this.inFlightBytes=0),this.trimQueue(),this.persist(),this.queue.length>0&&!this.isDisabled()&&this.scheduleFlush(Math.max(0,this.backoffUntil-Date.now()))}}requestInit(t,e){const i={method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:`{"events":[${t.map(t=>t.serialized).join(",")}]}`};return"web"===r.Platform.OS&&e?.keepalive&&(i.keepalive=!0),i}applyBackoff(){return this.consecutiveFailures++,this.backoffMs=Math.min(1e3*2**(this.consecutiveFailures-1),nt),this.backoffUntil=Date.now()+this.backoffMs,this.consecutiveFailures>=this.maxConsecutiveFailures&&(this.queue=[],this.queueBytes=0,this.disable("failures",this.failuresCooldownMs),this.config.onTransportError?.("failures"),!0)}isDisabled(){return"failures"===this.disabledReason&&Date.now()>=this.disabledUntil&&(this.disabledReason=null,this.disabledUntil=0,this.consecutiveFailures=0,this.persist()),null!==this.disabledReason&&Date.now()<this.disabledUntil}disable(t,e){this.disabledReason=t,this.disabledUntil=e===1/0?1/0:Date.now()+Math.max(0,e)}trimQueue(){let t=0,e=this.queueBytes,r=this.queue.length;for(;t<this.queue.length&&(this.inFlight.length+r>100||this.persistedSize(r,e)>512e3);)e-=this.queue[t].serialized.length,r--,t++;t>0&&(this.queue=this.queue.slice(t),this.queueBytes=e)}buildPayloadMetadata(){const t=Date.now(),e={v:2,savedAt:(new Date).toISOString(),apiKeyHash:this.apiKeyHash};return this.backoffUntil>t&&(e.backoffUntil=this.backoffUntil),this.consecutiveFailures>0&&(e.consecutiveFailures=this.consecutiveFailures),"failures"===this.disabledReason&&Number.isFinite(this.disabledUntil)&&this.disabledUntil>t&&(e.disabledUntil=this.disabledUntil),e}allEntries(){return[...this.inFlight,...this.queue]}persistedSize(t,e){const r=ht(this.buildPayloadMetadata()).length,i=this.inFlight.length+t;return r+12+this.inFlightBytes+e+Math.max(0,i-1)}createEntry(t){return{serialized:ht(t)}}persist(){if(!this.store)return;this.dirty=!0;const t=null===this.lastWriteAt?500:Date.now()-this.lastWriteAt;t>=500?this.commit():this.writeTimer||(this.writeTimer=setTimeout(()=>{this.writeTimer=null,this.commit()},500-t))}serializePersistedQueue(){const t=ht(this.buildPayloadMetadata()),e=this.allEntries();return`${t.slice(0,-1)},"events":[${e.map(t=>t.serialized).join(",")}]}`}hydrate(){if(!this.store)return;let t;try{t=this.store.read()}catch(t){return void(this.config.debug&&console.debug("[CatDoes Watch] Queue hydration failed:",t))}if(t)try{const e=JSON.parse(t);if(!st(e)||2!==e.v||e.apiKeyHash!==this.apiKeyHash||!Array.isArray(e.events))return void this.store.remove();this.queue=e.events.filter(ot).map(t=>this.createEntry(t)),this.queueBytes=this.queue.reduce((t,e)=>t+e.serialized.length,0),this.restoreThrottleState(e),this.trimQueue(),0===this.queue.length&&0===this.backoffUntil&&null===this.disabledReason?this.store.remove():this.persist()}catch{try{this.store.remove()}catch{}}}restoreThrottleState(t){const e=Date.now(),r=Number(t.backoffUntil);Number.isFinite(r)&&r>e&&(this.backoffUntil=Math.min(r,e+nt),this.backoffMs=this.backoffUntil-e);const i=Number(t.consecutiveFailures);Number.isFinite(i)&&i>0&&(this.consecutiveFailures=Math.min(Math.floor(i),this.maxConsecutiveFailures));const n=Number(t.disabledUntil);Number.isFinite(n)&&n>e&&this.disable("failures",Math.min(n-e,this.failuresCooldownMs))}},ct="2.0.0";function lt(t,e){try{const r=t[e];return"string"==typeof r?r:void 0}catch{return}}function dt(t,e){return void 0===t?void 0:t.slice(0,e)}var ft=class _WatchClient{constructor(t){this.context={},this.user=null,this.recentErrors=new Map,this.recentErrorsCleanupTimer=null,this.config={apiKey:t.apiKey,endpoint:t.endpoint??"https://app.catdoes.com/api/watch/ingest",environment:t.environment??m(),debug:t.debug??!1,installGlobalHandlers:t.installGlobalHandlers??!0,captureConsoleErrors:t.captureConsoleErrors??!1,captureHttpBreadcrumbs:t.captureHttpBreadcrumbs??!1,maxBreadcrumbs:t.maxBreadcrumbs??20,maxBufferSize:t.maxBufferSize??10,flushInterval:t.flushInterval??5e3,fatalFlushTimeoutMs:t.fatalFlushTimeoutMs??2e3,beforeSend:t.beforeSend??(t=>t),dedupWindowMs:t.dedupWindowMs??5e3,dedupMaxEntries:t.dedupMaxEntries??500,storage:t.storage},this.transport=new ut({endpoint:this.config.endpoint,apiKey:this.config.apiKey,maxBufferSize:this.config.maxBufferSize,flushInterval:this.config.flushInterval,debug:this.config.debug,storage:this.config.storage}),this.breadcrumbs=new p(this.config.maxBreadcrumbs),this.context={...t.initialContext};try{g()}catch{}}static init(t){let e=T();return e||(e=new _WatchClient(t),function(t){E().clientInstance=t}(e),t.apiKey?(x(e),e.config.installGlobalHandlers&&H(e),e.config.captureConsoleErrors&&function(t){x(t),Q||(G=console.error,console.error=(...t)=>{try{const e=k(),r=t.find(t=>t instanceof Error),i=t.map(J).join(" ");if(e&&!S()&&!i.startsWith("Warning: ")&&(!r||!_(r))&&!0!==r?.isComponentError){const t=r??new Error(i);if(!r&&t.stack){const e=t.stack.split("\n");t.stack=[e[0],...e.slice(3)].join("\n")}$(t),e.captureError(t,{source:"console.error",synthetic:!r})}}catch{}G?.apply(console,t)},Q=!0)}(e),e.config.captureHttpBreadcrumbs&&M(e)):"undefined"!=typeof __DEV__&&__DEV__&&console.warn("[CatDoes Watch] No API key provided. Errors will not be reported.")),e}static getInstance(){return T()}captureError(t,e){if(!this.config.apiKey)return;const r=function(t){if(t instanceof Error)return t;if("string"==typeof t)return new Error(t);if("object"==typeof t&&null!==t){const e=lt(t,"message");if(void 0!==e){const r=new Error(e),i=lt(t,"name"),n=lt(t,"stack");return i&&(r.name=i),n&&(r.stack=n),r}}try{return new Error(String(t))}catch{return new Error("Unknown error")}}(t);let i;$(r);try{throw new Error("__CALL_SITE_CAPTURE__")}catch(t){i=t.stack}const n=this.getErrorKey(r);if(this.hasSeenErrorRecently(n))return void(this.config.debug&&console.debug("[CatDoes Watch] Skipping duplicate error:",r.message));this.markErrorAsSeen(n);const s=this.ensureStack(r,i),o=this.buildEvent(s,e),a=this.config.beforeSend(o);a&&this.transport.send(a)}captureMessage(t,e="error"){const r=new Error(t);if(r.stack){const t=r.stack.split("\n");r.stack=[t[0],...t.slice(2)].join("\n")}this.captureError(r,{level:e,synthetic:!0})}addBreadcrumb(t){this.breadcrumbs.add(t)}setContext(t,e){this.context[t]=e}clearContext(t){delete this.context[t]}setUser(t){this.user=t}async flush(t){await this.transport.flush(t)}async flushWithTimeout(t){await this.transport.flushWithTimeout(t)}commit(){this.transport.commit()}getStats(){return{queueSize:this.transport.queueSize,sessionId:Z(),transport:this.transport.getStats()}}getConfig(){return this.config}get initialized(){return!0}buildEvent(t,e){let r={};try{r=g()}catch{}const i="string"==typeof e?.componentStack?dt(e.componentStack,4e3):void 0,n={...this.context,...e};delete n.componentStack;const s={eventId:X(),message:dt(t.message||"Unknown error",1e4),stack:dt(t.stack,16e3),componentStack:i,timestamp:(new Date).toISOString(),environment:this.config.environment,platform:y(),sessionId:Z(),deviceInfo:r,sdkVersion:ct,extra:{...n,...this.user?{user:this.user}:{}},breadcrumbs:this.breadcrumbs.getAll()};if("development"!==this.config.environment&&t.stack){let r=t.stack.match(/at\s+(?:.*?\s+\()?(.+?):(\d+):(\d+)\)?/);if(r||(r=t.stack.match(/^[^@]+@(.+?):(\d+):(\d+)$/m)),r){const t=function(t){const e=function(t){if(!t)return t;let e=t;const r=e.indexOf("?");r>=0&&(e=e.slice(0,r));const i=e.indexOf("&");for(i>=0&&(e=e.slice(0,i));e.endsWith("/")&&e.length>1;)e=e.slice(0,-1);return e}(t),r=["/app/","/src/","/components/","/screens/"];for(const t of r){const r=e.indexOf(t);if(r>=0)return tt(e.slice(r+1))}try{const t=new URL(e).pathname.replace(/^\//,"");if(t)return t}catch{}return tt(e)}(r[1]);!t||!(o=t)||o.includes("node_modules")||/\.bundle(\/|$|:)/.test(o)||/bundle(\.js|\.map)$/.test(o)||o.startsWith("[native code]")||o.startsWith("native ")||(s.filename=t),s.lineno=Number.parseInt(r[2],10),s.colno=Number.parseInt(r[3],10)}void 0===s.lineno&&"number"==typeof e?.lineno&&(s.lineno=e.lineno),void 0===s.colno&&"number"==typeof e?.colno&&(s.colno=e.colno),s.filename||"string"!=typeof e?.filename||(s.filename=e.filename)}var o;return s}ensureStack(t,e){if(t.stack&&t.stack.length>100&&/:\d+:\d+/.test(t.stack))return t;if(e&&e.length>100&&/:\d+:\d+/.test(e)){const r=e.split("\n").filter(t=>!(t.includes("__CALL_SITE_CAPTURE__")||t.includes("ensureStack")||t.includes("captureError")||t.includes("WatchClient"))).map(t=>t.trim()).filter(t=>t.includes("@")||t.startsWith("at "));if(r.length>0){const e=new Error(t.message);return e.name=t.name,e.stack=[`${t.name}: ${t.message}`,...r].join("\n"),Object.assign(e,t),e}}return t}getErrorKey(t){return`${t.message}::${t.stack?.split("\n")[1]?.trim()??""}`}hasSeenErrorRecently(t){const e=this.recentErrors.get(t);return void 0!==e&&(Date.now()-e<=this.config.dedupWindowMs||(this.recentErrors.delete(t),!1))}markErrorAsSeen(t){if(this.recentErrors.delete(t),this.recentErrors.set(t,Date.now()),this.recentErrors.size>this.config.dedupMaxEntries){const t=this.recentErrors.keys().next().value;t&&this.recentErrors.delete(t)}this.scheduleRecentErrorsCleanup()}scheduleRecentErrorsCleanup(){this.recentErrorsCleanupTimer||(this.recentErrorsCleanupTimer=setTimeout(()=>{const t=Date.now()-this.config.dedupWindowMs;for(const[e,r]of this.recentErrors)r<t&&this.recentErrors.delete(e);this.recentErrorsCleanupTimer=null,this.recentErrors.size>0&&this.scheduleRecentErrorsCleanup()},this.config.dedupWindowMs))}},pt={init:t=>ft.init(t),getInstance:()=>ft.getInstance(),captureError(t,e){ft.getInstance()?.captureError(t,e)},captureMessage(t,e="error"){ft.getInstance()?.captureMessage(t,e)},addBreadcrumb(t){ft.getInstance()?.addBreadcrumb(t)},setContext(t,e){ft.getInstance()?.setContext(t,e)},clearContext(t){ft.getInstance()?.clearContext(t)},setUser(t){ft.getInstance()?.setUser(t)},flush:t=>ft.getInstance()?.flush(t)??Promise.resolve(),flushWithTimeout:t=>ft.getInstance()?.flushWithTimeout(t)??Promise.resolve(),getStats:()=>ft.getInstance()?.getStats()??null},yt=class extends f.default.Component{constructor(){super(...arguments),this.state={hasError:!1,error:null,errorInfo:null},this.resetError=()=>{this.setState({hasError:!1,error:null,errorInfo:null})}}static getDerivedStateFromError(t){return{hasError:!0,error:t}}componentDidCatch(t,e){this.setState({errorInfo:e});const{captureErrors:r=!0,onError:i}=this.props;r&&!_(t)&&k()?.captureError(t,{componentStack:e.componentStack??void 0,source:"WatchErrorBoundary"}),i?.(t,e)}render(){const{hasError:t,error:e,errorInfo:r}=this.state,{children:i,fallback:n}=this.props;return t&&e?n?a.jsx(n,{error:e,errorInfo:r??{componentStack:null},resetError:this.resetError}):null:i}};yt.displayName="WatchErrorBoundary",exports.BreadcrumbManager=p,exports.SDK_VERSION=ct,exports.Watch=pt,exports.WatchClient=ft,exports.WatchErrorBoundary=yt,exports.createConsoleBreadcrumb=function(t,e){return{type:"console",message:`[${t}] ${e}`.slice(0,200),data:{level:t}}},exports.createCustomBreadcrumb=function(t,e){return{type:"custom",message:t,data:e}},exports.createHttpBreadcrumb=function(t,e,r){return{type:"http",message:`${t} ${e}${r?` [${r}]`:""}`,data:{method:t,url:e,statusCode:r}}},exports.createNavigationBreadcrumb=function(t,e){return{type:"navigation",message:`Navigated from ${t} to ${e}`,data:{from:t,to:e}}},exports.createUIBreadcrumb=function(t,e){return{type:"ui",message:e?`${t} on ${e}`:t,data:{action:t,target:e}}},exports.getDeviceInfo=g,exports.getEnvironment=m,exports.getPlatform=y,exports.getSessionId=Z,exports.installHttpBreadcrumbs=M,exports.removeGlobalHandlers=function(){x(null),R?.remove(),R=null,"undefined"!=typeof window&&(F&&window.removeEventListener("error",F),O&&window.removeEventListener("unhandledrejection",O),z&&window.removeEventListener("beforeunload",z),L&&window.removeEventListener("pagehide",L)),F=null,O=null,z=null,L=null,N()},exports.removeHttpBreadcrumbs=N,exports.resetSession=function(){K=null},exports.setSessionId=function(t){K=t},exports.setupGlobalHandlers=H,exports.withWatchErrorBoundary=function(t,e){const r=t.displayName||t.name||"Component",i=r=>a.jsx(yt,{...e,children:a.jsx(t,{...r})});return i.displayName=`withWatchErrorBoundary(${r})`,i};
|