pino-testkit 10.4.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.
Files changed (196) hide show
  1. package/.nojekyll +0 -0
  2. package/.prettierignore +1 -0
  3. package/CNAME +1 -0
  4. package/CONTRIBUTING.md +30 -0
  5. package/LICENSE +21 -0
  6. package/README.md +175 -0
  7. package/SECURITY.md +87 -0
  8. package/benchmarks/basic.bench.js +95 -0
  9. package/benchmarks/child-child.bench.js +52 -0
  10. package/benchmarks/child-creation.bench.js +73 -0
  11. package/benchmarks/child.bench.js +62 -0
  12. package/benchmarks/deep-object.bench.js +88 -0
  13. package/benchmarks/formatters.bench.js +50 -0
  14. package/benchmarks/internal/custom-levels.js +67 -0
  15. package/benchmarks/internal/just-pino-heavy.bench.js +76 -0
  16. package/benchmarks/internal/just-pino.bench.js +182 -0
  17. package/benchmarks/internal/parent-vs-child.bench.js +75 -0
  18. package/benchmarks/internal/redact.bench.js +86 -0
  19. package/benchmarks/long-string.bench.js +81 -0
  20. package/benchmarks/multi-arg.bench.js +193 -0
  21. package/benchmarks/multistream.js +98 -0
  22. package/benchmarks/object.bench.js +82 -0
  23. package/benchmarks/utils/generate-benchmark-doc.js +36 -0
  24. package/benchmarks/utils/runbench.js +138 -0
  25. package/benchmarks/utils/wrap-log-level.js +55 -0
  26. package/bin.js +6 -0
  27. package/browser.js +547 -0
  28. package/build/sync-version.js +25 -0
  29. package/docs/api.md +1593 -0
  30. package/docs/asynchronous.md +40 -0
  31. package/docs/benchmarks.md +55 -0
  32. package/docs/browser.md +278 -0
  33. package/docs/bundling.md +40 -0
  34. package/docs/child-loggers.md +95 -0
  35. package/docs/diagnostics.md +16 -0
  36. package/docs/ecosystem.md +86 -0
  37. package/docs/help.md +345 -0
  38. package/docs/lts.md +64 -0
  39. package/docs/pretty.md +35 -0
  40. package/docs/redaction.md +135 -0
  41. package/docs/transports.md +1376 -0
  42. package/docs/web.md +309 -0
  43. package/eslint.config.js +28 -0
  44. package/examples/basic.js +43 -0
  45. package/examples/transport.js +68 -0
  46. package/favicon.ico +0 -0
  47. package/file.js +12 -0
  48. package/inc-version.sh +42 -0
  49. package/index.html +55 -0
  50. package/lib/caller.js +30 -0
  51. package/lib/constants.js +28 -0
  52. package/lib/contract.js +8 -0
  53. package/lib/deprecations.js +8 -0
  54. package/lib/levels.js +241 -0
  55. package/lib/meta.js +3 -0
  56. package/lib/multistream.js +203 -0
  57. package/lib/proto.js +256 -0
  58. package/lib/redaction.js +114 -0
  59. package/lib/symbols.js +74 -0
  60. package/lib/time.js +39 -0
  61. package/lib/tools.js +427 -0
  62. package/lib/transport-stream.js +56 -0
  63. package/lib/transport.js +289 -0
  64. package/lib/worker.js +194 -0
  65. package/package.json +119 -0
  66. package/pino.d.ts +908 -0
  67. package/pino.js +240 -0
  68. package/test/basic.test.js +886 -0
  69. package/test/broken-pipe.test.js +59 -0
  70. package/test/browser-child.test.js +132 -0
  71. package/test/browser-disabled.test.js +87 -0
  72. package/test/browser-early-console-freeze.test.js +12 -0
  73. package/test/browser-is-level-enabled.test.js +101 -0
  74. package/test/browser-levels.test.js +241 -0
  75. package/test/browser-serializers.test.js +352 -0
  76. package/test/browser-timestamp.test.js +88 -0
  77. package/test/browser-transmit.test.js +417 -0
  78. package/test/browser.test.js +698 -0
  79. package/test/complex-objects.test.js +36 -0
  80. package/test/crlf.test.js +34 -0
  81. package/test/custom-levels.test.js +267 -0
  82. package/test/diagnostics.test.js +107 -0
  83. package/test/error-key.test.js +37 -0
  84. package/test/error.test.js +403 -0
  85. package/test/escaping.test.js +93 -0
  86. package/test/esm/esm.mjs +14 -0
  87. package/test/esm/index.test.js +21 -0
  88. package/test/esm/named-exports.mjs +29 -0
  89. package/test/exit.test.js +79 -0
  90. package/test/fixtures/broken-pipe/basic.js +9 -0
  91. package/test/fixtures/broken-pipe/destination.js +10 -0
  92. package/test/fixtures/broken-pipe/syncfalse.js +12 -0
  93. package/test/fixtures/console-transport.js +13 -0
  94. package/test/fixtures/crashing-transport.js +13 -0
  95. package/test/fixtures/default-exit.js +8 -0
  96. package/test/fixtures/destination-exit.js +8 -0
  97. package/test/fixtures/eval/index.js +13 -0
  98. package/test/fixtures/eval/node_modules/14-files.js +3 -0
  99. package/test/fixtures/eval/node_modules/2-files.js +3 -0
  100. package/test/fixtures/eval/node_modules/file1.js +5 -0
  101. package/test/fixtures/eval/node_modules/file10.js +5 -0
  102. package/test/fixtures/eval/node_modules/file11.js +5 -0
  103. package/test/fixtures/eval/node_modules/file12.js +5 -0
  104. package/test/fixtures/eval/node_modules/file13.js +5 -0
  105. package/test/fixtures/eval/node_modules/file14.js +11 -0
  106. package/test/fixtures/eval/node_modules/file2.js +5 -0
  107. package/test/fixtures/eval/node_modules/file3.js +5 -0
  108. package/test/fixtures/eval/node_modules/file4.js +5 -0
  109. package/test/fixtures/eval/node_modules/file5.js +5 -0
  110. package/test/fixtures/eval/node_modules/file6.js +5 -0
  111. package/test/fixtures/eval/node_modules/file7.js +5 -0
  112. package/test/fixtures/eval/node_modules/file8.js +5 -0
  113. package/test/fixtures/eval/node_modules/file9.js +5 -0
  114. package/test/fixtures/noop-transport.js +10 -0
  115. package/test/fixtures/pretty/null-prototype.js +8 -0
  116. package/test/fixtures/stdout-hack-protection.js +11 -0
  117. package/test/fixtures/syncfalse-child.js +6 -0
  118. package/test/fixtures/syncfalse-exit.js +9 -0
  119. package/test/fixtures/syncfalse-flush-exit.js +10 -0
  120. package/test/fixtures/syncfalse.js +6 -0
  121. package/test/fixtures/syntax-error-esm.mjs +2 -0
  122. package/test/fixtures/to-file-transport-with-transform.js +20 -0
  123. package/test/fixtures/to-file-transport.js +13 -0
  124. package/test/fixtures/to-file-transport.mjs +8 -0
  125. package/test/fixtures/transport/index.js +12 -0
  126. package/test/fixtures/transport/package.json +5 -0
  127. package/test/fixtures/transport-exit-immediately-with-async-dest.js +16 -0
  128. package/test/fixtures/transport-exit-immediately.js +11 -0
  129. package/test/fixtures/transport-exit-on-ready.js +12 -0
  130. package/test/fixtures/transport-invalid-node-options.js +26 -0
  131. package/test/fixtures/transport-main.js +9 -0
  132. package/test/fixtures/transport-many-lines.js +29 -0
  133. package/test/fixtures/transport-preload-main.mjs +13 -0
  134. package/test/fixtures/transport-preload.mjs +13 -0
  135. package/test/fixtures/transport-string-stdout.js +9 -0
  136. package/test/fixtures/transport-transform.js +21 -0
  137. package/test/fixtures/transport-uses-pino-config.js +33 -0
  138. package/test/fixtures/transport-with-on-exit.js +12 -0
  139. package/test/fixtures/transport-worker-data.js +19 -0
  140. package/test/fixtures/transport-worker-name.js +24 -0
  141. package/test/fixtures/transport-worker.js +15 -0
  142. package/test/fixtures/transport-wrong-export-type.js +3 -0
  143. package/test/fixtures/ts/to-file-transport-native.mts +15 -0
  144. package/test/fixtures/ts/to-file-transport-with-transform.ts +18 -0
  145. package/test/fixtures/ts/to-file-transport.ts +11 -0
  146. package/test/fixtures/ts/transpile.cjs +36 -0
  147. package/test/fixtures/ts/transport-exit-immediately-with-async-dest.ts +15 -0
  148. package/test/fixtures/ts/transport-exit-immediately.ts +10 -0
  149. package/test/fixtures/ts/transport-exit-on-ready.ts +11 -0
  150. package/test/fixtures/ts/transport-main.ts +8 -0
  151. package/test/fixtures/ts/transport-string-stdout.ts +8 -0
  152. package/test/fixtures/ts/transport-worker.ts +14 -0
  153. package/test/formatters.test.js +364 -0
  154. package/test/helper.d.ts +4 -0
  155. package/test/helper.js +155 -0
  156. package/test/hooks.test.js +114 -0
  157. package/test/http.test.js +214 -0
  158. package/test/internals/version.test.js +17 -0
  159. package/test/is-level-enabled.test.js +179 -0
  160. package/test/jest/basic.spec.js +10 -0
  161. package/test/levels.test.js +810 -0
  162. package/test/metadata.test.js +120 -0
  163. package/test/mixin-merge-strategy.test.js +57 -0
  164. package/test/mixin.test.js +241 -0
  165. package/test/multistream.test.js +729 -0
  166. package/test/redact.test.js +893 -0
  167. package/test/serializers.test.js +257 -0
  168. package/test/stdout-protection.test.js +41 -0
  169. package/test/syncfalse.test.js +186 -0
  170. package/test/timestamp-nano.test.js +37 -0
  171. package/test/timestamp.test.js +124 -0
  172. package/test/transport/big.test.js +42 -0
  173. package/test/transport/bundlers-support.test.js +99 -0
  174. package/test/transport/caller.test.js +24 -0
  175. package/test/transport/core.test.js +733 -0
  176. package/test/transport/core.transpiled.test.ts +114 -0
  177. package/test/transport/crash.test.js +36 -0
  178. package/test/transport/module-link.test.js +241 -0
  179. package/test/transport/native-type-stripping.test.mjs +151 -0
  180. package/test/transport/node-options.test.js +116 -0
  181. package/test/transport/pipeline.test.js +137 -0
  182. package/test/transport/preload.test.js +54 -0
  183. package/test/transport/repl.test.js +15 -0
  184. package/test/transport/sync-false.test.js +67 -0
  185. package/test/transport/sync-true.test.js +57 -0
  186. package/test/transport/targets.test.js +48 -0
  187. package/test/transport/uses-pino-config.test.js +166 -0
  188. package/test/transport-stream.test.js +40 -0
  189. package/test/types/pino-import.test-d.cts +30 -0
  190. package/test/types/pino-multistream.test-d.ts +28 -0
  191. package/test/types/pino-top-export.test-d.ts +34 -0
  192. package/test/types/pino-transport.test-d.ts +156 -0
  193. package/test/types/pino-type-only.test-d.ts +75 -0
  194. package/test/types/pino.test-d.ts +683 -0
  195. package/test/types/pino.ts +91 -0
  196. package/tsconfig.json +14 -0
@@ -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,289 @@
1
+ 'use strict'
2
+
3
+ const { createRequire } = require('module')
4
+ const { existsSync } = require('node:fs')
5
+ const getCallers = require('./caller')
6
+ const { join, isAbsolute, sep } = require('node:path')
7
+ const { fileURLToPath } = require('node:url')
8
+ const sleep = require('atomic-sleep')
9
+ const onExit = require('on-exit-leak-free')
10
+ const ThreadStream = require('thread-stream')
11
+
12
+ function setupOnExit (stream) {
13
+ // This is leak free, it does not leave event handlers
14
+ onExit.register(stream, autoEnd)
15
+ onExit.registerBeforeExit(stream, flush)
16
+
17
+ stream.on('close', function () {
18
+ onExit.unregister(stream)
19
+ })
20
+ }
21
+
22
+ // Check if preload flags exist in execArgv.
23
+ // During preload phase (require.main undefined), we pass empty execArgv to prevent infinite worker spawning.
24
+ // We don't try to filter and pass other flags because many (like --stack-trace-limit, --tls-cipher-list)
25
+ // aren't valid for worker threads and would cause ERR_WORKER_INVALID_EXEC_ARGV.
26
+ function hasPreloadFlags () {
27
+ const execArgv = process.execArgv
28
+ for (let i = 0; i < execArgv.length; i++) {
29
+ const arg = execArgv[i]
30
+ if (arg === '--import' || arg === '--require' || arg === '-r') {
31
+ return true
32
+ }
33
+ if (arg.startsWith('--import=') || arg.startsWith('--require=') || arg.startsWith('-r=')) {
34
+ return true
35
+ }
36
+ }
37
+ return false
38
+ }
39
+
40
+ function sanitizeNodeOptions (nodeOptions) {
41
+ const tokens = nodeOptions.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)
42
+ if (!tokens) {
43
+ return nodeOptions
44
+ }
45
+
46
+ const sanitized = []
47
+ let changed = false
48
+
49
+ for (let i = 0; i < tokens.length; i++) {
50
+ const token = tokens[i]
51
+
52
+ if (token === '--require' || token === '-r' || token === '--import') {
53
+ const next = tokens[i + 1]
54
+ if (next && shouldDropPreload(next)) {
55
+ changed = true
56
+ i++
57
+ continue
58
+ }
59
+
60
+ sanitized.push(token)
61
+ if (next) {
62
+ sanitized.push(next)
63
+ i++
64
+ }
65
+ continue
66
+ }
67
+
68
+ if (token.startsWith('--require=') || token.startsWith('-r=') || token.startsWith('--import=')) {
69
+ const value = token.slice(token.indexOf('=') + 1)
70
+ if (shouldDropPreload(value)) {
71
+ changed = true
72
+ continue
73
+ }
74
+ }
75
+
76
+ sanitized.push(token)
77
+ }
78
+
79
+ return changed ? sanitized.join(' ') : nodeOptions
80
+ }
81
+
82
+ function shouldDropPreload (value) {
83
+ const unquoted = stripQuotes(value)
84
+ if (!unquoted) {
85
+ return false
86
+ }
87
+
88
+ let path = unquoted
89
+ if (path.startsWith('file://')) {
90
+ try {
91
+ path = fileURLToPath(path)
92
+ } catch {
93
+ return false
94
+ }
95
+ }
96
+
97
+ return isAbsolute(path) && !existsSync(path)
98
+ }
99
+
100
+ function stripQuotes (value) {
101
+ const first = value[0]
102
+ const last = value[value.length - 1]
103
+
104
+ if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
105
+ return value.slice(1, -1)
106
+ }
107
+
108
+ return value
109
+ }
110
+
111
+ function buildStream (filename, workerData, workerOpts, sync, name) {
112
+ // When pino is loaded during a preload phase (via --import or --require),
113
+ // pass empty execArgv to prevent infinite spawning. Each worker would
114
+ // otherwise re-run the preload, creating another transport.
115
+ if (!workerOpts.execArgv && hasPreloadFlags() && require.main === undefined) {
116
+ workerOpts = {
117
+ ...workerOpts,
118
+ execArgv: []
119
+ }
120
+ }
121
+
122
+ if (!workerOpts.env && process.env.NODE_OPTIONS) {
123
+ const nodeOptions = sanitizeNodeOptions(process.env.NODE_OPTIONS)
124
+ if (nodeOptions !== process.env.NODE_OPTIONS) {
125
+ workerOpts = {
126
+ ...workerOpts,
127
+ env: {
128
+ ...process.env,
129
+ NODE_OPTIONS: nodeOptions
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ workerOpts = { ...workerOpts, name }
136
+
137
+ const stream = new ThreadStream({
138
+ filename,
139
+ workerData,
140
+ workerOpts,
141
+ sync
142
+ })
143
+
144
+ stream.on('ready', onReady)
145
+ stream.on('close', function () {
146
+ process.removeListener('exit', onExit)
147
+ })
148
+
149
+ process.on('exit', onExit)
150
+
151
+ function onReady () {
152
+ process.removeListener('exit', onExit)
153
+ stream.unref()
154
+
155
+ if (workerOpts.autoEnd !== false) {
156
+ setupOnExit(stream)
157
+ }
158
+ }
159
+
160
+ function onExit () {
161
+ /* istanbul ignore next */
162
+ if (stream.closed) {
163
+ return
164
+ }
165
+ stream.flushSync()
166
+ // Apparently there is a very sporadic race condition
167
+ // that in certain OS would prevent the messages to be flushed
168
+ // because the thread might not have been created still.
169
+ // Unfortunately we need to sleep(100) in this case.
170
+ sleep(100)
171
+ stream.end()
172
+ }
173
+
174
+ return stream
175
+ }
176
+
177
+ function autoEnd (stream) {
178
+ stream.ref()
179
+ stream.flushSync()
180
+ stream.end()
181
+ stream.once('close', function () {
182
+ stream.unref()
183
+ })
184
+ }
185
+
186
+ function flush (stream) {
187
+ stream.flushSync()
188
+ }
189
+
190
+ function transport (fullOptions) {
191
+ const { pipeline, targets, levels, dedupe, worker = {}, caller = getCallers(), sync = false } = fullOptions
192
+
193
+ const options = {
194
+ ...fullOptions.options
195
+ }
196
+
197
+ // Backwards compatibility
198
+ const callers = typeof caller === 'string' ? [caller] : caller
199
+
200
+ // This will be eventually modified by bundlers
201
+ const bundlerOverrides = (typeof globalThis === 'object' &&
202
+ Object.prototype.hasOwnProperty.call(globalThis, '__bundlerPathsOverrides') &&
203
+ globalThis.__bundlerPathsOverrides &&
204
+ typeof globalThis.__bundlerPathsOverrides === 'object')
205
+ ? globalThis.__bundlerPathsOverrides
206
+ : Object.create(null)
207
+
208
+ let target = fullOptions.target
209
+
210
+ if (target && targets) {
211
+ throw new Error('only one of target or targets can be specified')
212
+ }
213
+
214
+ if (targets) {
215
+ target = bundlerOverrides['pino-worker'] || join(__dirname, 'worker.js')
216
+ options.targets = targets.filter(dest => dest.target).map((dest) => {
217
+ return {
218
+ ...dest,
219
+ target: fixTarget(dest.target)
220
+ }
221
+ })
222
+ options.pipelines = targets.filter(dest => dest.pipeline).map((dest) => {
223
+ return dest.pipeline.map((t) => {
224
+ return {
225
+ ...t,
226
+ level: dest.level, // duplicate the pipeline `level` property defined in the upper level
227
+ target: fixTarget(t.target)
228
+ }
229
+ })
230
+ })
231
+ } else if (pipeline) {
232
+ target = bundlerOverrides['pino-worker'] || join(__dirname, 'worker.js')
233
+ options.pipelines = [pipeline.map((dest) => {
234
+ return {
235
+ ...dest,
236
+ target: fixTarget(dest.target)
237
+ }
238
+ })]
239
+ }
240
+
241
+ if (levels) {
242
+ options.levels = levels
243
+ }
244
+
245
+ if (dedupe) {
246
+ options.dedupe = dedupe
247
+ }
248
+
249
+ options.pinoWillSendConfig = true
250
+
251
+ const name = (targets || pipeline) ? 'pino.transport' : target
252
+ return buildStream(fixTarget(target), options, worker, sync, name)
253
+
254
+ function fixTarget (origin) {
255
+ origin = bundlerOverrides[origin] || origin
256
+
257
+ if (isAbsolute(origin) || origin.indexOf('file://') === 0) {
258
+ return origin
259
+ }
260
+
261
+ if (origin === 'pino/file') {
262
+ return join(__dirname, '..', 'file.js')
263
+ }
264
+
265
+ let fixTarget
266
+
267
+ for (const filePath of callers) {
268
+ try {
269
+ const context = filePath === 'node:repl'
270
+ ? process.cwd() + sep
271
+ : filePath
272
+
273
+ fixTarget = createRequire(context).resolve(origin)
274
+ break
275
+ } catch (err) {
276
+ // Silent catch
277
+ continue
278
+ }
279
+ }
280
+
281
+ if (!fixTarget) {
282
+ throw new Error(`unable to determine transport target for "${origin}"`)
283
+ }
284
+
285
+ return fixTarget
286
+ }
287
+ }
288
+
289
+ module.exports = transport
package/lib/worker.js ADDED
@@ -0,0 +1,194 @@
1
+ 'use strict'
2
+
3
+ const EE = require('node:events')
4
+ const { pipeline, PassThrough } = require('node:stream')
5
+ const pino = require('../pino.js')
6
+ const build = require('pino-abstract-transport')
7
+ const loadTransportStreamBuilder = require('./transport-stream')
8
+
9
+ // This file is not checked by the code coverage tool,
10
+ // as it is not reliable.
11
+
12
+ /* istanbul ignore file */
13
+
14
+ /*
15
+ * > Multiple targets & pipelines
16
+ *
17
+ *
18
+ * ┌─────────────────────────────────────────────────┐ ┌─────┐
19
+ * │ │ │ p │
20
+ * │ │ │ i │
21
+ * │ target │ │ n │
22
+ * │ │ ────────────────────────────────┼────┤ o │
23
+ * │ targets │ target │ │ . │
24
+ * │ ────────────► │ ────────────────────────────────┼────┤ m │ source
25
+ * │ │ target │ │ u │ │
26
+ * │ │ ────────────────────────────────┼────┤ l │ │write
27
+ * │ │ │ │ t │ ▼
28
+ * │ │ pipeline ┌───────────────┐ │ │ i │ ┌────────┐
29
+ * │ │ ──────────► │ PassThrough ├───┼────┤ s ├──────┤ │
30
+ * │ │ └───────────────┘ │ │ t │ write│ Thread │
31
+ * │ │ │ │ r │◄─────┤ Stream │
32
+ * │ │ pipeline ┌───────────────┐ │ │ e │ │ │
33
+ * │ │ ──────────► │ PassThrough ├───┼────┤ a │ └────────┘
34
+ * │ └───────────────┘ │ │ m │
35
+ * │ │ │ │
36
+ * └─────────────────────────────────────────────────┘ └─────┘
37
+ *
38
+ *
39
+ *
40
+ * > One single pipeline or target
41
+ *
42
+ *
43
+ * source
44
+ * │
45
+ * ┌────────────────────────────────────────────────┐ │write
46
+ * │ │ ▼
47
+ * │ │ ┌────────┐
48
+ * │ targets │ target │ │ │
49
+ * │ ────────────► │ ──────────────────────────────┤ │ │
50
+ * │ │ │ │ │
51
+ * │ ├──────┤ │
52
+ * │ │ │ │
53
+ * │ │ │ │
54
+ * │ OR │ │ │
55
+ * │ │ │ │
56
+ * │ │ │ │
57
+ * │ ┌──────────────┐ │ │ │
58
+ * │ targets │ pipeline │ │ │ │ Thread │
59
+ * │ ────────────► │ ────────────►│ PassThrough ├─┤ │ Stream │
60
+ * │ │ │ │ │ │ │
61
+ * │ └──────────────┘ │ │ │
62
+ * │ │ │ │
63
+ * │ OR │ write│ │
64
+ * │ │◄─────┤ │
65
+ * │ │ │ │
66
+ * │ ┌──────────────┐ │ │ │
67
+ * │ pipeline │ │ │ │ │
68
+ * │ ──────────────►│ PassThrough ├────────────────┤ │ │
69
+ * │ │ │ │ │ │
70
+ * │ └──────────────┘ │ └────────┘
71
+ * │ │
72
+ * │ │
73
+ * └────────────────────────────────────────────────┘
74
+ */
75
+
76
+ module.exports = async function ({ targets, pipelines, levels, dedupe }) {
77
+ const targetStreams = []
78
+
79
+ // Process targets
80
+ if (targets && targets.length) {
81
+ targets = await Promise.all(targets.map(async (t) => {
82
+ const fn = await loadTransportStreamBuilder(t.target)
83
+ const stream = await fn(t.options)
84
+ return {
85
+ level: t.level,
86
+ stream
87
+ }
88
+ }))
89
+
90
+ targetStreams.push(...targets)
91
+ }
92
+
93
+ // Process pipelines
94
+ if (pipelines && pipelines.length) {
95
+ pipelines = await Promise.all(
96
+ pipelines.map(async (p) => {
97
+ let level
98
+ const pipeDests = await Promise.all(
99
+ p.map(async (t) => {
100
+ // level assigned to pipeline is duplicated over all its targets, just store it
101
+ level = t.level
102
+ const fn = await loadTransportStreamBuilder(t.target)
103
+ const stream = await fn(t.options)
104
+ return stream
105
+ }
106
+ ))
107
+
108
+ return {
109
+ level,
110
+ stream: createPipeline(pipeDests)
111
+ }
112
+ })
113
+ )
114
+ targetStreams.push(...pipelines)
115
+ }
116
+
117
+ // Skip building the multistream step if either one single pipeline or target is defined and
118
+ // return directly the stream instance back to TreadStream.
119
+ // This is equivalent to define either:
120
+ //
121
+ // pino.transport({ target: ... })
122
+ //
123
+ // OR
124
+ //
125
+ // pino.transport({ pipeline: ... })
126
+ if (targetStreams.length === 1) {
127
+ return targetStreams[0].stream
128
+ } else {
129
+ return build(process, {
130
+ parse: 'lines',
131
+ metadata: true,
132
+ close (err, cb) {
133
+ let expected = 0
134
+ for (const transport of targetStreams) {
135
+ expected++
136
+ transport.stream.on('close', closeCb)
137
+ transport.stream.end()
138
+ }
139
+
140
+ function closeCb () {
141
+ if (--expected === 0) {
142
+ cb(err)
143
+ }
144
+ }
145
+ }
146
+ })
147
+ }
148
+
149
+ // TODO: Why split2 was not used for pipelines?
150
+ function process (stream) {
151
+ const multi = pino.multistream(targetStreams, { levels, dedupe })
152
+ // TODO manage backpressure
153
+ stream.on('data', function (chunk) {
154
+ const { lastTime, lastMsg, lastObj, lastLevel } = this
155
+ multi.lastLevel = lastLevel
156
+ multi.lastTime = lastTime
157
+ multi.lastMsg = lastMsg
158
+ multi.lastObj = lastObj
159
+
160
+ // TODO handle backpressure
161
+ multi.write(chunk + '\n')
162
+ })
163
+ }
164
+
165
+ /**
166
+ * Creates a pipeline using the provided streams and return an instance of `PassThrough` stream
167
+ * as a source for the pipeline.
168
+ *
169
+ * @param {(TransformStream|WritableStream)[]} streams An array of streams.
170
+ * All intermediate streams in the array *MUST* be `Transform` streams and only the last one `Writable`.
171
+ * @returns A `PassThrough` stream instance representing the source stream of the pipeline
172
+ */
173
+ function createPipeline (streams) {
174
+ const ee = new EE()
175
+ const stream = new PassThrough({
176
+ autoDestroy: true,
177
+ destroy (_, cb) {
178
+ ee.on('error', cb)
179
+ ee.on('closed', cb)
180
+ }
181
+ })
182
+
183
+ pipeline(stream, ...streams, function (err) {
184
+ if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') {
185
+ ee.emit('error', err)
186
+ return
187
+ }
188
+
189
+ ee.emit('closed')
190
+ })
191
+
192
+ return stream
193
+ }
194
+ }
package/package.json ADDED
@@ -0,0 +1,119 @@
1
+ {
2
+ "name": "pino-testkit",
3
+ "version": "10.4.5",
4
+ "description": "super fast, all natural json logger",
5
+ "main": "pino.js",
6
+ "type": "commonjs",
7
+ "types": "pino.d.ts",
8
+ "browser": "./browser.js",
9
+ "scripts": {
10
+ "borp": "borp --timeout 60000 --coverage --check-coverage --lines 95 --functions 95 --branches 95 --statements 95",
11
+ "docs": "docsify serve",
12
+ "browser-test": "airtap --local 8080 test/browser*test.js",
13
+ "lint": "eslint .",
14
+ "test": "npm run lint && npm run transpile && npm run borp && jest test/jest && npm run test-types",
15
+ "test-ci": "npm run lint && npm run transpile && npm run borp && npm run test-types",
16
+ "test-ci-pnpm": "pnpm run lint && npm run transpile && borp --timeout 60000 && pnpm run test-types",
17
+ "test-ci-yarn-pnp": "yarn run lint && npm run transpile && borp --timeout 60000",
18
+ "test-types": "tsc && tsd && ts-node test/types/pino.ts && attw --pack .",
19
+ "test:smoke": "smoker smoke:pino && smoker smoke:browser && smoker smoke:file",
20
+ "smoke:pino": "node ./pino.js",
21
+ "smoke:browser": "node ./browser.js",
22
+ "smoke:file": "node ./file.js",
23
+ "transpile": "node ./test/fixtures/ts/transpile.cjs",
24
+ "cov-ui": "tap --ts --coverage-report=html",
25
+ "bench": "node benchmarks/utils/runbench all",
26
+ "bench-basic": "node benchmarks/utils/runbench basic",
27
+ "bench-object": "node benchmarks/utils/runbench object",
28
+ "bench-deep-object": "node benchmarks/utils/runbench deep-object",
29
+ "bench-multi-arg": "node benchmarks/utils/runbench multi-arg",
30
+ "bench-long-string": "node benchmarks/utils/runbench long-string",
31
+ "bench-child": "node benchmarks/utils/runbench child",
32
+ "bench-child-child": "node benchmarks/utils/runbench child-child",
33
+ "bench-child-creation": "node benchmarks/utils/runbench child-creation",
34
+ "bench-formatters": "node benchmarks/utils/runbench formatters",
35
+ "update-bench-doc": "node benchmarks/utils/generate-benchmark-doc > docs/benchmarks.md"
36
+ },
37
+ "bin": {
38
+ "pino": "./bin.js"
39
+ },
40
+ "precommit": "test",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/pinojs/pino-testkit.git"
44
+ },
45
+ "keywords": [
46
+ "fast",
47
+ "logger",
48
+ "stream",
49
+ "json"
50
+ ],
51
+ "author": "Matteo Collina <hello@matteocollina.com>",
52
+ "contributors": [
53
+ "David Mark Clements <huperekchuno@googlemail.com>",
54
+ "James Sumners <james.sumners@gmail.com>",
55
+ "Thomas Watson Steen <w@tson.dk> (https://twitter.com/wa7son)"
56
+ ],
57
+ "license": "MIT",
58
+ "bugs": {
59
+ "url": "https://github.com/pinojs/pino-tookit/issues"
60
+ },
61
+ "homepage": "https://getpino.io",
62
+ "devDependencies": {
63
+ "@arethetypeswrong/cli": "^0.18.1",
64
+ "@matteo.collina/tspl": "^0.2.0",
65
+ "@types/flush-write-stream": "^1.0.0",
66
+ "@types/node": "^25.0.3",
67
+ "airtap": "5.0.0",
68
+ "bole": "^5.0.5",
69
+ "borp": "^0.21.0",
70
+ "bunyan": "^1.8.14",
71
+ "debug": "^4.3.4",
72
+ "docsify-cli": "^4.4.4",
73
+ "eslint": "^9.37.0",
74
+ "eslint-plugin-import": "^2.26.0",
75
+ "eslint-plugin-n": "17.23.2",
76
+ "eslint-plugin-node": "^11.1.0",
77
+ "eslint-plugin-promise": "^6.0.0",
78
+ "execa": "^5.0.0",
79
+ "fastbench": "^1.0.1",
80
+ "flush-write-stream": "^2.0.0",
81
+ "import-fresh": "^3.2.1",
82
+ "jest": "^30.0.3",
83
+ "log": "^6.0.0",
84
+ "loglevel": "^1.6.7",
85
+ "midnight-smoker": "1.1.1",
86
+ "neostandard": "^0.12.2",
87
+ "pino-pretty": "^13.0.0",
88
+ "pre-commit": "^1.2.2",
89
+ "proxyquire": "^2.1.3",
90
+ "pump": "^3.0.0",
91
+ "rimraf": "^6.0.1",
92
+ "semver": "^7.3.7",
93
+ "split2": "^4.0.0",
94
+ "steed": "^1.1.3",
95
+ "strip-ansi": "^6.0.0",
96
+ "tape": "^5.5.3",
97
+ "through2": "^4.0.0",
98
+ "ts-node": "^10.9.1",
99
+ "tsd": "^0.33.0",
100
+ "typescript": "~5.9.2",
101
+ "winston": "^3.7.2"
102
+ },
103
+ "dependencies": {
104
+ "atomic-sleep": "^1.0.0",
105
+ "on-exit-leak-free": "^2.1.0",
106
+ "pino-abstract-transport": "^3.0.0",
107
+ "pino-std-serializers": "^7.0.0",
108
+ "process-warning": "^5.0.0",
109
+ "quick-format-unescaped": "^4.0.3",
110
+ "real-require": "^0.2.0",
111
+ "safe-stable-stringify": "^2.3.1",
112
+ "@pinojs/redact": "^0.4.0",
113
+ "sonic-boom": "^4.0.1",
114
+ "thread-stream": "^4.0.0"
115
+ },
116
+ "tsd": {
117
+ "directory": "test/types"
118
+ }
119
+ }