src: unflag --experimental-webstorage by default

PR-URL: https://github.com/nodejs/node/pull/57666
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Edy Silva <edigleyssonsilva@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Daniel M Brasil 2025-09-25 08:59:58 -03:00 committed by GitHub
parent eb1e671d31
commit 3312e4e946
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 85 additions and 81 deletions

View File

@ -12,7 +12,7 @@ function nextLocalStorage() {
} }
const options = { const options = {
flags: ['--experimental-webstorage', `--localstorage-file=${nextLocalStorage()}`], flags: [`--localstorage-file=${nextLocalStorage()}`],
}; };
const bench = common.createBenchmark(main, { const bench = common.createBenchmark(main, {

View File

@ -12,7 +12,7 @@ function nextLocalStorage() {
} }
const options = { const options = {
flags: ['--experimental-webstorage', `--localstorage-file=${nextLocalStorage()}`], flags: [`--localstorage-file=${nextLocalStorage()}`],
}; };
const bench = common.createBenchmark(main, { const bench = common.createBenchmark(main, {

View File

@ -12,7 +12,7 @@ function nextLocalStorage() {
} }
const options = { const options = {
flags: ['--experimental-webstorage', `--localstorage-file=${nextLocalStorage()}`], flags: [`--localstorage-file=${nextLocalStorage()}`],
}; };
const bench = common.createBenchmark(main, { const bench = common.createBenchmark(main, {

View File

@ -1305,14 +1305,6 @@ changes:
Enable experimental WebAssembly System Interface (WASI) support. Enable experimental WebAssembly System Interface (WASI) support.
### `--experimental-webstorage`
<!-- YAML
added: v22.4.0
-->
Enable experimental [`Web Storage`][] support.
### `--experimental-worker-inspection` ### `--experimental-worker-inspection`
<!-- YAML <!-- YAML
@ -1753,8 +1745,8 @@ added: v22.4.0
The file used to store `localStorage` data. If the file does not exist, it is The file used to store `localStorage` data. If the file does not exist, it is
created the first time `localStorage` is accessed. The same file may be shared created the first time `localStorage` is accessed. The same file may be shared
between multiple Node.js processes concurrently. This flag is a no-op unless between multiple Node.js processes concurrently. This flag is a no-op if
Node.js is started with the `--experimental-webstorage` flag. Node.js is started with the `--no-webstorage` (or `--no-experimental-webstorage`) flag.
### `--max-http-header-size=size` ### `--max-http-header-size=size`
@ -1979,6 +1971,14 @@ added: v6.0.0
Silence all process warnings (including deprecations). Silence all process warnings (including deprecations).
### `--no-webstorage`
<!-- YAML
added: REPLACEME
-->
Disable [`Web Storage`][] support.
### `--node-memory-debug` ### `--node-memory-debug`
<!-- YAML <!-- YAML
@ -3503,7 +3503,6 @@ one is included in the list below.
* `--experimental-transform-types` * `--experimental-transform-types`
* `--experimental-vm-modules` * `--experimental-vm-modules`
* `--experimental-wasi-unstable-preview1` * `--experimental-wasi-unstable-preview1`
* `--experimental-webstorage`
* `--force-context-aware` * `--force-context-aware`
* `--force-fips` * `--force-fips`
* `--force-node-api-uncaught-exceptions-policy` * `--force-node-api-uncaught-exceptions-policy`
@ -3537,11 +3536,13 @@ one is included in the list below.
* `--no-experimental-sqlite` * `--no-experimental-sqlite`
* `--no-experimental-strip-types` * `--no-experimental-strip-types`
* `--no-experimental-websocket` * `--no-experimental-websocket`
* `--no-experimental-webstorage`
* `--no-extra-info-on-fatal-exception` * `--no-extra-info-on-fatal-exception`
* `--no-force-async-hooks-checks` * `--no-force-async-hooks-checks`
* `--no-global-search-paths` * `--no-global-search-paths`
* `--no-network-family-autoselection` * `--no-network-family-autoselection`
* `--no-warnings` * `--no-warnings`
* `--no-webstorage`
* `--node-memory-debug` * `--node-memory-debug`
* `--openssl-config` * `--openssl-config`
* `--openssl-legacy-provider` * `--openssl-legacy-provider`

View File

@ -640,13 +640,11 @@ A browser-compatible implementation of {Headers}.
added: v22.4.0 added: v22.4.0
--> -->
> Stability: 1.0 - Early development.
A browser-compatible implementation of [`localStorage`][]. Data is stored A browser-compatible implementation of [`localStorage`][]. Data is stored
unencrypted in the file specified by the [`--localstorage-file`][] CLI flag. unencrypted in the file specified by the [`--localstorage-file`][] CLI flag.
The maximum amount of data that can be stored is 10 MB. The maximum amount of data that can be stored is 10 MB.
Any modification of this data outside of the Web Storage API is not supported. Any modification of this data outside of the Web Storage API is not supported.
Enable this API with the [`--experimental-webstorage`][] CLI flag. Disable this API with the [`--no-webstorage`][] (or its alias `--no-experimental-webstorage`) CLI flag.
`localStorage` data is not stored per user or per request when used in the context `localStorage` data is not stored per user or per request when used in the context
of a server, it is shared across all users and requests. of a server, it is shared across all users and requests.
@ -1108,9 +1106,10 @@ added: v22.4.0
--> -->
> Stability: 1.0 - Early development. Enable this API with the > Stability: 1.0 - Early development. Enable this API with the
> [`--experimental-webstorage`][] CLI flag. > \[`--experimental-webstorage`]\[] CLI flag.
A browser-compatible implementation of {Storage}. A browser-compatible implementation of {Storage}. Disable this API with the
[`--no-webstorage`][] (or its alias `--no-experimental-webstorage`) CLI flag.
## `structuredClone(value[, options])` ## `structuredClone(value[, options])`
@ -1321,10 +1320,10 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][].
[RFC 5646]: https://www.rfc-editor.org/rfc/rfc5646.txt [RFC 5646]: https://www.rfc-editor.org/rfc/rfc5646.txt
[Web Crypto API]: webcrypto.md [Web Crypto API]: webcrypto.md
[`--experimental-eventsource`]: cli.md#--experimental-eventsource [`--experimental-eventsource`]: cli.md#--experimental-eventsource
[`--experimental-webstorage`]: cli.md#--experimental-webstorage
[`--localstorage-file`]: cli.md#--localstorage-filefile [`--localstorage-file`]: cli.md#--localstorage-filefile
[`--no-experimental-global-navigator`]: cli.md#--no-experimental-global-navigator [`--no-experimental-global-navigator`]: cli.md#--no-experimental-global-navigator
[`--no-experimental-websocket`]: cli.md#--no-experimental-websocket [`--no-experimental-websocket`]: cli.md#--no-experimental-websocket
[`--no-webstorage`]: cli.md#--no-webstorage
[`ByteLengthQueuingStrategy`]: webstreams.md#class-bytelengthqueuingstrategy [`ByteLengthQueuingStrategy`]: webstreams.md#class-bytelengthqueuingstrategy
[`CompressionStream`]: webstreams.md#class-compressionstream [`CompressionStream`]: webstreams.md#class-compressionstream
[`CountQueuingStrategy`]: webstreams.md#class-countqueuingstrategy [`CountQueuingStrategy`]: webstreams.md#class-countqueuingstrategy

View File

@ -189,9 +189,6 @@
"experimental-websocket": { "experimental-websocket": {
"type": "boolean" "type": "boolean"
}, },
"experimental-webstorage": {
"type": "boolean"
},
"extra-info-on-fatal-exception": { "extra-info-on-fatal-exception": {
"type": "boolean" "type": "boolean"
}, },
@ -597,6 +594,9 @@
"watch-preserve-output": { "watch-preserve-output": {
"type": "boolean" "type": "boolean"
}, },
"webstorage": {
"type": "boolean"
},
"zero-fill-buffers": { "zero-fill-buffers": {
"type": "boolean" "type": "boolean"
} }

View File

@ -207,8 +207,8 @@ Enable experimental support for the EventSource Web API.
.It Fl -no-experimental-websocket .It Fl -no-experimental-websocket
Disable experimental support for the WebSocket API. Disable experimental support for the WebSocket API.
. .
.It Fl -experimental-webstorage .It Fl -no-webstorage
Enable experimental support for the Web Storage API. Disable webstorage.
. .
.It Fl -no-experimental-repl-await .It Fl -no-experimental-repl-await
Disable top-level await keyword support in REPL. Disable top-level await keyword support in REPL.

View File

@ -365,7 +365,7 @@ function setupQuic() {
function setupWebStorage() { function setupWebStorage() {
if (getEmbedderOptions().noBrowserGlobals || if (getEmbedderOptions().noBrowserGlobals ||
!getOptionValue('--experimental-webstorage')) { !getOptionValue('--webstorage')) {
return; return;
} }

View File

@ -1,16 +1,13 @@
'use strict'; 'use strict';
const { const {
ObjectDefineProperties, ObjectDefineProperties,
Proxy,
} = primordials; } = primordials;
const { ERR_INVALID_ARG_VALUE } = require('internal/errors').codes;
const { getOptionValue } = require('internal/options'); const { getOptionValue } = require('internal/options');
const { emitExperimentalWarning } = require('internal/util');
const { kConstructorKey, Storage } = internalBinding('webstorage'); const { kConstructorKey, Storage } = internalBinding('webstorage');
const { getValidatedPath } = require('internal/fs/utils'); const { getValidatedPath } = require('internal/fs/utils');
const kInMemoryPath = ':memory:'; const kInMemoryPath = ':memory:';
emitExperimentalWarning('Web Storage');
module.exports = { Storage }; module.exports = { Storage };
let lazyLocalStorage; let lazyLocalStorage;
@ -27,12 +24,31 @@ ObjectDefineProperties(module.exports, {
const location = getOptionValue('--localstorage-file'); const location = getOptionValue('--localstorage-file');
if (location === '') { if (location === '') {
throw new ERR_INVALID_ARG_VALUE('--localstorage-file', let warningEmitted = false;
location, const handler = {
'is an invalid localStorage location'); __proto__: null,
} get(target, prop) {
if (!warningEmitted) {
process.emitWarning('`--localstorage-file` was provided without a valid path');
warningEmitted = true;
}
lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); return undefined;
},
set(target, prop, value) {
if (!warningEmitted) {
process.emitWarning('`--localstorage-file` was provided without a valid path');
warningEmitted = true;
}
return false;
},
};
lazyLocalStorage = new Proxy({}, handler);
} else {
lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location));
}
} }
return lazyLocalStorage; return lazyLocalStorage;

View File

@ -559,10 +559,12 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
NoOp{}, NoOp{},
#endif #endif
kAllowedInEnvvar); kAllowedInEnvvar);
AddOption("--experimental-webstorage", AddOption("--webstorage",
"experimental Web Storage API", "Web Storage API",
&EnvironmentOptions::experimental_webstorage, &EnvironmentOptions::webstorage,
kAllowedInEnvvar); kAllowedInEnvvar,
true);
AddAlias("--experimental-webstorage", "--webstorage");
AddOption("--localstorage-file", AddOption("--localstorage-file",
"file used to persist localStorage data", "file used to persist localStorage data",
&EnvironmentOptions::localstorage_file, &EnvironmentOptions::localstorage_file,

View File

@ -127,7 +127,7 @@ class EnvironmentOptions : public Options {
bool experimental_fetch = true; bool experimental_fetch = true;
bool experimental_websocket = true; bool experimental_websocket = true;
bool experimental_sqlite = true; bool experimental_sqlite = true;
bool experimental_webstorage = false; bool webstorage = true;
#ifndef OPENSSL_NO_QUIC #ifndef OPENSSL_NO_QUIC
bool experimental_quic = false; bool experimental_quic = false;
#endif #endif

View File

@ -59,6 +59,8 @@ for (const moduleName of builtinModules) {
'fetch', 'fetch',
'crypto', 'crypto',
'navigator', 'navigator',
'localStorage',
'sessionStorage',
]; ];
assert.deepStrictEqual(new Set(Object.keys(globalThis)), new Set(expected)); assert.deepStrictEqual(new Set(Object.keys(globalThis)), new Set(expected));
expected.forEach((value) => { expected.forEach((value) => {

View File

@ -1,4 +1,5 @@
'use strict'; 'use strict';
const { skipIfSQLiteMissing, spawnPromisified } = require('../common'); const { skipIfSQLiteMissing, spawnPromisified } = require('../common');
skipIfSQLiteMissing(); skipIfSQLiteMissing();
const tmpdir = require('../common/tmpdir'); const tmpdir = require('../common/tmpdir');
@ -14,35 +15,9 @@ function nextLocalStorage() {
return join(tmpdir.path, `${++cnt}.localstorage`); return join(tmpdir.path, `${++cnt}.localstorage`);
} }
test('disabled without --experimental-webstorage', async () => {
for (const api of ['Storage', 'localStorage', 'sessionStorage']) {
const cp = await spawnPromisified(process.execPath, ['-e', api]);
assert.strictEqual(cp.code, 1);
assert.strictEqual(cp.signal, null);
assert.strictEqual(cp.stdout, '');
assert(cp.stderr.includes(`ReferenceError: ${api} is not defined`));
}
});
test('emits a warning when used', async () => {
for (const api of ['Storage', 'localStorage', 'sessionStorage']) {
const cp = await spawnPromisified(process.execPath, [
'--experimental-webstorage',
'--localstorage-file', nextLocalStorage(),
'-e', api,
]);
assert.strictEqual(cp.code, 0);
assert.strictEqual(cp.signal, null);
assert.strictEqual(cp.stdout, '');
assert.match(cp.stderr, /ExperimentalWarning: Web Storage/);
}
});
test('Storage instances cannot be created in userland', async () => { test('Storage instances cannot be created in userland', async () => {
const cp = await spawnPromisified(process.execPath, [ const cp = await spawnPromisified(process.execPath, [
'--experimental-webstorage', '-e', 'new globalThis.Storage()', '-e', 'new globalThis.Storage()',
]); ]);
assert.strictEqual(cp.code, 1); assert.strictEqual(cp.code, 1);
@ -53,33 +28,30 @@ test('Storage instances cannot be created in userland', async () => {
test('sessionStorage is not persisted', async () => { test('sessionStorage is not persisted', async () => {
let cp = await spawnPromisified(process.execPath, [ let cp = await spawnPromisified(process.execPath, [
'--experimental-webstorage', '-pe', 'sessionStorage.foo = "barbaz"', '-pe', 'sessionStorage.foo = "barbaz"',
]); ]);
assert.strictEqual(cp.code, 0); assert.strictEqual(cp.code, 0);
assert.match(cp.stdout, /barbaz/); assert.match(cp.stdout, /barbaz/);
cp = await spawnPromisified(process.execPath, [ cp = await spawnPromisified(process.execPath, [
'--experimental-webstorage', '-pe', 'sessionStorage.foo', '-pe', 'sessionStorage.foo',
]); ]);
assert.strictEqual(cp.code, 0); assert.strictEqual(cp.code, 0);
assert.match(cp.stdout, /undefined/); assert.match(cp.stdout, /undefined/);
assert.strictEqual((await readdir(tmpdir.path)).length, 0); assert.strictEqual((await readdir(tmpdir.path)).length, 0);
}); });
test('localStorage throws without --localstorage-file ', async () => { test('localStorage emits a warning when used without --localstorage-file ', async () => {
const cp = await spawnPromisified(process.execPath, [ const cp = await spawnPromisified(process.execPath, [
'--experimental-webstorage', '-pe', 'localStorage.length',
'-pe', 'localStorage === globalThis.localStorage',
]); ]);
assert.strictEqual(cp.code, 1); assert.strictEqual(cp.code, 0);
assert.strictEqual(cp.signal, null); assert.strictEqual(cp.signal, null);
assert.strictEqual(cp.stdout, ''); assert.match(cp.stderr, /Warning: `--localstorage-file` was provided without a valid path/);
assert.match(cp.stderr, /The argument '--localstorage-file' is an invalid localStorage location/);
}); });
test('localStorage is not persisted if it is unused', async () => { test('localStorage is not persisted if it is unused', async () => {
const cp = await spawnPromisified(process.execPath, [ const cp = await spawnPromisified(process.execPath, [
'--experimental-webstorage',
'--localstorage-file', nextLocalStorage(), '--localstorage-file', nextLocalStorage(),
'-pe', 'localStorage === globalThis.localStorage', '-pe', 'localStorage === globalThis.localStorage',
]); ]);
@ -91,7 +63,6 @@ test('localStorage is not persisted if it is unused', async () => {
test('localStorage is persisted if it is used', async () => { test('localStorage is persisted if it is used', async () => {
const localStorageFile = nextLocalStorage(); const localStorageFile = nextLocalStorage();
let cp = await spawnPromisified(process.execPath, [ let cp = await spawnPromisified(process.execPath, [
'--experimental-webstorage',
'--localstorage-file', localStorageFile, '--localstorage-file', localStorageFile,
'-pe', 'localStorage.foo = "barbaz"', '-pe', 'localStorage.foo = "barbaz"',
]); ]);
@ -102,7 +73,6 @@ test('localStorage is persisted if it is used', async () => {
assert.match(entries[0], /\d+\.localstorage/); assert.match(entries[0], /\d+\.localstorage/);
cp = await spawnPromisified(process.execPath, [ cp = await spawnPromisified(process.execPath, [
'--experimental-webstorage',
'--localstorage-file', localStorageFile, '--localstorage-file', localStorageFile,
'-pe', 'localStorage.foo', '-pe', 'localStorage.foo',
]); ]);
@ -117,7 +87,6 @@ describe('webstorage quota for localStorage and sessionStorage', () => {
test('localStorage can store and retrieve a max of 10 MB quota', async () => { test('localStorage can store and retrieve a max of 10 MB quota', async () => {
const localStorageFile = nextLocalStorage(); const localStorageFile = nextLocalStorage();
const cp = await spawnPromisified(process.execPath, [ const cp = await spawnPromisified(process.execPath, [
'--experimental-webstorage',
'--localstorage-file', localStorageFile, '--localstorage-file', localStorageFile,
// Each character is 2 bytes // Each character is 2 bytes
'-pe', ` '-pe', `
@ -133,7 +102,6 @@ describe('webstorage quota for localStorage and sessionStorage', () => {
test('sessionStorage can store a max of 10 MB quota', async () => { test('sessionStorage can store a max of 10 MB quota', async () => {
const cp = await spawnPromisified(process.execPath, [ const cp = await spawnPromisified(process.execPath, [
'--experimental-webstorage',
// Each character is 2 bytes // Each character is 2 bytes
'-pe', `sessionStorage['a'.repeat(${MAX_STORAGE_SIZE} / 2)] = ''; '-pe', `sessionStorage['a'.repeat(${MAX_STORAGE_SIZE} / 2)] = '';
console.error('filled'); console.error('filled');
@ -145,3 +113,20 @@ describe('webstorage quota for localStorage and sessionStorage', () => {
assert.match(cp.stderr, /QuotaExceededError/); assert.match(cp.stderr, /QuotaExceededError/);
}); });
}); });
test('disabled with --no-webstorage', async () => {
for (const api of ['Storage', 'localStorage', 'sessionStorage']) {
const cp = await spawnPromisified(process.execPath, [
'--no-webstorage',
'--localstorage-file',
'./test/fixtures/localstoragefile-global-test',
'-e',
api,
]);
assert.strictEqual(cp.code, 1);
assert.strictEqual(cp.signal, null);
assert.strictEqual(cp.stdout, '');
assert(cp.stderr.includes(`ReferenceError: ${api} is not defined`));
}
});

View File

@ -9,7 +9,6 @@ const runner = new WPTRunner('webstorage', { concurrency: 1 });
tmpdir.refresh(); tmpdir.refresh();
runner.setFlags([ runner.setFlags([
'--experimental-webstorage',
'--localstorage-file', join(tmpdir.path, 'wpt-tests.localstorage'), '--localstorage-file', join(tmpdir.path, 'wpt-tests.localstorage'),
]); ]);
runner.setInitScript(` runner.setInitScript(`