Skip to content

Commit 8a50f3c

Browse files
authored
Merge branch 'nodejs:main' into fix/constrain-maybstackbuffer-tostring-char-traits
2 parents 2951d47 + 726b220 commit 8a50f3c

File tree

14 files changed

+669
-20
lines changed

14 files changed

+669
-20
lines changed

doc/api/fs.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5059,6 +5059,9 @@ The `atime` and `mtime` arguments follow these rules:
50595059
<!-- YAML
50605060
added: v0.5.10
50615061
changes:
5062+
- version: REPLACEME
5063+
pr-url: https://github.com/nodejs/node/pull/61870
5064+
description: Added `throwIfNoEntry` option.
50625065
- version: v19.1.0
50635066
pr-url: https://github.com/nodejs/node/pull/45098
50645067
description: Added recursive support for Linux, AIX and IBMi.
@@ -5087,6 +5090,8 @@ changes:
50875090
* `encoding` {string} Specifies the character encoding to be used for the
50885091
filename passed to the listener. **Default:** `'utf8'`.
50895092
* `signal` {AbortSignal} allows closing the watcher with an AbortSignal.
5093+
* `throwIfNoEntry` {boolean} Indicates whether an exception should be thrown when the
5094+
path does not exist. **Default:** `true`.
50905095
* `ignore` {string|RegExp|Function|Array} Pattern(s) to ignore. Strings are
50915096
glob patterns (using [`minimatch`][]), RegExp patterns are tested against
50925097
the filename, and functions receive the filename and return `true` to

doc/api/sqlite.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,89 @@ Opens the database specified in the `path` argument of the `DatabaseSync`
531531
constructor. This method should only be used when the database is not opened via
532532
the constructor. An exception is thrown if the database is already open.
533533

534+
### `database.serialize([dbName])`
535+
536+
<!-- YAML
537+
added: REPLACEME
538+
-->
539+
540+
* `dbName` {string} Name of the database to serialize. This can be `'main'`
541+
(the default primary database) or any other database that has been added with
542+
[`ATTACH DATABASE`][]. **Default:** `'main'`.
543+
* Returns: {Uint8Array} A binary representation of the database.
544+
545+
Serializes the database into a binary representation, returned as a
546+
`Uint8Array`. This is useful for saving, cloning, or transferring an in-memory
547+
database. This method is a wrapper around [`sqlite3_serialize()`][].
548+
549+
```mjs
550+
import { DatabaseSync } from 'node:sqlite';
551+
552+
const db = new DatabaseSync(':memory:');
553+
db.exec('CREATE TABLE t(key INTEGER PRIMARY KEY, value TEXT)');
554+
db.exec("INSERT INTO t VALUES (1, 'hello')");
555+
const buffer = db.serialize();
556+
console.log(buffer.length); // Prints the byte length of the database
557+
```
558+
559+
```cjs
560+
const { DatabaseSync } = require('node:sqlite');
561+
562+
const db = new DatabaseSync(':memory:');
563+
db.exec('CREATE TABLE t(key INTEGER PRIMARY KEY, value TEXT)');
564+
db.exec("INSERT INTO t VALUES (1, 'hello')");
565+
const buffer = db.serialize();
566+
console.log(buffer.length); // Prints the byte length of the database
567+
```
568+
569+
### `database.deserialize(buffer[, options])`
570+
571+
<!-- YAML
572+
added: REPLACEME
573+
-->
574+
575+
* `buffer` {Uint8Array} A binary representation of a database, such as the
576+
output of [`database.serialize()`][].
577+
* `options` {Object} Optional configuration for the deserialization.
578+
* `dbName` {string} Name of the database to deserialize into.
579+
**Default:** `'main'`.
580+
581+
Loads a serialized database into this connection, replacing the current
582+
database. The deserialized database is writable. Existing prepared statements
583+
are finalized before deserialization is attempted, even if the operation
584+
subsequently fails. This method is a wrapper around
585+
[`sqlite3_deserialize()`][].
586+
587+
```mjs
588+
import { DatabaseSync } from 'node:sqlite';
589+
590+
const original = new DatabaseSync(':memory:');
591+
original.exec('CREATE TABLE t(key INTEGER PRIMARY KEY, value TEXT)');
592+
original.exec("INSERT INTO t VALUES (1, 'hello')");
593+
const buffer = original.serialize();
594+
original.close();
595+
596+
const clone = new DatabaseSync(':memory:');
597+
clone.deserialize(buffer);
598+
console.log(clone.prepare('SELECT value FROM t').get());
599+
// Prints: { value: 'hello' }
600+
```
601+
602+
```cjs
603+
const { DatabaseSync } = require('node:sqlite');
604+
605+
const original = new DatabaseSync(':memory:');
606+
original.exec('CREATE TABLE t(key INTEGER PRIMARY KEY, value TEXT)');
607+
original.exec("INSERT INTO t VALUES (1, 'hello')");
608+
const buffer = original.serialize();
609+
original.close();
610+
611+
const clone = new DatabaseSync(':memory:');
612+
clone.deserialize(buffer);
613+
console.log(clone.prepare('SELECT value FROM t').get());
614+
// Prints: { value: 'hello' }
615+
```
616+
534617
### `database.prepare(sql[, options])`
535618

536619
<!-- YAML
@@ -1545,6 +1628,7 @@ callback function to indicate what type of operation is being authorized.
15451628
[`SQLTagStore`]: #class-sqltagstore
15461629
[`database.applyChangeset()`]: #databaseapplychangesetchangeset-options
15471630
[`database.createTagStore()`]: #databasecreatetagstoremaxsize
1631+
[`database.serialize()`]: #databaseserializedbname
15481632
[`database.setAuthorizer()`]: #databasesetauthorizercallback
15491633
[`sqlite3_backup_finish()`]: https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupfinish
15501634
[`sqlite3_backup_init()`]: https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupinit
@@ -1559,12 +1643,14 @@ callback function to indicate what type of operation is being authorized.
15591643
[`sqlite3_create_function_v2()`]: https://www.sqlite.org/c3ref/create_function.html
15601644
[`sqlite3_create_window_function()`]: https://www.sqlite.org/c3ref/create_function.html
15611645
[`sqlite3_db_filename()`]: https://sqlite.org/c3ref/db_filename.html
1646+
[`sqlite3_deserialize()`]: https://sqlite.org/c3ref/deserialize.html
15621647
[`sqlite3_exec()`]: https://www.sqlite.org/c3ref/exec.html
15631648
[`sqlite3_expanded_sql()`]: https://www.sqlite.org/c3ref/expanded_sql.html
15641649
[`sqlite3_get_autocommit()`]: https://sqlite.org/c3ref/get_autocommit.html
15651650
[`sqlite3_last_insert_rowid()`]: https://www.sqlite.org/c3ref/last_insert_rowid.html
15661651
[`sqlite3_load_extension()`]: https://www.sqlite.org/c3ref/load_extension.html
15671652
[`sqlite3_prepare_v2()`]: https://www.sqlite.org/c3ref/prepare.html
1653+
[`sqlite3_serialize()`]: https://sqlite.org/c3ref/serialize.html
15681654
[`sqlite3_set_authorizer()`]: https://sqlite.org/c3ref/set_authorizer.html
15691655
[`sqlite3_sql()`]: https://www.sqlite.org/c3ref/expanded_sql.html
15701656
[`sqlite3changeset_apply()`]: https://www.sqlite.org/session/sqlite3changeset_apply.html

lib/fs.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2496,6 +2496,7 @@ function appendFileSync(path, data, options) {
24962496
* recursive?: boolean;
24972497
* encoding?: string;
24982498
* signal?: AbortSignal;
2499+
* throwIfNoEntry?: boolean;
24992500
* }} [options]
25002501
* @param {(
25012502
* eventType?: string,
@@ -2514,6 +2515,7 @@ function watch(filename, options, listener) {
25142515

25152516
if (options.persistent === undefined) options.persistent = true;
25162517
if (options.recursive === undefined) options.recursive = false;
2518+
if (options.throwIfNoEntry === undefined) options.throwIfNoEntry = true;
25172519

25182520
let watcher;
25192521
const watchers = require('internal/fs/watchers');
@@ -2531,7 +2533,8 @@ function watch(filename, options, listener) {
25312533
options.persistent,
25322534
options.recursive,
25332535
options.encoding,
2534-
options.ignore);
2536+
options.ignore,
2537+
options.throwIfNoEntry);
25352538
}
25362539

25372540
if (listener) {

lib/internal/fs/recursive_watch.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class FSWatcher extends EventEmitter {
5252
assert(typeof options === 'object');
5353

5454
const { persistent, recursive, signal, encoding, ignore } = options;
55+
let { throwIfNoEntry } = options;
5556

5657
// TODO(anonrig): Add non-recursive support to non-native-watcher for IBMi & AIX support.
5758
if (recursive != null) {
@@ -66,6 +67,12 @@ class FSWatcher extends EventEmitter {
6667
validateAbortSignal(signal, 'options.signal');
6768
}
6869

70+
if (throwIfNoEntry != null) {
71+
validateBoolean(throwIfNoEntry, 'options.throwIfNoEntry');
72+
} else {
73+
throwIfNoEntry = true;
74+
}
75+
6976
if (encoding != null) {
7077
// This is required since on macOS and Windows it throws ERR_INVALID_ARG_VALUE
7178
if (typeof encoding !== 'string') {
@@ -76,7 +83,7 @@ class FSWatcher extends EventEmitter {
7683
validateIgnoreOption(ignore, 'options.ignore');
7784
this.#ignoreMatcher = createIgnoreMatcher(ignore);
7885

79-
this.#options = { persistent, recursive, signal, encoding };
86+
this.#options = { persistent, recursive, signal, encoding, throwIfNoEntry };
8087
}
8188

8289
close() {
@@ -222,7 +229,7 @@ class FSWatcher extends EventEmitter {
222229
this.#watchFolder(filename);
223230
}
224231
} catch (error) {
225-
if (error.code === 'ENOENT') {
232+
if (!this.#options.throwIfNoEntry && error.code === 'ENOENT') {
226233
error.filename = filename;
227234
throw error;
228235
}

lib/internal/fs/watchers.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const {
3535
} = internalBinding('fs');
3636

3737
const { FSEvent } = internalBinding('fs_event_wrap');
38-
const { UV_ENOSPC } = internalBinding('uv');
38+
const { UV_ENOSPC, UV_ENOENT } = internalBinding('uv');
3939
const { EventEmitter } = require('events');
4040

4141
const {
@@ -293,7 +293,8 @@ FSWatcher.prototype[kFSWatchStart] = function(filename,
293293
persistent,
294294
recursive,
295295
encoding,
296-
ignore) {
296+
ignore,
297+
throwIfNoEntry = true) {
297298
if (this._handle === null) { // closed
298299
return;
299300
}
@@ -313,6 +314,10 @@ FSWatcher.prototype[kFSWatchStart] = function(filename,
313314
recursive,
314315
encoding);
315316
if (err) {
317+
if (!throwIfNoEntry && err === UV_ENOENT) {
318+
return;
319+
}
320+
316321
const error = new UVException({
317322
errno: err,
318323
syscall: 'watch',

lib/internal/main/watch_mode.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,8 @@ markBootstrapComplete();
3434

3535
const kKillSignal = convertToValidSignal(getOptionValue('--watch-kill-signal'));
3636
const kShouldFilterModules = getOptionValue('--watch-path').length === 0;
37-
const kEnvFiles = [
38-
...getOptionValue('--env-file'),
39-
...getOptionValue('--env-file-if-exists'),
40-
];
37+
const kEnvFiles = getOptionValue('--env-file');
38+
const kOptionalEnvFiles = getOptionValue('--env-file-if-exists');
4139
const kWatchedPaths = ArrayPrototypeMap(getOptionValue('--watch-path'), (path) => resolve(path));
4240
const kPreserveOutput = getOptionValue('--watch-preserve-output');
4341
const kCommand = ArrayPrototypeSlice(process.argv, 1);
@@ -105,6 +103,10 @@ function start() {
105103
if (kEnvFiles.length > 0) {
106104
ArrayPrototypeForEach(kEnvFiles, (file) => watcher.filterFile(resolve(file)));
107105
}
106+
if (kOptionalEnvFiles.length > 0) {
107+
ArrayPrototypeForEach(kOptionalEnvFiles,
108+
(file) => watcher.filterFile(resolve(file), undefined, { allowMissing: true }));
109+
}
108110
child.once('exit', (code) => {
109111
exited = true;
110112
const waitingForChanges = 'Waiting for file changes before restarting...';
@@ -160,6 +162,7 @@ async function stop(child) {
160162
}
161163

162164
let restarting = false;
165+
163166
async function restart(child) {
164167
if (restarting) return;
165168
restarting = true;
@@ -198,5 +201,6 @@ function signalHandler(signal) {
198201
process.exit(exitCode ?? kNoFailure);
199202
};
200203
}
204+
201205
process.on('SIGTERM', signalHandler('SIGTERM'));
202206
process.on('SIGINT', signalHandler('SIGINT'));

lib/internal/watch_mode/files_watcher.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,13 @@ class FilesWatcher extends EventEmitter {
110110
return [...this.#watchers.keys()];
111111
}
112112

113-
watchPath(path, recursive = true) {
113+
watchPath(path, recursive = true, options = kEmptyObject) {
114114
if (this.#isPathWatched(path)) {
115115
return;
116116
}
117-
const watcher = watch(path, { recursive, signal: this.#signal });
117+
const { allowMissing = false } = options;
118+
119+
const watcher = watch(path, { recursive, signal: this.#signal, throwIfNoEntry: !allowMissing });
118120
watcher.on('change', (eventType, fileName) => {
119121
// `fileName` can be `null` if it cannot be determined. See
120122
// https://github.com/nodejs/node/pull/49891#issuecomment-1744673430.
@@ -126,14 +128,14 @@ class FilesWatcher extends EventEmitter {
126128
}
127129
}
128130

129-
filterFile(file, owner) {
131+
filterFile(file, owner, options = kEmptyObject) {
130132
if (!file) return;
131133
if (supportsRecursiveWatching) {
132-
this.watchPath(dirname(file));
134+
this.watchPath(dirname(file), true, options);
133135
} else {
134136
// Having multiple FSWatcher's seems to be slower
135137
// than a single recursive FSWatcher
136-
this.watchPath(file, false);
138+
this.watchPath(file, false, options);
137139
}
138140
this.#filteredFiles.add(file);
139141
if (owner) {

lib/internal/webstreams/adapters.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,8 @@ function newStreamWritableFromWritableStream(writableStream, options = kEmptyObj
314314

315315
writev(chunks, callback) {
316316
function done(error) {
317-
error = error.filter((e) => e);
318317
try {
319-
callback(error.length === 0 ? undefined : error);
318+
callback(error);
320319
} catch (error) {
321320
// In a next tick because this is happening within
322321
// a promise context, and if there are any errors
@@ -334,7 +333,7 @@ function newStreamWritableFromWritableStream(writableStream, options = kEmptyObj
334333
SafePromiseAll(
335334
chunks,
336335
(data) => writer.write(data.chunk)),
337-
done,
336+
() => done(),
338337
done);
339338
},
340339
done);
@@ -787,9 +786,8 @@ function newStreamDuplexFromReadableWritablePair(pair = kEmptyObject, options =
787786

788787
writev(chunks, callback) {
789788
function done(error) {
790-
error = error.filter((e) => e);
791789
try {
792-
callback(error.length === 0 ? undefined : error);
790+
callback(error);
793791
} catch (error) {
794792
// In a next tick because this is happening within
795793
// a promise context, and if there are any errors
@@ -807,7 +805,7 @@ function newStreamDuplexFromReadableWritablePair(pair = kEmptyObject, options =
807805
SafePromiseAll(
808806
chunks,
809807
(data) => writer.write(data.chunk)),
810-
done,
808+
() => done(),
811809
done);
812810
},
813811
done);

0 commit comments

Comments
 (0)