node-denv 1.3.5

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.
package/lib/tools.js ADDED
@@ -0,0 +1,395 @@
1
+ 'use strict'
2
+
3
+ /* eslint no-prototype-builtins: 0 */
4
+
5
+ const format = require('quick-format-unescaped')
6
+ const { mapHttpRequest, mapHttpResponse } = require('pino-std-serializers')
7
+ const SonicBoom = require('sonic-boom')
8
+ const onExit = require('on-exit-leak-free')
9
+ const {
10
+ lsCacheSym,
11
+ chindingsSym,
12
+ writeSym,
13
+ serializersSym,
14
+ formatOptsSym,
15
+ endSym,
16
+ stringifiersSym,
17
+ stringifySym,
18
+ stringifySafeSym,
19
+ wildcardFirstSym,
20
+ nestedKeySym,
21
+ formattersSym,
22
+ messageKeySym,
23
+ errorKeySym,
24
+ nestedKeyStrSym,
25
+ msgPrefixSym
26
+ } = require('./symbols')
27
+ const { isMainThread } = require('worker_threads')
28
+ const transport = require('./transport')
29
+
30
+ /* eslint-disable */
31
+ function noop () {
32
+ }
33
+
34
+ function genLog (level, hook) {
35
+ if (!hook) return LOG
36
+
37
+ return function hookWrappedLog (...args) {
38
+ hook.call(this, args, LOG, level)
39
+ }
40
+
41
+ function LOG (o, ...n) {
42
+ if (typeof o === 'object') {
43
+ let msg = o
44
+ if (o !== null) {
45
+ if (o.method && o.headers && o.socket) {
46
+ o = mapHttpRequest(o)
47
+ } else if (typeof o.setHeader === 'function') {
48
+ o = mapHttpResponse(o)
49
+ }
50
+ }
51
+ let formatParams
52
+ if (msg === null && n.length === 0) {
53
+ formatParams = [null]
54
+ } else {
55
+ msg = n.shift()
56
+ formatParams = n
57
+ }
58
+ // We do not use a coercive check for `msg` as it is
59
+ // measurably slower than the explicit checks.
60
+ if (typeof this[msgPrefixSym] === 'string' && msg !== undefined && msg !== null) {
61
+ msg = this[msgPrefixSym] + msg
62
+ }
63
+ this[writeSym](o, format(msg, formatParams, this[formatOptsSym]), level)
64
+ } else {
65
+ let msg = o === undefined ? n.shift() : o
66
+
67
+ // We do not use a coercive check for `msg` as it is
68
+ // measurably slower than the explicit checks.
69
+ if (typeof this[msgPrefixSym] === 'string' && msg !== undefined && msg !== null) {
70
+ msg = this[msgPrefixSym] + msg
71
+ }
72
+ this[writeSym](null, format(msg, n, this[formatOptsSym]), level)
73
+ }
74
+ }
75
+ }
76
+
77
+ // magically escape strings for json
78
+ // relying on their charCodeAt
79
+ // everything below 32 needs JSON.stringify()
80
+ // 34 and 92 happens all the time, so we
81
+ // have a fast case for them
82
+ function asString (str) {
83
+ let result = ''
84
+ let last = 0
85
+ let found = false
86
+ let point = 255
87
+ const l = str.length
88
+ if (l > 100) {
89
+ return JSON.stringify(str)
90
+ }
91
+ for (var i = 0; i < l && point >= 32; i++) {
92
+ point = str.charCodeAt(i)
93
+ if (point === 34 || point === 92) {
94
+ result += str.slice(last, i) + '\\'
95
+ last = i
96
+ found = true
97
+ }
98
+ }
99
+ if (!found) {
100
+ result = str
101
+ } else {
102
+ result += str.slice(last)
103
+ }
104
+ return point < 32 ? JSON.stringify(str) : '"' + result + '"'
105
+ }
106
+
107
+ function asJson (obj, msg, num, time) {
108
+ const stringify = this[stringifySym]
109
+ const stringifySafe = this[stringifySafeSym]
110
+ const stringifiers = this[stringifiersSym]
111
+ const end = this[endSym]
112
+ const chindings = this[chindingsSym]
113
+ const serializers = this[serializersSym]
114
+ const formatters = this[formattersSym]
115
+ const messageKey = this[messageKeySym]
116
+ const errorKey = this[errorKeySym]
117
+ let data = this[lsCacheSym][num] + time
118
+
119
+ // we need the child bindings added to the output first so instance logged
120
+ // objects can take precedence when JSON.parse-ing the resulting log line
121
+ data = data + chindings
122
+
123
+ let value
124
+ if (formatters.log) {
125
+ obj = formatters.log(obj)
126
+ }
127
+ const wildcardStringifier = stringifiers[wildcardFirstSym]
128
+ let propStr = ''
129
+ for (const key in obj) {
130
+ value = obj[key]
131
+ if (Object.prototype.hasOwnProperty.call(obj, key) && value !== undefined) {
132
+ if (serializers[key]) {
133
+ value = serializers[key](value)
134
+ } else if (key === errorKey && serializers.err) {
135
+ value = serializers.err(value)
136
+ }
137
+
138
+ const stringifier = stringifiers[key] || wildcardStringifier
139
+
140
+ switch (typeof value) {
141
+ case 'undefined':
142
+ case 'function':
143
+ continue
144
+ case 'number':
145
+ /* eslint no-fallthrough: "off" */
146
+ if (Number.isFinite(value) === false) {
147
+ value = null
148
+ }
149
+ // this case explicitly falls through to the next one
150
+ case 'boolean':
151
+ if (stringifier) value = stringifier(value)
152
+ break
153
+ case 'string':
154
+ value = (stringifier || asString)(value)
155
+ break
156
+ default:
157
+ value = (stringifier || stringify)(value, stringifySafe)
158
+ }
159
+ if (value === undefined) continue
160
+ const strKey = asString(key)
161
+ propStr += ',' + strKey + ':' + value
162
+ }
163
+ }
164
+
165
+ let msgStr = ''
166
+ if (msg !== undefined) {
167
+ value = serializers[messageKey] ? serializers[messageKey](msg) : msg
168
+ const stringifier = stringifiers[messageKey] || wildcardStringifier
169
+
170
+ switch (typeof value) {
171
+ case 'function':
172
+ break
173
+ case 'number':
174
+ /* eslint no-fallthrough: "off" */
175
+ if (Number.isFinite(value) === false) {
176
+ value = null
177
+ }
178
+ // this case explicitly falls through to the next one
179
+ case 'boolean':
180
+ if (stringifier) value = stringifier(value)
181
+ msgStr = ',"' + messageKey + '":' + value
182
+ break
183
+ case 'string':
184
+ value = (stringifier || asString)(value)
185
+ msgStr = ',"' + messageKey + '":' + value
186
+ break
187
+ default:
188
+ value = (stringifier || stringify)(value, stringifySafe)
189
+ msgStr = ',"' + messageKey + '":' + value
190
+ }
191
+ }
192
+
193
+ if (this[nestedKeySym] && propStr) {
194
+ // place all the obj properties under the specified key
195
+ // the nested key is already formatted from the constructor
196
+ return data + this[nestedKeyStrSym] + propStr.slice(1) + '}' + msgStr + end
197
+ } else {
198
+ return data + propStr + msgStr + end
199
+ }
200
+ }
201
+
202
+ function asChindings (instance, bindings) {
203
+ let value
204
+ let data = instance[chindingsSym]
205
+ const stringify = instance[stringifySym]
206
+ const stringifySafe = instance[stringifySafeSym]
207
+ const stringifiers = instance[stringifiersSym]
208
+ const wildcardStringifier = stringifiers[wildcardFirstSym]
209
+ const serializers = instance[serializersSym]
210
+ const formatter = instance[formattersSym].bindings
211
+ bindings = formatter(bindings)
212
+
213
+ for (const key in bindings) {
214
+ value = bindings[key]
215
+ const valid = key !== 'level' &&
216
+ key !== 'serializers' &&
217
+ key !== 'formatters' &&
218
+ key !== 'customLevels' &&
219
+ bindings.hasOwnProperty(key) &&
220
+ value !== undefined
221
+ if (valid === true) {
222
+ value = serializers[key] ? serializers[key](value) : value
223
+ value = (stringifiers[key] || wildcardStringifier || stringify)(value, stringifySafe)
224
+ if (value === undefined) continue
225
+ data += ',"' + key + '":' + value
226
+ }
227
+ }
228
+ return data
229
+ }
230
+
231
+ function hasBeenTampered (stream) {
232
+ return stream.write !== stream.constructor.prototype.write
233
+ }
234
+
235
+ const hasNodeCodeCoverage = process.env.NODE_V8_COVERAGE || process.env.V8_COVERAGE
236
+
237
+ function buildSafeSonicBoom (opts) {
238
+ const stream = new SonicBoom(opts)
239
+ stream.on('error', filterBrokenPipe)
240
+ // If we are sync: false, we must flush on exit
241
+ // We must disable this if there is node code coverage due to
242
+ // https://github.com/nodejs/node/issues/49344#issuecomment-1741776308.
243
+ if (!hasNodeCodeCoverage && !opts.sync && isMainThread) {
244
+ onExit.register(stream, autoEnd)
245
+
246
+ stream.on('close', function () {
247
+ onExit.unregister(stream)
248
+ })
249
+ }
250
+ return stream
251
+
252
+ function filterBrokenPipe (err) {
253
+ // Impossible to replicate across all operating systems
254
+ /* istanbul ignore next */
255
+ if (err.code === 'EPIPE') {
256
+ // If we get EPIPE, we should stop logging here
257
+ // however we have no control to the consumer of
258
+ // SonicBoom, so we just overwrite the write method
259
+ stream.write = noop
260
+ stream.end = noop
261
+ stream.flushSync = noop
262
+ stream.destroy = noop
263
+ return
264
+ }
265
+ stream.removeListener('error', filterBrokenPipe)
266
+ stream.emit('error', err)
267
+ }
268
+ }
269
+
270
+ function autoEnd (stream, eventName) {
271
+ // This check is needed only on some platforms
272
+ /* istanbul ignore next */
273
+ if (stream.destroyed) {
274
+ return
275
+ }
276
+
277
+ if (eventName === 'beforeExit') {
278
+ // We still have an event loop, let's use it
279
+ stream.flush()
280
+ stream.on('drain', function () {
281
+ stream.end()
282
+ })
283
+ } else {
284
+ // For some reason istanbul is not detecting this, but it's there
285
+ /* istanbul ignore next */
286
+ // We do not have an event loop, so flush synchronously
287
+ stream.flushSync()
288
+ }
289
+ }
290
+
291
+ function createArgsNormalizer (defaultOptions) {
292
+ return function normalizeArgs (instance, caller, opts = {}, stream) {
293
+ // support stream as a string
294
+ if (typeof opts === 'string') {
295
+ stream = buildSafeSonicBoom({ dest: opts })
296
+ opts = {}
297
+ } else if (typeof stream === 'string') {
298
+ if (opts && opts.transport) {
299
+ throw Error('only one of option.transport or stream can be specified')
300
+ }
301
+ stream = buildSafeSonicBoom({ dest: stream })
302
+ } else if (opts instanceof SonicBoom || opts.writable || opts._writableState) {
303
+ stream = opts
304
+ opts = {}
305
+ } else if (opts.transport) {
306
+ if (opts.transport instanceof SonicBoom || opts.transport.writable || opts.transport._writableState) {
307
+ throw Error('option.transport do not allow stream, please pass to option directly. e.g. pino(transport)')
308
+ }
309
+ if (opts.transport.targets && opts.transport.targets.length && opts.formatters && typeof opts.formatters.level === 'function') {
310
+ throw Error('option.transport.targets do not allow custom level formatters')
311
+ }
312
+
313
+ let customLevels
314
+ if (opts.customLevels) {
315
+ customLevels = opts.useOnlyCustomLevels ? opts.customLevels : Object.assign({}, opts.levels, opts.customLevels)
316
+ }
317
+ stream = transport({ caller, ...opts.transport, levels: customLevels })
318
+ }
319
+ opts = Object.assign({}, defaultOptions, opts)
320
+ opts.serializers = Object.assign({}, defaultOptions.serializers, opts.serializers)
321
+ opts.formatters = Object.assign({}, defaultOptions.formatters, opts.formatters)
322
+
323
+ if (opts.prettyPrint) {
324
+ throw new Error('prettyPrint option is no longer supported, see the pino-pretty package (https://github.com/pinojs/pino-pretty)')
325
+ }
326
+
327
+ const { enabled, onChild } = opts
328
+ if (enabled === false) opts.level = 'silent'
329
+ if (!onChild) opts.onChild = noop
330
+ if (!stream) {
331
+ if (!hasBeenTampered(process.stdout)) {
332
+ // If process.stdout.fd is undefined, it means that we are running
333
+ // in a worker thread. Let's assume we are logging to file descriptor 1.
334
+ stream = buildSafeSonicBoom({ fd: process.stdout.fd || 1 })
335
+ } else {
336
+ stream = process.stdout
337
+ }
338
+ }
339
+ return { opts, stream }
340
+ }
341
+ }
342
+
343
+ function stringify (obj, stringifySafeFn) {
344
+ try {
345
+ return JSON.stringify(obj)
346
+ } catch (_) {
347
+ try {
348
+ const stringify = stringifySafeFn || this[stringifySafeSym]
349
+ return stringify(obj)
350
+ } catch (_) {
351
+ return '"[unable to serialize, circular reference is too complex to analyze]"'
352
+ }
353
+ }
354
+ }
355
+
356
+ function buildFormatters (level, bindings, log) {
357
+ return {
358
+ level,
359
+ bindings,
360
+ log
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Convert a string integer file descriptor to a proper native integer
366
+ * file descriptor.
367
+ *
368
+ * @param {string} destination The file descriptor string to attempt to convert.
369
+ *
370
+ * @returns {Number}
371
+ */
372
+ function normalizeDestFileDescriptor (destination) {
373
+ const fd = Number(destination)
374
+ if (typeof destination === 'string' && Number.isFinite(fd)) {
375
+ return fd
376
+ }
377
+ // destination could be undefined if we are in a worker
378
+ if (destination === undefined) {
379
+ // This is stdout in UNIX systems
380
+ return 1
381
+ }
382
+ return destination
383
+ }
384
+
385
+ module.exports = {
386
+ noop,
387
+ buildSafeSonicBoom,
388
+ asChindings,
389
+ asJson,
390
+ genLog,
391
+ createArgsNormalizer,
392
+ stringify,
393
+ buildFormatters,
394
+ normalizeDestFileDescriptor
395
+ }
@@ -0,0 +1,56 @@
1
+ 'use strict'
2
+
3
+ const { realImport, realRequire } = require('real-require')
4
+
5
+ module.exports = loadTransportStreamBuilder
6
+
7
+ /**
8
+ * Loads & returns a function to build transport streams
9
+ * @param {string} target
10
+ * @returns {Promise<function(object): Promise<import('node:stream').Writable>>}
11
+ * @throws {Error} In case the target module does not export a function
12
+ */
13
+ async function loadTransportStreamBuilder (target) {
14
+ let fn
15
+ try {
16
+ const toLoad = target.startsWith('file://') ? target : 'file://' + target
17
+
18
+ if (toLoad.endsWith('.ts') || toLoad.endsWith('.cts')) {
19
+ // TODO: add support for the TSM modules loader ( https://github.com/lukeed/tsm ).
20
+ if (process[Symbol.for('ts-node.register.instance')]) {
21
+ realRequire('ts-node/register')
22
+ } else if (process.env && process.env.TS_NODE_DEV) {
23
+ realRequire('ts-node-dev')
24
+ }
25
+ // TODO: Support ES imports once tsc, tap & ts-node provide better compatibility guarantees.
26
+ fn = realRequire(decodeURIComponent(target))
27
+ } else {
28
+ fn = (await realImport(toLoad))
29
+ }
30
+ } catch (error) {
31
+ // See this PR for details: https://github.com/pinojs/thread-stream/pull/34
32
+ if ((error.code === 'ENOTDIR' || error.code === 'ERR_MODULE_NOT_FOUND')) {
33
+ fn = realRequire(target)
34
+ } else if (error.code === undefined || error.code === 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING') {
35
+ // When bundled with pkg, an undefined error is thrown when called with realImport
36
+ // When bundled with pkg and using node v20, an ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING error is thrown when called with realImport
37
+ // More info at: https://github.com/pinojs/thread-stream/issues/143
38
+ try {
39
+ fn = realRequire(decodeURIComponent(target))
40
+ } catch {
41
+ throw error
42
+ }
43
+ } else {
44
+ throw error
45
+ }
46
+ }
47
+
48
+ // Depending on how the default export is performed, and on how the code is
49
+ // transpiled, we may find cases of two nested "default" objects.
50
+ // See https://github.com/pinojs/pino/issues/1243#issuecomment-982774762
51
+ if (typeof fn === 'object') fn = fn.default
52
+ if (typeof fn === 'object') fn = fn.default
53
+ if (typeof fn !== 'function') throw Error('exported worker is not a function')
54
+
55
+ return fn
56
+ }
@@ -0,0 +1,168 @@
1
+ 'use strict'
2
+
3
+ /* eslint-disable */
4
+ const { createRequire } = require('module')
5
+ const getCallers = require('./caller')
6
+ const { join, isAbsolute, sep } = require('node:path')
7
+ const sleep = require('atomic-sleep')
8
+ const onExit = require('on-exit-leak-free')
9
+ const ThreadStream = require('thread-stream')
10
+
11
+ function setupOnExit (stream) {
12
+ // This is leak free, it does not leave event handlers
13
+ onExit.register(stream, autoEnd)
14
+ onExit.registerBeforeExit(stream, flush)
15
+
16
+ stream.on('close', function () {
17
+ onExit.unregister(stream)
18
+ })
19
+ }
20
+
21
+ function buildStream (filename, workerData, workerOpts, sync) {
22
+ const stream = new ThreadStream({
23
+ filename,
24
+ workerData,
25
+ workerOpts,
26
+ sync
27
+ })
28
+
29
+ stream.on('ready', onReady)
30
+ stream.on('close', function () {
31
+ process.removeListener('exit', onExit)
32
+ })
33
+
34
+ process.on('exit', onExit)
35
+
36
+ function onReady () {
37
+ process.removeListener('exit', onExit)
38
+ stream.unref()
39
+
40
+ if (workerOpts.autoEnd !== false) {
41
+ setupOnExit(stream)
42
+ }
43
+ }
44
+
45
+ function onExit () {
46
+ /* istanbul ignore next */
47
+ if (stream.closed) {
48
+ return
49
+ }
50
+ stream.flushSync()
51
+ // Apparently there is a very sporadic race condition
52
+ // that in certain OS would prevent the messages to be flushed
53
+ // because the thread might not have been created still.
54
+ // Unfortunately we need to sleep(100) in this case.
55
+ sleep(100)
56
+ stream.end()
57
+ }
58
+
59
+ return stream
60
+ }
61
+
62
+ function autoEnd (stream) {
63
+ stream.ref()
64
+ stream.flushSync()
65
+ stream.end()
66
+ stream.once('close', function () {
67
+ stream.unref()
68
+ })
69
+ }
70
+
71
+ function flush (stream) {
72
+ stream.flushSync()
73
+ }
74
+
75
+ function transport (fullOptions) {
76
+ const { pipeline, targets, levels, dedupe, worker = {}, caller = getCallers(), sync = false } = fullOptions
77
+
78
+ const options = {
79
+ ...fullOptions.options
80
+ }
81
+
82
+ // Backwards compatibility
83
+ const callers = typeof caller === 'string' ? [caller] : caller
84
+
85
+ // This will be eventually modified by bundlers
86
+ const bundlerOverrides = '__bundlerPathsOverrides' in globalThis ? globalThis.__bundlerPathsOverrides : {}
87
+
88
+ let target = fullOptions.target
89
+
90
+ if (target && targets) {
91
+ throw new Error('only one of target or targets can be specified')
92
+ }
93
+
94
+ if (targets) {
95
+ target = bundlerOverrides['pino-worker'] || join(__dirname, 'worker.js')
96
+ options.targets = targets.filter(dest => dest.target).map((dest) => {
97
+ return {
98
+ ...dest,
99
+ target: fixTarget(dest.target)
100
+ }
101
+ })
102
+ options.pipelines = targets.filter(dest => dest.pipeline).map((dest) => {
103
+ return dest.pipeline.map((t) => {
104
+ return {
105
+ ...t,
106
+ level: dest.level, // duplicate the pipeline `level` property defined in the upper level
107
+ target: fixTarget(t.target)
108
+ }
109
+ })
110
+ })
111
+ } else if (pipeline) {
112
+ target = bundlerOverrides['pino-worker'] || join(__dirname, 'worker.js')
113
+ options.pipelines = [pipeline.map((dest) => {
114
+ return {
115
+ ...dest,
116
+ target: fixTarget(dest.target)
117
+ }
118
+ })]
119
+ }
120
+
121
+ if (levels) {
122
+ options.levels = levels
123
+ }
124
+
125
+ if (dedupe) {
126
+ options.dedupe = dedupe
127
+ }
128
+
129
+ options.pinoWillSendConfig = true
130
+
131
+ return buildStream(fixTarget(target), options, worker, sync)
132
+
133
+ function fixTarget (origin) {
134
+ origin = bundlerOverrides[origin] || origin
135
+
136
+ if (isAbsolute(origin) || origin.indexOf('file://') === 0) {
137
+ return origin
138
+ }
139
+
140
+ if (origin === 'pino/file') {
141
+ return join(__dirname, '..', 'file.js')
142
+ }
143
+
144
+ let fixTarget
145
+
146
+ for (const filePath of callers) {
147
+ try {
148
+ const context = filePath === 'node:repl'
149
+ ? process.cwd() + sep
150
+ : filePath
151
+
152
+ fixTarget = createRequire(context).resolve(origin)
153
+ break
154
+ } catch (err) {
155
+ // Silent catch
156
+ continue
157
+ }
158
+ }
159
+
160
+ if (!fixTarget) {
161
+ throw new Error(`unable to determine transport target for "${origin}"`)
162
+ }
163
+
164
+ return fixTarget
165
+ }
166
+ }
167
+
168
+ module.exports = transport