diff --git a/plugin/platforms/ios/src/NSSentry.h b/plugin/platforms/ios/src/NSSentry.h index 4f1375c..bb977ca 100644 --- a/plugin/platforms/ios/src/NSSentry.h +++ b/plugin/platforms/ios/src/NSSentry.h @@ -10,5 +10,7 @@ @property (class, nonatomic, assign, readonly) SentryScreenFrames *currentScreenFrames; @property (class, nullable, nonatomic, readonly) SentryAppStartMeasurement *appStartMeasurement; +@property (class, nonatomic, assign) BOOL appStartMeasurementHybridSDKMode; +@property (class, nonatomic, assign) BOOL framesTrackingMeasurementHybridSDKMode; @end \ No newline at end of file diff --git a/plugin/platforms/ios/src/NSSentry.m b/plugin/platforms/ios/src/NSSentry.m index d29693d..a1dc26d 100644 --- a/plugin/platforms/ios/src/NSSentry.m +++ b/plugin/platforms/ios/src/NSSentry.m @@ -3,6 +3,14 @@ @implementation NSSentrySDK ++ (void)captureEnvelope:(SentryEnvelope *)envelope +{ + return [PrivateSentrySDKOnly captureEnvelope:envelope]; +} ++ (void)storeEnvelope:(SentryEnvelope *)envelope +{ + return [PrivateSentrySDKOnly storeEnvelope:envelope]; +} + (nullable SentryEnvelope *)envelopeWithData:(NSData *)data { return [PrivateSentrySDKOnly envelopeWithData:data]; @@ -27,5 +35,12 @@ + (BOOL)isFramesTrackingRunning { return [PrivateSentrySDKOnly isFramesTrackingRunning]; } - ++ (void)setAppStartMeasurementHybridSDKMode:(BOOL)appStartMeasurementHybridSDKMode +{ + [PrivateSentrySDKOnly setAppStartMeasurementHybridSDKMode:appStartMeasurementHybridSDKMode]; +} ++ (void)setFramesTrackingMeasurementHybridSDKMode:(BOOL)framesTrackingMeasurementHybridSDKMode +{ + [PrivateSentrySDKOnly setFramesTrackingMeasurementHybridSDKMode:framesTrackingMeasurementHybridSDKMode]; +} @end diff --git a/src/client.ts b/src/client.ts index 15e27bd..e0d879f 100755 --- a/src/client.ts +++ b/src/client.ts @@ -105,6 +105,19 @@ export class NativescriptClient extends BaseClient { this._sendEnvelope(envelope); } + /** + * @inheritDoc + */ + // sendEvent(event: Event, hint = {}) { + // if (this._dsn) { + // if (NATIVE.sendEvent) { + // NATIVE.sendEvent(event, hint); + // } else { + // super.sendEvent(event, hint); + // } + // } + // } + /** * @inheritdoc */ diff --git a/src/index.ts b/src/index.ts index 2fe85a2..956876b 100755 --- a/src/index.ts +++ b/src/index.ts @@ -16,7 +16,6 @@ export { captureException, captureEvent, captureMessage, - configureScope, getHubFromCarrier, getCurrentHub, Hub, @@ -24,11 +23,12 @@ export { setContext, setExtra, setExtras, + withScope, + configureScope, setTag, setTags, setUser, - startTransaction, - withScope, + startTransaction } from '@sentry/core'; // We need to import it so we patch the hub with global functions diff --git a/src/integrations/devicecontext.ts b/src/integrations/devicecontext.ts index 83e5107..e277d0e 100755 --- a/src/integrations/devicecontext.ts +++ b/src/integrations/devicecontext.ts @@ -25,6 +25,7 @@ export class DeviceContext implements Integration { } try { + console.log('about to fetchNativeDeviceContexts'); const contexts = await NATIVE.fetchNativeDeviceContexts(); const context = contexts['context'] as Contexts ?? {}; diff --git a/src/integrations/nativescripterrorhandlers.ts b/src/integrations/nativescripterrorhandlers.ts index c691d1c..023c4ea 100755 --- a/src/integrations/nativescripterrorhandlers.ts +++ b/src/integrations/nativescripterrorhandlers.ts @@ -93,6 +93,7 @@ export class NativescriptErrorHandlers implements Integration { handlingFatal = false; private async globalHander(error: any, isFatal?: boolean) { + console.log('globalHander', error, isFatal); // We want to handle fatals, but only in production mode. const shouldHandleFatal = isFatal && !__DEV__; if (shouldHandleFatal) { diff --git a/src/scope.ts b/src/scope.ts index ed57941..21d0c34 100644 --- a/src/scope.ts +++ b/src/scope.ts @@ -1,4 +1,4 @@ -import { Scope } from '@sentry/hub'; +import { Scope } from '@sentry/core'; import { Attachment, Breadcrumb, User } from '@sentry/types'; import { NATIVE } from './wrapper'; @@ -6,90 +6,104 @@ import { NATIVE } from './wrapper'; /** * Extends the scope methods to set scope on the Native SDKs */ -export class NativescriptScope extends Scope { - /** - * @inheritDoc - */ - public setUser(user: User | null): this { - NATIVE.setUser(user); - return super.setUser(user); - } - /** - * @inheritDoc - */ - public setTag(key: string, value: string): this { - NATIVE.setTag(key, value); - return super.setTag(key, value); - } +const methods = ['addBreadcrumb', 'addAttachment']; - /** - * @inheritDoc - */ - public setTags(tags: { [key: string]: string }): this { - // As native only has setTag, we just loop through each tag key. - Object.keys(tags).forEach((key) => { - NATIVE.setTag(key, tags[key]); - }); - return super.setTags(tags); - } +// methods.forEach(m=>{ +// const originalMethod =Scope.prototype[m] as Function; +// Scope.prototype[m] = function(...args) { +// NATIVE[m](...args); +// return originalMethod.call(this, ...args); +// }; +// }); - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public setExtras(extras: { [key: string]: any }): this { - Object.keys(extras).forEach((key) => { - NATIVE.setExtra(key, extras[key]); - }); - return super.setExtras(extras); - } +// export class NativescriptScope extends Scope { +// /** +// * @inheritDoc +// */ +// public setUser(user: User | null): this { +// NATIVE.setUser(user); +// return super.setUser(user); +// } - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types,@typescript-eslint/no-explicit-any - public setExtra(key: string, extra: any): this { - NATIVE.setExtra(key, extra); - return super.setExtra(key, extra); - } +// /** +// * @inheritDoc +// */ +// public setTag(key: string, value: string): this { +// NATIVE.setTag(key, value); +// return super.setTag(key, value); +// } - /** - * @inheritDoc - */ - public addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number): this { - NATIVE.addBreadcrumb(breadcrumb); - return super.addBreadcrumb(breadcrumb, maxBreadcrumbs); - } +// /** +// * @inheritDoc +// */ +// public setTags(tags: { [key: string]: string }): this { +// // As native only has setTag, we just loop through each tag key. +// Object.keys(tags).forEach((key) => { +// NATIVE.setTag(key, tags[key]); +// }); +// return super.setTags(tags); +// } - /** - * @inheritDoc - */ - public clearBreadcrumbs(): this { - NATIVE.clearBreadcrumbs(); - return super.clearBreadcrumbs(); - } +// /** +// * @inheritDoc +// */ +// // eslint-disable-next-line @typescript-eslint/no-explicit-any +// public setExtras(extras: { [key: string]: any }): this { +// Object.keys(extras).forEach((key) => { +// NATIVE.setExtra(key, extras[key]); +// }); +// return super.setExtras(extras); +// } - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public setContext(key: string, context: { [key: string]: any } | null): this { - NATIVE.setContext(key, context); - return super.setContext(key, context); - } +// /** +// * @inheritDoc +// */ +// // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types,@typescript-eslint/no-explicit-any +// public setExtra(key: string, extra: any): this { +// NATIVE.setExtra(key, extra); +// return super.setExtra(key, extra); +// } - /** - * @inheritDoc - */ - public addAttachment(attachment: Attachment): this { - return super.addAttachment(attachment); - } +// /** +// * @inheritDoc +// */ +// public addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number): this { +// NATIVE.addBreadcrumb(breadcrumb); +// return super.addBreadcrumb(breadcrumb, maxBreadcrumbs); +// } - /** - * @inheritDoc - */ - public clearAttachments(): this { - return super.clearAttachments(); - } -} +// /** +// * @inheritDoc +// */ +// public clearBreadcrumbs(): this { +// NATIVE.clearBreadcrumbs(); +// return super.clearBreadcrumbs(); +// } + +// /** +// * @inheritDoc +// */ +// // eslint-disable-next-line @typescript-eslint/no-explicit-any +// public setContext(key: string, context: { [key: string]: any } | null): this { +// NATIVE.setContext(key, context); +// return this; +// // return super.setContext(key, context); +// } + +// /** +// * @inheritDoc +// */ +// public addAttachment(attachment: Attachment): this { +// console.log('test addAttachment', attachment); +// NATIVE.addAttachment(attachment); +// return super.addAttachment(attachment); +// } + +// /** +// * @inheritDoc +// */ +// public clearAttachments(): this { +// return super.clearAttachments(); +// } +// } diff --git a/src/sdk.ts b/src/sdk.ts index 05695ab..ede852d 100755 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -1,22 +1,22 @@ import { Integrations, defaultStackParser, getCurrentHub, defaultIntegrations as sentryDefaultIntegrations } from '@sentry/browser'; -import { getIntegrationsToSetup, initAndBind, setExtra } from '@sentry/core'; -import { Hub, makeMain } from '@sentry/hub'; +import { Hub, Scope, getIntegrationsToSetup, initAndBind, makeMain, setExtra } from '@sentry/core'; import { RewriteFrames } from '@sentry/integrations'; -import { Integration, Scope, StackFrame, UserFeedback } from '@sentry/types'; -import { getGlobalObject, logger, stackParserFromStackParserOptions } from '@sentry/utils'; +import { Integration, StackFrame, UserFeedback } from '@sentry/types'; +import { logger, stackParserFromStackParserOptions } from '@sentry/utils'; -import { NativescriptClientOptions, NativescriptOptions, NativescriptWrapperOptions } from './options'; +import { NativescriptClientOptions, NativescriptOptions } from './options'; import { NativescriptClient } from './client'; -import { NativescriptScope } from './scope'; -import { DebugSymbolicator, DeviceContext, NativescriptErrorHandlers, Release } from './integrations'; +import { DeviceContext, NativescriptErrorHandlers, Release } from './integrations'; import { EventOrigin } from './integrations/eventorigin'; +import { NativescriptErrorHandlersOptions } from './integrations/nativescripterrorhandlers'; import { SdkInfo } from './integrations/sdkinfo'; +// import { NativescriptScope } from './scope'; +import { NativescriptTracing } from './tracing'; import { makeNativescriptTransport } from './transports/native'; -import { NativescriptErrorHandlersOptions } from './integrations/nativescripterrorhandlers'; import { makeUtf8TextEncoder } from './transports/TextEncoder'; import { safeFactory, safeTracesSampler } from './utils/safe'; -import { NativescriptTracing } from './tracing'; +import { NATIVE } from './wrapper'; const IGNORED_DEFAULT_INTEGRATIONS = [ 'GlobalHandlers', // We will use the react-native internal handlers @@ -46,9 +46,9 @@ export let rewriteFrameIntegration: { */ export function init(passedOptions: NativescriptOptions): void { - const NativescriptHub = new Hub(undefined, new NativescriptScope()); + const NativescriptHub = new Hub(undefined, new Scope()); + // const NativescriptHub = new Hub(undefined, new NativescriptScope()); makeMain(NativescriptHub); - const options: NativescriptClientOptions & NativescriptOptions = { ...DEFAULT_OPTIONS, ...passedOptions, @@ -111,6 +111,7 @@ export function init(passedOptions: NativescriptOptions): void { new EventOrigin(), new SdkInfo() ]); + console.log('test', options.enableNative); if (!!options.enableNative) { defaultIntegrations.push(new DeviceContext()); } @@ -211,7 +212,9 @@ export function captureUserFeedback(feedback: UserFeedback): void { export function withScope(callback: (scope: Scope) => void): ReturnType { const safeCallback = (scope: Scope): void => { try { - callback(scope); + NATIVE.withScope(nscope=>{ + callback(scope); + }); } catch (e) { logger.error('Error while running withScope callback', e); } diff --git a/src/tracing/nstracing.ts b/src/tracing/nstracing.ts index 2d68a32..c3a0aa6 100644 --- a/src/tracing/nstracing.ts +++ b/src/tracing/nstracing.ts @@ -147,7 +147,7 @@ export class NativescriptTracing implements Integration { } = this.options; this._getCurrentHub = getCurrentHub; - + console.log('NativescriptTracing', this.options); if (enableAppStartTracking) { void this._instrumentAppStart(); } diff --git a/src/typings/ns.ios.d.ts b/src/typings/ns.ios.d.ts index 110cd0f..2897b38 100644 --- a/src/typings/ns.ios.d.ts +++ b/src/typings/ns.ios.d.ts @@ -18,4 +18,6 @@ declare class NSSentrySDK extends SentrySDK { static readonly installationID: string; static readonly isFramesTrackingRunning: boolean; + static appStartMeasurementHybridSDKMode: boolean; + static framesTrackingMeasurementHybridSDKMode: boolean; } diff --git a/src/wrapper.android.ts b/src/wrapper.android.ts index 162268a..ebbe315 100644 --- a/src/wrapper.android.ts +++ b/src/wrapper.android.ts @@ -1,15 +1,17 @@ import { Application, Utils } from '@nativescript/core'; import { android as androidApp } from '@nativescript/core/application'; -import { BaseEnvelopeItemHeaders, Breadcrumb, Envelope, EnvelopeItem, Event, SeverityLevel, User } from '@sentry/types'; +import { Attachment, BaseEnvelopeItemHeaders, Breadcrumb, Envelope, EnvelopeItem, Event, SeverityLevel, User } from '@sentry/types'; import { convertNativescriptFramesToSentryFrames, parseErrorStack } from './integrations/debugsymbolicator'; import { UserFeedback } from './wrapper'; import { rewriteFrameIntegration } from './sdk'; import { SentryError, logger, normalize } from '@sentry/utils'; import { NativescriptOptions } from './options'; -import { pointsFromBuffer } from '@nativescript-community/arraybuffers'; +import { createArrayBuffer, pointsFromBuffer } from '@nativescript-community/arraybuffers'; import { isHardCrash } from './misc'; import { utf8ToBytes } from './vendor'; import { SDK_NAME } from './version'; +import { TextEncoder } from '@nativescript/core/text'; +import { Scope } from '@sentry/core'; // function utf8ToBytes(str: string) {{ // return new java.lang.String(str).getBytes(java.nio.charset.StandardCharsets.UTF_8); // }} @@ -19,6 +21,27 @@ import { SDK_NAME } from './version'; // // } // const EOL = utf8ToBytes('\n')[0]; +function isArrayBuffer(value: unknown): boolean { + return (value instanceof ArrayBuffer || Object.prototype.toString.call(value) === '[object ArrayBuffer]'); +} +let encoder; +function strToTypedArray(str: string) { + if (!encoder) { + const charset = java.nio.charset.Charset.forName('UTF-8'); + encoder = charset.newEncoder(); + } + return new Uint8Array((ArrayBuffer as any).from(encoder.encode(java.nio.CharBuffer.wrap(str)))); +} + +function concatTypedArrays(a, b) { + if (!b || b.length === 0) return a; + if (!a || a.length === 0) return b; + const c = createArrayBuffer(a.length + b.length, true, false); + c.set(a, 0); + c.set(b, a.length); + return c; +} + export namespace NATIVE { let enableNative = true; const _DisabledNativeError = new SentryError('Native is disabled'); @@ -174,16 +197,16 @@ export namespace NATIVE { * the envelope. */ function _getBreadcrumbs(event: Event): Breadcrumb[] | undefined { - let breadcrumbs: Breadcrumb[] | undefined = event.breadcrumbs; + const breadcrumbs: Breadcrumb[] | undefined = event.breadcrumbs; const hardCrashed = isHardCrash(event); - if (event.breadcrumbs && !hardCrashed) { - breadcrumbs = undefined; - } + // if (event.breadcrumbs && !hardCrashed) { + // breadcrumbs = undefined; + // } breadcrumbs && breadcrumbs.forEach(b=>{ // fix for native SDK not supporting number - b.timestamp = new Date(b.timestamp).toISOString() as any; + b.timestamp = new Date(b.timestamp* 1000).toISOString() as any; }); return breadcrumbs && breadcrumbs.length ? breadcrumbs : undefined; @@ -238,7 +261,7 @@ export namespace NATIVE { const event = _processLevels(itemPayload as Event); // fix for native SDK - event.timestamp = new Date(event.timestamp).toISOString() as any; + event.timestamp = new Date(event.timestamp* 1000).toISOString() as any; if ('message' in event) { // @ts-ignore Android still uses the old message object, without this the serialization of events will break. event.message = { message: event.message }; @@ -255,7 +278,17 @@ export namespace NATIVE { console.warn('Event was skipped as native SDK is not enabled.'); return; } - + // let startTime = Date.now(); + const envelopeBytes = prepareEnvelope(envelope); + // console.log('prepareEnvelope', Date.now() - startTime, 'ms', envelopeBytes.length); + // startTime = Date.now(); + // const envelopeBytesNative = prepareEnvelopeNative(envelope); + await captureEnvelope(envelopeBytes); + if (sentryOptions.flushSendEvent) { + flush(0); + } + } + export function prepareEnvelope(envelope: Envelope) { const [EOL] = utf8ToBytes('\n'); const [envelopeHeader, envelopeItems] = envelope; @@ -273,9 +306,10 @@ export namespace NATIVE { bytesPayload = utf8ToBytes(itemPayload); } else if (itemPayload instanceof Uint8Array) { bytesPayload = [...itemPayload]; + } else if (itemPayload instanceof ArrayBuffer) { + bytesPayload = [...new Uint8Array(itemPayload)]; } else { bytesPayload = utf8ToBytes(JSON.stringify(itemPayload)); - console.log('bytesPayload', JSON.stringify(itemPayload)); if (!hardCrashed) { hardCrashed = isHardCrash(itemPayload); } @@ -284,17 +318,53 @@ export namespace NATIVE { // Content type is not inside BaseEnvelopeItemHeaders. (itemHeader as BaseEnvelopeItemHeaders).content_type = 'application/json'; (itemHeader as BaseEnvelopeItemHeaders).length = bytesPayload.length; - const serializedItemHeader = JSON.stringify(itemHeader); + const serializedItemHeader = JSON.stringify(itemHeader) + '\n'; envelopeBytes.push(...utf8ToBytes(serializedItemHeader)); - envelopeBytes.push(EOL); + // envelopeBytes.push(EOL); envelopeBytes.push(...bytesPayload); envelopeBytes.push(EOL); } - await captureEnvelope(envelopeBytes, { store: hardCrashed }); - if (sentryOptions.flushSendEvent) { - flush(0); + return envelopeBytes; + } + + export function prepareEnvelopeNative(envelope: Envelope) { + const [envelopeHeader, envelopeItems] = envelope; + + const headerString = JSON.stringify(envelopeHeader); + let envelopeBytes = strToTypedArray(headerString + '\n'); + + let hardCrashed: boolean = false; + for (const rawItem of envelopeItems) { + const [itemHeader, itemPayload] = _processItem(rawItem); + + let bytesPayload; + if (typeof itemPayload === 'string') { + bytesPayload = strToTypedArray(itemPayload); + } else if (itemPayload instanceof Uint8Array) { + bytesPayload = itemPayload; + } else { + bytesPayload = strToTypedArray(JSON.stringify(itemPayload)); + // console.log('bytesPayload', JSON.stringify(itemPayload)); + if (!hardCrashed) { + hardCrashed = isHardCrash(itemPayload); + } + } + + // Content type is not inside BaseEnvelopeItemHeaders. + (itemHeader as BaseEnvelopeItemHeaders).content_type = 'application/json'; + (itemHeader as BaseEnvelopeItemHeaders).length = bytesPayload.length; + // const serializedItemHeader = JSON.stringify(itemHeader) + '\n'; + const serializedItemHeaderBytes = strToTypedArray(JSON.stringify(itemHeader) + '\n'); + + const rawItemBytes = createArrayBuffer(serializedItemHeaderBytes.length + bytesPayload.length + 1, true, false); + rawItemBytes.set(serializedItemHeaderBytes, 0); + rawItemBytes.set(bytesPayload, serializedItemHeaderBytes.length); + rawItemBytes[serializedItemHeaderBytes.length + bytesPayload.length] = '\n'.charCodeAt(0); + + envelopeBytes = concatTypedArrays(envelopeBytes, rawItemBytes); } + return envelopeBytes; } @@ -464,13 +534,12 @@ export namespace NATIVE { // }; // } - export async function captureEnvelope(envelope: string | Uint8Array | number[], {store}: {store?: boolean}) { - console.log('captureEnvelope', envelope.length, nSentryOptions.getOutboxPath()); + export async function captureEnvelope(envelope: string | Uint8Array | number[], {store}: {store?: boolean} = {}) { + console.log('captureEnvelope', envelope.length, nSentryOptions.getOutboxPath(), ArrayBuffer.isView(envelope), Array.isArray(envelope)); try { const outboxPath = new java.io.File(nSentryOptions.getOutboxPath(), java.util.UUID.randomUUID().toString()); const out = new java.io.FileOutputStream(outboxPath); - const isBufferView = ArrayBuffer.isView(envelope); - if (isBufferView) { + if (ArrayBuffer.isView(envelope)) { out.write(pointsFromBuffer(envelope, true, false)); } else if (Array.isArray(envelope)) { out.write(envelope); @@ -479,7 +548,7 @@ export namespace NATIVE { } return true; } catch(err) { - console.error('captureEnvelope error', err); + console.error('captureEnvelope error', err, err.stack); return false; } } @@ -825,138 +894,179 @@ export namespace NATIVE { // }); } - export function captureUserFeedback(feedback: UserFeedback) { - if (!enableNative) { - return; - } - const userFeedback = new io.sentry.UserFeedback(new io.sentry.protocol.SentryId(feedback.eventId)); - if (feedback.comments) { - userFeedback.setComments(feedback.comments); - } - if (feedback.email) { - userFeedback.setEmail(feedback.email); - } - if (feedback.name) { - userFeedback.setName(feedback.name); - } - io.sentry.Sentry.captureUserFeedback(userFeedback); - } + // export function captureUserFeedback(feedback: UserFeedback) { + // if (!enableNative) { + // return; + // } + // const userFeedback = new io.sentry.UserFeedback(new io.sentry.protocol.SentryId(feedback.eventId)); + // if (feedback.comments) { + // userFeedback.setComments(feedback.comments); + // } + // if (feedback.email) { + // userFeedback.setEmail(feedback.email); + // } + // if (feedback.name) { + // userFeedback.setName(feedback.name); + // } + // io.sentry.Sentry.captureUserFeedback(userFeedback); + // } - export function setUser(user: User | null, otherUserKeys) { - if (!enableNative) { - return; - } - io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ - run(scope) { - if (user == null && otherUserKeys == null) { - scope.setUser(null); - } else { - const userInstance = new io.sentry.protocol.User(); - - if (user) { - if (user.email) { - userInstance.setEmail(user.email); - } + // export function setUser(user: User | null, otherUserKeys) { + // if (!enableNative) { + // return; + // } + // io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ + // run(scope) { + // if (user == null && otherUserKeys == null) { + // scope.setUser(null); + // } else { + // const userInstance = new io.sentry.protocol.User(); + + // if (user) { + // if (user.email) { + // userInstance.setEmail(user.email); + // } - if (user.id) { - userInstance.setId(user.id); - } + // if (user.id) { + // userInstance.setId(user.id); + // } - if (user.username) { - userInstance.setUsername(user.username); - } + // if (user.username) { + // userInstance.setUsername(user.username); + // } - if (user.ip_address) { - userInstance.setIpAddress(user.ip_address); - } - } + // if (user.ip_address) { + // userInstance.setIpAddress(user.ip_address); + // } + // } - if (otherUserKeys ) { - userInstance.setOthers(getNativeHashMap(otherUserKeys)); - } - scope.setUser(userInstance); - } - } - })); + // if (otherUserKeys ) { + // userInstance.setOthers(getNativeHashMap(otherUserKeys)); + // } + // scope.setUser(userInstance); + // } + // } + // })); - } - export function setTag(key: string, value: string) { - if (!enableNative) { - return; - } - io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ - run(scope) { - scope.setTag(key, value); - } - })); - } + // } + // export function setTag(key: string, value: string) { + // if (!enableNative) { + // return; + // } + // io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ + // run(scope) { + // scope.setTag(key, value); + // } + // })); + // } - export function setExtra(key: string, extra: string) { - if (!enableNative) { - return; - } - io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ - run(scope) { - scope.setExtra(key, extra); - } - })); - } + // export function setExtra(key: string, extra: string) { + // if (!enableNative) { + // return; + // } + // io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ + // run(scope) { + // scope.setExtra(key, extra); + // } + // })); + // } - export function addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number) { - if (!enableNative) { - return; - } - io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ - run(scope) { - const breadcrumbInstance = new io.sentry.Breadcrumb(); + // export function withScope(callback: (scope: Scope) => void) { + // io.sentry.Sentry.withScope(new io.sentry.ScopeCallback({ + // run(nscope) { + // // nscope is ignored + // console.log('native withScope', nscope); + // callback(nscope as any); + // } + // })); + // } - if (breadcrumb.message) { - breadcrumbInstance.setMessage(breadcrumb.message); - } + // export function addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number) { + // if (!enableNative) { + // return; + // } + // io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ + // run(scope) { + // try { + // console.log('addBreadcrumb', breadcrumb, scope); - if (breadcrumb.type) { - breadcrumbInstance.setType(breadcrumb.type); - } + // const breadcrumbInstance = new io.sentry.Breadcrumb(); - if (breadcrumb.category) { - breadcrumbInstance.setCategory(breadcrumb.category); - } + // if (breadcrumb.message) { + // breadcrumbInstance.setMessage(breadcrumb.message); + // } - if (breadcrumb.level) { - breadcrumbInstance.setLevel(eventLevel(breadcrumb.level)); - } + // if (breadcrumb.type) { + // breadcrumbInstance.setType(breadcrumb.type); + // } - if (breadcrumb.data) { - Object.keys(breadcrumb.data).forEach(key => { - breadcrumbInstance.setData(key, breadcrumb.data[key]); - }); - } + // if (breadcrumb.category) { + // breadcrumbInstance.setCategory(breadcrumb.category); + // } - scope.addBreadcrumb(breadcrumbInstance); - } - })); - } - export function clearBreadcrumbs() { - if (!enableNative) { - return; - } - io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ - run(scope) { - scope.clearBreadcrumbs(); - } - })); - } - export function setContext(key: string, context: { [key: string]: any } | null) { - if (!enableNative) { - return; - } - io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ - run(scope) { - scope.setContexts(key, getNativeHashMap(context)); - } - })); - } + // if (breadcrumb.level) { + // breadcrumbInstance.setLevel(eventLevel(breadcrumb.level)); + // } + + // if (breadcrumb.data) { + // Object.keys(breadcrumb.data).forEach(key => { + // breadcrumbInstance.setData(key, breadcrumb.data[key]); + // }); + // } + + // scope.addBreadcrumb(breadcrumbInstance); + // } catch (error) { + // console.error('addBreadcrumb', error, error.stack); + + // } + // } + // })); + // } + // export function addAttachment(attachment: Attachment) { + // if (!enableNative) { + // return; + // } + // io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ + // run(scope) { + // console.log('addAttachment', attachment, scope); + // try { + // if (attachment.data) { + // if (typeof attachment.data === 'string') { + // attachment.data = new TextEncoder().encode(attachment.data); + // } + // const bytes = pointsFromBuffer(attachment.data, true, false); + // console.log('addAttachment', typeof attachment.data, attachment.data.length, bytes, bytes.length); + // scope.addAttachment(new io.sentry.Attachment(bytes, attachment.filename)); + // } else { + // scope.addAttachment(new io.sentry.Attachment(attachment.filename)); + // } + // } catch (error) { + // console.error('addAttachment', error, error.stack); + // } + // } + // })); + // } + // export function clearBreadcrumbs() { + // if (!enableNative) { + // return; + // } + // io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ + // run(scope) { + // scope.clearBreadcrumbs(); + // } + // })); + // } + // export function setContext(key: string, context: { [key: string]: any } | null) { + // if (!enableNative) { + // return; + // } + // io.sentry.Sentry.configureScope(new io.sentry.ScopeCallback({ + // run(scope) { + // scope.setContexts(key, getNativeHashMap(context)); + // } + // })); + // } function isFrameMetricsAggregatorAvailable() { return frameMetricsAggregator != null; diff --git a/src/wrapper.d.ts b/src/wrapper.d.ts index 5735a1d..3776415 100644 --- a/src/wrapper.d.ts +++ b/src/wrapper.d.ts @@ -1,4 +1,5 @@ import { + Attachment, AttachmentItem, BaseEnvelopeItemHeaders, Breadcrumb, @@ -14,6 +15,7 @@ import { } from '@sentry/types'; import { NativescriptOptions } from './options'; import { SentryError, logger } from '@sentry/utils'; +import { Hub, Scope } from '@sentry/core'; export interface NativeAppStartResponse { isColdStart: boolean; @@ -43,7 +45,7 @@ export namespace NATIVE { const nativeClientAvailable: boolean; const nativeTransport: boolean; let enableNative: boolean; - function sendEvent(event: Event): Promise; + function sendEvent(event: Event, hint?): Promise; function sendEnvelope(envelope: Envelope): Promise; function initNativeSdk(options: NativescriptOptions): Promise; function closeNativeSdk(): Promise; @@ -74,11 +76,13 @@ export namespace NATIVE { function setExtra(key: string, extra: any); function addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number); + function addAttachment(attachment: Attachment); function clearBreadcrumbs(); function setContext(key: string, context: { [key: string]: any } | null); + function withScope(callback: (scope: Scope) => void): ReturnType; function utf8ToBytes(str: string): Uint8Array; } diff --git a/src/wrapper.ios.ts b/src/wrapper.ios.ts index 4adbf4d..adec094 100644 --- a/src/wrapper.ios.ts +++ b/src/wrapper.ios.ts @@ -1,11 +1,69 @@ +import { pointsFromBuffer } from '@nativescript-community/arraybuffers'; import { getClass } from '@nativescript/core/utils/types'; -import { BaseEnvelopeItemHeaders, Breadcrumb, Envelope, EnvelopeItem, Event, SeverityLevel, User } from '@sentry/types'; +import { Scope } from '@sentry/core'; +import { Attachment, BaseEnvelopeItemHeaders, Breadcrumb, Envelope, EnvelopeItem, Event, SeverityLevel, User } from '@sentry/types'; import { SentryError, logger } from '@sentry/utils'; import { isHardCrash } from './misc'; import { NativescriptOptions } from './options'; import { utf8ToBytes } from './vendor'; import { UserFeedback } from './wrapper'; +const numberHasDecimals = function (value: number): boolean { + return !(value % 1 === 0); +}; + +const numberIs64Bit = function (value: number): boolean { + return value < -Math.pow(2, 31) + 1 || value > Math.pow(2, 31) - 1; +}; +function dataSerialize (data?: any, wrapPrimitives?: boolean) { + switch (typeof data) { + case 'string': + case 'boolean': { + return data; + } + case 'number': { + const hasDecimals = numberHasDecimals(data); + if (numberIs64Bit(data)) { + if (hasDecimals) { + return NSNumber.alloc().initWithDouble(data); + } else { + return NSNumber.alloc().initWithLongLong(data); + } + } else { + if (hasDecimals) { + return NSNumber.alloc().initWithFloat(data); + } else { + return data; + } + } + } + + case 'object': { + if (data instanceof Date) { + return NSDate.dateWithTimeIntervalSince1970(data.getTime() / 1000); + } + + if (!data) { + return null; + } + + if (Array.isArray(data)) { + return NSArray.arrayWithArray((data as any).map(dataSerialize)); + } + + const node = {} as any; + Object.keys(data).forEach(function (key) { + const value = data[key]; + node[key] = dataSerialize(value, wrapPrimitives); + }); + return NSDictionary.dictionaryWithDictionary(node); + } + + default: + return null; + } +}; + export namespace NATIVE { let enableNative = true; const _DisabledNativeError = new SentryError('Native is disabled'); @@ -106,11 +164,77 @@ export namespace NATIVE { if (itemHeader.type === 'event' || itemHeader.type === 'transaction') { const event = _processLevels(itemPayload as Event); + event.breadcrumbs = _getBreadcrumbs(event); return [itemHeader, event]; } return item; } + function setEventEnvironmentTag(event: SentryEvent, environment: string) { + event.tags = NSDictionary.dictionaryWithDictionary({ + 'event.origin': 'ios', + 'event.environment': environment + } as any); + } + function setEventOriginTag(event: SentryEvent) { + if (event.sdk) { + const sdkName = event.sdk.objectForKey('name'); + + // If the event is from react native, it gets set there and we do not handle + // it here. + if (sdkName === 'sentry.cocoa') { + setEventEnvironmentTag(event, 'native'); + } + } + } + export async function sendEvent(event: Event) { + try { + // Process and convert deprecated levels + // event.level = event.level ? _processLevel(event.level) : undefined; + console.error('sendEvent', JSON.stringify(event)); + + const payload = { + ...event, + message: { + message: event.message + } + }; + payload.tags = payload.tags || {}; + + payload.tags['event.origin'] = 'ios'; + payload.tags['event.environment'] = 'nativescript'; + // Serialize and remove any instances that will crash the native bridge such as Spans + const serializedPayload = JSON.parse(JSON.stringify(payload)); + const eventId = SentryId.alloc().initWithUUIDString(event.event_id); + const envelopeHeader = SentryEnvelopeHeader.alloc().initWithId(eventId); + const envelopeItemData = NSJSONSerialization.dataWithJSONObjectOptionsError(serializedPayload, 0); + + let itemType = payload.type; + if (!itemType) { + // Default to event type. + itemType = 'event' as any; + } + + const envelopeItemHeader = SentryEnvelopeItemHeader.alloc().initWithTypeLength(itemType, envelopeItemData.length); + const envelopeItem = SentryEnvelopeItem.alloc().initWithHeaderData(envelopeItemHeader, envelopeItemData); + + const envelope = SentryEnvelope.alloc().initWithHeaderSingleItem(envelopeHeader, envelopeItem); + + if (event.level === 'fatal') { + // captureEvent queues the event for submission, so storing to disk happens asynchronously + // We need to make sure the event is written to disk before resolving the promise. + // This could be replaced by SentrySDK.flush() when available. + NSSentrySDK.storeEnvelope(envelope); + } else { + NSSentrySDK.captureEnvelope(envelope); + } + // if (sentryOptions.flushSendEvent === true) { + + // } + } catch (err) { + console.error('sendEvent', err, err.stack); + } + } /** * Sending the envelope over the bridge to native * @param envelope Envelope @@ -137,6 +261,8 @@ export namespace NATIVE { bytesPayload = utf8ToBytes(itemPayload); } else if (itemPayload instanceof Uint8Array) { bytesPayload = [...itemPayload]; + } else if (itemPayload instanceof ArrayBuffer) { + bytesPayload = [...new Uint8Array(itemPayload)]; } else { bytesPayload = utf8ToBytes(JSON.stringify(itemPayload)); if (!hardCrashed) { @@ -172,49 +298,98 @@ export namespace NATIVE { let sentryOptions: NativescriptOptions; let nSentryOptions: SentryOptions; export async function initNativeSdk(originalOptions: NativescriptOptions = {}): Promise { - const options = { - enableNative: true, - ...originalOptions, - }; - if (!options.enableNative) { - if (options.enableNativeNagger) { - console.warn('Note: Native Sentry SDK is disabled.'); + try { + + const options = { + enableNative: true, + ...originalOptions, + }; + if (!options.enableNative) { + if (options.enableNativeNagger) { + console.warn('Note: Native Sentry SDK is disabled.'); + } + enableNative = false; + return false; } - enableNative = false; - return false; - } - if (!options.autoInitializeNativeSdk) { - if (options.enableNativeNagger) { + if (!options.autoInitializeNativeSdk) { + if (options.enableNativeNagger) { + console.warn( + 'Note: Native Sentry SDK was not initialized automatically, you will need to initialize it manually. If you wish to disable the native SDK and get rid of this warning, pass enableNative: false' + ); + } + return false; + } + + if (!options.dsn) { console.warn( - 'Note: Native Sentry SDK was not initialized automatically, you will need to initialize it manually. If you wish to disable the native SDK and get rid of this warning, pass enableNative: false' + 'Warning: No DSN was provided. The Sentry SDK will be disabled. Native SDK will also not be initalized.' ); + return false; } - return false; - } + sentryOptions = options; + const {tracesSampleRate, tracesSampler, beforeSend, ...toPassOptions} = options; + + Object.keys(toPassOptions).forEach((k) => { + const value = toPassOptions[k]; + const valuetype = typeof value; + if (valuetype === 'undefined' || valuetype === 'object' || valuetype === 'function' || Array.isArray(value) ) { + delete toPassOptions[k]; + } + }); + console.log('toPassOptions', Object.keys(toPassOptions)); + const mutDict = NSMutableDictionary.alloc().initWithDictionary(dataSerialize(toPassOptions) as any); - if (!options.dsn) { - console.warn( - 'Warning: No DSN was provided. The Sentry SDK will be disabled. Native SDK will also not be initalized.' - ); - return false; - } - sentryOptions = options; - NSSentrySDK.startWithConfigureOptions((obj) => { - const beforeSend = options.beforeSend; - delete options.beforeSend; - obj.beforeSend = (event: SentryEvent)=>{ + nSentryOptions = SentryOptions.alloc().initWithDictDidFailWithError(mutDict as any); + nSentryOptions.beforeSend = (event: SentryEvent)=>{ + console.log('beforeSend', event); if (beforeSend) { beforeSend(event as any, null); } + setEventOriginTag(event); return event; }; - Object.keys(options).forEach((k) => { - obj[k] = options[k]; - }); - nSentryOptions = obj; - }); - return (true); + if (toPassOptions.hasOwnProperty('enableNativeCrashHandling')) { + if (!toPassOptions.enableNativeCrashHandling) { + const integrations = nSentryOptions.integrations.mutableCopy(); + // console.log('integrations', typeof nSentryOptions, nSentryOptions.length, nSentryOptions[0]); + integrations.removeObject('SentryCrashIntegration'); + sentryOptions.integrations = integrations; + } + } + + if (toPassOptions.hasOwnProperty('enableAutoPerformanceTracking')) { + NSSentrySDK.appStartMeasurementHybridSDKMode = toPassOptions.enableAutoPerformanceTracking; + NSSentrySDK.framesTrackingMeasurementHybridSDKMode = toPassOptions.enableAutoPerformanceTracking; + } + NSSentrySDK.startWithOptionsObject(nSentryOptions); + + // NSSentrySDK.startWithConfigureOptions((obj) => { + // const beforeSend = options.beforeSend; + // delete options.beforeSend; + + // obj.beforeSend = (event: SentryEvent)=>{ + // console.log('beforeSend', event); + // if (beforeSend) { + // beforeSend(event as any, null); + // } + // return event; + // }; + // Object.keys(options).forEach((k) => { + // if (options[k] !== undefined ) { + // // console.log('setting sentry option',k, obj.hasOwnProperty(k), options[k] ); + // obj[k] = options[k]; + + // } + // }); + // nSentryOptions = obj; + // }); + return (true); + } catch (error) { + enableNative = false; + console.error('initNativeSdk', error, error.stack); + return false; + } } // export function nativeLogLevel(level: number) { @@ -307,40 +482,48 @@ export namespace NATIVE { } const contexts = {}; NSSentrySDK.configureScope((scope) => { - const serializedScope = scope.serialize(); - const tempContexts = (serializedScope.valueForKey('context')); - const tempUser = serializedScope.valueForKey('user'); - const user = {}; - if (tempUser) { - Object.assign(user, dictToJSON(tempUser.valueForKey('user'))); - } else { - user['id'] = NSSentrySDK.installationID; - } - contexts['user'] = user; + try { + + const serializedScope = scope.serialize(); + // console.log('toto' , dictToJSON(serializedScope), NSString.alloc().initWithDataEncoding(NSJSONSerialization.dataWithJSONObjectOptionsError(serializedScope, 0), NSUTF8StringEncoding)); + // const context2 = SentryEvent.alloc().init().context; + // console.log('context2' , context2 && dictToJSON(context2)); + const tempContexts = (serializedScope.valueForKey('context')); + const tempUser = serializedScope.valueForKey('user'); + const user = {}; + if (tempUser) { + Object.assign(user, dictToJSON(tempUser.valueForKey('user'))); + } else { + user['id'] = NSSentrySDK.installationID; + } + contexts['user'] = user; - if (tempContexts) { - contexts['context'] = dictToJSON(tempContexts); + if (tempContexts) { + contexts['context'] = dictToJSON(tempContexts); + } + } catch (error) { + console.error('fetchNativeDeviceContexts', error, error.stack); } }); return (contexts); } - export function captureUserFeedback(feedback: UserFeedback) { - if (!enableNative) { - return; - } - const userFeedback = SentryUserFeedback.alloc().initWithEventId(SentryId.alloc().initWithUUIDString(feedback.eventId)); - if (feedback.comments) { - userFeedback.comments = feedback.comments; - } - if (feedback.email) { - userFeedback.email = feedback.email; - } - if (feedback.name) { - userFeedback.name = feedback.name; - } - NSSentrySDK.captureUserFeedback(userFeedback); - } + // export function captureUserFeedback(feedback: UserFeedback) { + // if (!enableNative) { + // return; + // } + // const userFeedback = SentryUserFeedback.alloc().initWithEventId(SentryId.alloc().initWithUUIDString(feedback.eventId)); + // if (feedback.comments) { + // userFeedback.comments = feedback.comments; + // } + // if (feedback.email) { + // userFeedback.email = feedback.email; + // } + // if (feedback.name) { + // userFeedback.name = feedback.name; + // } + // NSSentrySDK.captureUserFeedback(userFeedback); + // } function eventLevel(level) { switch (level) { case 'fatal': @@ -357,97 +540,125 @@ export namespace NATIVE { } } - export function setUser(user: User | null, otherUserKeys) { - if (!enableNative) { - return; - } - NSSentrySDK.configureScope((scope: SentryScope) => { - if (!user && !otherUserKeys) { - scope.setUser(null); - } else { - const userInstance = SentryUser.alloc().init(); - - if (user) { - userInstance.userId = user.id; - userInstance.email = user.email; - userInstance.username = user.username; - } - - if (otherUserKeys) { - const nStr = NSString.stringWithString(JSON.stringify(otherUserKeys)); - const nData = nStr.dataUsingEncoding(NSUTF8StringEncoding); - userInstance.data = NSJSONSerialization.JSONObjectWithDataOptionsError(nData, 0); - } - - scope.setUser(userInstance); - } - }); - } - export function setTag(key: string, value: string) { - if (!enableNative) { - return; - } - NSSentrySDK.configureScope((scope: SentryScope) => { - scope.setTagValueForKey(value, key); - }); - } + // export function setUser(user: User | null, otherUserKeys) { + // if (!enableNative) { + // return; + // } + // NSSentrySDK.configureScope((scope: SentryScope) => { + // if (!user && !otherUserKeys) { + // scope.setUser(null); + // } else { + // const userInstance = SentryUser.alloc().init(); + + // if (user) { + // userInstance.userId = user.id; + // userInstance.email = user.email; + // userInstance.username = user.username; + // } + + // if (otherUserKeys) { + // const nStr = NSString.stringWithString(JSON.stringify(otherUserKeys)); + // const nData = nStr.dataUsingEncoding(NSUTF8StringEncoding); + // userInstance.data = NSJSONSerialization.JSONObjectWithDataOptionsError(nData, 0); + // } + + // scope.setUser(userInstance); + // } + // }); + // } + // export function setTag(key: string, value: string) { + // if (!enableNative) { + // return; + // } + // NSSentrySDK.configureScope((scope: SentryScope) => { + // scope.setTagValueForKey(value, key); + // }); + // } - export function setExtra(key: string, extra: any) { - if (!enableNative) { - return; - } - NSSentrySDK.configureScope((scope: SentryScope) => { - scope.setExtraValueForKey(extra, key); - }); - } + // export function setExtra(key: string, extra: any) { + // if (!enableNative) { + // return; + // } + // NSSentrySDK.configureScope((scope: SentryScope) => { + // scope.setExtraValueForKey(extra, key); + // }); + // } - export function addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number) { - if (!enableNative) { - return; - } - NSSentrySDK.configureScope((scope: SentryScope) => { - const breadcrumbInstance = SentryBreadcrumb.alloc().init(); + // export function addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number) { + // if (!enableNative) { + // return; + // } + // NSSentrySDK.configureScope((scope: SentryScope) => { + // const breadcrumbInstance = SentryBreadcrumb.alloc().init(); - if (breadcrumb.level) { - breadcrumbInstance.level = eventLevel(breadcrumb.level); + // if (breadcrumb.level) { + // breadcrumbInstance.level = eventLevel(breadcrumb.level); - } - breadcrumbInstance.category = breadcrumb.category; + // } + // breadcrumbInstance.category = breadcrumb.category; - breadcrumbInstance.type = breadcrumb.type; + // breadcrumbInstance.type = breadcrumb.type; - breadcrumbInstance.message = breadcrumb.message; - if (breadcrumb.data) { - const nStr = NSString.stringWithString(JSON.stringify(breadcrumb.data)); - const nData = nStr.dataUsingEncoding(NSUTF8StringEncoding); - breadcrumbInstance.data = NSJSONSerialization.JSONObjectWithDataOptionsError(nData, 0); - } + // breadcrumbInstance.message = breadcrumb.message; + // if (breadcrumb.data) { + // const nStr = NSString.stringWithString(JSON.stringify(breadcrumb.data)); + // const nData = nStr.dataUsingEncoding(NSUTF8StringEncoding); + // breadcrumbInstance.data = NSJSONSerialization.JSONObjectWithDataOptionsError(nData, 0); + // } - scope.addBreadcrumb(breadcrumbInstance); - }); - } - export function clearBreadcrumbs() { - if (!enableNative) { - return; - } - NSSentrySDK.configureScope((scope: SentryScope) => { - scope.clearBreadcrumbs(); - }); - } - export function setContext(key: string, context: { [key: string]: any } | null) { - if (!enableNative) { - return; - } - NSSentrySDK.configureScope((scope: SentryScope) => { - if (!context) { - scope.setContextValueForKey(null, key); - } else { - const nStr = NSString.stringWithString(JSON.stringify(context)); - const nData = nStr.dataUsingEncoding(NSUTF8StringEncoding); - scope.setContextValueForKey(NSJSONSerialization.JSONObjectWithDataOptionsError(nData, 0), key); - } - }); - } + // scope.addBreadcrumb(breadcrumbInstance); + // }); + // } + // let scopeScope = null; + // export function withScope(callback: (scope: Scope) => void) { + // scopeScope = SentryScope.alloc().init(); + // // NSSentrySDK.withScope(new io.sentry.ScopeCallback({ + // // run(nscope) { + // // nscope is ignored + // // console.log('native withScope', nscope); + // callback(null); + // scopeScope = null; + // // } + // // })); + // } + // export function addAttachment(attachment: Attachment) { + // if (!enableNative) { + // return; + // } + // NSSentrySDK.configureScope((scope: SentryScope) => { + + // if (attachment.data) { + // if (typeof attachment.data === 'string') { + // attachment.data = new TextEncoder().encode(attachment.data); + // } + // scope.addAttachment(SentryAttachment.alloc().initWithDataFilenameContentType(attachment.data.buffer as any, attachment.filename, attachment.contentType)); + // } else { + // scope.addAttachment(SentryAttachment.alloc().initWithPath(attachment.filename)); + // } + // }); + // } + // export function clearBreadcrumbs() { + // if (!enableNative) { + // return; + // } + // NSSentrySDK.configureScope((scope: SentryScope) => { + // scope.clearBreadcrumbs(); + // }); + // } + // export function setContext(key: string, context: { [key: string]: any } | null) { + // if (!enableNative) { + // return; + // } + // NSSentrySDK.configureScope((scope: SentryScope) => { + // if (!context) { + // scope.setContextValueForKey(null, key); + // } else { + // const nStr = NSString.stringWithString(JSON.stringify(context)); + // const nData = nStr.dataUsingEncoding(NSUTF8StringEncoding); + // scope.setContextValueForKey(NSJSONSerialization.JSONObjectWithDataOptionsError(nData, 0), key); + // } + // }); + // } export function disableNativeFramesTracking() { // only for android