Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci-gates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,19 @@ jobs:
# The diff-aware suite escalates when it cannot resolve a safe scope.
# Coverage shards verify the complete suite on each pushed commit.
main-script: pnpm run test --ci
- name: Set up Node.js 18.20.8
uses: ./.github/actions/fleet/setup
with:
node-version: '18.20.8'
- name: Verify the built width module on Node.js 18.20.8
run: >-
node -e "const { stringWidth } = require('./dist/strings/width.js');
const { findSelfNestedProcesses } = require('./dist/process/tree.js');
const rows = [10, 20, 30, 40].map((pid, index) => ({ pid, ppid:
index === 0 ? 1 : pid - 10, command: 'wrapper' })); if
(stringWidth('👨‍👩‍👧‍👦') !== 2 ||
findSelfNestedProcesses(rows, row => row.command).map(row =>
row.pid).join(',') !== '40,30') process.exitCode = 1"

cover-shards:
if: github.event_name != 'pull_request'
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4336,7 +4336,7 @@
]
},
"engines": {
"node": ">=24",
"node": ">=18.20.8",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Engines constraints conflict on Node 18

Medium Severity

Lowering engines.node to >=18.20.8 leaves engines.npm at >=12.2.0 and engines.pnpm at >=12.12.0. npm 12 requires Node ^22.22.2, ^24.15.0, or >=26, so a Node 18.20.8 consumer cannot satisfy the full engines set. With engine-strict, install fails and the new runtime claim does not hold.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b4be3fd. Configure here.

"npm": ">=12.2.0",
"pnpm": ">=12.12.0"
},
Expand Down
2 changes: 1 addition & 1 deletion scripts/repo/package-exports.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ export const config: ExportsConfig = {
'dist/**/shared-*.js',
'src/**',
],
nodeRange: '>=24',
nodeRange: '>=18.20.8',
outDir: 'dist',
publicNames: {
'./spinner/create': './spinner/spinner',
Expand Down
6 changes: 5 additions & 1 deletion src/process/tree.mts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { getNodeChildProcess } from '../node/child-process.mjs'
import { arrayToSorted } from '../polyfills/array.mjs'

export interface ProcessTreeRow {
command: string
Expand Down Expand Up @@ -59,7 +60,10 @@ export function findSelfNestedProcesses(
}
depthOf.set(pid, hops)
}
return nested.toSorted((a, b) => depthOf.get(b.pid)! - depthOf.get(a.pid)!)
return arrayToSorted(
nested,
(a, b) => depthOf.get(b.pid)! - depthOf.get(a.pid)!,
)
}

/**
Expand Down
30 changes: 19 additions & 11 deletions src/strings/width.mts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import { stripAnsi } from '../term/ansi/strip.mjs'
import { IntlSegmenter } from '../primordials/intl.mjs'
import { RegExpPrototypeTest } from '../primordials/regexp.mjs'
import { RegExpCtor, RegExpPrototypeTest } from '../primordials/regexp.mjs'
import {
StringPrototypeCharCodeAt,
StringPrototypeCodePointAt,
Expand Down Expand Up @@ -70,21 +70,29 @@ let emojiRegex: RegExp

try {
// Try 'v' flag first (Node 20+) for most accurate Unicode property support.
zeroWidthClusterRegex =
/^(?:\p{Control}|\p{Default_Ignorable_Code_Point}|\p{Mark}|\p{Surrogate})+$/v
zeroWidthClusterRegex = new RegExpCtor(
'^(?:\\p{Control}|\\p{Default_Ignorable_Code_Point}|\\p{Mark}|\\p{Surrogate})+$',
'v',
)

leadingNonPrintingRegex =
/^[\p{Default_Ignorable_Code_Point}\p{Control}\p{Format}\p{Mark}\p{Surrogate}]+/v
leadingNonPrintingRegex = new RegExpCtor(
'^[\\p{Default_Ignorable_Code_Point}\\p{Control}\\p{Format}\\p{Mark}\\p{Surrogate}]+',
'v',
)

emojiRegex = /^\p{RGI_Emoji}$/v
emojiRegex = new RegExpCtor('^\\p{RGI_Emoji}$', 'v')
/* c8 ignore start */
} catch {
// Fall back to 'u' flag (Node 18+) — no \p{Surrogate}, broader emoji.
zeroWidthClusterRegex =
/^(?:\p{Control}|\p{Default_Ignorable_Code_Point}|\p{Mark})+$/u
leadingNonPrintingRegex =
/^[\p{Default_Ignorable_Code_Point}\p{Control}\p{Format}\p{Mark}]+/u
emojiRegex = /^\p{Extended_Pictographic}$/u
zeroWidthClusterRegex = new RegExpCtor(
'^(?:\\p{Control}|\\p{Default_Ignorable_Code_Point}|\\p{Mark})+$',
'u',
)
leadingNonPrintingRegex = new RegExpCtor(
'^[\\p{Default_Ignorable_Code_Point}\\p{Control}\\p{Format}\\p{Mark}]+',
'u',
)
emojiRegex = new RegExpCtor('^\\p{Extended_Pictographic}$', 'u')
}
/* c8 ignore stop */

Expand Down
Loading