@quiltt/react-native 4.5.1 → 5.0.1

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.
@@ -0,0 +1,201 @@
1
+ import Honeybadger from '@honeybadger-io/react-native';
2
+ import React from 'react';
3
+ import { Platform } from 'react-native';
4
+ import { getUserAgent as getUserAgent$1 } from '@quiltt/core';
5
+ import DeviceInfo from 'react-native-device-info';
6
+
7
+ var version = "5.0.1";
8
+
9
+ // Custom Error Reporter to avoid hooking into or colliding with a client's Honeybadger singleton
10
+ class ErrorReporter {
11
+ constructor(userAgent){
12
+ // Create an isolated Honeybadger instance to avoid colliding with client's singleton
13
+ this.client = Honeybadger.factory({
14
+ apiKey: process.env.HONEYBADGER_API_KEY_REACT_NATIVE || '',
15
+ environment: userAgent,
16
+ revision: version,
17
+ reportData: true,
18
+ enableUncaught: false,
19
+ enableUnhandledRejection: false
20
+ });
21
+ }
22
+ async notify(error, context) {
23
+ if (!this.client) {
24
+ console.warn('ErrorReporter: Honeybadger client not initialized');
25
+ return;
26
+ }
27
+ try {
28
+ // Set context for this error report
29
+ if (context) {
30
+ this.client.setContext(context);
31
+ }
32
+ // Notify Honeybadger
33
+ await this.client.notify(error);
34
+ } catch (err) {
35
+ console.error('ErrorReporter: Failed to send error report', err);
36
+ } finally{
37
+ // Clear context after reporting (always runs if context was set)
38
+ if (context) {
39
+ this.client.clear();
40
+ }
41
+ }
42
+ }
43
+ }
44
+
45
+ const getErrorMessage = (responseStatus, error)=>{
46
+ if (error) return `An error occurred while checking the Connector URL: ${error?.name} \n${error?.message}`;
47
+ return responseStatus ? `An error occurred loading the Connector. Response status: ${responseStatus}` : 'An error occurred while checking the Connector URL';
48
+ };
49
+
50
+ /**
51
+ * Gets the React version from the runtime
52
+ */ const getReactVersion = ()=>{
53
+ return React.version || 'unknown';
54
+ };
55
+ /**
56
+ * Gets the React Native version from Platform constants
57
+ */ const getReactNativeVersion = ()=>{
58
+ try {
59
+ const rnVersion = Platform.constants?.reactNativeVersion;
60
+ if (rnVersion) {
61
+ return `${rnVersion.major}.${rnVersion.minor}.${rnVersion.patch}`;
62
+ }
63
+ } catch (error) {
64
+ console.warn('Failed to get React Native version:', error);
65
+ }
66
+ return 'unknown';
67
+ };
68
+ /**
69
+ * Gets OS information (platform and version)
70
+ */ const getOSInfo = ()=>{
71
+ try {
72
+ const os = Platform.OS // 'ios' or 'android'
73
+ ;
74
+ const osVersion = Platform.Version // string (iOS) or number (Android)
75
+ ;
76
+ // Map platform names to correct capitalization
77
+ const platformNames = {
78
+ ios: 'iOS',
79
+ android: 'Android'
80
+ };
81
+ const osName = platformNames[os] || 'Unknown';
82
+ return `${osName}/${osVersion}`;
83
+ } catch (error) {
84
+ console.warn('Failed to get OS info:', error);
85
+ return 'Unknown/Unknown';
86
+ }
87
+ };
88
+ /**
89
+ * Gets device model information
90
+ */ const getDeviceModel = async ()=>{
91
+ try {
92
+ const model = await DeviceInfo.getModel();
93
+ return model || 'Unknown';
94
+ } catch (error) {
95
+ console.warn('Failed to get device model:', error);
96
+ return 'Unknown';
97
+ }
98
+ };
99
+ /**
100
+ * Generates platform information string for React Native
101
+ * Format: React/<version>; ReactNative/<version>; <OS>/<version>; <device-model>
102
+ */ const getPlatformInfo = async ()=>{
103
+ const reactVersion = getReactVersion();
104
+ const rnVersion = getReactNativeVersion();
105
+ const osInfo = getOSInfo();
106
+ const deviceModel = await getDeviceModel();
107
+ return `React/${reactVersion}; ReactNative/${rnVersion}; ${osInfo}; ${deviceModel}`;
108
+ };
109
+ /**
110
+ * Synchronously generates platform information string for React Native
111
+ * Format: React/<version>; ReactNative/<version>; <OS>/<version>; Unknown
112
+ * Note: Device model is set to 'Unknown' since it requires async DeviceInfo call
113
+ */ const getPlatformInfoSync = ()=>{
114
+ const reactVersion = getReactVersion();
115
+ const rnVersion = getReactNativeVersion();
116
+ const osInfo = getOSInfo();
117
+ return `React/${reactVersion}; ReactNative/${rnVersion}; ${osInfo}; Unknown`;
118
+ };
119
+ /**
120
+ * Generates User-Agent string for React Native SDK
121
+ * Format: Quiltt/<sdk-version> (React/<version>; ReactNative/<version>; <OS>/<version>; <device-model>)
122
+ */ const getUserAgent = async (sdkVersion)=>{
123
+ const platformInfo = await getPlatformInfo();
124
+ return getUserAgent$1(sdkVersion, platformInfo);
125
+ };
126
+
127
+ /**
128
+ * Checks if a string appears to be already URL encoded
129
+ * @param str The string to check
130
+ * @returns boolean indicating if the string appears to be URL encoded
131
+ */ const isEncoded = (str)=>{
132
+ // Check for typical URL encoding patterns like %20, %3A, etc.
133
+ const hasEncodedChars = /%[0-9A-F]{2}/i.test(str);
134
+ // Check if double encoding has occurred (e.g., %253A instead of %3A)
135
+ const hasDoubleEncoding = /%25[0-9A-F]{2}/i.test(str);
136
+ // If we have encoded chars but no double encoding, it's likely properly encoded
137
+ return hasEncodedChars && !hasDoubleEncoding;
138
+ };
139
+ /**
140
+ * Smart URL encoder that ensures a string is encoded exactly once
141
+ * @param str The string to encode
142
+ * @returns A properly URL encoded string
143
+ */ const smartEncodeURIComponent = (str)=>{
144
+ if (!str) return str;
145
+ // If it's already encoded, return as is
146
+ if (isEncoded(str)) {
147
+ console.log('URL already encoded, skipping encoding');
148
+ return str;
149
+ }
150
+ // Otherwise, encode it
151
+ const encoded = encodeURIComponent(str);
152
+ console.log('URL encoded');
153
+ return encoded;
154
+ };
155
+ /**
156
+ * Creates a URL with proper parameter encoding
157
+ * @param baseUrl The base URL string
158
+ * @param params Object containing key-value pairs to be added as search params
159
+ * @returns A properly formatted URL string
160
+ */ const createUrlWithParams = (baseUrl, params)=>{
161
+ try {
162
+ const url = new URL(baseUrl);
163
+ // Add each parameter without additional encoding
164
+ // (URLSearchParams.append will encode them automatically)
165
+ Object.entries(params).forEach(([key, value])=>{
166
+ // Skip undefined or null values
167
+ if (value == null) return;
168
+ // For oauth_redirect_url specifically, ensure it's not double encoded
169
+ if (key === 'oauth_redirect_url' && isEncoded(value)) {
170
+ // Decode once to counteract the automatic encoding that will happen
171
+ const decodedOnce = decodeURIComponent(value);
172
+ url.searchParams.append(key, decodedOnce);
173
+ } else {
174
+ url.searchParams.append(key, value);
175
+ }
176
+ });
177
+ return url.toString();
178
+ } catch (error) {
179
+ console.error('Error creating URL with params:', error);
180
+ return baseUrl;
181
+ }
182
+ };
183
+ /**
184
+ * Checks if a string appears to be double-encoded
185
+ */ const isDoubleEncoded = (str)=>{
186
+ if (!str) return false;
187
+ return /%25[0-9A-F]{2}/i.test(str);
188
+ };
189
+ /**
190
+ * Normalizes a URL string by decoding it once if it appears to be double-encoded
191
+ */ const normalizeUrlEncoding = (urlStr)=>{
192
+ if (isDoubleEncoded(urlStr)) {
193
+ console.log('Detected double-encoded URL:', urlStr);
194
+ const normalized = decodeURIComponent(urlStr);
195
+ console.log('Normalized to:', normalized);
196
+ return normalized;
197
+ }
198
+ return urlStr;
199
+ };
200
+
201
+ export { ErrorReporter, createUrlWithParams, getDeviceModel, getErrorMessage, getOSInfo, getPlatformInfo, getPlatformInfoSync, getReactNativeVersion, getReactVersion, getUserAgent, isEncoded, normalizeUrlEncoding, smartEncodeURIComponent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quiltt/react-native",
3
- "version": "4.5.1",
3
+ "version": "5.0.1",
4
4
  "description": "React Native Components for Quiltt Connector",
5
5
  "homepage": "https://github.com/quiltt/quiltt-js/tree/main/packages/react-native#readme",
6
6
  "repository": {
@@ -16,7 +16,27 @@
16
16
  "exports": {
17
17
  ".": {
18
18
  "types": "./dist/index.d.ts",
19
+ "require": "./dist/index.cjs",
20
+ "import": "./dist/index.js",
19
21
  "default": "./dist/index.js"
22
+ },
23
+ "./components": {
24
+ "types": "./dist/components/index.d.ts",
25
+ "require": "./dist/components/index.cjs",
26
+ "import": "./dist/components/index.js",
27
+ "default": "./dist/components/index.js"
28
+ },
29
+ "./providers": {
30
+ "types": "./dist/providers/index.d.ts",
31
+ "require": "./dist/providers/index.cjs",
32
+ "import": "./dist/providers/index.js",
33
+ "default": "./dist/providers/index.js"
34
+ },
35
+ "./utils": {
36
+ "types": "./dist/utils/index.d.ts",
37
+ "require": "./dist/utils/index.cjs",
38
+ "import": "./dist/utils/index.js",
39
+ "default": "./dist/utils/index.js"
20
40
  }
21
41
  },
22
42
  "module": "./dist/index.js",
@@ -28,24 +48,24 @@
28
48
  ],
29
49
  "main": "dist/index.js",
30
50
  "dependencies": {
31
- "@honeybadger-io/core": "6.6.0",
51
+ "@honeybadger-io/react-native": "6.4.7",
32
52
  "lodash.debounce": "4.0.8",
33
- "@quiltt/react": "4.5.1",
34
- "@quiltt/core": "4.5.1"
53
+ "react-native-device-info": "15.0.1",
54
+ "@quiltt/react": "5.0.1",
55
+ "@quiltt/core": "5.0.1"
35
56
  },
36
57
  "devDependencies": {
37
- "@biomejs/biome": "2.3.8",
58
+ "@biomejs/biome": "2.3.14",
38
59
  "@types/base-64": "1.0.2",
39
60
  "@types/lodash.debounce": "4.0.9",
40
- "@types/node": "22.19.2",
41
- "@types/react": "19.2.7",
61
+ "@types/node": "24.10.10",
62
+ "@types/react": "19.2.11",
42
63
  "base-64": "1.0.0",
43
- "bunchee": "6.6.2",
44
- "react": "19.2.3",
64
+ "bunchee": "6.9.4",
65
+ "react": "19.2.4",
45
66
  "react-native": "0.81.5",
46
67
  "react-native-url-polyfill": "3.0.0",
47
68
  "react-native-webview": "13.15.0",
48
- "react-test-renderer": "19.2.3",
49
69
  "rimraf": "6.1.2",
50
70
  "typescript": "5.9.3"
51
71
  },
@@ -18,6 +18,7 @@ import type { ShouldStartLoadRequest } from 'react-native-webview/lib/WebViewTyp
18
18
  import {
19
19
  ErrorReporter,
20
20
  getErrorMessage,
21
+ getUserAgent,
21
22
  isEncoded,
22
23
  normalizeUrlEncoding,
23
24
  smartEncodeURIComponent,
@@ -28,7 +29,6 @@ import { AndroidSafeAreaView } from './AndroidSafeAreaView'
28
29
  import { ErrorScreen } from './ErrorScreen'
29
30
  import { LoadingScreen } from './LoadingScreen'
30
31
 
31
- const errorReporter = new ErrorReporter(`${Platform.OS} ${Platform.Version}`)
32
32
  const PREFLIGHT_RETRY_COUNT = 3
33
33
 
34
34
  export type PreFlightCheck = {
@@ -55,6 +55,7 @@ const parseMetadata = (url: URL, connectorId: string): ConnectorSDKCallbackMetad
55
55
 
56
56
  export const checkConnectorUrl = async (
57
57
  connectorUrl: string,
58
+ errorReporter: ErrorReporter,
58
59
  retryCount = 0
59
60
  ): Promise<PreFlightCheck> => {
60
61
  let responseStatus: number | undefined
@@ -85,7 +86,7 @@ export const checkConnectorUrl = async (
85
86
  const delay = 50 * 2 ** retryCount
86
87
  await new Promise((resolve) => setTimeout(resolve, delay))
87
88
  console.log(`Retrying connection... Attempt ${retryCount + 1}`)
88
- return checkConnectorUrl(connectorUrl, retryCount + 1)
89
+ return checkConnectorUrl(connectorUrl, errorReporter, retryCount + 1)
89
90
  }
90
91
 
91
92
  // Report error after retries exhausted
@@ -170,6 +171,25 @@ const QuilttConnector = forwardRef<QuilttConnectorHandle, QuilttConnectorProps>(
170
171
  const webViewRef = useRef<WebView>(null)
171
172
  const { session } = useQuilttSession()
172
173
  const [preFlightCheck, setPreFlightCheck] = useState<PreFlightCheck>({ checked: false })
174
+ const [errorReporter, setErrorReporter] = useState<ErrorReporter | null>(null)
175
+ const [userAgent, setUserAgent] = useState<string>('')
176
+
177
+ // Initialize error reporter and user agent
178
+ useEffect(() => {
179
+ let mounted = true
180
+ const init = async () => {
181
+ const agent = await getUserAgent(version)
182
+ if (mounted) {
183
+ setUserAgent(agent)
184
+ setErrorReporter(new ErrorReporter(agent))
185
+ }
186
+ }
187
+ init()
188
+
189
+ return () => {
190
+ mounted = false
191
+ }
192
+ }, [])
173
193
 
174
194
  // Script to disable scrolling on header
175
195
  const disableHeaderScrollScript = `
@@ -197,11 +217,13 @@ const QuilttConnector = forwardRef<QuilttConnectorHandle, QuilttConnectorProps>(
197
217
  }, [oauthRedirectUrl])
198
218
 
199
219
  const connectorUrl = useMemo(() => {
220
+ if (!userAgent) return null
221
+
200
222
  const url = new URL(`https://${connectorId}.quiltt.app`)
201
223
 
202
224
  // For normal parameters, just append them directly
203
225
  url.searchParams.append('mode', 'webview')
204
- url.searchParams.append('agent', `react-native-${version}`)
226
+ url.searchParams.append('agent', userAgent)
205
227
 
206
228
  // For the oauth_redirect_url, we need to be careful
207
229
  // If it's already encoded, we need to decode it once to prevent
@@ -214,16 +236,16 @@ const QuilttConnector = forwardRef<QuilttConnectorHandle, QuilttConnectorProps>(
214
236
  }
215
237
 
216
238
  return url.toString()
217
- }, [connectorId, safeOAuthRedirectUrl])
239
+ }, [connectorId, safeOAuthRedirectUrl, userAgent])
218
240
 
219
241
  useEffect(() => {
220
- if (preFlightCheck.checked) return
242
+ if (preFlightCheck.checked || !connectorUrl || !errorReporter) return
221
243
  const fetchDataAndSetState = async () => {
222
- const connectorUrlStatus = await checkConnectorUrl(connectorUrl)
244
+ const connectorUrlStatus = await checkConnectorUrl(connectorUrl, errorReporter)
223
245
  setPreFlightCheck(connectorUrlStatus)
224
246
  }
225
247
  fetchDataAndSetState()
226
- }, [connectorUrl, preFlightCheck])
248
+ }, [connectorUrl, preFlightCheck, errorReporter])
227
249
 
228
250
  const initInjectedJavaScript = useCallback(() => {
229
251
  const script = `\
@@ -404,7 +426,7 @@ const QuilttConnector = forwardRef<QuilttConnectorHandle, QuilttConnectorProps>(
404
426
  []
405
427
  )
406
428
 
407
- if (!preFlightCheck.checked) {
429
+ if (!preFlightCheck.checked || !connectorUrl) {
408
430
  return <LoadingScreen testId="loading-screen" />
409
431
  }
410
432
 
package/src/index.ts CHANGED
@@ -7,16 +7,38 @@ if (!global.atob) {
7
7
  }
8
8
 
9
9
  export * from '@quiltt/core'
10
+ // Re-export Apollo Client types (via @quiltt/react)
11
+ export type {
12
+ ApolloQueryResult,
13
+ DocumentNode,
14
+ ErrorPolicy,
15
+ FetchPolicy,
16
+ MutationHookOptions,
17
+ MutationResult,
18
+ NormalizedCacheObject,
19
+ OperationVariables,
20
+ QueryHookOptions,
21
+ QueryResult,
22
+ SubscriptionHookOptions,
23
+ SubscriptionResult,
24
+ TypedDocumentNode,
25
+ WatchQueryFetchPolicy,
26
+ } from '@quiltt/react'
27
+ // Re-export Apollo Client utilities and hooks (via @quiltt/react)
28
+ // Re-export Quiltt-specific providers and hooks
10
29
  export {
11
- QuilttAuthProvider,
12
- QuilttProvider,
13
30
  QuilttSettingsProvider,
31
+ useLazyQuery,
32
+ useMutation,
33
+ useQuery,
14
34
  useQuilttClient,
15
35
  useQuilttConnector,
16
36
  useQuilttSession,
17
37
  useQuilttSettings,
18
38
  useSession,
19
39
  useStorage,
40
+ useSubscription,
20
41
  } from '@quiltt/react'
21
42
 
22
43
  export * from './components'
44
+ export * from './providers'
@@ -0,0 +1,46 @@
1
+ import type { FC } from 'react'
2
+ import { useMemo } from 'react'
3
+
4
+ import { createVersionLink, InMemoryCache, QuilttClient } from '@quiltt/core'
5
+ import type { QuilttAuthProviderProps as ReactQuilttAuthProviderProps } from '@quiltt/react'
6
+ import { QuilttAuthProvider as ReactQuilttAuthProvider } from '@quiltt/react'
7
+
8
+ import { getPlatformInfoSync } from '@/utils/telemetry'
9
+
10
+ export type QuilttAuthProviderProps = ReactQuilttAuthProviderProps
11
+
12
+ /**
13
+ * React Native-specific QuilttAuthProvider that injects platform information
14
+ * into the GraphQL client's User-Agent header.
15
+ *
16
+ * If a token is provided, will validate the token against the api and then import
17
+ * it into trusted storage. While this process is happening, the component is put
18
+ * into a loading state and the children are not rendered to prevent race conditions
19
+ * from triggering within the transitionary state.
20
+ */
21
+ export const QuilttAuthProvider: FC<QuilttAuthProviderProps> = ({
22
+ graphqlClient,
23
+ token,
24
+ children,
25
+ }) => {
26
+ // Create React Native-specific GraphQL client with platform info if no custom client provided
27
+ const platformClient = useMemo(() => {
28
+ if (graphqlClient) {
29
+ return graphqlClient
30
+ }
31
+
32
+ const platformInfo = getPlatformInfoSync()
33
+ return new QuilttClient({
34
+ cache: new InMemoryCache(),
35
+ versionLink: createVersionLink(platformInfo),
36
+ })
37
+ }, [graphqlClient])
38
+
39
+ return (
40
+ <ReactQuilttAuthProvider token={token} graphqlClient={platformClient}>
41
+ {children}
42
+ </ReactQuilttAuthProvider>
43
+ )
44
+ }
45
+
46
+ export default QuilttAuthProvider
@@ -0,0 +1,30 @@
1
+ import type { FC } from 'react'
2
+
3
+ import type { QuilttSettingsProviderProps } from '@quiltt/react'
4
+ import { QuilttSettingsProvider } from '@quiltt/react'
5
+
6
+ import type { QuilttAuthProviderProps } from './QuilttAuthProvider'
7
+ import { QuilttAuthProvider } from './QuilttAuthProvider'
8
+
9
+ type QuilttProviderProps = QuilttSettingsProviderProps & QuilttAuthProviderProps
10
+
11
+ /**
12
+ * React Native-specific QuilttProvider that combines settings and auth providers
13
+ * with platform-specific GraphQL client configuration.
14
+ */
15
+ export const QuilttProvider: FC<QuilttProviderProps> = ({
16
+ clientId,
17
+ graphqlClient,
18
+ token,
19
+ children,
20
+ }) => {
21
+ return (
22
+ <QuilttSettingsProvider clientId={clientId}>
23
+ <QuilttAuthProvider token={token} graphqlClient={graphqlClient}>
24
+ {children}
25
+ </QuilttAuthProvider>
26
+ </QuilttSettingsProvider>
27
+ )
28
+ }
29
+
30
+ export default QuilttProvider
@@ -0,0 +1,2 @@
1
+ export * from './QuilttAuthProvider'
2
+ export * from './QuilttProvider'
@@ -1,101 +1,44 @@
1
1
  // Custom Error Reporter to avoid hooking into or colliding with a client's Honeybadger singleton
2
- import type { Notice, NoticeTransportPayload } from '@honeybadger-io/core/build/src/types'
3
- import { generateStackTrace, getCauses, makeBacktrace } from '@honeybadger-io/core/build/src/util'
2
+ import Honeybadger from '@honeybadger-io/react-native'
4
3
 
5
4
  import { version } from '@/version'
6
5
 
7
- const notifier = {
8
- name: 'Quiltt React Native SDK Reporter',
9
- url: 'https://www.quiltt.dev/connector/sdk/react-native',
10
- version: version,
11
- }
12
-
13
- type HoneybadgerResponseData = {
14
- id: string
15
- }
16
-
17
6
  class ErrorReporter {
18
- private noticeUrl: string
19
- private apiKey: string
20
- private clientName: string
21
- private clientVersion: string
22
- private platform: string
23
- private logger: Console
24
- private userAgent: string
25
-
26
- constructor(platform: string) {
27
- this.noticeUrl = 'https://api.honeybadger.io/v1/notices'
28
- this.apiKey = process.env.HONEYBADGER_API_KEY_REACT_NATIVE || ''
29
- this.clientName = 'react-native-sdk'
30
- this.clientVersion = version
31
- this.platform = platform
32
- this.logger = console
33
- this.userAgent = `${this.clientName} ${this.clientVersion}; ${this.platform}`
7
+ private client: typeof Honeybadger
8
+
9
+ constructor(userAgent: string) {
10
+ // Create an isolated Honeybadger instance to avoid colliding with client's singleton
11
+ this.client = Honeybadger.factory({
12
+ apiKey: process.env.HONEYBADGER_API_KEY_REACT_NATIVE || '',
13
+ environment: userAgent,
14
+ revision: version,
15
+ reportData: true,
16
+ enableUncaught: false, // Don't hook into global error handlers
17
+ enableUnhandledRejection: false, // Don't hook into global rejection handlers
18
+ })
34
19
  }
35
20
 
36
21
  async notify(error: Error, context?: any): Promise<void> {
37
- const headers = {
38
- 'X-API-Key': this.apiKey,
39
- 'Content-Type': 'application/json',
40
- Accept: 'application/json',
41
- 'User-Agent': `${this.clientName} ${this.clientVersion}; ${this.platform}`,
22
+ if (!this.client) {
23
+ console.warn('ErrorReporter: Honeybadger client not initialized')
24
+ return
42
25
  }
43
26
 
44
- const payload = await this.buildPayload(error, context)
45
- const method = 'POST'
46
- const body = JSON.stringify(payload)
47
- const mode = 'cors'
48
-
49
- fetch(this.noticeUrl, { headers, method, body, mode })
50
- .then((response) => {
51
- if (response.status !== 201) {
52
- this.logger.warn(
53
- `Error report failed: unknown response from server. code=${response.status}`
54
- )
55
- return
56
- }
57
- return response.json()
58
- })
59
- .then((data: HoneybadgerResponseData) => {
60
- if (data) {
61
- this.logger.info(`Error report sent ⚡ https://app.honeybadger.io/notice/${data?.id}`)
62
- }
63
- })
64
- }
65
-
66
- async buildPayload(error: Error, localContext = {}): Promise<Partial<NoticeTransportPayload>> {
67
- const notice: Notice = error as Notice
68
- notice.stack = generateStackTrace()
69
-
70
- notice.backtrace = makeBacktrace(notice.stack)
71
-
72
- return {
73
- notifier,
74
- error: {
75
- class: notice.name as string,
76
- message: notice.message as string,
77
- backtrace: notice.backtrace,
78
- // fingerprint: this.calculateFingerprint(notice),
79
- tags: notice.tags || [],
80
- causes: getCauses(notice, this.logger),
81
- },
82
- request: {
83
- url: notice.url,
84
- component: notice.component,
85
- action: notice.action,
86
- context: localContext || {},
87
- cgi_data: {},
88
- params: {},
89
- session: {},
90
- },
91
- server: {
92
- project_root: notice.projectRoot,
93
- environment_name: this.userAgent,
94
- revision: version,
95
- hostname: this.platform,
96
- time: new Date().toUTCString(),
97
- },
98
- details: notice.details || {},
27
+ try {
28
+ // Set context for this error report
29
+ if (context) {
30
+ this.client.setContext(context)
31
+ }
32
+
33
+ // Notify Honeybadger
34
+ await this.client.notify(error)
35
+ } catch (err) {
36
+ console.error('ErrorReporter: Failed to send error report', err)
37
+ } finally {
38
+ // Clear context after reporting (always runs if context was set)
39
+ if (context) {
40
+ this.client.clear()
41
+ }
99
42
  }
100
43
  }
101
44
  }
@@ -1,2 +1,3 @@
1
1
  export * from './error'
2
+ export * from './telemetry'
2
3
  export * from './url'