Repository: pinojs/pino
Pull request #2083 (2024-11-11 15:26:13)

build(deps-dev): bump pino-pretty from 11.3.0 to 13.0.0
Bumps [pino-pretty](https://github.com/pinojs/pino-pretty) from 11.3.0 to 13.0.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/pinojs/pino-pretty/releases">pino-pretty's releases</a>.</em></p>
<blockquote>
<h2>v13.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>chore: replace readable-stream with built-in stream by <a href="https://github.com/benmccann"><code>@​benmccann</code></a> in <a href="https://redirect.github.com/pinojs/pino-pretty/pull/542">pinojs/pino-pretty#542</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/benmccann"><code>@​benmccann</code></a> made their first contribution in <a href="https://redirect.github.com/pinojs/pino-pretty/pull/542">pinojs/pino-pretty#542</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/pinojs/pino-pretty/compare/v12.1.0...v13.0.0">https://github.com/pinojs/pino-pretty/compare/v12.1.0...v13.0.0</a></p>
<h2>v12.1.0</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: unescape \ for levelKey by <a href="https://github.com/eliw00d"><code>@​eliw00d</code></a> in <a href="https://redirect.github.com/pinojs/pino-pretty/pull/538">pinojs/pino-pretty#538</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/pinojs/pino-pretty/compare/v12.0.0...v12.1.0">https://github.com/pinojs/pino-pretty/compare/v12.0.0...v12.1.0</a></p>
<h2>v12.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>build(deps-dev): lock typescript minor version by <a href="https://github.com/Fdawgs"><code>@​Fdawgs</code></a> in <a href="https://redirect.github.com/pinojs/pino-pretty/pull/534">pinojs/pino-pretty#534</a></li>
<li>Bump typescript from 5.0.4 to 5.6.3 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/pinojs/pino-pretty/pull/535">pinojs/pino-pretty#535</a></li>
<li>Drop support for Node.js 14 &amp; 16 by <a href="https://github.com/eliw00d"><code>@​eliw00d</code></a> in <a href="https://redirect.github.com/pinojs/pino-pretty/pull/540">pinojs/pino-pretty#540</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/eliw00d"><code>@​eliw00d</code></a> made their first contribution in <a href="https://redirect.github.com/pinojs/pino-pretty/pull/540">pinojs/pino-pretty#540</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/pinojs/pino-pretty/compare/v11.3.0...v12.0.0">https://github.com/pinojs/pino-pretty/compare/v11.3.0...v12.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/pinojs/pino-pretty/commit/aa5abb3831c6b813f87181b8944abd57ff8b9abb"><code>aa5abb3</code></a> Bumped v13.0.0</li>
<li><a href="https://github.com/pinojs/pino-pretty/commit/f4b3ca769adb47b6cd31c50533dd5a903e307a65"><code>f4b3ca7</code></a> chore: replace readable-stream with built-in stream (<a href="https://redirect.github.com/pinojs/pino-pretty/issues/542">#542</a>)</li>
<li><a href="https://github.com/pinojs/pino-pretty/commit/772c5b8bf6308c2f7845bdf092d06068d31e4eea"><code>772c5b8</code></a> v12.1.0</li>
<li><a href="https://github.com/pinojs/pino-pretty/commit/2a85c0dcdb72fdc5d63966613ea1436662cd155e"><code>2a85c0d</code></a> fix: unescape \ for levelKey (<a href="https://redirect.github.com/pinojs/pino-pretty/issues/538">#538</a>)</li>
<li><a href="https://github.com/pinojs/pino-pretty/commit/163ef0b834c40814add3ed246fe005bd954c7ee5"><code>163ef0b</code></a> v12.0.0</li>
<li><a href="https://github.com/pinojs/pino-pretty/commit/f0d054d727abeae02165e27911f35d673921e4d0"><code>f0d054d</code></a> Drop support for Node.js 14 &amp; 16 (<a href="https://redirect.github.com/pinojs/pino-pretty/issues/540">#540</a>)</li>
<li><a href="https://github.com/pinojs/pino-pretty/commit/f1a568318761662dfced0658f94d3bb9121b6786"><code>f1a5683</code></a> Bump typescript from 5.0.4 to 5.6.3 (<a href="https://redirect.github.com/pinojs/pino-pretty/issues/535">#535</a>)</li>
<li><a href="https://github.com/pinojs/pino-pretty/commit/cd021fa99a19ff375f6dca59f2607a976573260e"><code>cd021fa</code></a> build(deps-dev): lock typescript minor version (<a href="https://redirect.github.com/pinojs/pino-pretty/issues/534">#534</a>)</li>
<li>See full diff in <a href="https://github.com/pinojs/pino-pretty/compare/v11.3.0...v13.0.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pino-pretty&package-manager=npm_and_yarn&previous-version=11.3.0&new-version=13.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

> **Note**
> Automatic rebases have been disabled on this pull request as it has been open for over 30 days.



Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index dfa7fa6..35d66ca 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -9,5 +9,5 @@ updates:
   - package-ecosystem: "npm"
     directory: "/"
     schedule:
-      interval: "weekly"
+      interval: "monthly"
     open-pull-requests-limit: 10
diff --git a/browser.js b/browser.js
index 7d0aa2b..bfdb8ea 100644
--- a/browser.js
+++ b/browser.js
@@ -109,6 +109,7 @@ function pino (opts) {
     transmit,
     serialize,
     asObject: opts.browser.asObject,
+    asObjectBindingsOnly: opts.browser.asObjectBindingsOnly,
     formatters: opts.browser.formatters,
     levels,
     timestamp: getTimeFunction(opts),
@@ -118,6 +119,13 @@ function pino (opts) {
   logger.levels = getLevels(opts)
   logger.level = level
 
+  logger.isLevelEnabled = function (level) {
+    if (!this.levels.values[level]) {
+      return false
+    }
+
+    return this.levels.values[level] >= this.levels.values[this.level]
+  }
   logger.setMaxListeners = logger.getMaxListeners =
   logger.emit = logger.addListener = logger.on =
   logger.prependListener = logger.once =
@@ -317,7 +325,7 @@ function createWrap (self, opts, rootLogger, level) {
         argsIsSerialized = true
       }
       if (opts.asObject || opts.formatters) {
-        write.call(proto, asObject(this, level, args, ts, opts))
+        write.call(proto, ...asObject(this, level, args, ts, opts))
       } else write.apply(proto, args)
 
       if (opts.transmit) {
@@ -347,6 +355,10 @@ function asObject (logger, level, args, ts, opts) {
   const argsCloned = args.slice()
   let msg = argsCloned[0]
   const logObject = {}
+
+  let lvl = (logger._childLevel | 0) + 1
+  if (lvl < 1) lvl = 1
+
   if (ts) {
     logObject.time = ts
   }
@@ -358,19 +370,28 @@ function asObject (logger, level, args, ts, opts) {
     logObject.level = logger.levels.values[level]
   }
 
-  let lvl = (logger._childLevel | 0) + 1
-  if (lvl < 1) lvl = 1
-  // deliberate, catching objects, arrays
-  if (msg !== null && typeof msg === 'object') {
-    while (lvl-- && typeof argsCloned[0] === 'object') {
-      Object.assign(logObject, argsCloned.shift())
+  if (opts.asObjectBindingsOnly) {
+    if (msg !== null && typeof msg === 'object') {
+      while (lvl-- && typeof argsCloned[0] === 'object') {
+        Object.assign(logObject, argsCloned.shift())
+      }
     }
-    msg = argsCloned.length ? format(argsCloned.shift(), argsCloned) : undefined
-  } else if (typeof msg === 'string') msg = format(argsCloned.shift(), argsCloned)
-  if (msg !== undefined) logObject[opts.messageKey] = msg
 
-  const formattedLogObject = logObjectFormatter(logObject)
-  return formattedLogObject
+    const formattedLogObject = logObjectFormatter(logObject)
+    return [formattedLogObject, ...argsCloned]
+  } else {
+    // deliberate, catching objects, arrays
+    if (msg !== null && typeof msg === 'object') {
+      while (lvl-- && typeof argsCloned[0] === 'object') {
+        Object.assign(logObject, argsCloned.shift())
+      }
+      msg = argsCloned.length ? format(argsCloned.shift(), argsCloned) : undefined
+    } else if (typeof msg === 'string') msg = format(argsCloned.shift(), argsCloned)
+    if (msg !== undefined) logObject[opts.messageKey] = msg
+
+    const formattedLogObject = logObjectFormatter(logObject)
+    return [formattedLogObject]
+  }
 }
 
 function applySerializers (args, serialize, serializers, stdErrSerialize) {
diff --git a/docs/api.md b/docs/api.md
index c68ba5c..6789e3f 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -484,7 +484,7 @@ child.info('User authenticated!')
 <a id=opt-base></a>
 #### `base` (Object)
 
-Default: `{pid: process.pid, hostname: os.hostname}`
+Default: `{pid: process.pid, hostname: os.hostname()}`
 
 Key-value object added as child logger to each log line.
 
@@ -1307,6 +1307,9 @@ module.exports = function build () {
 }
 ```
 
+Note that _any `'error'`_ event emitted by the transport must be considered a fatal error and the process must be terminated.
+Error events are not recoverable.
+
 For more on transports, how they work, and how to create them see the [`Transports documentation`](/docs/transports.md).
 
 * See [`Transports`](/docs/transports.md)
diff --git a/docs/browser.md b/docs/browser.md
index a23ce83..9578e0c 100644
--- a/docs/browser.md
+++ b/docs/browser.md
@@ -27,6 +27,21 @@ pino.info('hi') // creates and logs {msg: 'hi', level: 30, time: <ts>}
 
 When `write` is set, `asObject` will always be `true`.
 
+### `asObjectBindingsOnly` (Boolean)
+
+```js
+const pino = require('pino')({browser: {asObjectBindingsOnly: true}})
+```
+
+The `asObjectBindingsOnly` is similar to `asObject` but will keep the message
+and arguments unformatted, this allows to defer formatting the message to the
+actual call to `console` methods, where browsers then have richer formatting in
+their devtools then when pino will format the message to a string first.
+
+```js
+pino.info('hello %s', 'world') // creates and logs {level: 30, time: <ts>}, 'hello %s', 'world'
+```
+
 ### `formatters` (Object)
 
 An object containing functions for formatting the shape of the log lines. When provided, it enables the logger to produce a pino-like log object with customized formatting. Currently, it supports formatting for the `level` object only.
diff --git a/docs/transports.md b/docs/transports.md
index a3391f4..89a21f1 100644
--- a/docs/transports.md
+++ b/docs/transports.md
@@ -437,6 +437,7 @@ PRs to this document are welcome for any new transports!
 + [pino-loki](#pino-loki)
 + [pino-opentelemetry-transport](#pino-opentelemetry-transport)
 + [pino-pretty](#pino-pretty)
++ [pino-roll](#pino-roll)
 + [pino-seq-transport](#pino-seq-transport)
 + [pino-sentry-transport](#pino-sentry-transport)
 + [pino-slack-webhook](#pino-slack-webhook)
@@ -994,6 +995,30 @@ $ node app.js | pino-redis -U redis://username:password@localhost:6379
 [pino-redis]: https://github.com/buianhthang/pino-redis
 [Redis]: https://redis.io/
 
+<a id="pino-roll"></a>
+### pino-roll
+
+`pino-roll` is a Pino transport that automatically rolls your log files based on size or time frequency.
+
+```js
+import { join } from 'path';
+import pino from 'pino';
+
+const transport = pino.transport({
+  target: 'pino-roll',
+  options: { file: join('logs', 'log'), frequency: 'daily', mkdir: true }
+});
+
+const logger = pino(transport);
+```
+
+then you can use the logger as usual:
+
+```js
+logger.info('Hello from pino-roll!');
+```
+For full documentation check the [README](https://github.com/mcollina/pino-roll?tab=readme-ov-file#pino-roll).
+
 <a id="pino-sentry"></a>
 ### pino-sentry
 
diff --git a/docs/web.md b/docs/web.md
index 49a31d0..8eb7f83 100644
--- a/docs/web.md
+++ b/docs/web.md
@@ -12,6 +12,7 @@ web framework ecosystem.
   - [Pino with Node core `http`](#pino-with-node-core-http)
   - [Pino with Nest](#pino-with-nest)
   - [Pino with H3](#pino-with-h3)
+  - [Pino with Hono](#pino-with-hono)
 
 <a id="fastify"></a>
 ## Pino with Fastify
@@ -267,3 +268,42 @@ router.get(
 Execute `npx --yes listhen -w --open ./server.mjs`.
 
 See the [pino-http README](https://npm.im/pino-http) for more info.
+
+
+<a id="h3"></a>
+## Pino with Hono
+
+```sh
+npm install pino pino-http hono
+```
+
+```js
+import { serve } from '@hono/node-server';
+import { Hono } from 'hono';
+import { requestId } from 'hono/request-id';
+import { pinoHttp } from 'pino-http';
+
+const app = new Hono();
+app.use(requestId());
+app.use(async (c, next) => {
+  // pass hono's request-id to pino-http
+  c.env.incoming.id = c.var.requestId;
+
+  // map express style middleware to hono
+  await new Promise((resolve) => pinoHttp()(c.env.incoming, c.env.outgoing, () => resolve()));
+
+  c.set('logger', c.env.incoming.log);
+
+  await next();
+});
+
+app.get('/', (c) => {
+  c.var.logger.info('something');
+
+  return c.text('Hello Node.js!');
+});
+
+serve(app);
+```
+
+See the [pino-http README](https://npm.im/pino-http) for more info.
diff --git a/lib/meta.js b/lib/meta.js
index 0dc4daa..c0cb051 100644
--- a/lib/meta.js
+++ b/lib/meta.js
@@ -1,3 +1,3 @@
 'use strict'
 
-module.exports = { version: '9.6.0' }
+module.exports = { version: '9.7.0' }
diff --git a/lib/tools.js b/lib/tools.js
index 5f68f58..b8b79d5 100644
--- a/lib/tools.js
+++ b/lib/tools.js
@@ -231,15 +231,11 @@ function hasBeenTampered (stream) {
   return stream.write !== stream.constructor.prototype.write
 }
 
-const hasNodeCodeCoverage = process.env.NODE_V8_COVERAGE || process.env.V8_COVERAGE
-
 function buildSafeSonicBoom (opts) {
   const stream = new SonicBoom(opts)
   stream.on('error', filterBrokenPipe)
   // If we are sync: false, we must flush on exit
-  // We must disable this if there is node code coverage due to
-  // https://github.com/nodejs/node/issues/49344#issuecomment-1741776308.
-  if (!hasNodeCodeCoverage && !opts.sync && isMainThread) {
+  if (!opts.sync && isMainThread) {
     onExit.register(stream, autoEnd)
 
     stream.on('close', function () {
diff --git a/package.json b/package.json
index b844168..e412c5c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "pino",
-  "version": "9.6.0",
+  "version": "9.7.0",
   "description": "super fast, all natural json logger",
   "main": "pino.js",
   "type": "commonjs",
@@ -63,7 +63,7 @@
     "@types/flush-write-stream": "^1.0.0",
     "@types/node": "^22.0.0",
     "@types/tap": "^15.0.6",
-    "@yao-pkg/pkg": "6.1.1",
+    "@yao-pkg/pkg": "6.3.0",
     "airtap": "5.0.0",
     "benchmark": "^2.1.4",
     "bole": "^5.0.5",
@@ -84,7 +84,7 @@
     "log": "^6.0.0",
     "loglevel": "^1.6.7",
     "midnight-smoker": "1.1.1",
-    "pino-pretty": "^11.0.0",
+    "pino-pretty": "^13.0.0",
     "pre-commit": "^1.2.2",
     "proxyquire": "^2.1.3",
     "pump": "^3.0.0",
@@ -97,8 +97,8 @@
     "tape": "^5.5.3",
     "through2": "^4.0.0",
     "ts-node": "^10.9.1",
-    "tsd": "^0.31.0",
-    "typescript": "~5.7.2",
+    "tsd": "^0.32.0",
+    "typescript": "~5.8.2",
     "winston": "^3.7.2"
   },
   "dependencies": {
@@ -107,7 +107,7 @@
     "on-exit-leak-free": "^2.1.0",
     "pino-abstract-transport": "^2.0.0",
     "pino-std-serializers": "^7.0.0",
-    "process-warning": "^4.0.0",
+    "process-warning": "^5.0.0",
     "quick-format-unescaped": "^4.0.3",
     "real-require": "^0.2.0",
     "safe-stable-stringify": "^2.3.1",


Test patch:
diff --git a/test/browser-is-level-enabled.test.js b/test/browser-is-level-enabled.test.js
new file mode 100644
index 0000000..16d8c88
--- /dev/null
+++ b/test/browser-is-level-enabled.test.js
@@ -0,0 +1,104 @@
+'use strict'
+
+const { test } = require('tap')
+const pino = require('../browser')
+
+const customLevels = {
+  trace: 10,
+  debug: 20,
+  info: 30,
+  warn: 40,
+  error: 50,
+  fatal: 60
+}
+
+test('Default levels suite', ({ test, end }) => {
+  test('can check if current level enabled', async ({ equal }) => {
+    const log = pino({ level: 'debug' })
+    equal(true, log.isLevelEnabled('debug'))
+  })
+
+  test('can check if current level enabled when as object', async ({ equal }) => {
+    const log = pino({ asObject: true, level: 'debug' })
+    equal(true, log.isLevelEnabled('debug'))
+  })
+
+  test('can check if level enabled after level set', async ({ equal }) => {
+    const log = pino()
+    equal(false, log.isLevelEnabled('debug'))
+    log.level = 'debug'
+    equal(true, log.isLevelEnabled('debug'))
+  })
+
+  test('can check if higher level enabled', async ({ equal }) => {
+    const log = pino({ level: 'debug' })
+    equal(true, log.isLevelEnabled('error'))
+  })
+
+  test('can check if lower level is disabled', async ({ equal }) => {
+    const log = pino({ level: 'error' })
+    equal(false, log.isLevelEnabled('trace'))
+  })
+
+  test('ASC: can check if child has current level enabled', async ({ equal }) => {
+    const log = pino().child({}, { level: 'debug' })
+    equal(true, log.isLevelEnabled('debug'))
+    equal(true, log.isLevelEnabled('error'))
+    equal(false, log.isLevelEnabled('trace'))
+  })
+
+  test('can check if custom level is enabled', async ({ equal }) => {
+    const log = pino({
+      customLevels: { foo: 35 },
+      level: 'debug'
+    })
+    equal(true, log.isLevelEnabled('foo'))
+    equal(true, log.isLevelEnabled('error'))
+    equal(false, log.isLevelEnabled('trace'))
+  })
+
+  end()
+})
+
+test('Custom levels suite', ({ test, end }) => {
+  test('can check if current level enabled', async ({ equal }) => {
+    const log = pino({ level: 'debug', customLevels })
+    equal(true, log.isLevelEnabled('debug'))
+  })
+
+  test('can check if level enabled after level set', async ({ equal }) => {
+    const log = pino({ customLevels })
+    equal(false, log.isLevelEnabled('debug'))
+    log.level = 'debug'
+    equal(true, log.isLevelEnabled('debug'))
+  })
+
+  test('can check if higher level enabled', async ({ equal }) => {
+    const log = pino({ level: 'debug', customLevels })
+    equal(true, log.isLevelEnabled('error'))
+  })
+
+  test('can check if lower level is disabled', async ({ equal }) => {
+    const log = pino({ level: 'error', customLevels })
+    equal(false, log.isLevelEnabled('trace'))
+  })
+
+  test('can check if child has current level enabled', async ({ equal }) => {
+    const log = pino().child({ customLevels }, { level: 'debug' })
+    equal(true, log.isLevelEnabled('debug'))
+    equal(true, log.isLevelEnabled('error'))
+    equal(false, log.isLevelEnabled('trace'))
+  })
+
+  test('can check if custom level is enabled', async ({ equal }) => {
+    const log = pino({
+      customLevels: { foo: 35, ...customLevels },
+      level: 'debug'
+    })
+    equal(true, log.isLevelEnabled('foo'))
+    equal(true, log.isLevelEnabled('error'))
+    equal(false, log.isLevelEnabled('trace'))
+  })
+
+  end()
+})
diff --git a/test/browser.test.js b/test/browser.test.js
index 8684006..0712e48 100644
--- a/test/browser.test.js
+++ b/test/browser.test.js
@@ -165,6 +165,7 @@ test('opts.browser.asObject logs pino-like object to console', ({ end, ok, is })
   instance.info('test')
   end()
 })
+
 test('opts.browser.asObject uses opts.messageKey in logs', ({ end, ok, is }) => {
   const messageKey = 'message'
   const instance = require('../browser')({
@@ -183,6 +184,25 @@ test('opts.browser.asObject uses opts.messageKey in logs', ({ end, ok, is }) =>
   end()
 })
 
+test('opts.browser.asObjectBindingsOnly passes the bindings but keep the message unformatted', ({ end, ok, is, deepEqual }) => {
+  const messageKey = 'message'
+  const instance = require('../browser')({
+    messageKey,
+    browser: {
+      asObjectBindingsOnly: true,
+      write: function (o, msg, ...args) {
+        is(o.level, 30)
+        ok(o.time)
+        is(msg, 'test %s')
+        deepEqual(args, ['foo'])
+      }
+    }
+  })
+
+  instance.info('test %s', 'foo')
+  end()
+})
+
 test('opts.browser.formatters (level) logs pino-like object to console', ({ end, ok, is }) => {
   const info = console.info
   console.info = function (o) {
diff --git a/test/fixtures/crashing-transport.js b/test/fixtures/crashing-transport.js
new file mode 100644
index 0000000..1f3d46e
--- /dev/null
+++ b/test/fixtures/crashing-transport.js
@@ -0,0 +1,13 @@
+const { Writable } = require('node:stream')
+
+module.exports = () =>
+  new Writable({
+    autoDestroy: true,
+    write (chunk, enc, cb) {
+      setImmediate(() => {
+        /* eslint-disable no-empty */
+        for (let i = 0; i < 1e3; i++) {}
+        process.exit(0)
+      })
+    }
+  })
diff --git a/test/pkg/pkg.test.js b/test/pkg/pkg.test.js
index a74b033..3cb28ad 100644
--- a/test/pkg/pkg.test.js
+++ b/test/pkg/pkg.test.js
@@ -8,6 +8,8 @@ const { join } = require('node:path')
 const { platform } = require('node:process')
 const execFile = promisify(require('node:child_process').execFile)
 
+const skip = process.env.PNPM_CI || process.env.CITGM || process.arch === 'ppc64'
+
 /**
  * The following regex is for tesintg the deprecation warning that is thrown by the `punycode` module.
  * Exact text that it's matching is:
@@ -18,7 +20,7 @@ const execFile = promisify(require('node:child_process').execFile)
  */
 const deprecationWarningRegex = /^\(\w+:\d+\)\s\[[\w|\d]+\]\sDeprecationWarning: The `punycode` module is deprecated\.\s+Please use a userland alternative instead\.\s+\(Use `node --trace-deprecation \.\.\.` to show where the warning was created\)\s+$/
 
-test('worker test when packaged into executable using pkg', { skip: !!process.env.PNPM_CI }, async (t) => {
+test('worker test when packaged into executable using pkg', { skip }, async (t) => {
   const packageName = 'index'
 
   // package the app into several node versions, check config for more info
diff --git a/test/transport/core.test.js b/test/transport/core.test.js
index 44449db..3bd55fe 100644
--- a/test/transport/core.test.js
+++ b/test/transport/core.test.js
@@ -3,6 +3,7 @@
 const os = require('node:os')
 const { join } = require('node:path')
 const { once } = require('node:events')
+const { setImmediate: immediate } = require('node:timers/promises')
 const { readFile, writeFile } = require('node:fs').promises
 const { watchFileCreated, watchForWrite, file } = require('../helper')
 const { test } = require('tap')
@@ -12,10 +13,8 @@ const strip = require('strip-ansi')
 const execa = require('execa')
 const writer = require('flush-write-stream')
 const rimraf = require('rimraf')
-const { promisify } = require('node:util')
 const { tmpdir } = os
 
-const immediate = promisify(setImmediate)
 const pid = process.pid
 const hostname = os.hostname()
 
diff --git a/test/transport/crash.test.js b/test/transport/crash.test.js
new file mode 100644
index 0000000..b5e31fe
--- /dev/null
+++ b/test/transport/crash.test.js
@@ -0,0 +1,34 @@
+'use strict'
+
+const { join } = require('node:path')
+const { once } = require('node:events')
+const { setImmediate: immediate } = require('node:timers/promises')
+const { test } = require('tap')
+const pino = require('../../')
+
+test('pino.transport emits error if the worker exits with 0 unexpectably', async ({ same, teardown, equal }) => {
+  // This test will take 10s, because flushSync waits for 10s
+  const transport = pino.transport({
+    target: join(__dirname, '..', 'fixtures', 'crashing-transport.js'),
+    sync: true
+  })
+  teardown(transport.end.bind(transport))
+
+  await once(transport, 'ready')
+
+  let maybeError
+  transport.on('error', (err) => {
+    maybeError = err
+  })
+
+  const logger = pino(transport)
+  for (let i = 0; i < 100000; i++) {
+    logger.info('hello')
+  }
+
+  await once(transport.worker, 'exit')
+
+  await immediate()
+
+  same(maybeError.message, 'the worker has exited')
+})