From c68dd4c84ef852230a4f510efaddc14f5d1482fd Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Fri, 28 Mar 2025 14:30:27 -0300 Subject: [PATCH 01/33] src: unflag --experimental-webstorage by default --- src/node_options.cc | 3 ++- src/node_options.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/node_options.cc b/src/node_options.cc index c98be3257978ca..0d9038ddf3d61a 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -562,7 +562,8 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { AddOption("--experimental-webstorage", "experimental Web Storage API", &EnvironmentOptions::experimental_webstorage, - kAllowedInEnvvar); + kAllowedInEnvvar, + true); AddOption("--localstorage-file", "file used to persist localStorage data", &EnvironmentOptions::localstorage_file, diff --git a/src/node_options.h b/src/node_options.h index 4d6b4103ce6e24..40cbfeb9bc10b3 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -127,7 +127,7 @@ class EnvironmentOptions : public Options { bool experimental_fetch = true; bool experimental_websocket = true; bool experimental_sqlite = true; - bool experimental_webstorage = false; + bool experimental_webstorage = true; #ifndef OPENSSL_NO_QUIC bool experimental_quic = false; #endif From c26b9a52b50f32d9a1f99ac60ea051a0419e1dcb Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Fri, 28 Mar 2025 17:22:59 -0300 Subject: [PATCH 02/33] lib: return undefined for localStorage when location is invalid --- lib/internal/webstorage.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/internal/webstorage.js b/lib/internal/webstorage.js index 7f58299a1e835f..26a7198267d1ba 100644 --- a/lib/internal/webstorage.js +++ b/lib/internal/webstorage.js @@ -4,13 +4,11 @@ const { } = primordials; const { ERR_INVALID_ARG_VALUE } = require('internal/errors').codes; const { getOptionValue } = require('internal/options'); -const { emitExperimentalWarning } = require('internal/util'); const { kConstructorKey, Storage } = internalBinding('webstorage'); const { getValidatedPath } = require('internal/fs/utils'); +const { emitWarning } = require('process'); const kInMemoryPath = ':memory:'; -emitExperimentalWarning('Web Storage'); - module.exports = { Storage }; let lazyLocalStorage; @@ -27,9 +25,9 @@ ObjectDefineProperties(module.exports, { const location = getOptionValue('--localstorage-file'); if (location === '') { - throw new ERR_INVALID_ARG_VALUE('--localstorage-file', - location, - 'is an invalid localStorage location'); + emitWarning(`${location} is an invalid localStorage location`); + + return undefined; } lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); From 8c7f8837b9630d8befcabffe60c685d0fc435f19 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Wed, 2 Apr 2025 14:10:19 -0300 Subject: [PATCH 03/33] test: remove --experimental-webstorage flag from tests --- test/common/index.js | 7 ++++- test/parallel/test-webstorage.js | 45 +++++--------------------------- test/wpt/test-webstorage.js | 1 - 3 files changed, 12 insertions(+), 41 deletions(-) diff --git a/test/common/index.js b/test/common/index.js index b0d06541ae4be7..5172817124e81d 100755 --- a/test/common/index.js +++ b/test/common/index.js @@ -352,7 +352,12 @@ const knownGlobals = new Set([ 'localStorage', 'sessionStorage', ].forEach((i) => { - if (globalThis[i] !== undefined) { + if (i === 'localStorage') { + if (Object.hasOwn(globalThis, i)) { + globalThis[i] = {}; + knownGlobals.add(globalThis[i]); + } + } else if (globalThis[i] !== undefined) { knownGlobals.add(globalThis[i]); } }); diff --git a/test/parallel/test-webstorage.js b/test/parallel/test-webstorage.js index 9f9070df430f10..2ceffaa5ccfb55 100644 --- a/test/parallel/test-webstorage.js +++ b/test/parallel/test-webstorage.js @@ -14,35 +14,9 @@ function nextLocalStorage() { 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 () => { const cp = await spawnPromisified(process.execPath, [ - '--experimental-webstorage', '-e', 'new globalThis.Storage()', + '-e', 'new globalThis.Storage()', ]); assert.strictEqual(cp.code, 1); @@ -53,33 +27,30 @@ test('Storage instances cannot be created in userland', async () => { test('sessionStorage is not persisted', async () => { let cp = await spawnPromisified(process.execPath, [ - '--experimental-webstorage', '-pe', 'sessionStorage.foo = "barbaz"', + '-pe', 'sessionStorage.foo = "barbaz"', ]); assert.strictEqual(cp.code, 0); assert.match(cp.stdout, /barbaz/); cp = await spawnPromisified(process.execPath, [ - '--experimental-webstorage', '-pe', 'sessionStorage.foo', + '-pe', 'sessionStorage.foo', ]); assert.strictEqual(cp.code, 0); assert.match(cp.stdout, /undefined/); assert.strictEqual((await readdir(tmpdir.path)).length, 0); }); -test('localStorage throws without --localstorage-file ', async () => { +test('localStorage emits a warning without --localstorage-file ', async () => { const cp = await spawnPromisified(process.execPath, [ - '--experimental-webstorage', '-pe', 'localStorage === globalThis.localStorage', ]); - assert.strictEqual(cp.code, 1); + assert.strictEqual(cp.code, 0); assert.strictEqual(cp.signal, null); - assert.strictEqual(cp.stdout, ''); - assert.match(cp.stderr, /The argument '--localstorage-file' is an invalid localStorage location/); + assert.match(cp.stderr, /Warning: is an invalid localStorage location/); }); test('localStorage is not persisted if it is unused', async () => { const cp = await spawnPromisified(process.execPath, [ - '--experimental-webstorage', '--localstorage-file', nextLocalStorage(), '-pe', 'localStorage === globalThis.localStorage', ]); @@ -91,7 +62,6 @@ test('localStorage is not persisted if it is unused', async () => { test('localStorage is persisted if it is used', async () => { const localStorageFile = nextLocalStorage(); let cp = await spawnPromisified(process.execPath, [ - '--experimental-webstorage', '--localstorage-file', localStorageFile, '-pe', 'localStorage.foo = "barbaz"', ]); @@ -102,7 +72,6 @@ test('localStorage is persisted if it is used', async () => { assert.match(entries[0], /\d+\.localstorage/); cp = await spawnPromisified(process.execPath, [ - '--experimental-webstorage', '--localstorage-file', localStorageFile, '-pe', 'localStorage.foo', ]); @@ -117,7 +86,6 @@ describe('webstorage quota for localStorage and sessionStorage', () => { test('localStorage can store and retrieve a max of 10 MB quota', async () => { const localStorageFile = nextLocalStorage(); const cp = await spawnPromisified(process.execPath, [ - '--experimental-webstorage', '--localstorage-file', localStorageFile, // Each character is 2 bytes '-pe', ` @@ -133,7 +101,6 @@ describe('webstorage quota for localStorage and sessionStorage', () => { test('sessionStorage can store a max of 10 MB quota', async () => { const cp = await spawnPromisified(process.execPath, [ - '--experimental-webstorage', // Each character is 2 bytes '-pe', `sessionStorage['a'.repeat(${MAX_STORAGE_SIZE} / 2)] = ''; console.error('filled'); diff --git a/test/wpt/test-webstorage.js b/test/wpt/test-webstorage.js index 490fac8ed7e232..a677659f4b04f4 100644 --- a/test/wpt/test-webstorage.js +++ b/test/wpt/test-webstorage.js @@ -9,7 +9,6 @@ const runner = new WPTRunner('webstorage', { concurrency: 1 }); tmpdir.refresh(); runner.setFlags([ - '--experimental-webstorage', '--localstorage-file', join(tmpdir.path, 'wpt-tests.localstorage'), ]); runner.setInitScript(` From 210315797752c1dfdbcf7d7206efda348d2b036d Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Wed, 2 Apr 2025 14:10:39 -0300 Subject: [PATCH 04/33] lib: use process.emitWarning instead of emitWarning --- lib/internal/webstorage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/internal/webstorage.js b/lib/internal/webstorage.js index 26a7198267d1ba..1fbf93c261342c 100644 --- a/lib/internal/webstorage.js +++ b/lib/internal/webstorage.js @@ -6,7 +6,6 @@ const { ERR_INVALID_ARG_VALUE } = require('internal/errors').codes; const { getOptionValue } = require('internal/options'); const { kConstructorKey, Storage } = internalBinding('webstorage'); const { getValidatedPath } = require('internal/fs/utils'); -const { emitWarning } = require('process'); const kInMemoryPath = ':memory:'; module.exports = { Storage }; @@ -25,7 +24,7 @@ ObjectDefineProperties(module.exports, { const location = getOptionValue('--localstorage-file'); if (location === '') { - emitWarning(`${location} is an invalid localStorage location`); + process.emitWarning(`${location} is an invalid localStorage location`); return undefined; } From c9cda8ff044cb0eefa9ade5f073556a4a5ec17a2 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Fri, 4 Apr 2025 15:06:18 -0300 Subject: [PATCH 05/33] benchmark: remove --experimental-webstorage --- benchmark/webstorage/getItem.js | 2 +- benchmark/webstorage/removeItem.js | 2 +- benchmark/webstorage/setItem.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmark/webstorage/getItem.js b/benchmark/webstorage/getItem.js index c383f91ee0d75b..f92ee0318ff67e 100644 --- a/benchmark/webstorage/getItem.js +++ b/benchmark/webstorage/getItem.js @@ -12,7 +12,7 @@ function nextLocalStorage() { } const options = { - flags: ['--experimental-webstorage', `--localstorage-file=${nextLocalStorage()}`], + flags: [`--localstorage-file=${nextLocalStorage()}`], }; const bench = common.createBenchmark(main, { diff --git a/benchmark/webstorage/removeItem.js b/benchmark/webstorage/removeItem.js index 330f17bdf35293..0de686d45a3d2b 100644 --- a/benchmark/webstorage/removeItem.js +++ b/benchmark/webstorage/removeItem.js @@ -12,7 +12,7 @@ function nextLocalStorage() { } const options = { - flags: ['--experimental-webstorage', `--localstorage-file=${nextLocalStorage()}`], + flags: [`--localstorage-file=${nextLocalStorage()}`], }; const bench = common.createBenchmark(main, { diff --git a/benchmark/webstorage/setItem.js b/benchmark/webstorage/setItem.js index e6ef2f866de05a..5d88a39ef52ac1 100644 --- a/benchmark/webstorage/setItem.js +++ b/benchmark/webstorage/setItem.js @@ -12,7 +12,7 @@ function nextLocalStorage() { } const options = { - flags: ['--experimental-webstorage', `--localstorage-file=${nextLocalStorage()}`], + flags: [`--localstorage-file=${nextLocalStorage()}`], }; const bench = common.createBenchmark(main, { From 8c6d177942b1514493abee90ec7fc9c7229827db Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Fri, 4 Apr 2025 15:17:27 -0300 Subject: [PATCH 06/33] lib: remove unused import --- lib/internal/webstorage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/internal/webstorage.js b/lib/internal/webstorage.js index 1fbf93c261342c..48d07a969f2e9b 100644 --- a/lib/internal/webstorage.js +++ b/lib/internal/webstorage.js @@ -2,7 +2,6 @@ const { ObjectDefineProperties, } = primordials; -const { ERR_INVALID_ARG_VALUE } = require('internal/errors').codes; const { getOptionValue } = require('internal/options'); const { kConstructorKey, Storage } = internalBinding('webstorage'); const { getValidatedPath } = require('internal/fs/utils'); From ecc92a00af611e0d9a52a8ac65c123344aa53b3f Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Mon, 7 Apr 2025 09:15:56 -0300 Subject: [PATCH 07/33] lib: reword warning for --localstorage-file to fix no-regex-spaces lint --- lib/internal/webstorage.js | 2 +- test/parallel/test-webstorage.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/webstorage.js b/lib/internal/webstorage.js index 48d07a969f2e9b..512f5bbe51eb68 100644 --- a/lib/internal/webstorage.js +++ b/lib/internal/webstorage.js @@ -23,7 +23,7 @@ ObjectDefineProperties(module.exports, { const location = getOptionValue('--localstorage-file'); if (location === '') { - process.emitWarning(`${location} is an invalid localStorage location`); + process.emitWarning('`--localstorage-file` was provided without a valid path'); return undefined; } diff --git a/test/parallel/test-webstorage.js b/test/parallel/test-webstorage.js index 2ceffaa5ccfb55..acac4b5e48516a 100644 --- a/test/parallel/test-webstorage.js +++ b/test/parallel/test-webstorage.js @@ -46,7 +46,7 @@ test('localStorage emits a warning without --localstorage-file ', async () => { ]); assert.strictEqual(cp.code, 0); assert.strictEqual(cp.signal, null); - assert.match(cp.stderr, /Warning: is an invalid localStorage location/); + assert.match(cp.stderr, /Warning: `--localstorage-file` was provided without a valid path/); }); test('localStorage is not persisted if it is unused', async () => { From 1fe9092ea807f1baa804b4b15862adea80ce7c6a Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Mon, 7 Apr 2025 09:24:26 -0300 Subject: [PATCH 08/33] test: add localStorage and sessionStorage to globalThis test --- test/parallel/test-global.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/parallel/test-global.js b/test/parallel/test-global.js index 835bcc75a83e3b..8aba5c333a2ac3 100644 --- a/test/parallel/test-global.js +++ b/test/parallel/test-global.js @@ -59,6 +59,8 @@ for (const moduleName of builtinModules) { 'fetch', 'crypto', 'navigator', + 'localStorage', + 'sessionStorage', ]; assert.deepStrictEqual(new Set(Object.keys(globalThis)), new Set(expected)); expected.forEach((value) => { From 676430273ca7f78ca782891d3b808ecbcfb8178c Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Mon, 7 Apr 2025 10:42:43 -0300 Subject: [PATCH 09/33] doc: add --no-experimental-webstorage docs --- doc/api/cli.md | 17 +++++++++++++++-- doc/api/globals.md | 7 ++++--- doc/node.1 | 4 ++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/doc/api/cli.md b/doc/api/cli.md index 9775bbf267e204..a97d00899313cb 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1717,8 +1717,8 @@ added: v22.4.0 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 -between multiple Node.js processes concurrently. This flag is a no-op unless -Node.js is started with the `--experimental-webstorage` flag. +between multiple Node.js processes concurrently. This flag is a no-op if +Node.js is started with the `--no-experimental-webstorage` flag. ### `--max-http-header-size=size` @@ -1894,6 +1894,14 @@ added: v22.0.0 Disable exposition of {WebSocket} on the global scope. +### `--no-experimental-webstorage` + + + +Disable experimental [`Web Storage`][] support. + ### `--no-extra-info-on-fatal-exception` -> Stability: 1.0 - Early development. +> Stability: 2 - Stable A browser-compatible implementation of [`localStorage`][]. Data is stored unencrypted in the file specified by the [`--localstorage-file`][] CLI flag. From 39048d9e814665be8afc9738f770505c710473c1 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Wed, 11 Jun 2025 09:35:27 -0300 Subject: [PATCH 19/33] src: add --webstorage alias --- src/node_options.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node_options.cc b/src/node_options.cc index 0d9038ddf3d61a..cf5c0289f360f6 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -564,6 +564,7 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { &EnvironmentOptions::experimental_webstorage, kAllowedInEnvvar, true); + AddAlias("--webstorage", "--experimental-webstorage"); AddOption("--localstorage-file", "file used to persist localStorage data", &EnvironmentOptions::localstorage_file, From eff1a7076a9db9868f48a67bdec5c7759b9221cb Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Wed, 11 Jun 2025 09:36:13 -0300 Subject: [PATCH 20/33] lib: remove experimental warning --- lib/internal/webstorage.js | 3 --- test/parallel/test-webstorage.js | 18 ++---------------- test/wpt/test-webstorage.js | 3 +++ 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/lib/internal/webstorage.js b/lib/internal/webstorage.js index f9449a16bd7f71..512f5bbe51eb68 100644 --- a/lib/internal/webstorage.js +++ b/lib/internal/webstorage.js @@ -4,14 +4,11 @@ const { } = primordials; const { getOptionValue } = require('internal/options'); const { kConstructorKey, Storage } = internalBinding('webstorage'); -const { emitExperimentalWarning } = require('internal/util'); const { getValidatedPath } = require('internal/fs/utils'); const kInMemoryPath = ':memory:'; module.exports = { Storage }; -emitExperimentalWarning('Web Storage'); - let lazyLocalStorage; let lazySessionStorage; diff --git a/test/parallel/test-webstorage.js b/test/parallel/test-webstorage.js index d4f3e7eb5cd2ed..4a50b83501f8d0 100644 --- a/test/parallel/test-webstorage.js +++ b/test/parallel/test-webstorage.js @@ -27,20 +27,6 @@ test('Storage instances cannot be created in userland', async () => { assert.match(cp.stderr, /Error: Illegal constructor/); }); -test('emits a warning when used', async () => { - for (const api of ['Storage', 'localStorage', 'sessionStorage']) { - const cp = await spawnPromisified(process.execPath, [ - '--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('sessionStorage is not persisted', async () => { let cp = await spawnPromisified(process.execPath, [ '-pe', 'sessionStorage.foo = "barbaz"', @@ -129,10 +115,10 @@ describe('webstorage quota for localStorage and sessionStorage', () => { }); }); -test('disabled with --no-experimental-webstorage', async () => { +test('disabled with --no-webstorage', async () => { for (const api of ['Storage', 'localStorage', 'sessionStorage']) { const cp = await spawnPromisified(process.execPath, [ - '--no-experimental-webstorage', + '--no-webstorage', '--localstorage-file', './test/fixtures/localstoragefile-global-test', '-e', diff --git a/test/wpt/test-webstorage.js b/test/wpt/test-webstorage.js index a677659f4b04f4..1cb03bf29b9f18 100644 --- a/test/wpt/test-webstorage.js +++ b/test/wpt/test-webstorage.js @@ -1,4 +1,7 @@ 'use strict'; + +// Flags: --localstorage-file=./test/fixtures/localstoragefile-global-test + const { skipIfSQLiteMissing } = require('../common'); skipIfSQLiteMissing(); const tmpdir = require('../common/tmpdir'); From b17e1f5ddecc0094cf19889ef97935ffbd258f77 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Tue, 17 Jun 2025 09:20:40 -0300 Subject: [PATCH 21/33] doc: add --webstorage alias to docs --- doc/api/cli.md | 11 ++++++++++- doc/api/globals.md | 5 +++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/doc/api/cli.md b/doc/api/cli.md index a97d00899313cb..b114cbb4878ea9 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1718,7 +1718,7 @@ added: v22.4.0 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 between multiple Node.js processes concurrently. This flag is a no-op if -Node.js is started with the `--no-experimental-webstorage` flag. +Node.js is started with the `--no-experimental-webstorage` (or `--no-webstorage`) flag. ### `--max-http-header-size=size` @@ -1951,6 +1951,14 @@ added: v6.0.0 Silence all process warnings (including deprecations). +### `--no-webstorage` + + + +Alias for [`--no-experimental-webstorage`][]. + ### `--node-memory-debug` -Alias for [`--no-experimental-webstorage`][]. +Alias for `--no-experimental-webstorage`. ### `--node-memory-debug` From 3a4e2b5eb6323b0bc903720d22938d3c60e0ad1c Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Tue, 24 Jun 2025 14:08:12 -0300 Subject: [PATCH 24/33] lib: delay localstorage warning with proxy --- lib/internal/webstorage.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/internal/webstorage.js b/lib/internal/webstorage.js index 512f5bbe51eb68..8afea815f8520d 100644 --- a/lib/internal/webstorage.js +++ b/lib/internal/webstorage.js @@ -23,12 +23,19 @@ ObjectDefineProperties(module.exports, { const location = getOptionValue('--localstorage-file'); if (location === '') { - process.emitWarning('`--localstorage-file` was provided without a valid path'); - - return undefined; + lazyLocalStorage = new Proxy({}, { + get(target, prop) { + process.emitWarning('`--localstorage-file` was provided without a valid path'); + return undefined; + }, + set(target, prop, value) { + process.emitWarning('`--localstorage-file` was provided without a valid path'); + return false; + }, + }); + } else { + lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); } - - lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); } return lazyLocalStorage; From a6ce8fdb0d110060fa5bebea32b8638bd3bb030e Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Tue, 24 Jun 2025 14:09:18 -0300 Subject: [PATCH 25/33] test: remove --locastorage-file flag from tests --- test/fixtures/localstoragefile-global-test | 0 test/parallel/test-global.js | 1 - test/parallel/test-js-stream-call-properties.js | 2 +- .../test-process-env-allowed-flags-are-documented.js | 2 -- test/parallel/test-process-env-allowed-flags.js | 1 - test/parallel/test-tls-js-stream.js | 1 - test/parallel/test-webstorage.js | 5 ++--- ...er-message-port-transfer-fake-js-transferable-internal.js | 1 - ...test-worker-message-port-transfer-fake-js-transferable.js | 1 - test/parallel/test-wrap-js-stream-destroy.js | 2 +- test/parallel/test-wrap-js-stream-duplex.js | 2 +- test/parallel/test-wrap-js-stream-exceptions.js | 2 +- test/parallel/test-wrap-js-stream-read-stop.js | 2 +- test/wpt/test-webstorage.js | 2 -- 14 files changed, 7 insertions(+), 17 deletions(-) delete mode 100644 test/fixtures/localstoragefile-global-test diff --git a/test/fixtures/localstoragefile-global-test b/test/fixtures/localstoragefile-global-test deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/test/parallel/test-global.js b/test/parallel/test-global.js index fdca36b6c40bfb..8aba5c333a2ac3 100644 --- a/test/parallel/test-global.js +++ b/test/parallel/test-global.js @@ -22,7 +22,6 @@ // This test cannot run in strict mode because it tests that `baseFoo` is // treated as a global without being declared with `var`/`let`/`const`. -// Flags: --localstorage-file=./test/fixtures/localstoragefile-global-test /* eslint-disable strict */ const common = require('../common'); const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-js-stream-call-properties.js b/test/parallel/test-js-stream-call-properties.js index 1d49612f071d63..c001fbdb136726 100644 --- a/test/parallel/test-js-stream-call-properties.js +++ b/test/parallel/test-js-stream-call-properties.js @@ -1,4 +1,4 @@ -// Flags: --expose-internals --localstorage-file=./test/fixtures/localstoragefile-global-test +// Flags: --expose-internals 'use strict'; diff --git a/test/parallel/test-process-env-allowed-flags-are-documented.js b/test/parallel/test-process-env-allowed-flags-are-documented.js index 4a562964d5e5b5..a6904159fc858f 100644 --- a/test/parallel/test-process-env-allowed-flags-are-documented.js +++ b/test/parallel/test-process-env-allowed-flags-are-documented.js @@ -1,7 +1,5 @@ 'use strict'; -// Flags: --localstorage-file=./test/fixtures/localstoragefile-global-test - const common = require('../common'); const assert = require('assert'); diff --git a/test/parallel/test-process-env-allowed-flags.js b/test/parallel/test-process-env-allowed-flags.js index 6e4f58343b1bff..a4558200fa8af8 100644 --- a/test/parallel/test-process-env-allowed-flags.js +++ b/test/parallel/test-process-env-allowed-flags.js @@ -1,6 +1,5 @@ 'use strict'; -// Flags: --localstorage-file=./test/fixtures/localstoragefile-global-test const common = require('../common'); const assert = require('assert'); diff --git a/test/parallel/test-tls-js-stream.js b/test/parallel/test-tls-js-stream.js index b6f442683ce09c..298252962ba9cd 100644 --- a/test/parallel/test-tls-js-stream.js +++ b/test/parallel/test-tls-js-stream.js @@ -1,5 +1,4 @@ 'use strict'; -// Flags: --localstorage-file=./test/fixtures/localstoragefile-global-test const common = require('../common'); if (!common.hasCrypto) diff --git a/test/parallel/test-webstorage.js b/test/parallel/test-webstorage.js index 4a50b83501f8d0..bc48c9643773b5 100644 --- a/test/parallel/test-webstorage.js +++ b/test/parallel/test-webstorage.js @@ -1,6 +1,5 @@ 'use strict'; -// Flags: --localstorage-file=./test/fixtures/localstoragefile-global-test const { skipIfSQLiteMissing, spawnPromisified } = require('../common'); skipIfSQLiteMissing(); const tmpdir = require('../common/tmpdir'); @@ -42,9 +41,9 @@ test('sessionStorage is not persisted', async () => { assert.strictEqual((await readdir(tmpdir.path)).length, 0); }); -test('localStorage emits a warning without --localstorage-file ', async () => { +test('localStorage emits a warning when used without --localstorage-file ', async () => { const cp = await spawnPromisified(process.execPath, [ - '-pe', 'localStorage === globalThis.localStorage', + '-pe', 'localStorage.length', ]); assert.strictEqual(cp.code, 0); assert.strictEqual(cp.signal, null); diff --git a/test/parallel/test-worker-message-port-transfer-fake-js-transferable-internal.js b/test/parallel/test-worker-message-port-transfer-fake-js-transferable-internal.js index 4ce04f88ba29db..46895871e0a179 100644 --- a/test/parallel/test-worker-message-port-transfer-fake-js-transferable-internal.js +++ b/test/parallel/test-worker-message-port-transfer-fake-js-transferable-internal.js @@ -1,5 +1,4 @@ 'use strict'; -// Flags: --localstorage-file=./test/fixtures/localstoragefile-global-test const common = require('../common'); const assert = require('assert'); const fs = require('fs').promises; diff --git a/test/parallel/test-worker-message-port-transfer-fake-js-transferable.js b/test/parallel/test-worker-message-port-transfer-fake-js-transferable.js index bf12ba8cbe9402..da512090170c9c 100644 --- a/test/parallel/test-worker-message-port-transfer-fake-js-transferable.js +++ b/test/parallel/test-worker-message-port-transfer-fake-js-transferable.js @@ -1,6 +1,5 @@ 'use strict'; -// Flags: --localstorage-file=./test/fixtures/localstoragefile-global-test const common = require('../common'); const assert = require('assert'); const fs = require('fs').promises; diff --git a/test/parallel/test-wrap-js-stream-destroy.js b/test/parallel/test-wrap-js-stream-destroy.js index 44f1631bab6828..5c1ed1e7e65d10 100644 --- a/test/parallel/test-wrap-js-stream-destroy.js +++ b/test/parallel/test-wrap-js-stream-destroy.js @@ -1,4 +1,4 @@ -// Flags: --expose-internals --localstorage-file=./test/fixtures/localstoragefile-global-test +// Flags: --expose-internals 'use strict'; const common = require('../common'); diff --git a/test/parallel/test-wrap-js-stream-duplex.js b/test/parallel/test-wrap-js-stream-duplex.js index 8606d39e9725d6..6a817c1054cf55 100644 --- a/test/parallel/test-wrap-js-stream-duplex.js +++ b/test/parallel/test-wrap-js-stream-duplex.js @@ -1,4 +1,4 @@ -// Flags: --expose-internals --localstorage-file=./test/fixtures/localstoragefile-global-test +// Flags: --expose-internals 'use strict'; const common = require('../common'); const assert = require('assert'); diff --git a/test/parallel/test-wrap-js-stream-exceptions.js b/test/parallel/test-wrap-js-stream-exceptions.js index 16c7e8e80e3d94..b0269bd1282a4c 100644 --- a/test/parallel/test-wrap-js-stream-exceptions.js +++ b/test/parallel/test-wrap-js-stream-exceptions.js @@ -1,4 +1,4 @@ -// Flags: --expose-internals --localstorage-file=./test/fixtures/localstoragefile-global-test +// Flags: --expose-internals 'use strict'; const common = require('../common'); const assert = require('assert'); diff --git a/test/parallel/test-wrap-js-stream-read-stop.js b/test/parallel/test-wrap-js-stream-read-stop.js index f6b267b2b02e27..6d86dc2c21a519 100644 --- a/test/parallel/test-wrap-js-stream-read-stop.js +++ b/test/parallel/test-wrap-js-stream-read-stop.js @@ -1,4 +1,4 @@ -// Flags: --expose-internals --localstorage-file=./test/fixtures/localstoragefile-global-test +// Flags: --expose-internals 'use strict'; require('../common'); diff --git a/test/wpt/test-webstorage.js b/test/wpt/test-webstorage.js index 1cb03bf29b9f18..b9d0f6fef69a5a 100644 --- a/test/wpt/test-webstorage.js +++ b/test/wpt/test-webstorage.js @@ -1,7 +1,5 @@ 'use strict'; -// Flags: --localstorage-file=./test/fixtures/localstoragefile-global-test - const { skipIfSQLiteMissing } = require('../common'); skipIfSQLiteMissing(); const tmpdir = require('../common/tmpdir'); From ac15bcfd84335cc6934f3b725f1a0fefe45b7978 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Tue, 24 Jun 2025 14:31:38 -0300 Subject: [PATCH 26/33] src,lib: rename experimental_experimental option --- lib/internal/process/pre_execution.js | 2 +- src/node_options.cc | 8 ++++---- src/node_options.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index fc7a0e206928da..912b4601a06616 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -365,7 +365,7 @@ function setupQuic() { function setupWebStorage() { if (getEmbedderOptions().noBrowserGlobals || - !getOptionValue('--experimental-webstorage')) { + !getOptionValue('--webstorage')) { return; } diff --git a/src/node_options.cc b/src/node_options.cc index cf5c0289f360f6..f29929a996456f 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -559,12 +559,12 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { NoOp{}, #endif kAllowedInEnvvar); - AddOption("--experimental-webstorage", - "experimental Web Storage API", - &EnvironmentOptions::experimental_webstorage, + AddOption("--webstorage", + "Web Storage API", + &EnvironmentOptions::webstorage, kAllowedInEnvvar, true); - AddAlias("--webstorage", "--experimental-webstorage"); + AddAlias("--experimental-webstorage", "--webstorage"); AddOption("--localstorage-file", "file used to persist localStorage data", &EnvironmentOptions::localstorage_file, diff --git a/src/node_options.h b/src/node_options.h index 40cbfeb9bc10b3..3858547227bb9f 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -127,7 +127,7 @@ class EnvironmentOptions : public Options { bool experimental_fetch = true; bool experimental_websocket = true; bool experimental_sqlite = true; - bool experimental_webstorage = true; + bool webstorage = true; #ifndef OPENSSL_NO_QUIC bool experimental_quic = false; #endif From b9b890652d568a41955890c088ee31882b20db98 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Tue, 24 Jun 2025 14:32:00 -0300 Subject: [PATCH 27/33] doc: update docs --- doc/api/cli.md | 6 +++--- doc/api/globals.md | 2 +- doc/node.1 | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/api/cli.md b/doc/api/cli.md index cc5d8be28f5603..ea9e4426b9d45e 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1718,7 +1718,7 @@ added: v22.4.0 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 between multiple Node.js processes concurrently. This flag is a no-op if -Node.js is started with the `--no-experimental-webstorage` (or `--no-webstorage`) flag. +Node.js is started with the `--no-webstorage` (or `--no-experimental-webstorage`) flag. ### `--max-http-header-size=size` @@ -1900,7 +1900,7 @@ Disable exposition of {WebSocket} on the global scope. added: v22.4.0 --> -Disable experimental [`Web Storage`][] support. +Alias for `--no-webstorage`. ### `--no-extra-info-on-fatal-exception` @@ -1957,7 +1957,7 @@ Silence all process warnings (including deprecations). added: REPLACEME --> -Alias for `--no-experimental-webstorage`. +Disable experimental [`Web Storage`][] support. ### `--node-memory-debug` diff --git a/doc/api/globals.md b/doc/api/globals.md index 3c998d124a8ec4..e893516965e1d0 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -642,7 +642,7 @@ A browser-compatible implementation of [`localStorage`][]. Data is stored unencrypted in the file specified by the [`--localstorage-file`][] CLI flag. 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. -Disable this API with the [`--no-experimental-webstorage`][] (or its alias [`--no-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 of a server, it is shared across all users and requests. diff --git a/doc/node.1 b/doc/node.1 index 2be31bd9f15b69..9041affe63f348 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -205,8 +205,10 @@ Enable experimental support for the EventSource Web API. Disable experimental support for the WebSocket API. . .It Fl -no-experimental-webstorage -Disable experimental webstorage. +Disable webstorage. . +.It Fl -no-webstorage +Disable webstorage. .It Fl -no-experimental-repl-await Disable top-level await keyword support in REPL. . From e7b8440bcafb7d7defdaf30731f1a69d9d207ec9 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Tue, 24 Jun 2025 14:45:11 -0300 Subject: [PATCH 28/33] lib: fix linter errors --- lib/internal/webstorage.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/internal/webstorage.js b/lib/internal/webstorage.js index 8afea815f8520d..381ad2c12d92d1 100644 --- a/lib/internal/webstorage.js +++ b/lib/internal/webstorage.js @@ -1,6 +1,7 @@ 'use strict'; const { ObjectDefineProperties, + Proxy, } = primordials; const { getOptionValue } = require('internal/options'); const { kConstructorKey, Storage } = internalBinding('webstorage'); @@ -23,7 +24,8 @@ ObjectDefineProperties(module.exports, { const location = getOptionValue('--localstorage-file'); if (location === '') { - lazyLocalStorage = new Proxy({}, { + const handler = { + __proto__: null, get(target, prop) { process.emitWarning('`--localstorage-file` was provided without a valid path'); return undefined; @@ -32,7 +34,9 @@ ObjectDefineProperties(module.exports, { process.emitWarning('`--localstorage-file` was provided without a valid path'); return false; }, - }); + }; + + lazyLocalStorage = new Proxy({}, handler); } else { lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); } From 8e1972c939104d23c1b8e646b1a890ce8db598d3 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Tue, 24 Jun 2025 14:52:44 -0300 Subject: [PATCH 29/33] test: remove useless change --- .../test-worker-message-port-transfer-fake-js-transferable.js | 1 - test/wpt/test-webstorage.js | 1 - 2 files changed, 2 deletions(-) diff --git a/test/parallel/test-worker-message-port-transfer-fake-js-transferable.js b/test/parallel/test-worker-message-port-transfer-fake-js-transferable.js index da512090170c9c..7153ebc4d2f801 100644 --- a/test/parallel/test-worker-message-port-transfer-fake-js-transferable.js +++ b/test/parallel/test-worker-message-port-transfer-fake-js-transferable.js @@ -1,5 +1,4 @@ 'use strict'; - const common = require('../common'); const assert = require('assert'); const fs = require('fs').promises; diff --git a/test/wpt/test-webstorage.js b/test/wpt/test-webstorage.js index b9d0f6fef69a5a..a677659f4b04f4 100644 --- a/test/wpt/test-webstorage.js +++ b/test/wpt/test-webstorage.js @@ -1,5 +1,4 @@ 'use strict'; - const { skipIfSQLiteMissing } = require('../common'); skipIfSQLiteMissing(); const tmpdir = require('../common/tmpdir'); From 0ba26af54c67c2650a2038b515a95b5c78f20c8b Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Tue, 24 Jun 2025 16:19:02 -0300 Subject: [PATCH 30/33] doc: update node-config-schema.json --- doc/api/cli.md | 2 +- doc/node-config-schema.json | 6 +++--- doc/node.1 | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/doc/api/cli.md b/doc/api/cli.md index ea9e4426b9d45e..fa8ba06d881fe5 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1957,7 +1957,7 @@ Silence all process warnings (including deprecations). added: REPLACEME --> -Disable experimental [`Web Storage`][] support. +Disable [`Web Storage`][] support. ### `--node-memory-debug` diff --git a/doc/node-config-schema.json b/doc/node-config-schema.json index bffae1fc617917..e5104a3e16b516 100644 --- a/doc/node-config-schema.json +++ b/doc/node-config-schema.json @@ -186,9 +186,6 @@ "experimental-websocket": { "type": "boolean" }, - "experimental-webstorage": { - "type": "boolean" - }, "extra-info-on-fatal-exception": { "type": "boolean" }, @@ -594,6 +591,9 @@ "watch-preserve-output": { "type": "boolean" }, + "webstorage": { + "type": "boolean" + }, "zero-fill-buffers": { "type": "boolean" } diff --git a/doc/node.1 b/doc/node.1 index 9041affe63f348..110706933dbaa7 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -204,11 +204,9 @@ Enable experimental support for the EventSource Web API. .It Fl -no-experimental-websocket Disable experimental support for the WebSocket API. . -.It Fl -no-experimental-webstorage -Disable webstorage. -. .It Fl -no-webstorage Disable webstorage. +. .It Fl -no-experimental-repl-await Disable top-level await keyword support in REPL. . From abe849166872679289b11c7800ff9086d7edda2f Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Wed, 9 Jul 2025 09:26:19 -0300 Subject: [PATCH 31/33] doc: fix markdown error --- doc/api/globals.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/api/globals.md b/doc/api/globals.md index e893516965e1d0..d038a9cb2cac2a 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -636,8 +636,6 @@ A browser-compatible implementation of {Headers}. added: v22.4.0 --> -> Stability: 2 - Stable - A browser-compatible implementation of [`localStorage`][]. Data is stored unencrypted in the file specified by the [`--localstorage-file`][] CLI flag. The maximum amount of data that can be stored is 10 MB. @@ -1104,7 +1102,7 @@ added: v22.4.0 --> > Stability: 1.0 - Early development. Enable this API with the -> [`--experimental-webstorage`][] CLI flag. +> \[`--experimental-webstorage`]\[] CLI flag. A browser-compatible implementation of {Storage}. Disable this API with the [`--no-webstorage`][] (or its alias [`--no-experimental-webstorage`][]) CLI flag. From 4b152b14739cf8f9eaa83c78a3caf8e6cb44f066 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Fri, 1 Aug 2025 13:42:55 -0300 Subject: [PATCH 32/33] doc: fix failing tests --- doc/api/cli.md | 21 --------------------- doc/api/globals.md | 5 ++--- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/doc/api/cli.md b/doc/api/cli.md index fa8ba06d881fe5..5650649a48b172 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1269,14 +1269,6 @@ changes: Enable experimental WebAssembly System Interface (WASI) support. -### `--experimental-webstorage` - - - -Enable experimental [`Web Storage`][] support. - ### `--experimental-worker-inspection` - -Alias for `--no-webstorage`. - ### `--no-extra-info-on-fatal-exception`