Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Latest commit

 

History

History
History
79 lines (66 loc) · 2.96 KB

File metadata and controls

79 lines (66 loc) · 2.96 KB
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { inspect } from 'node:util'
import isErrorInstance from 'is-error-instance'
import { PREFIX } from './limit.js'
// Duplicate errors are only logged once because:
// - It makes logs clearer
// - It prevents creating too much CPU load or too many microtasks
// - It prevents creating too many logs, which can be expensive if logs are
// hosted remotely
// - It prevents infinite recursions if `opts.onError()` triggers itself an
// event (while still reporting that event once)
// `previousEvents`:
// - Is event-specific so that if events of a given event stopped being
// emitted, others still are.
// - Can take up some memory, but it should be cleaned up by
// `removeListener()`, i.e. once `eventListener` is garbage collected.
export const isRepeated = (value, previousEvents) => {
const previousEvent = getPreviousEvent(value)
if (previousEvents.includes(previousEvent)) {
return true
}
// eslint-disable-next-line fp/no-mutating-methods
previousEvents.push(previousEvent)
return false
}
// Serialize `value` into a short fingerprint.
// We truncate fingerprints to prevent consuming too much memory in case `value`
// is big.
// This introduces higher risk of false positives (see comment below).
// We do not hash as it would be too CPU-intensive if the value is big.
const getPreviousEvent = (value) => {
const previousEvent = isErrorInstance(value)
? serializeError(value)
: stableSerialize(value)
return previousEvent.slice(0, FINGERPRINT_MAX_LENGTH)
}
// We do not serialize `error.message` as it may contain dynamic values like
// timestamps. This means errors are only `error.name` + `error.stack`, which
// should be a good fingerprint.
// Also we only keep first 10 call sites in case of infinitely recursive stack.
const serializeError = ({ name, message, stack }) => {
const messageA = String(message).includes(PREFIX) ? `${message}\n` : ''
const stackA = serializeStack(stack)
return `${name}\n${messageA}${stackA}`
}
const serializeStack = (stack) =>
String(stack)
.split('\n')
.filter(isStackLine)
.slice(0, STACK_TRACE_MAX_LENGTH)
.join('\n')
const isStackLine = (line) => STACK_TRACE_LINE_REGEXP.test(line)
const STACK_TRACE_LINE_REGEXP = /^\s+at /u
const STACK_TRACE_MAX_LENGTH = 10
// This is meant when serialization must be predictable.
// We use `util.inspect()` instead of `JSON.stringify()` to support more
// types and circular references.
// `sorted` prevents the same event using different keys order from having
// a different fingerprint.
// Big arrays, objects or buffers will be truncated, which makes this call
// less CPU-intensive and the result value smaller in memory. However it
// introduces higher risk of false positives (event being flagged as repeated
// even though it's different). Process errors should be exceptional, so this
// is ok.
const stableSerialize = (value) => inspect(value, INSPECT_OPTS)
const INSPECT_OPTS = { getters: true, sorted: true }
const FINGERPRINT_MAX_LENGTH = 1e4
Morty Proxy This is a proxified and sanitized view of the page, visit original site.