react-hook-eslint 1.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,188 @@
1
+ 'use strict'
2
+
3
+ const metadata = Symbol.for('pino.metadata')
4
+ const { DEFAULT_LEVELS } = require('./constants')
5
+
6
+ const DEFAULT_INFO_LEVEL = DEFAULT_LEVELS.info
7
+
8
+ function multistream (streamsArray, opts) {
9
+ let counter = 0
10
+ streamsArray = streamsArray || []
11
+ opts = opts || { dedupe: false }
12
+
13
+ const streamLevels = Object.create(DEFAULT_LEVELS)
14
+ streamLevels.silent = Infinity
15
+ if (opts.levels && typeof opts.levels === 'object') {
16
+ Object.keys(opts.levels).forEach(i => {
17
+ streamLevels[i] = opts.levels[i]
18
+ })
19
+ }
20
+
21
+ const res = {
22
+ write,
23
+ add,
24
+ emit,
25
+ flushSync,
26
+ end,
27
+ minLevel: 0,
28
+ streams: [],
29
+ clone,
30
+ [metadata]: true,
31
+ streamLevels
32
+ }
33
+
34
+ if (Array.isArray(streamsArray)) {
35
+ streamsArray.forEach(add, res)
36
+ } else {
37
+ add.call(res, streamsArray)
38
+ }
39
+
40
+ // clean this object up
41
+ // or it will stay allocated forever
42
+ // as it is closed on the following closures
43
+ streamsArray = null
44
+
45
+ return res
46
+
47
+ // we can exit early because the streams are ordered by level
48
+ function write (data) {
49
+ let dest
50
+ const level = this.lastLevel
51
+ const { streams } = this
52
+ // for handling situation when several streams has the same level
53
+ let recordedLevel = 0
54
+ let stream
55
+
56
+ // if dedupe set to true we send logs to the stream with the highest level
57
+ // therefore, we have to change sorting order
58
+ for (let i = initLoopVar(streams.length, opts.dedupe); checkLoopVar(i, streams.length, opts.dedupe); i = adjustLoopVar(i, opts.dedupe)) {
59
+ dest = streams[i]
60
+ if (dest.level <= level) {
61
+ if (recordedLevel !== 0 && recordedLevel !== dest.level) {
62
+ break
63
+ }
64
+ stream = dest.stream
65
+ if (stream[metadata]) {
66
+ const { lastTime, lastMsg, lastObj, lastLogger } = this
67
+ stream.lastLevel = level
68
+ stream.lastTime = lastTime
69
+ stream.lastMsg = lastMsg
70
+ stream.lastObj = lastObj
71
+ stream.lastLogger = lastLogger
72
+ }
73
+ stream.write(data)
74
+ if (opts.dedupe) {
75
+ recordedLevel = dest.level
76
+ }
77
+ } else if (!opts.dedupe) {
78
+ break
79
+ }
80
+ }
81
+ }
82
+
83
+ function emit (...args) {
84
+ for (const { stream } of this.streams) {
85
+ if (typeof stream.emit === 'function') {
86
+ stream.emit(...args)
87
+ }
88
+ }
89
+ }
90
+
91
+ function flushSync () {
92
+ for (const { stream } of this.streams) {
93
+ if (typeof stream.flushSync === 'function') {
94
+ stream.flushSync()
95
+ }
96
+ }
97
+ }
98
+
99
+ function add (dest) {
100
+ if (!dest) {
101
+ return res
102
+ }
103
+
104
+ // Check that dest implements either StreamEntry or DestinationStream
105
+ const isStream = typeof dest.write === 'function' || dest.stream
106
+ const stream_ = dest.write ? dest : dest.stream
107
+ // This is necessary to provide a meaningful error message, otherwise it throws somewhere inside write()
108
+ if (!isStream) {
109
+ throw Error('stream object needs to implement either StreamEntry or DestinationStream interface')
110
+ }
111
+
112
+ const { streams, streamLevels } = this
113
+
114
+ let level
115
+ if (typeof dest.levelVal === 'number') {
116
+ level = dest.levelVal
117
+ } else if (typeof dest.level === 'string') {
118
+ level = streamLevels[dest.level]
119
+ } else if (typeof dest.level === 'number') {
120
+ level = dest.level
121
+ } else {
122
+ level = DEFAULT_INFO_LEVEL
123
+ }
124
+
125
+ const dest_ = {
126
+ stream: stream_,
127
+ level,
128
+ levelVal: undefined,
129
+ id: counter++
130
+ }
131
+
132
+ streams.unshift(dest_)
133
+ streams.sort(compareByLevel)
134
+
135
+ this.minLevel = streams[0].level
136
+
137
+ return res
138
+ }
139
+
140
+ function end () {
141
+ for (const { stream } of this.streams) {
142
+ if (typeof stream.flushSync === 'function') {
143
+ stream.flushSync()
144
+ }
145
+ stream.end()
146
+ }
147
+ }
148
+
149
+ function clone (level) {
150
+ const streams = new Array(this.streams.length)
151
+
152
+ for (let i = 0; i < streams.length; i++) {
153
+ streams[i] = {
154
+ level,
155
+ stream: this.streams[i].stream
156
+ }
157
+ }
158
+
159
+ return {
160
+ write,
161
+ add,
162
+ minLevel: level,
163
+ streams,
164
+ clone,
165
+ emit,
166
+ flushSync,
167
+ [metadata]: true
168
+ }
169
+ }
170
+ }
171
+
172
+ function compareByLevel (a, b) {
173
+ return a.level - b.level
174
+ }
175
+
176
+ function initLoopVar (length, dedupe) {
177
+ return dedupe ? length - 1 : 0
178
+ }
179
+
180
+ function adjustLoopVar (i, dedupe) {
181
+ return dedupe ? i - 1 : i + 1
182
+ }
183
+
184
+ function checkLoopVar (i, length, dedupe) {
185
+ return dedupe ? i >= 0 : i < length
186
+ }
187
+
188
+ module.exports = multistream
package/lib/proto.js ADDED
@@ -0,0 +1,234 @@
1
+ 'use strict'
2
+
3
+ /* eslint no-prototype-builtins: 0 */
4
+
5
+ const { EventEmitter } = require('node:events')
6
+ const {
7
+ lsCacheSym,
8
+ levelValSym,
9
+ setLevelSym,
10
+ getLevelSym,
11
+ chindingsSym,
12
+ parsedChindingsSym,
13
+ mixinSym,
14
+ asJsonSym,
15
+ writeSym,
16
+ mixinMergeStrategySym,
17
+ timeSym,
18
+ timeSliceIndexSym,
19
+ streamSym,
20
+ serializersSym,
21
+ formattersSym,
22
+ errorKeySym,
23
+ messageKeySym,
24
+ useOnlyCustomLevelsSym,
25
+ needsMetadataGsym,
26
+ redactFmtSym,
27
+ stringifySym,
28
+ formatOptsSym,
29
+ stringifiersSym,
30
+ msgPrefixSym,
31
+ hooksSym
32
+ } = require('./symbols')
33
+ const {
34
+ getLevel,
35
+ setLevel,
36
+ isLevelEnabled,
37
+ mappings,
38
+ initialLsCache,
39
+ genLsCache,
40
+ assertNoLevelCollisions
41
+ } = require('./levels')
42
+ const {
43
+ asChindings,
44
+ asJson,
45
+ buildFormatters,
46
+ stringify
47
+ } = require('./tools')
48
+ const {
49
+ version
50
+ } = require('./meta')
51
+ const redaction = require('./redaction')
52
+
53
+ // note: use of class is satirical
54
+ // https://github.com/pinojs/pino/pull/433#pullrequestreview-127703127
55
+ const constructor = class Pino {}
56
+ const prototype = {
57
+ constructor,
58
+ child,
59
+ bindings,
60
+ setBindings,
61
+ flush,
62
+ isLevelEnabled,
63
+ version,
64
+ get level () { return this[getLevelSym]() },
65
+ set level (lvl) { this[setLevelSym](lvl) },
66
+ get levelVal () { return this[levelValSym] },
67
+ set levelVal (n) { throw Error('levelVal is read-only') },
68
+ [lsCacheSym]: initialLsCache,
69
+ [writeSym]: write,
70
+ [asJsonSym]: asJson,
71
+ [getLevelSym]: getLevel,
72
+ [setLevelSym]: setLevel
73
+ }
74
+
75
+ Object.setPrototypeOf(prototype, EventEmitter.prototype)
76
+
77
+ // exporting and consuming the prototype object using factory pattern fixes scoping issues with getters when serializing
78
+ module.exports = function () {
79
+ return Object.create(prototype)
80
+ }
81
+
82
+ const resetChildingsFormatter = bindings => bindings
83
+ function child (bindings, options) {
84
+ if (!bindings) {
85
+ throw Error('missing bindings for child Pino')
86
+ }
87
+ options = options || {} // default options to empty object
88
+ const serializers = this[serializersSym]
89
+ const formatters = this[formattersSym]
90
+ const instance = Object.create(this)
91
+
92
+ if (options.hasOwnProperty('serializers') === true) {
93
+ instance[serializersSym] = Object.create(null)
94
+
95
+ for (const k in serializers) {
96
+ instance[serializersSym][k] = serializers[k]
97
+ }
98
+ const parentSymbols = Object.getOwnPropertySymbols(serializers)
99
+ /* eslint no-var: off */
100
+ for (var i = 0; i < parentSymbols.length; i++) {
101
+ const ks = parentSymbols[i]
102
+ instance[serializersSym][ks] = serializers[ks]
103
+ }
104
+
105
+ for (const bk in options.serializers) {
106
+ instance[serializersSym][bk] = options.serializers[bk]
107
+ }
108
+ const bindingsSymbols = Object.getOwnPropertySymbols(options.serializers)
109
+ for (var bi = 0; bi < bindingsSymbols.length; bi++) {
110
+ const bks = bindingsSymbols[bi]
111
+ instance[serializersSym][bks] = options.serializers[bks]
112
+ }
113
+ } else instance[serializersSym] = serializers
114
+ if (options.hasOwnProperty('formatters')) {
115
+ const { level, bindings: chindings, log } = options.formatters
116
+ instance[formattersSym] = buildFormatters(
117
+ level || formatters.level,
118
+ chindings || resetChildingsFormatter,
119
+ log || formatters.log
120
+ )
121
+ } else {
122
+ instance[formattersSym] = buildFormatters(
123
+ formatters.level,
124
+ resetChildingsFormatter,
125
+ formatters.log
126
+ )
127
+ }
128
+ if (options.hasOwnProperty('customLevels') === true) {
129
+ assertNoLevelCollisions(this.levels, options.customLevels)
130
+ instance.levels = mappings(options.customLevels, instance[useOnlyCustomLevelsSym])
131
+ genLsCache(instance)
132
+ }
133
+
134
+ // redact must place before asChindings and only replace if exist
135
+ if ((typeof options.redact === 'object' && options.redact !== null) || Array.isArray(options.redact)) {
136
+ instance.redact = options.redact // replace redact directly
137
+ const stringifiers = redaction(instance.redact, stringify)
138
+ const formatOpts = { stringify: stringifiers[redactFmtSym] }
139
+ instance[stringifySym] = stringify
140
+ instance[stringifiersSym] = stringifiers
141
+ instance[formatOptsSym] = formatOpts
142
+ }
143
+
144
+ if (typeof options.msgPrefix === 'string') {
145
+ instance[msgPrefixSym] = (this[msgPrefixSym] || '') + options.msgPrefix
146
+ }
147
+
148
+ instance[chindingsSym] = asChindings(instance, bindings)
149
+ const childLevel = options.level || this.level
150
+ instance[setLevelSym](childLevel)
151
+ this.onChild(instance)
152
+ return instance
153
+ }
154
+
155
+ function bindings () {
156
+ const chindings = this[chindingsSym]
157
+ const chindingsJson = `{${chindings.substr(1)}}` // at least contains ,"pid":7068,"hostname":"myMac"
158
+ const bindingsFromJson = JSON.parse(chindingsJson)
159
+ delete bindingsFromJson.pid
160
+ delete bindingsFromJson.hostname
161
+ return bindingsFromJson
162
+ }
163
+
164
+ function setBindings (newBindings) {
165
+ const chindings = asChindings(this, newBindings)
166
+ this[chindingsSym] = chindings
167
+ delete this[parsedChindingsSym]
168
+ }
169
+
170
+ /**
171
+ * Default strategy for creating `mergeObject` from arguments and the result from `mixin()`.
172
+ * Fields from `mergeObject` have higher priority in this strategy.
173
+ *
174
+ * @param {Object} mergeObject The object a user has supplied to the logging function.
175
+ * @param {Object} mixinObject The result of the `mixin` method.
176
+ * @return {Object}
177
+ */
178
+ function defaultMixinMergeStrategy (mergeObject, mixinObject) {
179
+ return Object.assign(mixinObject, mergeObject)
180
+ }
181
+
182
+ function write (_obj, msg, num) {
183
+ const t = this[timeSym]()
184
+ const mixin = this[mixinSym]
185
+ const errorKey = this[errorKeySym]
186
+ const messageKey = this[messageKeySym]
187
+ const mixinMergeStrategy = this[mixinMergeStrategySym] || defaultMixinMergeStrategy
188
+ let obj
189
+ const streamWriteHook = this[hooksSym].streamWrite
190
+
191
+ if (_obj === undefined || _obj === null) {
192
+ obj = {}
193
+ } else if (_obj instanceof Error) {
194
+ obj = { [errorKey]: _obj }
195
+ if (msg === undefined) {
196
+ msg = _obj.message
197
+ }
198
+ } else {
199
+ obj = _obj
200
+ if (msg === undefined && _obj[messageKey] === undefined && _obj[errorKey]) {
201
+ msg = _obj[errorKey].message
202
+ }
203
+ }
204
+
205
+ if (mixin) {
206
+ obj = mixinMergeStrategy(obj, mixin(obj, num, this))
207
+ }
208
+
209
+ const s = this[asJsonSym](obj, msg, num, t)
210
+
211
+ const stream = this[streamSym]
212
+ if (stream[needsMetadataGsym] === true) {
213
+ stream.lastLevel = num
214
+ stream.lastObj = obj
215
+ stream.lastMsg = msg
216
+ stream.lastTime = t.slice(this[timeSliceIndexSym])
217
+ stream.lastLogger = this // for child loggers
218
+ }
219
+ stream.write(streamWriteHook ? streamWriteHook(s) : s)
220
+ }
221
+
222
+ function noop () {}
223
+
224
+ function flush (cb) {
225
+ if (cb != null && typeof cb !== 'function') {
226
+ throw Error('callback must be a function')
227
+ }
228
+
229
+ const stream = this[streamSym]
230
+
231
+ if (typeof stream.flush === 'function') {
232
+ stream.flush(cb || noop)
233
+ } else if (cb) cb()
234
+ }
@@ -0,0 +1,118 @@
1
+ 'use strict'
2
+
3
+ const fastRedact = require('fast-redact')
4
+ const { redactFmtSym, wildcardFirstSym } = require('./symbols')
5
+ const { rx, validator } = fastRedact
6
+
7
+ const validate = validator({
8
+ ERR_PATHS_MUST_BE_STRINGS: () => 'pino – redacted paths must be strings',
9
+ ERR_INVALID_PATH: (s) => `pino – redact paths array contains an invalid path (${s})`
10
+ })
11
+
12
+ const CENSOR = '[Redacted]'
13
+ const strict = false // TODO should this be configurable?
14
+
15
+ function redaction (opts, serialize) {
16
+ const { paths, censor } = handle(opts)
17
+
18
+ const shape = paths.reduce((o, str) => {
19
+ rx.lastIndex = 0
20
+ const first = rx.exec(str)
21
+ const next = rx.exec(str)
22
+
23
+ // ns is the top-level path segment, brackets + quoting removed.
24
+ let ns = first[1] !== undefined
25
+ ? first[1].replace(/^(?:"|'|`)(.*)(?:"|'|`)$/, '$1')
26
+ : first[0]
27
+
28
+ if (ns === '*') {
29
+ ns = wildcardFirstSym
30
+ }
31
+
32
+ // top level key:
33
+ if (next === null) {
34
+ o[ns] = null
35
+ return o
36
+ }
37
+
38
+ // path with at least two segments:
39
+ // if ns is already redacted at the top level, ignore lower level redactions
40
+ if (o[ns] === null) {
41
+ return o
42
+ }
43
+
44
+ const { index } = next
45
+ const nextPath = `${str.substr(index, str.length - 1)}`
46
+
47
+ o[ns] = o[ns] || []
48
+
49
+ // shape is a mix of paths beginning with literal values and wildcard
50
+ // paths [ "a.b.c", "*.b.z" ] should reduce to a shape of
51
+ // { "a": [ "b.c", "b.z" ], *: [ "b.z" ] }
52
+ // note: "b.z" is in both "a" and * arrays because "a" matches the wildcard.
53
+ // (* entry has wildcardFirstSym as key)
54
+ if (ns !== wildcardFirstSym && o[ns].length === 0) {
55
+ // first time ns's get all '*' redactions so far
56
+ o[ns].push(...(o[wildcardFirstSym] || []))
57
+ }
58
+
59
+ if (ns === wildcardFirstSym) {
60
+ // new * path gets added to all previously registered literal ns's.
61
+ Object.keys(o).forEach(function (k) {
62
+ if (o[k]) {
63
+ o[k].push(nextPath)
64
+ }
65
+ })
66
+ }
67
+
68
+ o[ns].push(nextPath)
69
+ return o
70
+ }, {})
71
+
72
+ // the redactor assigned to the format symbol key
73
+ // provides top level redaction for instances where
74
+ // an object is interpolated into the msg string
75
+ const result = {
76
+ [redactFmtSym]: fastRedact({ paths, censor, serialize, strict })
77
+ }
78
+
79
+ const topCensor = (...args) => {
80
+ return typeof censor === 'function' ? serialize(censor(...args)) : serialize(censor)
81
+ }
82
+
83
+ return [...Object.keys(shape), ...Object.getOwnPropertySymbols(shape)].reduce((o, k) => {
84
+ // top level key:
85
+ if (shape[k] === null) {
86
+ o[k] = (value) => topCensor(value, [k])
87
+ } else {
88
+ const wrappedCensor = typeof censor === 'function'
89
+ ? (value, path) => {
90
+ return censor(value, [k, ...path])
91
+ }
92
+ : censor
93
+ o[k] = fastRedact({
94
+ paths: shape[k],
95
+ censor: wrappedCensor,
96
+ serialize,
97
+ strict
98
+ })
99
+ }
100
+ return o
101
+ }, result)
102
+ }
103
+
104
+ function handle (opts) {
105
+ if (Array.isArray(opts)) {
106
+ opts = { paths: opts, censor: CENSOR }
107
+ validate(opts)
108
+ return opts
109
+ }
110
+ let { paths, censor = CENSOR, remove } = opts
111
+ if (Array.isArray(paths) === false) { throw Error('pino – redact must contain an array of strings') }
112
+ if (remove === true) censor = undefined
113
+ validate({ paths, censor })
114
+
115
+ return { paths, censor }
116
+ }
117
+
118
+ module.exports = redaction
package/lib/symbols.js ADDED
@@ -0,0 +1,74 @@
1
+ 'use strict'
2
+
3
+ const setLevelSym = Symbol('pino.setLevel')
4
+ const getLevelSym = Symbol('pino.getLevel')
5
+ const levelValSym = Symbol('pino.levelVal')
6
+ const levelCompSym = Symbol('pino.levelComp')
7
+ const useLevelLabelsSym = Symbol('pino.useLevelLabels')
8
+ const useOnlyCustomLevelsSym = Symbol('pino.useOnlyCustomLevels')
9
+ const mixinSym = Symbol('pino.mixin')
10
+
11
+ const lsCacheSym = Symbol('pino.lsCache')
12
+ const chindingsSym = Symbol('pino.chindings')
13
+
14
+ const asJsonSym = Symbol('pino.asJson')
15
+ const writeSym = Symbol('pino.write')
16
+ const redactFmtSym = Symbol('pino.redactFmt')
17
+
18
+ const timeSym = Symbol('pino.time')
19
+ const timeSliceIndexSym = Symbol('pino.timeSliceIndex')
20
+ const streamSym = Symbol('pino.stream')
21
+ const stringifySym = Symbol('pino.stringify')
22
+ const stringifySafeSym = Symbol('pino.stringifySafe')
23
+ const stringifiersSym = Symbol('pino.stringifiers')
24
+ const endSym = Symbol('pino.end')
25
+ const formatOptsSym = Symbol('pino.formatOpts')
26
+ const messageKeySym = Symbol('pino.messageKey')
27
+ const errorKeySym = Symbol('pino.errorKey')
28
+ const nestedKeySym = Symbol('pino.nestedKey')
29
+ const nestedKeyStrSym = Symbol('pino.nestedKeyStr')
30
+ const mixinMergeStrategySym = Symbol('pino.mixinMergeStrategy')
31
+ const msgPrefixSym = Symbol('pino.msgPrefix')
32
+
33
+ const wildcardFirstSym = Symbol('pino.wildcardFirst')
34
+
35
+ // public symbols, no need to use the same pino
36
+ // version for these
37
+ const serializersSym = Symbol.for('pino.serializers')
38
+ const formattersSym = Symbol.for('pino.formatters')
39
+ const hooksSym = Symbol.for('pino.hooks')
40
+ const needsMetadataGsym = Symbol.for('pino.metadata')
41
+
42
+ module.exports = {
43
+ setLevelSym,
44
+ getLevelSym,
45
+ levelValSym,
46
+ levelCompSym,
47
+ useLevelLabelsSym,
48
+ mixinSym,
49
+ lsCacheSym,
50
+ chindingsSym,
51
+ asJsonSym,
52
+ writeSym,
53
+ serializersSym,
54
+ redactFmtSym,
55
+ timeSym,
56
+ timeSliceIndexSym,
57
+ streamSym,
58
+ stringifySym,
59
+ stringifySafeSym,
60
+ stringifiersSym,
61
+ endSym,
62
+ formatOptsSym,
63
+ messageKeySym,
64
+ errorKeySym,
65
+ nestedKeySym,
66
+ wildcardFirstSym,
67
+ needsMetadataGsym,
68
+ useOnlyCustomLevelsSym,
69
+ formattersSym,
70
+ hooksSym,
71
+ nestedKeyStrSym,
72
+ mixinMergeStrategySym,
73
+ msgPrefixSym
74
+ }
package/lib/time.js ADDED
@@ -0,0 +1,11 @@
1
+ 'use strict'
2
+
3
+ const nullTime = () => ''
4
+
5
+ const epochTime = () => `,"time":${Date.now()}`
6
+
7
+ const unixTime = () => `,"time":${Math.round(Date.now() / 1000.0)}`
8
+
9
+ const isoTime = () => `,"time":"${new Date(Date.now()).toISOString()}"` // using Date.now() for testability
10
+
11
+ module.exports = { nullTime, epochTime, unixTime, isoTime }