Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package bun-pty for openSUSE:Factory checked 
in at 2026-10-15 16:12:31
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/bun-pty (Old)
 and      /work/SRC/openSUSE:Factory/.bun-pty.new.3698736 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "bun-pty"

Thu Oct 15 16:12:31 2026 rev:4 rq:1384903 version:0.5.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/bun-pty/bun-pty.changes  2026-10-01 
16:45:35.647391938 +0200
+++ /work/SRC/openSUSE:Factory/.bun-pty.new.3698736/bun-pty.changes     
2026-10-15 16:12:56.560022511 +0200
@@ -1,0 +2,14 @@
+Thu Oct 15 05:02:21 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to version 0.5.1:
+  * Breaking: env is the child's whole environment as in node-pty,
+    no longer merged over the parent's (#48); spread process.env
+    to keep inherited variables
+  * Build the child environment as node-pty builds it: TERM from
+    name, PWD from cwd; drop tmux/COLUMNS/LINES on inherit (#49)
+  * Leave src/*.test.ts out of the npm package (#50)
+  * Linux ABI unchanged: same eight dlsym entry points, same
+    private librust_pty.so path; no consumer rebuild needed
+  * Refresh the vendored dependencies
+
+-------------------------------------------------------------------

Old:
----
  bun-pty-0.4.11.tar.gz

New:
----
  bun-pty-0.5.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ bun-pty.spec ++++++
--- /var/tmp/diff_new_pack.9mJdGY/_old  2026-10-15 16:12:57.781073603 +0200
+++ /var/tmp/diff_new_pack.9mJdGY/_new  2026-10-15 16:12:57.783073687 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           bun-pty
-Version:        0.4.11
+Version:        0.5.1
 Release:        0
 Summary:        Pseudo-terminal library for Bun FFI consumers
 License:        MIT

++++++ _service ++++++
--- /var/tmp/diff_new_pack.9mJdGY/_old  2026-10-15 16:12:57.826075486 +0200
+++ /var/tmp/diff_new_pack.9mJdGY/_new  2026-10-15 16:12:57.828075570 +0200
@@ -1,6 +1,6 @@
 <services>
   <service name="cargo_vendor" mode="manual">
-    <param name="srctar">bun-pty-0.4.11.tar.gz</param>
+    <param name="srctar">bun-pty-0.5.1.tar.gz</param>
     <param name="cargotoml">rust-pty/Cargo.toml</param>
     <param name="compression">zst</param>
   </service>

++++++ bun-pty-0.4.11.tar.gz -> bun-pty-0.5.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bun-pty-0.4.11/.github/workflows/test.yml 
new/bun-pty-0.5.1/.github/workflows/test.yml
--- old/bun-pty-0.4.11/.github/workflows/test.yml       2026-09-30 
07:52:47.000000000 +0200
+++ new/bun-pty-0.5.1/.github/workflows/test.yml        2026-10-12 
08:44:31.000000000 +0200
@@ -77,7 +77,7 @@
         run: bun run test:unit
 
       - name: Run unit tests with coverage
-        run: bun test --coverage --coverage-reporter=lcov 
src/interfaces.test.ts src/terminal.test.ts src/index.test.ts
+        run: bun test --coverage --coverage-reporter=lcov 
src/interfaces.test.ts src/terminal.test.ts src/index.test.ts src/env.test.ts
 
       - name: Run integration tests
         run: bun run test:integration
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bun-pty-0.4.11/CHANGELOG.md 
new/bun-pty-0.5.1/CHANGELOG.md
--- old/bun-pty-0.4.11/CHANGELOG.md     2026-09-30 07:52:47.000000000 +0200
+++ new/bun-pty-0.5.1/CHANGELOG.md      2026-10-12 08:44:31.000000000 +0200
@@ -5,6 +5,30 @@
 The format is based on [Keep a 
Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic 
Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [0.5.1] - 2026-10-12
+
+### Fixed
+- Leave the `src/*.test.ts` files out of the npm package (#50)
+  - `"files"` in `package.json` lists all of `src`, and npm does not apply 
`.npmignore` patterns to the paths it lists, so earlier tarballs included the 
tests
+
+## [0.5.0] - 2026-10-12
+
+### Changed
+- **Breaking:** `env` is now the child's whole environment, as in node-pty, 
instead of being merged over the parent's (#48)
+  - To keep inherited variables, spread `process.env`: `env: { ...process.env, 
FOO: "bar" }`
+  - Without `env`, the child gets the current `process.env`, so changes made 
at runtime reach it (before, it got the environment the process started with)
+  - When the map has no `PATH`, a bare program name such as `sh` is still 
found with the parent's `PATH`, which does not reach the child; on Unix, 
`argv[0]` is then the resolved path
+  - On Windows, a map with no entries keeps the parent's environment, as in 
node-pty
+- On Unix, the child's environment is now built as node-pty builds it (#49)
+  - `TERM` is set from the `name` option, then from `TERM` in the environment, 
then "xterm"; `name` was ignored before
+  - `PWD` is set from `cwd`
+  - When the child inherits `process.env`, the parent's tmux and screen 
variables (`TMUX`, `TMUX_PANE`, `STY`, `WINDOW`, `WINDOWID`, `TERMCAP`) and its 
`COLUMNS` and `LINES` are dropped
+- `IPtyForkOptions.name` is optional, and `env` accepts `undefined` values, 
which are skipped, so `env: process.env` type-checks (#49)
+
+### Fixed
+- Variables left out of `env`, such as tokens, no longer reach the child (#48)
+  - Fixes #47
+
 ## [0.4.11] - 2026-09-30
 
 ### Added
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bun-pty-0.4.11/README.md new/bun-pty-0.5.1/README.md
--- old/bun-pty-0.4.11/README.md        2026-09-30 07:52:47.000000000 +0200
+++ new/bun-pty-0.5.1/README.md 2026-10-12 08:44:31.000000000 +0200
@@ -146,11 +146,11 @@
 - `file`: The executable to launch
 - `args`: Arguments to pass to the executable
 - `options`: Configuration options
-  - `name`: Terminal name (e.g., "xterm-256color")
+  - `name`: Terminal type (e.g., "xterm-256color"), set as `TERM` in the 
child's environment on Unix (default: `TERM` from `env`, then "xterm")
   - `cols`: Number of columns (default: 80)
   - `rows`: Number of rows (default: 24)
   - `cwd`: Working directory (default: process.cwd())
-  - `env`: Environment variables
+  - `env`: The child's whole environment, as in node-pty (default: 
`process.env`). It replaces the parent's environment, so pass `{ 
...process.env, FOO: "bar" }` to add variables. Entries whose value is 
`undefined` are skipped. On Windows, a map with no entries keeps the parent's 
environment, as in node-pty. On Unix, `PWD` is set from `cwd`, and when the 
child inherits `process.env`, variables from the parent's tmux or screen 
session (`TMUX`, `STY`, ...) are dropped
 
 Returns an `IPty` instance.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bun-pty-0.4.11/package.json 
new/bun-pty-0.5.1/package.json
--- old/bun-pty-0.4.11/package.json     2026-09-30 07:52:47.000000000 +0200
+++ new/bun-pty-0.5.1/package.json      2026-10-12 08:44:31.000000000 +0200
@@ -1,6 +1,6 @@
 {
        "name": "bun-pty",
-       "version": "0.4.11",
+       "version": "0.5.1",
        "description": "Cross-platform pseudoterminal (PTY) implementation for 
Bun with native performance",
        "main": "./src/index.ts",
        "types": "./dist/index.d.ts",
@@ -47,10 +47,10 @@
                "build:ts": "tsc --emitDeclarationOnly --declaration --outDir 
dist",
                "build": "bun run build:rust && bun run build:ts",
                "test": "bun test",
-               "test:unit": "bun test src/interfaces.test.ts 
src/terminal.test.ts src/index.test.ts",
+               "test:unit": "bun test src/interfaces.test.ts 
src/terminal.test.ts src/index.test.ts src/env.test.ts",
                "test:integration": "RUN_INTEGRATION_TESTS=true bun test 
src/terminal.integration.test.ts",
                "test:all": "bun run test:unit && bun run test:integration",
-               "test:coverage": "bun test --coverage src/interfaces.test.ts 
src/terminal.test.ts src/index.test.ts",
+               "test:coverage": "bun test --coverage src/interfaces.test.ts 
src/terminal.test.ts src/index.test.ts src/env.test.ts",
                "clean": "rm -rf ./dist && cd rust-pty && cargo clean"
        },
        "engines": {
@@ -58,6 +58,7 @@
        },
        "files": [
                "src",
+               "!src/**/*.test.ts",
                "dist",
                "README.md",
                "LICENSE",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bun-pty-0.4.11/rust-pty/src/lib.rs 
new/bun-pty-0.5.1/rust-pty/src/lib.rs
--- old/bun-pty-0.4.11/rust-pty/src/lib.rs      2026-09-30 07:52:47.000000000 
+0200
+++ new/bun-pty-0.5.1/rust-pty/src/lib.rs       2026-10-12 08:44:31.000000000 
+0200
@@ -8,9 +8,10 @@
 use shell_words::split;                  // <-- NEW
 use std::{
     collections::HashMap,
-    ffi::CStr,
+    ffi::{CStr, OsStr, OsString},
     io::{Read, Write},
     os::raw::{c_char, c_int},
+    path::PathBuf,
     sync::{
         atomic::{AtomicBool, AtomicI32, Ordering},
         Arc, Mutex,
@@ -91,7 +92,21 @@
     }
 
     fn to_builder(&self) -> CommandBuilder {
-        let mut b = CommandBuilder::new(&self.cmd);
+        let mut b = CommandBuilder::new(self.program());
+        // `CommandBuilder::new` seeds the environment from the parent's
+        // startup environment. The map the caller passed is the whole
+        // environment, as in node-pty, so drop the seeded one first.
+        // portable-pty still sets SHELL when the map holds no usable one.
+        b.env_clear();
+        // On Windows an empty map keeps the parent's environment, as in
+        // node-pty; portable-pty would build an unterminated block for it.
+        // Copy the process environment, not portable-pty's seeded one,
+        // which also applies values from the registry.
+        if cfg!(windows) && self.env.is_empty() {
+            for (k, v) in std::env::vars_os() {
+                b.env(k, v);
+            }
+        }
         b.cwd(&self.cwd);
         for a in &self.args {
             b.arg(a);
@@ -101,6 +116,71 @@
         }
         b
     }
+
+    /// The map's value for `key`. Windows matches the key without case.
+    fn map_var(&self, key: &str) -> Option<&str> {
+        self.env
+            .iter()
+            .find(|(k, _)| if cfg!(windows) { k.eq_ignore_ascii_case(key) } 
else { k.as_str() == key })
+            .map(|(_, v)| v.as_str())
+    }
+
+    /// portable-pty looks up a relative program with the PATH in the
+    /// child's environment. Before `env_clear()`, a map with no PATH fell
+    /// back to the parent's PATH. Keep that fallback, so a bare name such
+    /// as `sh` or `cmd.exe` still resolves, without adding PATH to the child.
+    fn program(&self) -> OsString {
+        if self.cmd.is_empty() || self.map_var("PATH").is_some() {
+            return self.cmd.clone().into();
+        }
+        std::env::var_os("PATH")
+            .and_then(|path| self.search_path(&path))
+            .map_or_else(|| self.cmd.clone().into(), PathBuf::into_os_string)
+    }
+
+    /// Mirrors portable-pty's Unix lookup: a relative program found in the
+    /// cwd stays as it is, else the first executable match in `path`.
+    #[cfg(unix)]
+    fn search_path(&self, path: &OsStr) -> Option<PathBuf> {
+        use std::{os::unix::fs::PermissionsExt, path::Path};
+
+        let exe = Path::new(&self.cmd);
+        if exe.is_absolute() || Path::new(&self.cwd).join(exe).exists() {
+            return None;
+        }
+        std::env::split_paths(path).map(|dir| dir.join(exe)).find(|candidate| {
+            candidate
+                .metadata()
+                .is_ok_and(|m| m.is_file() && m.permissions().mode() & 0o111 
!= 0)
+        })
+    }
+
+    /// Mirrors portable-pty's Windows lookup: each `path` entry with the
+    /// name as given, then with each PATHEXT extension.
+    #[cfg(windows)]
+    fn search_path(&self, path: &OsStr) -> Option<PathBuf> {
+        let pathext = self
+            .map_var("PATHEXT")
+            .map(OsString::from)
+            .or_else(|| std::env::var_os("PATHEXT"))
+            .unwrap_or_else(|| ".EXE".into());
+        for dir in std::env::split_paths(path) {
+            let candidate = dir.join(&self.cmd);
+            if candidate.exists() {
+                return Some(candidate);
+            }
+            for ext in std::env::split_paths(&pathext) {
+                let Some(ext) = ext.to_str().and_then(|e| e.strip_prefix('.')) 
else {
+                    continue;
+                };
+                let candidate = dir.join(&self.cmd).with_extension(ext);
+                if candidate.exists() {
+                    return Some(candidate);
+                }
+            }
+        }
+        None
+    }
 }
 
 /* ---------- async message channel ---------- */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bun-pty-0.4.11/src/env.test.ts 
new/bun-pty-0.5.1/src/env.test.ts
--- old/bun-pty-0.4.11/src/env.test.ts  1970-01-01 01:00:00.000000000 +0100
+++ new/bun-pty-0.5.1/src/env.test.ts   2026-10-12 08:44:31.000000000 +0200
@@ -0,0 +1,94 @@
+import { expect, test, describe } from "bun:test";
+import { DEFAULT_NAME, childEnv, formatEnv } from "./env";
+
+const parent = {
+       PATH: "/usr/bin:/bin",
+       HOME: "/home/me",
+       TERM: "screen-256color",
+       PWD: "/somewhere/else",
+       TMUX: "/tmp/tmux-1000/default,1234,0",
+       TMUX_PANE: "%1",
+       STY: "1234.pts-0.host",
+       WINDOW: "0",
+       WINDOWID: "4194317",
+       TERMCAP: "SC|screen",
+       COLUMNS: "120",
+       LINES: "40",
+       UNSET: undefined,
+};
+
+describe("childEnv on Unix", () => {
+       test("a supplied map replaces the parent's environment", () => {
+               const env = childEnv({ name: "xterm-256color", env: { FOO: 
"bar" } }, "/work", "linux", parent);
+               expect(env).toEqual({ FOO: "bar", PWD: "/work", TERM: 
"xterm-256color" });
+       });
+
+       test("without a map the child gets the parent's environment", () => {
+               const env = childEnv({ name: "xterm-256color" }, "/work", 
"darwin", parent);
+               expect(env).toEqual({
+                       PATH: "/usr/bin:/bin",
+                       HOME: "/home/me",
+                       TERM: "xterm-256color",
+                       PWD: "/work",
+               });
+       });
+
+       test("drops the parent's tmux and screen variables when the map is the 
parent's environment", () => {
+               const env = childEnv({ env: parent }, "/work", "linux", parent);
+               expect(Object.keys(env).sort()).toEqual(["HOME", "PATH", "PWD", 
"TERM"]);
+       });
+
+       test("keeps tmux and screen variables a supplied map sets", () => {
+               const env = childEnv({ env: { TMUX: "mine", COLUMNS: "80" } }, 
"/work", "linux", parent);
+               expect(env.TMUX).toBe("mine");
+               expect(env.COLUMNS).toBe("80");
+       });
+
+       test("TERM comes from name, then from the environment, then the 
default", () => {
+               expect(childEnv({ name: "vt100", env: { TERM: "dumb" } }, "/", 
"linux", parent).TERM).toBe("vt100");
+               expect(childEnv({ env: { TERM: "dumb" } }, "/", "linux", 
parent).TERM).toBe("dumb");
+               expect(childEnv({}, "/", "linux", 
parent).TERM).toBe("screen-256color");
+               expect(childEnv({ env: {} }, "/", "linux", 
parent).TERM).toBe(DEFAULT_NAME);
+       });
+
+       test("PWD comes from cwd", () => {
+               expect(childEnv({ env: { PWD: "/old" } }, "/work", "linux", 
parent).PWD).toBe("/work");
+       });
+
+       test("skips undefined values", () => {
+               const env = childEnv({ env: { KEEP: "1", SKIP: undefined } }, 
"/", "linux", parent);
+               expect(Object.keys(env).sort()).toEqual(["KEEP", "PWD", 
"TERM"]);
+       });
+
+       test("does not change the supplied map", () => {
+               const supplied = { FOO: "bar" };
+               childEnv({ name: "xterm", env: supplied }, "/work", "linux", 
parent);
+               expect(supplied).toEqual({ FOO: "bar" });
+       });
+});
+
+describe("childEnv on Windows", () => {
+       test("passes the map as given, without TERM or PWD", () => {
+               const env = childEnv({ name: "xterm-256color", env: { FOO: 
"bar" } }, "C:\\work", "win32", parent);
+               expect(env).toEqual({ FOO: "bar" });
+       });
+
+       test("keeps an empty map empty, so the native side keeps the parent's 
environment", () => {
+               expect(childEnv({ env: {} }, "C:\\work", "win32", 
parent)).toEqual({});
+       });
+
+       test("without a map the child gets the parent's environment unchanged", 
() => {
+               const { UNSET, ...defined } = parent;
+               expect(childEnv({}, "C:\\work", "win32", 
parent)).toEqual(defined);
+       });
+});
+
+describe("formatEnv", () => {
+       test("NUL-terminates each KEY=VALUE pair", () => {
+               expect(formatEnv({ A: "1", B: "x=y" })).toBe("A=1\0B=x=y\0");
+       });
+
+       test("formats an empty environment as an empty string", () => {
+               expect(formatEnv({})).toBe("");
+       });
+});
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bun-pty-0.4.11/src/env.ts 
new/bun-pty-0.5.1/src/env.ts
--- old/bun-pty-0.4.11/src/env.ts       1970-01-01 01:00:00.000000000 +0100
+++ new/bun-pty-0.5.1/src/env.ts        2026-10-12 08:44:31.000000000 +0200
@@ -0,0 +1,58 @@
+// env.ts  —  the child's environment, kept out of terminal.ts so it can be
+// tested without loading the native library
+
+import type { IPtyForkOptions } from "./interfaces";
+
+export const DEFAULT_NAME = "xterm";
+
+/**
+ * Variables that tie a shell to the terminal this process runs in. As in
+ * node-pty, they are dropped when the child inherits process.env, so a shell
+ * in the new PTY does not think it runs inside the parent's tmux or screen
+ * session, or take the parent terminal's size.
+ */
+const PARENT_TERMINAL_VARS = ["TMUX", "TMUX_PANE", "STY", "WINDOW", 
"WINDOWID", "TERMCAP", "COLUMNS", "LINES"];
+
+/**
+ * The child's environment, as node-pty builds it. A supplied map replaces
+ * the parent's environment; without one the child gets `parentEnv`. On Unix,
+ * TERM comes from `name` and PWD from `cwd`.
+ *
+ * @param opts - The spawn options
+ * @param cwd - The child's working directory
+ * @param platform - The platform to build for
+ * @param parentEnv - This process's environment
+ * @returns The child's environment
+ */
+export function childEnv(
+       opts: Pick<IPtyForkOptions, "env" | "name">,
+       cwd: string,
+       platform: typeof process.platform = process.platform,
+       parentEnv: Record<string, string | undefined> = process.env,
+): Record<string, string> {
+       const source = opts.env ?? parentEnv;
+       const env: Record<string, string> = {};
+       for (const [key, value] of Object.entries(source)) {
+               if (value !== undefined) env[key] = value;
+       }
+       if (platform === "win32") return env;
+
+       if (source === parentEnv) {
+               for (const key of PARENT_TERMINAL_VARS) delete env[key];
+       }
+       env.PWD = cwd;
+       env.TERM = opts.name || env.TERM || DEFAULT_NAME;
+       return env;
+}
+
+/**
+ * Format an environment for the native side: NUL-terminated KEY=VALUE pairs,
+ * or an empty string for an empty environment.
+ *
+ * @param env - The environment to format
+ * @returns The formatted environment
+ */
+export function formatEnv(env: Record<string, string>): string {
+       const pairs = Object.entries(env).map(([key, value]) => 
`${key}=${value}`);
+       return pairs.length > 0 ? `${pairs.join("\0")}\0` : "";
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bun-pty-0.4.11/src/interfaces.ts 
new/bun-pty-0.5.1/src/interfaces.ts
--- old/bun-pty-0.4.11/src/interfaces.ts        2026-09-30 07:52:47.000000000 
+0200
+++ new/bun-pty-0.5.1/src/interfaces.ts 2026-10-12 08:44:31.000000000 +0200
@@ -40,9 +40,10 @@
  */
 export interface IPtyForkOptions {
   /**
-   * The name of the terminal to be set in environment variables.
+   * The terminal type, set as TERM in the child's environment on Unix.
+   * Defaults to TERM from the environment, then "xterm".
    */
-  name: string;
+  name?: string;
 
   /**
    * The number of columns in the PTY.
@@ -61,9 +62,13 @@
   cwd?: string;
 
   /**
-   * Environment variables to set for the process.
+   * The child's whole environment, as in node-pty: it replaces the parent's
+   * environment instead of adding to it, so pass
+   * `{ ...process.env, FOO: "bar" }` to add variables. Entries whose value
+   * is undefined are skipped. On Windows, a map with no entries keeps the
+   * parent's environment, as in node-pty. Defaults to `process.env`.
    */
-  env?: Record<string, string>;
+  env?: Record<string, string | undefined>;
 }
 
 /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bun-pty-0.4.11/src/terminal.integration.test.ts 
new/bun-pty-0.5.1/src/terminal.integration.test.ts
--- old/bun-pty-0.4.11/src/terminal.integration.test.ts 2026-09-30 
07:52:47.000000000 +0200
+++ new/bun-pty-0.5.1/src/terminal.integration.test.ts  2026-10-12 
08:44:31.000000000 +0200
@@ -452,6 +452,175 @@
     expect(dataReceived).toContain("Hello from PowerShell");
   }, 15000);
 
+  test.skipIf(isWindows)("Terminal replaces the parent environment with a 
supplied env map", async () => {
+    let dataReceived = "";
+    let hasExited = false;
+
+    const terminal = new Terminal("/usr/bin/env", [], {
+      name: "xterm",
+      env: {
+        BUN_PTY_ONLY: "from-map",
+      },
+    });
+    terminals.push(terminal);
+
+    terminal.onData((data) => {
+      dataReceived += data;
+    });
+
+    terminal.onExit(() => {
+      hasExited = true;
+    });
+
+    const timeout = 5000;
+    const start = Date.now();
+
+    while (!hasExited && Date.now() - start < timeout) {
+      await new Promise((resolve) => setTimeout(resolve, 100));
+    }
+
+    await new Promise((resolve) => setTimeout(resolve, 200));
+
+    const names = dataReceived
+      .split(/\r?\n/)
+      .filter((line) => line.includes("="))
+      .map((line) => line.slice(0, line.indexOf("=")))
+      .sort();
+
+    // portable-pty adds SHELL when the map holds none; PWD and TERM come from 
cwd and name
+    expect(names).toEqual(["BUN_PTY_ONLY", "PWD", "SHELL", "TERM"]);
+    expect(dataReceived).toContain("BUN_PTY_ONLY=from-map");
+    expect(dataReceived).toContain("TERM=xterm");
+  });
+
+  test.skipIf(isWindows)("Terminal passes the current process.env when no env 
map is supplied", async () => {
+    let dataReceived = "";
+    let hasExited = false;
+
+    process.env.BUN_PTY_RUNTIME = "set-at-runtime";
+    const terminal = new Terminal("sh", ["-c", "echo 
\"RUNTIME:[$BUN_PTY_RUNTIME]\""]);
+    delete process.env.BUN_PTY_RUNTIME;
+    terminals.push(terminal);
+
+    terminal.onData((data) => {
+      dataReceived += data;
+    });
+
+    terminal.onExit(() => {
+      hasExited = true;
+    });
+
+    const timeout = 5000;
+    const start = Date.now();
+
+    while (!hasExited && Date.now() - start < timeout) {
+      await new Promise((resolve) => setTimeout(resolve, 100));
+    }
+
+    await new Promise((resolve) => setTimeout(resolve, 200));
+
+    expect(dataReceived).toContain("RUNTIME:[set-at-runtime]");
+  });
+
+  test.skipIf(isWindows)("Terminal sets TERM from name and PWD from cwd", 
async () => {
+    let dataReceived = "";
+    let hasExited = false;
+
+    const terminal = new Terminal("sh", ["-c", "echo \"TERM:[$TERM] 
PWD:[$PWD]\""], {
+      name: "xterm-256color",
+      cwd: "/",
+    });
+    terminals.push(terminal);
+
+    terminal.onData((data) => {
+      dataReceived += data;
+    });
+
+    terminal.onExit(() => {
+      hasExited = true;
+    });
+
+    const timeout = 5000;
+    const start = Date.now();
+
+    while (!hasExited && Date.now() - start < timeout) {
+      await new Promise((resolve) => setTimeout(resolve, 100));
+    }
+
+    await new Promise((resolve) => setTimeout(resolve, 200));
+
+    expect(dataReceived).toContain("TERM:[xterm-256color] PWD:[/]");
+  });
+
+  test.skipIf(isWindows)("Terminal drops the parent's tmux variables when the 
child inherits process.env", async () => {
+    let dataReceived = "";
+    let hasExited = false;
+
+    const previous = process.env.TMUX;
+    process.env.TMUX = "/tmp/tmux-bun-pty/default,1,0";
+    const terminal = new Terminal("sh", ["-c", "echo \"TMUX:[$TMUX]\""]);
+    if (previous === undefined) delete process.env.TMUX;
+    else process.env.TMUX = previous;
+    terminals.push(terminal);
+
+    terminal.onData((data) => {
+      dataReceived += data;
+    });
+
+    terminal.onExit(() => {
+      hasExited = true;
+    });
+
+    const timeout = 5000;
+    const start = Date.now();
+
+    while (!hasExited && Date.now() - start < timeout) {
+      await new Promise((resolve) => setTimeout(resolve, 100));
+    }
+
+    await new Promise((resolve) => setTimeout(resolve, 200));
+
+    expect(dataReceived).toContain("TMUX:[]");
+  });
+
+  test.skipIf(isWindows)("Terminal finds a bare program name when the env map 
has no PATH", async () => {
+    let dataReceived = "";
+    let hasExited = false;
+
+    const terminal = new Terminal("env", [], {
+      env: {
+        BUN_PTY_ONLY: "from-map",
+      },
+    });
+    terminals.push(terminal);
+
+    terminal.onData((data) => {
+      dataReceived += data;
+    });
+
+    terminal.onExit(() => {
+      hasExited = true;
+    });
+
+    const timeout = 5000;
+    const start = Date.now();
+
+    while (!hasExited && Date.now() - start < timeout) {
+      await new Promise((resolve) => setTimeout(resolve, 100));
+    }
+
+    await new Promise((resolve) => setTimeout(resolve, 200));
+
+    const names = dataReceived
+      .split(/\r?\n/)
+      .filter((line) => line.includes("="))
+      .map((line) => line.slice(0, line.indexOf("=")))
+      .sort();
+
+    // The parent's PATH finds `env`, but does not reach the child
+    expect(names).toEqual(["BUN_PTY_ONLY", "PWD", "SHELL", "TERM"]);
+  });
+
   // Windows-specific: Test environment variables
   test.skipIf(!isWindows)("Terminal passes environment variables on Windows", 
async () => {
     let dataReceived = "";
@@ -487,6 +656,37 @@
     expect(dataReceived).toContain("HelloFromEnv");
   });
 
+  // Windows-specific: an empty env map keeps the parent environment, as in 
node-pty
+  test.skipIf(!isWindows)("Terminal keeps the parent environment for an empty 
env map on Windows", async () => {
+    let dataReceived = "";
+    let hasExited = false;
+
+    const terminal = new Terminal("cmd.exe", ["/c", "echo 
ROOT:[%SystemRoot%]"], {
+      name: "xterm",
+      env: {},
+    });
+    terminals.push(terminal);
+
+    terminal.onData((data) => {
+      dataReceived += data;
+    });
+
+    terminal.onExit(() => {
+      hasExited = true;
+    });
+
+    const timeout = 5000;
+    const start = Date.now();
+
+    while (!hasExited && Date.now() - start < timeout) {
+      await new Promise((resolve) => setTimeout(resolve, 100));
+    }
+
+    await new Promise((resolve) => setTimeout(resolve, 200));
+
+    expect(dataReceived).toContain(`ROOT:[${process.env.SystemRoot}]`);
+  });
+
   test("Terminal onData listener receives data when set immediately after 
construction", async () => {
     const start = Date.now();
     const maxRuntime = 4000;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bun-pty-0.4.11/src/terminal.ts 
new/bun-pty-0.5.1/src/terminal.ts
--- old/bun-pty-0.4.11/src/terminal.ts  2026-09-30 07:52:47.000000000 +0200
+++ new/bun-pty-0.5.1/src/terminal.ts   2026-10-12 08:44:31.000000000 +0200
@@ -5,13 +5,14 @@
 import { EventEmitter } from "./interfaces";
 import type { IPty, IPtyForkOptions, IExitEvent } from "./interfaces";
 import { libraryFilenames } from "./library";
+import { DEFAULT_NAME, childEnv, formatEnv } from "./env";
 import { join, dirname, basename } from "node:path";
 import { existsSync, readFileSync } from "node:fs";
 
 export const DEFAULT_COLS = 80;
 export const DEFAULT_ROWS = 24;
 export const DEFAULT_FILE = "sh";
-export const DEFAULT_NAME = "xterm";
+export { DEFAULT_NAME };
 
 /**
  * Quote a string for shell-words compatible splitting on the Rust side.
@@ -163,7 +164,7 @@
        constructor(
                file = DEFAULT_FILE,
                args: string[] = [],
-               opts: IPtyForkOptions = { name: DEFAULT_NAME },
+               opts: IPtyForkOptions = {},
        ) {
                this._cols = opts.cols ?? DEFAULT_COLS;
                this._rows = opts.rows ?? DEFAULT_ROWS;
@@ -171,12 +172,8 @@
                // Properly quote file and arguments to preserve spaces and 
special characters
                const cmdline = [shQuote(file), ...args.map(shQuote)].join(" ");
 
-               // Format environment variables as null-terminated string
-               let envStr = "";
-               if (opts.env) {
-                       const envPairs = Object.entries(opts.env).map(([k, v]) 
=> `${k}=${v}`);
-                       envStr = envPairs.join("\0") + "\0";
-               }
+               // The child's whole environment, built as node-pty builds it
+               const envStr = formatEnv(childEnv(opts, cwd));
 
                this.handle = lib.symbols.bun_pty_spawn(
                        Buffer.from(`${cmdline}\0`, "utf8"),

++++++ vendor.tar.zst ++++++
++++ 476229 lines of diff (skipped)

Reply via email to