npq 3.11.5 → 3.12.0

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.
package/bin/npq-hero.js CHANGED
@@ -100,8 +100,18 @@ marshall
100
100
  }
101
101
  })
102
102
  .catch((error) => {
103
+ // Ensure errorCode is always a number
104
+ let errorCode = -1
105
+ if (typeof error.code === 'number') {
106
+ errorCode = error.code
107
+ } else if (error.code === 'ABORT_ERR') {
108
+ errorCode = 1
109
+ } else if (error.code === 'USER_ABORT') {
110
+ errorCode = error.exitCode || 1
111
+ }
112
+
103
113
  CliParser.exit({
104
- errorCode: error.code || -1,
114
+ errorCode,
105
115
  message: error.message || 'An error occurred',
106
116
  spinner
107
117
  })
package/bin/npq.js CHANGED
@@ -126,8 +126,18 @@ Promise.resolve()
126
126
  }
127
127
  })
128
128
  .catch((error) => {
129
+ // Ensure errorCode is always a number
130
+ let errorCode = -1
131
+ if (typeof error.code === 'number') {
132
+ errorCode = error.code
133
+ } else if (error.code === 'ABORT_ERR') {
134
+ errorCode = 1
135
+ } else if (error.code === 'USER_ABORT') {
136
+ errorCode = error.exitCode || 1
137
+ }
138
+
129
139
  CliParser.exit({
130
- errorCode: error.code || -1,
140
+ errorCode,
131
141
  message: error.message || 'An error occurred',
132
142
  spinner
133
143
  })
package/lib/cli.js CHANGED
@@ -11,10 +11,13 @@ class CliParser {
11
11
  }
12
12
 
13
13
  if (message) {
14
+ console.error('\n')
14
15
  console.error(message)
15
16
  }
16
17
 
17
- process.exit(errorCode || -1)
18
+ // Ensure errorCode is always a number
19
+ const exitCode = typeof errorCode === 'number' ? errorCode : -1
20
+ process.exit(exitCode)
18
21
  }
19
22
 
20
23
  static _extractPackagesFromPositionals(positionals, earlyExitNoInstall = false) {
@@ -54,41 +54,129 @@ async function prompt(options = {}) {
54
54
  }
55
55
 
56
56
  return { [name]: result }
57
+ } catch (error) {
58
+ // Handle Ctrl+C gracefully - user cancelled the operation
59
+ if (error.code === 'ABORT_ERR') {
60
+ // Throw a specific error that can be caught and handled by the caller
61
+ const abortError = new Error('Operation aborted by user')
62
+ abortError.code = 'USER_ABORT'
63
+ abortError.exitCode = 1
64
+ throw abortError
65
+ }
66
+ throw error
57
67
  } finally {
58
68
  rl.close()
59
69
  }
60
70
  }
61
71
 
62
72
  async function autoContinue({ name, message, timeInSeconds = 5 } = {}) {
63
- // Show initial message with countdown
64
- process.stdout.write(`${message}${timeInSeconds}`)
73
+ let aborted = false
74
+ let userProceeded = false
75
+
76
+ // Setup stdin to capture keypresses during countdown
77
+ if (process.stdin.isTTY) {
78
+ process.stdin.setRawMode(true)
79
+ process.stdin.resume()
80
+
81
+ // Handle keypresses during countdown
82
+ const onKeypress = (chunk) => {
83
+ // Check for Ctrl+C (ASCII 3)
84
+ if (chunk && chunk.length === 1 && chunk[0] === 3) {
85
+ aborted = true
86
+ }
87
+ // Check for 'y' or 'Y' (ASCII 121 or 89)
88
+ else if (chunk && chunk.length === 1 && (chunk[0] === 121 || chunk[0] === 89)) {
89
+ userProceeded = true
90
+ }
91
+ // Ignore all other keypresses during countdown
92
+ }
65
93
 
66
- // Count down from timeInSeconds-1 to 1
67
- for (let i = timeInSeconds - 1; i > 0; i--) {
68
- await setTimeout(1000)
94
+ process.stdin.on('data', onKeypress)
69
95
 
70
- // Calculate how many characters to backspace (previous number length)
71
- const prevNumber = i + 1
72
- const prevLength = prevNumber.toString().length
73
- const currentLength = i.toString().length
96
+ // Cleanup function
97
+ const cleanup = () => {
98
+ process.stdin.removeListener('data', onKeypress)
99
+ if (process.stdin.isTTY) {
100
+ process.stdin.setRawMode(false)
101
+ process.stdin.pause()
102
+ }
103
+ }
74
104
 
75
- // Backspace to beginning of number
76
- const backspaces = '\b'.repeat(prevLength)
105
+ try {
106
+ // Show countdown message with instruction in parentheses
107
+ process.stdout.write(`${message}${timeInSeconds} (press 'y' to proceed)`)
77
108
 
78
- // Write new number and pad with spaces if needed to clear any remaining digits
79
- const padding = ' '.repeat(Math.max(0, prevLength - currentLength))
80
- const moveBack = '\b'.repeat(Math.max(0, prevLength - currentLength))
109
+ let currentNumber = timeInSeconds
81
110
 
82
- process.stdout.write(`${backspaces}${i}${padding}${moveBack}`)
83
- }
111
+ // Count down from timeInSeconds-1 to 1
112
+ for (let i = timeInSeconds - 1; i > 0 && !aborted && !userProceeded; i--) {
113
+ await setTimeout(1000)
114
+
115
+ if (aborted || userProceeded) break
116
+
117
+ // Update the countdown number using backspace
118
+ // We need to backspace over both the number AND the instruction text
119
+ const instructionText = " (press 'y' to proceed)"
120
+ const totalTextLength = currentNumber.toString().length + instructionText.length
121
+
122
+ // Backspace over everything, write new number and instruction
123
+ const backspaces = '\b'.repeat(totalTextLength)
124
+ process.stdout.write(`${backspaces}${i}${instructionText}`)
125
+
126
+ currentNumber = i
127
+ }
84
128
 
85
- // Wait for the final second
86
- await setTimeout(1000)
129
+ if (!aborted && !userProceeded) {
130
+ // Wait for the final second
131
+ await setTimeout(1000)
132
+ }
87
133
 
88
- // Move to next line after countdown completes
89
- console.log()
134
+ // Move to a clean new line for any follow-up output
135
+ if (!aborted) {
136
+ console.log('') // Just move to next line
137
+ }
90
138
 
91
- return { [name]: true }
139
+ cleanup()
140
+
141
+ if (aborted) {
142
+ const abortError = new Error('Operation aborted by user')
143
+ abortError.code = 'USER_ABORT'
144
+ abortError.exitCode = 1
145
+ throw abortError
146
+ }
147
+
148
+ return { [name]: true }
149
+ } catch (error) {
150
+ cleanup()
151
+ throw error
152
+ }
153
+ } else {
154
+ // Fallback for non-TTY environments (tests, etc.)
155
+ // Show countdown message with instruction
156
+ process.stdout.write(`${message}${timeInSeconds} (press 'y' to proceed)`)
157
+
158
+ // Count down from timeInSeconds-1 to 1
159
+ for (let i = timeInSeconds - 1; i > 0; i--) {
160
+ await setTimeout(1000)
161
+
162
+ // Update similar to TTY version - backspace over number and instruction
163
+ const instructionText = " (press 'y' to proceed)"
164
+ const prevNumber = i + 1
165
+ const totalTextLength = prevNumber.toString().length + instructionText.length
166
+
167
+ // Backspace over everything, write new number and instruction
168
+ const backspaces = '\b'.repeat(totalTextLength)
169
+ process.stdout.write(`${backspaces}${i}${instructionText}`)
170
+ }
171
+
172
+ // Wait for the final second
173
+ await setTimeout(1000)
174
+
175
+ // Move to next line after countdown completes
176
+ console.log('')
177
+
178
+ return { [name]: true }
179
+ }
92
180
  }
93
181
 
94
182
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npq",
3
- "version": "3.11.5",
3
+ "version": "3.12.0",
4
4
  "description": "marshall your npm/npm package installs with high quality and class 🎖",
5
5
  "bin": {
6
6
  "npq": "./bin/npq.js",
@@ -44,6 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "fastest-levenshtein": "^1.0.16",
47
+ "lodash": "^4.17.21",
47
48
  "npm-package-arg": "^13.0.0",
48
49
  "pacote": "^21.0.0",
49
50
  "semver": "^7.7.2"