@cloudant/couchbackup 2.11.11 → 2.11.12-392
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/includes/liner.js +36 -14
- package/includes/parser.js +1 -4
- package/includes/restore.js +1 -1
- package/includes/transforms.js +39 -27
- package/package.json +8 -8
package/includes/liner.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright © 2017,
|
|
1
|
+
// Copyright © 2017, 2025 IBM Corp. All rights reserved.
|
|
2
2
|
//
|
|
3
3
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
// you may not use this file except in compliance with the License.
|
|
@@ -13,9 +13,8 @@
|
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
|
|
15
15
|
const { createInterface } = require('node:readline');
|
|
16
|
-
const { PassThrough,
|
|
16
|
+
const { Duplex, PassThrough, Transform } = require('node:stream');
|
|
17
17
|
const debug = require('debug');
|
|
18
|
-
|
|
19
18
|
/**
|
|
20
19
|
* A Duplex stream that converts the input stream to a stream
|
|
21
20
|
* of line objects using the built-in readline interface.
|
|
@@ -29,19 +28,17 @@ const debug = require('debug');
|
|
|
29
28
|
*/
|
|
30
29
|
class Liner extends Duplex {
|
|
31
30
|
// Configure logging
|
|
32
|
-
log = debug(
|
|
31
|
+
log = debug('couchbackup:liner');
|
|
33
32
|
// Flag for whether the readline interface is running
|
|
34
33
|
isRunning = true;
|
|
34
|
+
// Flag for whether the readline interface is closed
|
|
35
|
+
isClosed = false;
|
|
35
36
|
// Line number state
|
|
36
37
|
lineNumber = 0;
|
|
37
38
|
// Buffer of processed lines
|
|
38
39
|
lines = [];
|
|
39
|
-
// Stream of bytes that will be processed to lines.
|
|
40
|
-
inStream = new PassThrough({ objectMode: false })
|
|
41
|
-
// if there is an error destroy this Duplex with it
|
|
42
|
-
.on('error', e => this.destroy(e));
|
|
43
40
|
|
|
44
|
-
constructor() {
|
|
41
|
+
constructor(sanitize = false) {
|
|
45
42
|
// Configuration of this Duplex:
|
|
46
43
|
// objectMode: false on the writable input (file chunks), true on the readable output (line objects)
|
|
47
44
|
// The readableHighWaterMark controls the number of lines buffered after this implementation calls
|
|
@@ -49,6 +46,25 @@ class Liner extends Duplex {
|
|
|
49
46
|
// there is additional buffering downstream and file processing is faster than the network ops
|
|
50
47
|
// we don't bottleneck here even without a large buffer.
|
|
51
48
|
super({ readableObjectMode: true, readableHighWaterMark: 0, writableObjectMode: false });
|
|
49
|
+
// Set up the stream of bytes that will be processed to lines.
|
|
50
|
+
if (sanitize) {
|
|
51
|
+
// Handle unescaped unicode "newlines" by escaping them before passing to readline
|
|
52
|
+
this.inStream = new Transform({
|
|
53
|
+
objectMode: false,
|
|
54
|
+
transform(chunk, encoding, callback) {
|
|
55
|
+
try {
|
|
56
|
+
this.push(chunk.toString('utf-8').replaceAll('\u2028', '\\u2028').replaceAll('\u2029', '\\u2029'), 'utf-8');
|
|
57
|
+
callback();
|
|
58
|
+
} catch (e) {
|
|
59
|
+
callback(e);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
this.inStream = new PassThrough({ objectMode: false });
|
|
65
|
+
}
|
|
66
|
+
// if there is an error destroy this Duplex with it
|
|
67
|
+
this.inStream.on('error', e => this.destroy(e));
|
|
52
68
|
// Built-in readline interface over the inStream
|
|
53
69
|
this.readlineInterface = createInterface({
|
|
54
70
|
input: this.inStream, // the writable side of Liner, passed through
|
|
@@ -60,7 +76,8 @@ class Liner extends Duplex {
|
|
|
60
76
|
const bufferedLines = this.lines.push(this.wrapLine(line));
|
|
61
77
|
this.log(`Liner processed line ${this.lineNumber}. Buffered lines available: ${bufferedLines}.`);
|
|
62
78
|
this.pushAvailable();
|
|
63
|
-
}).
|
|
79
|
+
}).once('close', () => {
|
|
80
|
+
this.isClosed = true;
|
|
64
81
|
this.log('Liner readline interface closed.');
|
|
65
82
|
// Push null onto our lines buffer to signal EOF to downstream consumers.
|
|
66
83
|
this.lines.push(null);
|
|
@@ -87,13 +104,16 @@ class Liner extends Duplex {
|
|
|
87
104
|
// Check readline is running flag and whether there is content to push.
|
|
88
105
|
while (this.isRunning && this.lines.length > 0) {
|
|
89
106
|
if (!this.push(this.lines.shift())) {
|
|
107
|
+
this.log(`Back-pressure from push. Buffered lines available: ${this.lines.length}.`);
|
|
90
108
|
// Push returned false, this indicates downstream back-pressure.
|
|
91
109
|
// Pause the readline interface to stop pushing more lines downstream.
|
|
92
110
|
// Resumption is triggered by downstream calling _read which happens
|
|
93
111
|
// when it is ready for more data.
|
|
94
|
-
this.log(`Liner pausing after back-pressure from push. Buffered lines available: ${this.lines.length}.`);
|
|
95
112
|
this.isRunning = false;
|
|
96
|
-
this.
|
|
113
|
+
if (!this.isClosed) {
|
|
114
|
+
this.log('Liner pausing.');
|
|
115
|
+
this.readlineInterface.pause();
|
|
116
|
+
}
|
|
97
117
|
break;
|
|
98
118
|
} else {
|
|
99
119
|
this.log(`Liner pushed. Buffered lines available: ${this.lines.length}.`);
|
|
@@ -114,9 +134,11 @@ class Liner extends Duplex {
|
|
|
114
134
|
// is called to ensure that pushes are able to happen (and thereby trigger)
|
|
115
135
|
// subsequent reads.
|
|
116
136
|
if (!this.isRunning) {
|
|
117
|
-
this.log('Liner resuming after read.');
|
|
118
137
|
this.isRunning = true;
|
|
119
|
-
this.
|
|
138
|
+
if (!this.isClosed) {
|
|
139
|
+
this.log('Liner resuming after read.');
|
|
140
|
+
this.readlineInterface.resume();
|
|
141
|
+
}
|
|
120
142
|
}
|
|
121
143
|
this.pushAvailable();
|
|
122
144
|
}
|
package/includes/parser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright © 2017,
|
|
1
|
+
// Copyright © 2017, 2025 IBM Corp. All rights reserved.
|
|
2
2
|
//
|
|
3
3
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
// you may not use this file except in compliance with the License.
|
|
@@ -62,7 +62,6 @@ function parseBackupArgs() {
|
|
|
62
62
|
Number)
|
|
63
63
|
.option('-u, --url <url>',
|
|
64
64
|
cliutils.getUsage('URL of the CouchDB/Cloudant server', defaults.url))
|
|
65
|
-
.allowExcessArguments()
|
|
66
65
|
.parse(process.argv);
|
|
67
66
|
|
|
68
67
|
// Remove defaults that don't apply when using shallow mode
|
|
@@ -81,7 +80,6 @@ function parseBackupArgs() {
|
|
|
81
80
|
// We have to do this check here for the CLI case because of the default.
|
|
82
81
|
error.terminationCallback(new error.BackupError('NoLogFileName', 'To resume a backup, a log file must be specified'));
|
|
83
82
|
}
|
|
84
|
-
|
|
85
83
|
return opts;
|
|
86
84
|
}
|
|
87
85
|
|
|
@@ -118,7 +116,6 @@ function parseRestoreArgs() {
|
|
|
118
116
|
Number)
|
|
119
117
|
.option('-u, --url <url>',
|
|
120
118
|
cliutils.getUsage('URL of the CouchDB/Cloudant server', defaults.url))
|
|
121
|
-
.allowExcessArguments()
|
|
122
119
|
.parse(process.argv);
|
|
123
120
|
|
|
124
121
|
// Apply the options in order so that the CLI overrides env vars and env variables
|
package/includes/restore.js
CHANGED
|
@@ -50,7 +50,7 @@ module.exports = function(dbClient, options, readstream, ee) {
|
|
|
50
50
|
|
|
51
51
|
const batchPreparationStreams = [
|
|
52
52
|
readstream, // the backup file
|
|
53
|
-
new Liner(), // line by line
|
|
53
|
+
new Liner(true), // line by line (for Node.js 24 compatibility santize unicode line separators)
|
|
54
54
|
new MappingStream(restore.backupLineToDocsArray), // convert line to a docs array
|
|
55
55
|
new BatchingStream(options.bufferSize, true), // make new arrays of the correct buffer size
|
|
56
56
|
new MappingStream(restore.docsToRestoreBatch) // make a restore batch
|
package/includes/transforms.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright © 2023,
|
|
1
|
+
// Copyright © 2023, 2025 IBM Corp. All rights reserved.
|
|
2
2
|
//
|
|
3
3
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
// you may not use this file except in compliance with the License.
|
|
@@ -187,45 +187,57 @@ class DelegateWritable extends Writable {
|
|
|
187
187
|
|
|
188
188
|
_write(chunk, encoding, callback) {
|
|
189
189
|
const toWrite = (this.chunkMapFn) ? this.chunkMapFn(chunk) : chunk;
|
|
190
|
-
this.targetWritable.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.postWriteFn
|
|
190
|
+
if (!this.targetWritable.destroyed) {
|
|
191
|
+
this.targetWritable.write(toWrite, encoding, (err) => {
|
|
192
|
+
if (!err) {
|
|
193
|
+
this.log('completed target chunk write');
|
|
194
|
+
if (this.postWriteFn) {
|
|
195
|
+
this.postWriteFn(chunk);
|
|
196
|
+
}
|
|
195
197
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
198
|
+
callback(err);
|
|
199
|
+
});
|
|
200
|
+
} else {
|
|
201
|
+
// Avoid write after destroy errors
|
|
202
|
+
this.log('supressing write after destroy error');
|
|
203
|
+
callback();
|
|
204
|
+
}
|
|
199
205
|
}
|
|
200
206
|
|
|
201
207
|
_final(callback) {
|
|
202
208
|
this.log('Finalizing');
|
|
203
209
|
const lastChunk = (this.lastChunkFn && this.lastChunkFn()) || null;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
210
|
+
if (!this.targetWritable.destroyed) {
|
|
211
|
+
// We can't 'end' stdout, so use a final write instead for that case
|
|
212
|
+
if (this.targetWritable === process.stdout) {
|
|
213
|
+
// we can't 'write' null, so don't do anything if there is no last chunk
|
|
214
|
+
if (lastChunk) {
|
|
215
|
+
this.targetWritable.write(lastChunk, 'utf-8', (err) => {
|
|
216
|
+
if (!err) {
|
|
217
|
+
this.log('wrote last chunk to stdout');
|
|
218
|
+
} else {
|
|
219
|
+
this.log('error writing last chunk to stdout');
|
|
220
|
+
}
|
|
221
|
+
callback(err);
|
|
222
|
+
});
|
|
223
|
+
} else {
|
|
224
|
+
this.log('no last chunk to write to stdout');
|
|
225
|
+
callback();
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
this.targetWritable.end(lastChunk, 'utf-8', (err) => {
|
|
209
229
|
if (!err) {
|
|
210
|
-
this.log('wrote last chunk
|
|
230
|
+
this.log('wrote last chunk and ended target writable');
|
|
211
231
|
} else {
|
|
212
|
-
this.log('error
|
|
232
|
+
this.log('error ending target writable');
|
|
213
233
|
}
|
|
214
234
|
callback(err);
|
|
215
235
|
});
|
|
216
|
-
} else {
|
|
217
|
-
this.log('no last chunk to write to stdout');
|
|
218
|
-
callback();
|
|
219
236
|
}
|
|
220
237
|
} else {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
} else {
|
|
225
|
-
this.log('error ending target writable');
|
|
226
|
-
}
|
|
227
|
-
callback(err);
|
|
228
|
-
});
|
|
238
|
+
// Avoid write after destroy errors
|
|
239
|
+
this.log('supressing write after destroy error');
|
|
240
|
+
callback();
|
|
229
241
|
}
|
|
230
242
|
}
|
|
231
243
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudant/couchbackup",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.12-392",
|
|
4
4
|
"description": "CouchBackup - command-line backup utility for Cloudant/CouchDB",
|
|
5
5
|
"homepage": "https://github.com/IBM/couchbackup",
|
|
6
6
|
"repository": {
|
|
@@ -20,16 +20,16 @@
|
|
|
20
20
|
},
|
|
21
21
|
"license": "Apache-2.0",
|
|
22
22
|
"engines": {
|
|
23
|
-
"node": "^20 || ^22"
|
|
23
|
+
"node": "^20 || ^22 || ^24"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@ibm-cloud/cloudant": "0.12.
|
|
27
|
-
"commander": "14.0.
|
|
26
|
+
"@ibm-cloud/cloudant": "0.12.13",
|
|
27
|
+
"commander": "14.0.2",
|
|
28
28
|
"debug": "4.4.3"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"ibm-cloud-sdk-core": "^5.4.
|
|
32
|
-
"axios": "^1.
|
|
31
|
+
"ibm-cloud-sdk-core": "^5.4.5",
|
|
32
|
+
"axios": "^1.13.2"
|
|
33
33
|
},
|
|
34
34
|
"main": "app.js",
|
|
35
35
|
"bin": {
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
"couchrestore": "bin/couchrestore.bin.js"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"eslint": "9.
|
|
40
|
+
"eslint": "9.39.1",
|
|
41
41
|
"eslint-plugin-header": "3.1.1",
|
|
42
42
|
"eslint-plugin-import": "2.32.0",
|
|
43
43
|
"http-proxy": "1.18.1",
|
|
44
|
-
"mocha": "11.7.
|
|
44
|
+
"mocha": "11.7.5",
|
|
45
45
|
"neostandard": "0.12.2",
|
|
46
46
|
"nock": "13.5.6",
|
|
47
47
|
"tail": "2.2.6",
|