'use strict' const metadata = Symbol.for('pino.metadata') const split = require('split2') const { Duplex } = require('readable-stream') const { parentPort, workerData } = require('worker_threads') function createDeferred () { let resolve let reject const promise = new Promise((_resolve, _reject) => { resolve = _resolve reject = _reject }) promise.resolve = resolve promise.reject = reject return promise } module.exports = function build (fn, opts = {}) { const waitForConfig = opts.expectPinoConfig === true && workerData?.workerData?.pinoWillSendConfig === true const parseLines = opts.parse === 'lines' const parseLine = typeof opts.parseLine === 'function' ? opts.parseLine : JSON.parse const close = opts.close || defaultClose const stream = split(function (line) { let value try { value = parseLine(line) } catch (error) { this.emit('unknown', line, error) return } if (value === null) { this.emit('unknown', line, 'Null value ignored') return } if (typeof value !== 'object') { value = { data: value, time: Date.now() } } if (stream[metadata]) { stream.lastTime = value.time stream.lastLevel = value.level stream.lastObj = value } if (parseLines) { return line } return value }, { autoDestroy: true }) stream._destroy = function (err, cb) { const promise = close(err, cb) if (promise && typeof promise.then === 'function') { promise.then(cb, cb) } } if (opts.expectPinoConfig === true && workerData?.workerData?.pinoWillSendConfig !== true) { setImmediate(() => { stream.emit('error', new Error('This transport is not compatible with the current version of pino. Please upgrade pino to the latest version.')) }) } if (opts.metadata !== false) { stream[metadata] = true stream.lastTime = 0 stream.lastLevel = 0 stream.lastObj = null } if (waitForConfig) { let pinoConfig = {} const configReceived = createDeferred() parentPort.on('message', function handleMessage (message) { if (message.code === 'PINO_CONFIG') { pinoConfig = message.config configReceived.resolve() parentPort.off('message', handleMessage) } }) Object.defineProperties(stream, { levels: { get () { return pinoConfig.levels } }, messageKey: { get () { return pinoConfig.messageKey } }, errorKey: { get () { return pinoConfig.errorKey } } }) return configReceived.then(finish) } return finish() function finish () { let res = fn(stream) if (res && typeof res.catch === 'function') { res.catch((err) => { stream.destroy(err) }) // set it to null to not retain a reference to the promise res = null } else if (opts.enablePipelining && res) { return Duplex.from({ writable: stream, readable: res }) } return stream } } function defaultClose (err, cb) { process.nextTick(cb, err) }