Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F8390906
baseclient.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
23 KB
Subscribers
None
baseclient.js
View Options
import
{
makeDsn
,
logger
,
checkOrSetAlreadyCaught
,
isPrimitive
,
resolvedSyncPromise
,
addItemToEnvelope
,
createAttachmentEnvelopeItem
,
SyncPromise
,
uuid4
,
dateTimestampInSeconds
,
normalize
,
truncate
,
rejectedSyncPromise
,
SentryError
,
isThenable
,
isPlainObject
}
from
'@sentry/utils'
;
import
{
getEnvelopeEndpointWithUrlEncodedAuth
}
from
'./api.js'
;
import
{
createEventEnvelope
,
createSessionEnvelope
}
from
'./envelope.js'
;
import
{
setupIntegrations
}
from
'./integration.js'
;
import
{
Scope
}
from
'./scope.js'
;
import
{
updateSession
}
from
'./session.js'
;
const
ALREADY_SEEN_ERROR
=
"Not capturing exception because it's already been captured."
;
/**
* Base implementation for all JavaScript SDK clients.
*
* Call the constructor with the corresponding options
* specific to the client subclass. To access these options later, use
* {@link Client.getOptions}.
*
* If a Dsn is specified in the options, it will be parsed and stored. Use
* {@link Client.getDsn} to retrieve the Dsn at any moment. In case the Dsn is
* invalid, the constructor will throw a {@link SentryException}. Note that
* without a valid Dsn, the SDK will not send any events to Sentry.
*
* Before sending an event, it is passed through
* {@link BaseClient._prepareEvent} to add SDK information and scope data
* (breadcrumbs and context). To add more custom information, override this
* method and extend the resulting prepared event.
*
* To issue automatically created events (e.g. via instrumentation), use
* {@link Client.captureEvent}. It will prepare the event and pass it through
* the callback lifecycle. To issue auto-breadcrumbs, use
* {@link Client.addBreadcrumb}.
*
* @example
* class NodeClient extends BaseClient<NodeOptions> {
* public constructor(options: NodeOptions) {
* super(options);
* }
*
* // ...
* }
*/
class
BaseClient
{
/** Options passed to the SDK. */
/** The client Dsn, if specified in options. Without this Dsn, the SDK will be disabled. */
/** Array of set up integrations. */
__init
()
{
this
.
_integrations
=
{};}
/** Indicates whether this client's integrations have been set up. */
__init2
()
{
this
.
_integrationsInitialized
=
false
;}
/** Number of calls being processed */
__init3
()
{
this
.
_numProcessing
=
0
;}
/** Holds flushable */
__init4
()
{
this
.
_outcomes
=
{};}
/**
* Initializes this client instance.
*
* @param options Options for the client.
*/
constructor
(
options
)
{;
BaseClient
.
prototype
.
__init
.
call
(
this
);
BaseClient
.
prototype
.
__init2
.
call
(
this
);
BaseClient
.
prototype
.
__init3
.
call
(
this
);
BaseClient
.
prototype
.
__init4
.
call
(
this
);
this
.
_options
=
options
;
if
(
options
.
dsn
)
{
this
.
_dsn
=
makeDsn
(
options
.
dsn
);
const
url
=
getEnvelopeEndpointWithUrlEncodedAuth
(
this
.
_dsn
,
options
);
this
.
_transport
=
options
.
transport
({
recordDroppedEvent
:
this
.
recordDroppedEvent
.
bind
(
this
),
...
options
.
transportOptions
,
url
,
});
}
else
{
(
typeof
__SENTRY_DEBUG__
===
'undefined'
||
__SENTRY_DEBUG__
)
&&
logger
.
warn
(
'No DSN provided, client will not do anything.'
);
}
}
/**
* @inheritDoc
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
captureException
(
exception
,
hint
,
scope
)
{
// ensure we haven't captured this very object before
if
(
checkOrSetAlreadyCaught
(
exception
))
{
(
typeof
__SENTRY_DEBUG__
===
'undefined'
||
__SENTRY_DEBUG__
)
&&
logger
.
log
(
ALREADY_SEEN_ERROR
);
return
;
}
let
eventId
=
hint
&&
hint
.
event_id
;
this
.
_process
(
this
.
eventFromException
(
exception
,
hint
)
.
then
(
event
=>
this
.
_captureEvent
(
event
,
hint
,
scope
))
.
then
(
result
=>
{
eventId
=
result
;
}),
);
return
eventId
;
}
/**
* @inheritDoc
*/
captureMessage
(
message
,
// eslint-disable-next-line deprecation/deprecation
level
,
hint
,
scope
,
)
{
let
eventId
=
hint
&&
hint
.
event_id
;
const
promisedEvent
=
isPrimitive
(
message
)
?
this
.
eventFromMessage
(
String
(
message
),
level
,
hint
)
:
this
.
eventFromException
(
message
,
hint
);
this
.
_process
(
promisedEvent
.
then
(
event
=>
this
.
_captureEvent
(
event
,
hint
,
scope
))
.
then
(
result
=>
{
eventId
=
result
;
}),
);
return
eventId
;
}
/**
* @inheritDoc
*/
captureEvent
(
event
,
hint
,
scope
)
{
// ensure we haven't captured this very object before
if
(
hint
&&
hint
.
originalException
&&
checkOrSetAlreadyCaught
(
hint
.
originalException
))
{
(
typeof
__SENTRY_DEBUG__
===
'undefined'
||
__SENTRY_DEBUG__
)
&&
logger
.
log
(
ALREADY_SEEN_ERROR
);
return
;
}
let
eventId
=
hint
&&
hint
.
event_id
;
this
.
_process
(
this
.
_captureEvent
(
event
,
hint
,
scope
).
then
(
result
=>
{
eventId
=
result
;
}),
);
return
eventId
;
}
/**
* @inheritDoc
*/
captureSession
(
session
)
{
if
(
!
this
.
_isEnabled
())
{
(
typeof
__SENTRY_DEBUG__
===
'undefined'
||
__SENTRY_DEBUG__
)
&&
logger
.
warn
(
'SDK not enabled, will not capture session.'
);
return
;
}
if
(
!
(
typeof
session
.
release
===
'string'
))
{
(
typeof
__SENTRY_DEBUG__
===
'undefined'
||
__SENTRY_DEBUG__
)
&&
logger
.
warn
(
'Discarded session because of missing or non-string release'
);
}
else
{
this
.
sendSession
(
session
);
// After sending, we set init false to indicate it's not the first occurrence
updateSession
(
session
,
{
init
:
false
});
}
}
/**
* @inheritDoc
*/
getDsn
()
{
return
this
.
_dsn
;
}
/**
* @inheritDoc
*/
getOptions
()
{
return
this
.
_options
;
}
/**
* @inheritDoc
*/
getTransport
()
{
return
this
.
_transport
;
}
/**
* @inheritDoc
*/
flush
(
timeout
)
{
const
transport
=
this
.
_transport
;
if
(
transport
)
{
return
this
.
_isClientDoneProcessing
(
timeout
).
then
(
clientFinished
=>
{
return
transport
.
flush
(
timeout
).
then
(
transportFlushed
=>
clientFinished
&&
transportFlushed
);
});
}
else
{
return
resolvedSyncPromise
(
true
);
}
}
/**
* @inheritDoc
*/
close
(
timeout
)
{
return
this
.
flush
(
timeout
).
then
(
result
=>
{
this
.
getOptions
().
enabled
=
false
;
return
result
;
});
}
/**
* Sets up the integrations
*/
setupIntegrations
()
{
if
(
this
.
_isEnabled
()
&&
!
this
.
_integrationsInitialized
)
{
this
.
_integrations
=
setupIntegrations
(
this
.
_options
.
integrations
);
this
.
_integrationsInitialized
=
true
;
}
}
/**
* Gets an installed integration by its `id`.
*
* @returns The installed integration or `undefined` if no integration with that `id` was installed.
*/
getIntegrationById
(
integrationId
)
{
return
this
.
_integrations
[
integrationId
];
}
/**
* @inheritDoc
*/
getIntegration
(
integration
)
{
try
{
return
(
this
.
_integrations
[
integration
.
id
]
)
||
null
;
}
catch
(
_oO
)
{
(
typeof
__SENTRY_DEBUG__
===
'undefined'
||
__SENTRY_DEBUG__
)
&&
logger
.
warn
(
`Cannot retrieve integration
${
integration
.
id
}
from the current Client`
);
return
null
;
}
}
/**
* @inheritDoc
*/
sendEvent
(
event
,
hint
=
{})
{
if
(
this
.
_dsn
)
{
let
env
=
createEventEnvelope
(
event
,
this
.
_dsn
,
this
.
_options
.
_metadata
,
this
.
_options
.
tunnel
);
for
(
const
attachment
of
hint
.
attachments
||
[])
{
env
=
addItemToEnvelope
(
env
,
createAttachmentEnvelopeItem
(
attachment
,
this
.
_options
.
transportOptions
&&
this
.
_options
.
transportOptions
.
textEncoder
,
),
);
}
this
.
_sendEnvelope
(
env
);
}
}
/**
* @inheritDoc
*/
sendSession
(
session
)
{
if
(
this
.
_dsn
)
{
const
env
=
createSessionEnvelope
(
session
,
this
.
_dsn
,
this
.
_options
.
_metadata
,
this
.
_options
.
tunnel
);
this
.
_sendEnvelope
(
env
);
}
}
/**
* @inheritDoc
*/
recordDroppedEvent
(
reason
,
category
)
{
if
(
this
.
_options
.
sendClientReports
)
{
// We want to track each category (error, transaction, session) separately
// but still keep the distinction between different type of outcomes.
// We could use nested maps, but it's much easier to read and type this way.
// A correct type for map-based implementation if we want to go that route
// would be `Partial<Record<SentryRequestType, Partial<Record<Outcome, number>>>>`
// With typescript 4.1 we could even use template literal types
const
key
=
`
${
reason
}
:
${
category
}
`
;
(
typeof
__SENTRY_DEBUG__
===
'undefined'
||
__SENTRY_DEBUG__
)
&&
logger
.
log
(
`Adding outcome: "
${
key
}
"`
);
// The following works because undefined + 1 === NaN and NaN is falsy
this
.
_outcomes
[
key
]
=
this
.
_outcomes
[
key
]
+
1
||
1
;
}
}
/** Updates existing session based on the provided event */
_updateSessionFromEvent
(
session
,
event
)
{
let
crashed
=
false
;
let
errored
=
false
;
const
exceptions
=
event
.
exception
&&
event
.
exception
.
values
;
if
(
exceptions
)
{
errored
=
true
;
for
(
const
ex
of
exceptions
)
{
const
mechanism
=
ex
.
mechanism
;
if
(
mechanism
&&
mechanism
.
handled
===
false
)
{
crashed
=
true
;
break
;
}
}
}
// A session is updated and that session update is sent in only one of the two following scenarios:
// 1. Session with non terminal status and 0 errors + an error occurred -> Will set error count to 1 and send update
// 2. Session with non terminal status and 1 error + a crash occurred -> Will set status crashed and send update
const
sessionNonTerminal
=
session
.
status
===
'ok'
;
const
shouldUpdateAndSend
=
(
sessionNonTerminal
&&
session
.
errors
===
0
)
||
(
sessionNonTerminal
&&
crashed
);
if
(
shouldUpdateAndSend
)
{
updateSession
(
session
,
{
...(
crashed
&&
{
status
:
'crashed'
}),
errors
:
session
.
errors
||
Number
(
errored
||
crashed
),
});
this
.
captureSession
(
session
);
}
}
/**
* Determine if the client is finished processing. Returns a promise because it will wait `timeout` ms before saying
* "no" (resolving to `false`) in order to give the client a chance to potentially finish first.
*
* @param timeout The time, in ms, after which to resolve to `false` if the client is still busy. Passing `0` (or not
* passing anything) will make the promise wait as long as it takes for processing to finish before resolving to
* `true`.
* @returns A promise which will resolve to `true` if processing is already done or finishes before the timeout, and
* `false` otherwise
*/
_isClientDoneProcessing
(
timeout
)
{
return
new
SyncPromise
(
resolve
=>
{
let
ticked
=
0
;
const
tick
=
1
;
const
interval
=
setInterval
(()
=>
{
if
(
this
.
_numProcessing
==
0
)
{
clearInterval
(
interval
);
resolve
(
true
);
}
else
{
ticked
+=
tick
;
if
(
timeout
&&
ticked
>=
timeout
)
{
clearInterval
(
interval
);
resolve
(
false
);
}
}
},
tick
);
});
}
/** Determines whether this SDK is enabled and a valid Dsn is present. */
_isEnabled
()
{
return
this
.
getOptions
().
enabled
!==
false
&&
this
.
_dsn
!==
undefined
;
}
/**
* Adds common information to events.
*
* The information includes release and environment from `options`,
* breadcrumbs and context (extra, tags and user) from the scope.
*
* Information that is already present in the event is never overwritten. For
* nested objects, such as the context, keys are merged.
*
* @param event The original event.
* @param hint May contain additional information about the original exception.
* @param scope A scope containing event metadata.
* @returns A new event with more information.
*/
_prepareEvent
(
event
,
hint
,
scope
)
{
const
{
normalizeDepth
=
3
,
normalizeMaxBreadth
=
1000
}
=
this
.
getOptions
();
const
prepared
=
{
...
event
,
event_id
:
event
.
event_id
||
hint
.
event_id
||
uuid4
(),
timestamp
:
event
.
timestamp
||
dateTimestampInSeconds
(),
};
this
.
_applyClientOptions
(
prepared
);
this
.
_applyIntegrationsMetadata
(
prepared
);
// If we have scope given to us, use it as the base for further modifications.
// This allows us to prevent unnecessary copying of data if `captureContext` is not provided.
let
finalScope
=
scope
;
if
(
hint
.
captureContext
)
{
finalScope
=
Scope
.
clone
(
finalScope
).
update
(
hint
.
captureContext
);
}
// We prepare the result here with a resolved Event.
let
result
=
resolvedSyncPromise
(
prepared
);
// This should be the last thing called, since we want that
// {@link Hub.addEventProcessor} gets the finished prepared event.
if
(
finalScope
)
{
// Collect attachments from the hint and scope
const
attachments
=
[...(
hint
.
attachments
||
[]),
...
finalScope
.
getAttachments
()];
if
(
attachments
.
length
)
{
hint
.
attachments
=
attachments
;
}
// In case we have a hub we reassign it.
result
=
finalScope
.
applyToEvent
(
prepared
,
hint
);
}
return
result
.
then
(
evt
=>
{
if
(
typeof
normalizeDepth
===
'number'
&&
normalizeDepth
>
0
)
{
return
this
.
_normalizeEvent
(
evt
,
normalizeDepth
,
normalizeMaxBreadth
);
}
return
evt
;
});
}
/**
* Applies `normalize` function on necessary `Event` attributes to make them safe for serialization.
* Normalized keys:
* - `breadcrumbs.data`
* - `user`
* - `contexts`
* - `extra`
* @param event Event
* @returns Normalized event
*/
_normalizeEvent
(
event
,
depth
,
maxBreadth
)
{
if
(
!
event
)
{
return
null
;
}
const
normalized
=
{
...
event
,
...(
event
.
breadcrumbs
&&
{
breadcrumbs
:
event
.
breadcrumbs
.
map
(
b
=>
({
...
b
,
...(
b
.
data
&&
{
data
:
normalize
(
b
.
data
,
depth
,
maxBreadth
),
}),
})),
}),
...(
event
.
user
&&
{
user
:
normalize
(
event
.
user
,
depth
,
maxBreadth
),
}),
...(
event
.
contexts
&&
{
contexts
:
normalize
(
event
.
contexts
,
depth
,
maxBreadth
),
}),
...(
event
.
extra
&&
{
extra
:
normalize
(
event
.
extra
,
depth
,
maxBreadth
),
}),
};
// event.contexts.trace stores information about a Transaction. Similarly,
// event.spans[] stores information about child Spans. Given that a
// Transaction is conceptually a Span, normalization should apply to both
// Transactions and Spans consistently.
// For now the decision is to skip normalization of Transactions and Spans,
// so this block overwrites the normalized event to add back the original
// Transaction information prior to normalization.
if
(
event
.
contexts
&&
event
.
contexts
.
trace
&&
normalized
.
contexts
)
{
normalized
.
contexts
.
trace
=
event
.
contexts
.
trace
;
// event.contexts.trace.data may contain circular/dangerous data so we need to normalize it
if
(
event
.
contexts
.
trace
.
data
)
{
normalized
.
contexts
.
trace
.
data
=
normalize
(
event
.
contexts
.
trace
.
data
,
depth
,
maxBreadth
);
}
}
// event.spans[].data may contain circular/dangerous data so we need to normalize it
if
(
event
.
spans
)
{
normalized
.
spans
=
event
.
spans
.
map
(
span
=>
{
// We cannot use the spread operator here because `toJSON` on `span` is non-enumerable
if
(
span
.
data
)
{
span
.
data
=
normalize
(
span
.
data
,
depth
,
maxBreadth
);
}
return
span
;
});
}
return
normalized
;
}
/**
* Enhances event using the client configuration.
* It takes care of all "static" values like environment, release and `dist`,
* as well as truncating overly long values.
* @param event event instance to be enhanced
*/
_applyClientOptions
(
event
)
{
const
options
=
this
.
getOptions
();
const
{
environment
,
release
,
dist
,
maxValueLength
=
250
}
=
options
;
if
(
!
(
'environment'
in
event
))
{
event
.
environment
=
'environment'
in
options
?
environment
:
'production'
;
}
if
(
event
.
release
===
undefined
&&
release
!==
undefined
)
{
event
.
release
=
release
;
}
if
(
event
.
dist
===
undefined
&&
dist
!==
undefined
)
{
event
.
dist
=
dist
;
}
if
(
event
.
message
)
{
event
.
message
=
truncate
(
event
.
message
,
maxValueLength
);
}
const
exception
=
event
.
exception
&&
event
.
exception
.
values
&&
event
.
exception
.
values
[
0
];
if
(
exception
&&
exception
.
value
)
{
exception
.
value
=
truncate
(
exception
.
value
,
maxValueLength
);
}
const
request
=
event
.
request
;
if
(
request
&&
request
.
url
)
{
request
.
url
=
truncate
(
request
.
url
,
maxValueLength
);
}
}
/**
* This function adds all used integrations to the SDK info in the event.
* @param event The event that will be filled with all integrations.
*/
_applyIntegrationsMetadata
(
event
)
{
const
integrationsArray
=
Object
.
keys
(
this
.
_integrations
);
if
(
integrationsArray
.
length
>
0
)
{
event
.
sdk
=
event
.
sdk
||
{};
event
.
sdk
.
integrations
=
[...(
event
.
sdk
.
integrations
||
[]),
...
integrationsArray
];
}
}
/**
* Processes the event and logs an error in case of rejection
* @param event
* @param hint
* @param scope
*/
_captureEvent
(
event
,
hint
=
{},
scope
)
{
return
this
.
_processEvent
(
event
,
hint
,
scope
).
then
(
finalEvent
=>
{
return
finalEvent
.
event_id
;
},
reason
=>
{
if
((
typeof
__SENTRY_DEBUG__
===
'undefined'
||
__SENTRY_DEBUG__
))
{
// If something's gone wrong, log the error as a warning. If it's just us having used a `SentryError` for
// control flow, log just the message (no stack) as a log-level log.
const
sentryError
=
reason
;
if
(
sentryError
.
logLevel
===
'log'
)
{
logger
.
log
(
sentryError
.
message
);
}
else
{
logger
.
warn
(
sentryError
);
}
}
return
undefined
;
},
);
}
/**
* Processes an event (either error or message) and sends it to Sentry.
*
* This also adds breadcrumbs and context information to the event. However,
* platform specific meta data (such as the User's IP address) must be added
* by the SDK implementor.
*
*
* @param event The event to send to Sentry.
* @param hint May contain additional information about the original exception.
* @param scope A scope containing event metadata.
* @returns A SyncPromise that resolves with the event or rejects in case event was/will not be send.
*/
_processEvent
(
event
,
hint
,
scope
)
{
const
options
=
this
.
getOptions
();
const
{
sampleRate
}
=
options
;
if
(
!
this
.
_isEnabled
())
{
return
rejectedSyncPromise
(
new
SentryError
(
'SDK not enabled, will not capture event.'
,
'log'
));
}
const
isTransaction
=
event
.
type
===
'transaction'
;
const
beforeSendProcessorName
=
isTransaction
?
'beforeSendTransaction'
:
'beforeSend'
;
const
beforeSendProcessor
=
options
[
beforeSendProcessorName
];
// 1.0 === 100% events are sent
// 0.0 === 0% events are sent
// Sampling for transaction happens somewhere else
if
(
!
isTransaction
&&
typeof
sampleRate
===
'number'
&&
Math
.
random
()
>
sampleRate
)
{
this
.
recordDroppedEvent
(
'sample_rate'
,
'error'
);
return
rejectedSyncPromise
(
new
SentryError
(
`Discarding event because it's not included in the random sample (sampling rate =
${
sampleRate
}
)`
,
'log'
,
),
);
}
return
this
.
_prepareEvent
(
event
,
hint
,
scope
)
.
then
(
prepared
=>
{
if
(
prepared
===
null
)
{
this
.
recordDroppedEvent
(
'event_processor'
,
event
.
type
||
'error'
);
throw
new
SentryError
(
'An event processor returned `null`, will not send event.'
,
'log'
);
}
const
isInternalException
=
hint
.
data
&&
(
hint
.
data
).
__sentry__
===
true
;
if
(
isInternalException
||
!
beforeSendProcessor
)
{
return
prepared
;
}
const
beforeSendResult
=
beforeSendProcessor
(
prepared
,
hint
);
return
_validateBeforeSendResult
(
beforeSendResult
,
beforeSendProcessorName
);
})
.
then
(
processedEvent
=>
{
if
(
processedEvent
===
null
)
{
this
.
recordDroppedEvent
(
'before_send'
,
event
.
type
||
'error'
);
throw
new
SentryError
(
`\`
${
beforeSendProcessorName
}
\` returned \`null\`, will not send event.`
,
'log'
);
}
const
session
=
scope
&&
scope
.
getSession
();
if
(
!
isTransaction
&&
session
)
{
this
.
_updateSessionFromEvent
(
session
,
processedEvent
);
}
// None of the Sentry built event processor will update transaction name,
// so if the transaction name has been changed by an event processor, we know
// it has to come from custom event processor added by a user
const
transactionInfo
=
processedEvent
.
transaction_info
;
if
(
isTransaction
&&
transactionInfo
&&
processedEvent
.
transaction
!==
event
.
transaction
)
{
const
source
=
'custom'
;
processedEvent
.
transaction_info
=
{
...
transactionInfo
,
source
,
changes
:
[
...
transactionInfo
.
changes
,
{
source
,
// use the same timestamp as the processed event.
timestamp
:
processedEvent
.
timestamp
,
propagations
:
transactionInfo
.
propagations
,
},
],
};
}
this
.
sendEvent
(
processedEvent
,
hint
);
return
processedEvent
;
})
.
then
(
null
,
reason
=>
{
if
(
reason
instanceof
SentryError
)
{
throw
reason
;
}
this
.
captureException
(
reason
,
{
data
:
{
__sentry__
:
true
,
},
originalException
:
reason
,
});
throw
new
SentryError
(
`Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason:
${
reason
}
`
,
);
});
}
/**
* Occupies the client with processing and event
*/
_process
(
promise
)
{
this
.
_numProcessing
+=
1
;
void
promise
.
then
(
value
=>
{
this
.
_numProcessing
-=
1
;
return
value
;
},
reason
=>
{
this
.
_numProcessing
-=
1
;
return
reason
;
},
);
}
/**
* @inheritdoc
*/
_sendEnvelope
(
envelope
)
{
if
(
this
.
_transport
&&
this
.
_dsn
)
{
this
.
_transport
.
send
(
envelope
).
then
(
null
,
reason
=>
{
(
typeof
__SENTRY_DEBUG__
===
'undefined'
||
__SENTRY_DEBUG__
)
&&
logger
.
error
(
'Error while sending event:'
,
reason
);
});
}
else
{
(
typeof
__SENTRY_DEBUG__
===
'undefined'
||
__SENTRY_DEBUG__
)
&&
logger
.
error
(
'Transport disabled'
);
}
}
/**
* Clears outcomes on this client and returns them.
*/
_clearOutcomes
()
{
const
outcomes
=
this
.
_outcomes
;
this
.
_outcomes
=
{};
return
Object
.
keys
(
outcomes
).
map
(
key
=>
{
const
[
reason
,
category
]
=
key
.
split
(
':'
)
;
return
{
reason
,
category
,
quantity
:
outcomes
[
key
],
};
});
}
/**
* @inheritDoc
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
}
/**
* Verifies that return value of configured `beforeSend` or `beforeSendTransaction` is of expected type, and returns the value if so.
*/
function
_validateBeforeSendResult
(
beforeSendResult
,
beforeSendProcessorName
,
)
{
const
invalidValueError
=
`\`
${
beforeSendProcessorName
}
\` must return \`null\` or a valid event.`
;
if
(
isThenable
(
beforeSendResult
))
{
return
beforeSendResult
.
then
(
event
=>
{
if
(
!
isPlainObject
(
event
)
&&
event
!==
null
)
{
throw
new
SentryError
(
invalidValueError
);
}
return
event
;
},
e
=>
{
throw
new
SentryError
(
`\`
${
beforeSendProcessorName
}
\` rejected with
${
e
}
`
);
},
);
}
else
if
(
!
isPlainObject
(
beforeSendResult
)
&&
beforeSendResult
!==
null
)
{
throw
new
SentryError
(
invalidValueError
);
}
return
beforeSendResult
;
}
export
{
BaseClient
};
//# sourceMappingURL=baseclient.js.map
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Jun 4 2025, 6:36 PM (14 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3341388
Attached To
rDWAPPS Web applications
Event Timeline
Log In to Comment