@automattic/vip 4.0.7 → 4.0.9

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.
@@ -20,6 +20,7 @@ var _proxyAgent = require("../lib/http/proxy-agent");
20
20
  var _token = _interopRequireDefault(require("../lib/token"));
21
21
  var _tracker = require("../lib/tracker");
22
22
  var _helpers = require("../lib/wp/helpers");
23
+ var _readline2 = require("../lib/wp/readline");
23
24
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
24
25
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
25
26
  const debug = (0, _debug.default)('@automattic/vip:wp');
@@ -107,8 +108,8 @@ const bindStreamEvents = ({
107
108
  subShellRl.close();
108
109
  process.exit();
109
110
  }
110
- subShellRl.resume();
111
- subShellRl.prompt();
111
+ (0, _readline2.safeResume)(subShellRl);
112
+ (0, _readline2.safePrompt)(subShellRl);
112
113
  });
113
114
  };
114
115
  const getTokenForCommand = async (appId, envId, command) => {
@@ -233,7 +234,7 @@ const bindReconnectEvents = ({
233
234
  });
234
235
 
235
236
  // Resume readline interface
236
- subShellRl.resume();
237
+ (0, _readline2.safeResume)(subShellRl);
237
238
  });
238
239
  currentJob.socket.on('retry', async () => {
239
240
  debug('socket: retry');
@@ -391,6 +392,7 @@ const examples = [{
391
392
  const commandState = (0, _helpers.initState)();
392
393
  let seenWP = false;
393
394
  const subShellRl = _readline.default.createInterface(subShellSettings);
395
+ (0, _readline2.trackReadline)(subShellRl);
394
396
  subShellRl.on('line', async line => {
395
397
  if (commandRunning) {
396
398
  return;
@@ -398,7 +400,7 @@ const examples = [{
398
400
 
399
401
  // Handle plain return / newline
400
402
  if (!line) {
401
- subShellRl.prompt();
403
+ (0, _readline2.safePrompt)(subShellRl);
402
404
  return;
403
405
  }
404
406
 
@@ -411,7 +413,7 @@ const examples = [{
411
413
  if (userCmdCancelled) {
412
414
  seenWP = false;
413
415
  (0, _helpers.resetState)(commandState);
414
- subShellRl.prompt();
416
+ (0, _readline2.safePrompt)(subShellRl);
415
417
  return;
416
418
  }
417
419
  if (!seenWP && line.trimStart().startsWith('wp ')) {
@@ -426,10 +428,10 @@ const examples = [{
426
428
  } else {
427
429
  (0, _helpers.resetState)(commandState);
428
430
  console.log(_chalk.default.red('Error:'), 'invalid command, please pass a valid WP-CLI command.');
429
- subShellRl.prompt();
431
+ (0, _readline2.safePrompt)(subShellRl);
430
432
  return;
431
433
  }
432
- subShellRl.pause();
434
+ (0, _readline2.safePause)(subShellRl);
433
435
  let result;
434
436
  const wpCliCmd = commandState.command.replace(/^wp\s+/, '');
435
437
  seenWP = false;
@@ -450,7 +452,7 @@ const examples = [{
450
452
  subShellRl.close();
451
453
  process.exit(1);
452
454
  }
453
- subShellRl.prompt();
455
+ (0, _readline2.safePrompt)(subShellRl);
454
456
  return;
455
457
  }
456
458
  const {
@@ -522,9 +524,9 @@ const examples = [{
522
524
  });
523
525
  if (!isSubShell) {
524
526
  mutableStdout.muted = true;
525
- subShellRl.write(`wp ${cmd}\n`);
527
+ (0, _readline2.safeWrite)(subShellRl, `wp ${cmd}\n`);
526
528
  mutableStdout.muted = false;
527
529
  return;
528
530
  }
529
- subShellRl.prompt();
531
+ (0, _readline2.safePrompt)(subShellRl);
530
532
  });
@@ -44,6 +44,43 @@ function parseEtagHeader(etag) {
44
44
  return normalizedEtag.replace(/^"(.*)"$/u, '$1');
45
45
  }
46
46
  const isStreamBody = body => typeof body?.pipe === 'function';
47
+ function createProgressTransform(onChunk) {
48
+ return new _stream.Transform({
49
+ transform(chunk, _encoding, callback) {
50
+ onChunk(chunk);
51
+ callback(null, chunk);
52
+ }
53
+ });
54
+ }
55
+ function omitUnsupportedFetchHeaders(init) {
56
+ if (!init.headers) {
57
+ return init;
58
+ }
59
+ const headers = new _undici.Headers(init.headers);
60
+ headers.delete('expect');
61
+ return {
62
+ ...init,
63
+ headers
64
+ };
65
+ }
66
+ function withCauseMessage(err) {
67
+ if (!(err instanceof Error)) {
68
+ return err;
69
+ }
70
+ const cause = err.cause;
71
+ let causeMessage;
72
+ if (cause instanceof Error) {
73
+ causeMessage = cause.message;
74
+ } else if (typeof cause === 'string') {
75
+ causeMessage = cause;
76
+ }
77
+ if (!causeMessage || err.message.includes(causeMessage)) {
78
+ return err;
79
+ }
80
+ return new Error(`${err.message}: ${causeMessage}`, {
81
+ cause: err
82
+ });
83
+ }
47
84
 
48
85
  /**
49
86
  * Wraps `fetch` with exponential-backoff retries.
@@ -75,10 +112,10 @@ async function fetchWithRetry(input, init = {}, retries = 3, createBody) {
75
112
  }
76
113
  try {
77
114
  // eslint-disable-next-line no-await-in-loop
78
- return await (0, _undici.fetch)(input, requestInit);
115
+ return await (0, _undici.fetch)(input, omitUnsupportedFetchHeaders(requestInit));
79
116
  } catch (err) {
80
117
  if (attempt === maxAttempts) {
81
- throw err;
118
+ throw withCauseMessage(err);
82
119
  }
83
120
  // eslint-disable-next-line no-await-in-loop
84
121
  await (0, _promises3.setTimeout)(Math.pow(2, attempt) * 1000); // 1000, 2000, 4000
@@ -260,8 +297,7 @@ async function uploadUsingPutObject({
260
297
  return fileContent;
261
298
  }
262
299
  let readBytes = 0;
263
- const progressPassThrough = new _stream.PassThrough();
264
- progressPassThrough.on('data', data => {
300
+ const progressTransform = createProgressTransform(data => {
265
301
  readBytes += data.length;
266
302
  const percentage = `${Math.floor(100 * readBytes / fileSize)}%`;
267
303
  debug(percentage);
@@ -269,7 +305,7 @@ async function uploadUsingPutObject({
269
305
  progressCallback(percentage);
270
306
  }
271
307
  });
272
- return (0, _fs.createReadStream)(fileName).pipe(progressPassThrough);
308
+ return (0, _fs.createReadStream)(fileName).pipe(progressTransform);
273
309
  };
274
310
  const response = await fetchWithRetry(presignedRequest.url, fetchOptions, 3, createBody);
275
311
  if (response.status === 200) {
@@ -507,15 +543,14 @@ async function uploadParts({
507
543
  const createBody = () => {
508
544
  partBytesRead[index] = 0;
509
545
  partPercentages[index] = 0;
510
- const progressPassThrough = new _stream.PassThrough();
511
- progressPassThrough.on('data', data => {
546
+ const progressTransform = createProgressTransform(data => {
512
547
  partBytesRead[index] += data.length;
513
548
  partPercentages[index] = Math.floor(100 * partBytesRead[index] / partSize);
514
549
  });
515
550
  return (0, _fs.createReadStream)(fileMeta.fileName, {
516
551
  start,
517
552
  end
518
- }).pipe(progressPassThrough);
553
+ }).pipe(progressTransform);
519
554
  };
520
555
  await readyForPartUpload();
521
556
  const uploadResult = await uploadPart({
@@ -86,21 +86,16 @@ const getSqlFileStreamFromCompressedFile = async filePath => {
86
86
  }
87
87
  throw new Error('Not a supported compressed file');
88
88
  };
89
+
90
+ // Replaces the byte-size value that mydumper embeds in chunk header comments (e.g. `-- tablename 1234`)
91
+ // with -1. mydumper exports the original file size in those headers, but since we modify the dump,
92
+ // the size is no longer accurate — setting it to -1 tells mydumper to ignore it.
93
+ // Lines starting with `-- metadata` are left untouched as they use the same format but are not chunk headers.
89
94
  const fixMyDumperTransform = () => {
90
- const regex = /^-- ([^ ]+) \d+$/;
95
+ const regex = /^-- (?!metadata)([^ ]+) \d+$/gm;
91
96
  return new _nodeStream.Transform({
92
97
  transform(chunk, _encoding, callback) {
93
- const chunkString = chunk.toString();
94
- const lineEnding = chunkString.includes('\r\n') ? '\r\n' : '\n';
95
- const lines = chunk.toString().split(lineEnding).map(line => {
96
- const match = regex.exec(line);
97
- if (!match) {
98
- return line;
99
- }
100
- const tablePart = match[1];
101
- return `-- ${tablePart} -1`;
102
- });
103
- callback(null, lines.join(lineEnding));
98
+ callback(null, chunk.toString().replace(regex, '-- $1 -1'));
104
99
  }
105
100
  });
106
101
  };
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.isReadlineClosed = isReadlineClosed;
5
+ exports.safePause = safePause;
6
+ exports.safePrompt = safePrompt;
7
+ exports.safeResume = safeResume;
8
+ exports.safeWrite = safeWrite;
9
+ exports.trackReadline = trackReadline;
10
+ /**
11
+ * Safe wrappers around `readline.Interface` lifecycle methods.
12
+ *
13
+ * On Node.js >= 24, calling `resume()`, `pause()`, `prompt()`, or `write()` on a
14
+ * readline interface that has already been closed throws
15
+ * `Error [ERR_USE_AFTER_CLOSE]: readline was closed`. On Node 20/22 the same
16
+ * calls are silent no-ops.
17
+ *
18
+ * `vip wp` drives non-interactive, one-shot commands through a readline
19
+ * interface bound to `process.stdin`. When stdin is redirected from
20
+ * `/dev/null` (no TTY, e.g. CI or container runs) the interface receives EOF
21
+ * immediately and closes, while the command keeps streaming output. On a
22
+ * websocket reconnect during a long-running command the reconnect handler then
23
+ * calls `resume()` on the already-closed interface, crashing the process on
24
+ * Node 24.
25
+ *
26
+ * These helpers guard every such call so the interface is only driven while it
27
+ * is still open. Closure is tracked through the documented `'close'` event
28
+ * rather than any internal interface property, so the behaviour does not depend
29
+ * on Node.js implementation details.
30
+ *
31
+ * Register an interface with {@link trackReadline} once, right after it is
32
+ * created, so the `'close'` listener is attached before the interface can
33
+ * close. Calls made through the `safe*` helpers on an untracked-but-open
34
+ * interface still work; tracking only adds the closed-state guard.
35
+ *
36
+ * @see https://nodejs.org/api/readline.html#event-close
37
+ */
38
+
39
+ const closedInterfaces = new WeakSet();
40
+
41
+ /**
42
+ * Begin tracking a readline interface so its closed state can be detected.
43
+ *
44
+ * Attaches a one-time `'close'` listener that records the interface as closed.
45
+ * Safe to call more than once for the same interface.
46
+ *
47
+ * @param rl The readline interface to track.
48
+ * @returns The same interface, for convenient chaining.
49
+ */
50
+ function trackReadline(rl) {
51
+ rl.once('close', () => {
52
+ closedInterfaces.add(rl);
53
+ });
54
+ return rl;
55
+ }
56
+
57
+ /**
58
+ * Whether a tracked readline interface has already emitted `'close'`.
59
+ *
60
+ * @param rl The readline interface to inspect.
61
+ * @returns `true` if the interface has been closed, otherwise `false`.
62
+ */
63
+ function isReadlineClosed(rl) {
64
+ return closedInterfaces.has(rl);
65
+ }
66
+
67
+ /**
68
+ * Resume the readline interface unless it has already been closed.
69
+ *
70
+ * @param rl The readline interface to resume.
71
+ */
72
+ function safeResume(rl) {
73
+ if (!isReadlineClosed(rl)) {
74
+ rl.resume();
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Pause the readline interface unless it has already been closed.
80
+ *
81
+ * @param rl The readline interface to pause.
82
+ */
83
+ function safePause(rl) {
84
+ if (!isReadlineClosed(rl)) {
85
+ rl.pause();
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Display the prompt unless the readline interface has already been closed.
91
+ *
92
+ * @param rl The readline interface to prompt on.
93
+ * @param preserveCursor When `true`, prevents the cursor from being reset to 0.
94
+ */
95
+ function safePrompt(rl, preserveCursor) {
96
+ if (!isReadlineClosed(rl)) {
97
+ rl.prompt(preserveCursor);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Write data into the readline interface unless it has already been closed.
103
+ *
104
+ * @param rl The readline interface to write to.
105
+ * @param data The data to write.
106
+ * @param key An optional key sequence, mirroring `Interface#write`.
107
+ */
108
+ function safeWrite(rl, data, key) {
109
+ if (!isReadlineClosed(rl)) {
110
+ rl.write(data, key);
111
+ }
112
+ }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@automattic/vip",
3
- "version": "4.0.7",
3
+ "version": "4.0.9",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@automattic/vip",
9
- "version": "4.0.7",
9
+ "version": "4.0.9",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "dependencies": {
@@ -41,7 +41,7 @@
41
41
  "socks-proxy-agent": "^10.0.0",
42
42
  "ssh2": "1.17.0",
43
43
  "tar": "^7.4.0",
44
- "undici": "^7.0.0",
44
+ "undici": "^8.5.0",
45
45
  "update-notifier": "7.3.1",
46
46
  "xml2js": "^0.6.2"
47
47
  },
@@ -137,7 +137,7 @@
137
137
  "typescript": "^6.0.2"
138
138
  },
139
139
  "engines": {
140
- "node": "^20.19.0 || ^22.12.0 || >=23.0.0",
140
+ "node": ">=22.19.0",
141
141
  "npm": ">=8"
142
142
  },
143
143
  "optionalDependencies": {
@@ -11418,9 +11418,9 @@
11418
11418
  "license": "MIT"
11419
11419
  },
11420
11420
  "node_modules/js-yaml": {
11421
- "version": "5.0.0",
11422
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.0.0.tgz",
11423
- "integrity": "sha512-GSvaPUbk1U+FMZ7rJzF+F8e5YVtu7KnD40et/5rBXXRBv2jCO9L3qCewvIDDdudC0QycTFlf6EAA+h3kxBsuUw==",
11421
+ "version": "5.1.0",
11422
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.1.0.tgz",
11423
+ "integrity": "sha512-s8VA5jkR8f22S3NAXmhKPFqGUduqZGlsufabVOgN14iTdw/RXcym7bKkbwjxLK9Yw2lEvvmJjFp119+KPeo8Kg==",
11424
11424
  "funding": [
11425
11425
  {
11426
11426
  "type": "github",
@@ -14624,12 +14624,12 @@
14624
14624
  }
14625
14625
  },
14626
14626
  "node_modules/undici": {
14627
- "version": "7.25.0",
14628
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz",
14629
- "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==",
14627
+ "version": "8.5.0",
14628
+ "resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz",
14629
+ "integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==",
14630
14630
  "license": "MIT",
14631
14631
  "engines": {
14632
- "node": ">=20.18.1"
14632
+ "node": ">=22.19.0"
14633
14633
  }
14634
14634
  },
14635
14635
  "node_modules/undici-types": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip",
3
- "version": "4.0.7",
3
+ "version": "4.0.9",
4
4
  "description": "The VIP Javascript library & CLI",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -107,7 +107,7 @@
107
107
  ],
108
108
  "license": "MIT",
109
109
  "engines": {
110
- "node": "^20.19.0 || ^22.12.0 || >=23.0.0",
110
+ "node": ">=22.19.0",
111
111
  "npm": ">=8"
112
112
  },
113
113
  "bugs": {
@@ -172,7 +172,7 @@
172
172
  "socks-proxy-agent": "^10.0.0",
173
173
  "ssh2": "1.17.0",
174
174
  "tar": "^7.4.0",
175
- "undici": "^7.0.0",
175
+ "undici": "^8.5.0",
176
176
  "update-notifier": "7.3.1",
177
177
  "xml2js": "^0.6.2"
178
178
  },