From 99cb1e0ad8a538c8ecc67954def86bdeeb0a2838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Szabo?= Date: Sat, 10 Feb 2024 02:42:23 -0300 Subject: [PATCH 01/11] Migrating IndexedDB to a different file --- src/state-store/indexed-db.js | 141 ++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/state-store/indexed-db.js diff --git a/src/state-store/indexed-db.js b/src/state-store/indexed-db.js new file mode 100644 index 0000000000..4e64d6edae --- /dev/null +++ b/src/state-store/indexed-db.js @@ -0,0 +1,141 @@ +'use strict'; + +module.exports = class IndexedDBStateStore { + constructor(databaseName, version) { + this.connected = false; + this.databaseName = databaseName; + this.version = version; + } + + get dbPromise() { + if (!this._dbPromise) { + this._dbPromise = new Promise(resolve => { + const dbOpenRequest = indexedDB.open(this.databaseName, this.version); + dbOpenRequest.onupgradeneeded = event => { + let db = event.target.result; + db.onerror = error => { + atom.notifications.addFatalError('Error loading database', { + stack: new Error('Error loading database').stack, + dismissable: true + }); + console.error('Error loading database', error); + }; + db.createObjectStore('states'); + }; + dbOpenRequest.onsuccess = () => { + this.connected = true; + resolve(dbOpenRequest.result); + }; + dbOpenRequest.onerror = error => { + atom.notifications.addFatalError('Could not connect to indexedDB', { + stack: new Error('Could not connect to indexedDB').stack, + dismissable: true + }); + console.error('Could not connect to indexedDB', error); + this.connected = false; + resolve(null); + }; + }); + } + + return this._dbPromise; + } + + isConnected() { + return this.connected; + } + + connect() { + return this.dbPromise.then(db => !!db); + } + + save(key, value) { + return new Promise((resolve, reject) => { + this.dbPromise.then(db => { + if (db == null) return resolve(); + + const request = db + .transaction(['states'], 'readwrite') + .objectStore('states') + .put({ value: value, storedAt: new Date().toString() }, key); + + request.onsuccess = resolve; + request.onerror = reject; + }); + }); + } + + load(key) { + return this.dbPromise.then(db => { + if (!db) return; + + return new Promise((resolve, reject) => { + const request = db + .transaction(['states']) + .objectStore('states') + .get(key); + + request.onsuccess = event => { + let result = event.target.result; + if (result && !result.isJSON) { + resolve(result.value); + } else { + resolve(null); + } + }; + + request.onerror = event => reject(event); + }); + }); + } + + delete(key) { + return new Promise((resolve, reject) => { + this.dbPromise.then(db => { + if (db == null) return resolve(); + + const request = db + .transaction(['states'], 'readwrite') + .objectStore('states') + .delete(key); + + request.onsuccess = resolve; + request.onerror = reject; + }); + }); + } + + clear() { + return this.dbPromise.then(db => { + if (!db) return; + + return new Promise((resolve, reject) => { + const request = db + .transaction(['states'], 'readwrite') + .objectStore('states') + .clear(); + + request.onsuccess = resolve; + request.onerror = reject; + }); + }); + } + + count() { + return this.dbPromise.then(db => { + if (!db) return; + + return new Promise((resolve, reject) => { + const request = db + .transaction(['states']) + .objectStore('states') + .count(); + + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = reject; + }); + }); + } +}; From a60c8e2e77fced49989d599b67117cc5bc633b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Szabo?= Date: Sat, 10 Feb 2024 02:42:32 -0300 Subject: [PATCH 02/11] Generating a SQL state store --- src/state-store/sql.js | 97 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/state-store/sql.js diff --git a/src/state-store/sql.js b/src/state-store/sql.js new file mode 100644 index 0000000000..f0c7244a30 --- /dev/null +++ b/src/state-store/sql.js @@ -0,0 +1,97 @@ +'use strict'; + +const sqlite3 = require('sqlite3').verbose(); +const path = require('path'); + +module.exports = class SQLStateStore { + constructor(databaseName, version) { + const table = databaseName + version; + this.tableName = '"' + table + '"'; + this.dbPromise = (async () => { + await awaitForAtomGlobal(); + const dbPath = path.join(atom.getConfigDirPath(), 'session-store.db'); + const db = new sqlite3.Database(dbPath); + await getOne(db, + `CREATE TABLE IF NOT EXISTS ${this.tableName} (key VARCHAR, value JSON)` + ); + await getOne(db, + `CREATE UNIQUE INDEX IF NOT EXISTS "${table}_index" ON ${this.tableName}(key)` + ); + return db; + })(); + // this. + this.connected = false; + this.dbPromise.then(_ => this.connected = true); + } + + isConnected() { + return this.connected; + } + + connect() { + return this.dbPromise.then(db => !!db); + } + + save(key, value) { + return this.dbPromise.then(db => + getOne(db, + `REPLACE INTO ${this.tableName} VALUES (?, ?)`, + key, + JSON.stringify({ value: value, storedAt: new Date().toString() }) + ) + ) + } + + load(key) { + return this.dbPromise.then(db => + getOne(db, `SELECT value FROM ${this.tableName} WHERE key = ?`, key ) + ).then(result => { + if(result) { + const parsed = JSON.parse(result.value); + return parsed?.value + } + return null; + }); + } + + delete(key) { + return this.dbPromise.then(db => + getOne(db, `DELETE FROM ${this.tableName} WHERE key = ?`, key ) + ); + } + + clear() { + return this.dbPromise.then(db => + getOne(db, `DROP TABLE ${this.tableName}`) + ); + } + + count() { + return this.dbPromise.then(db => + getOne(db, `SELECT COUNT(key) c FROM ${this.tableName}`).then(r => r.c) + ); + } +}; + +function getOne(db, sql, ...params) { + return new Promise((resolve, reject) => { + db.get(sql, ...params, (error, result) => { + if(error) { + reject(error); + } else { + resolve(result); + } + }); + }); +} + +function awaitForAtomGlobal() { + return new Promise(resolve => { + const i = setInterval(() => { + if(atom) { + clearInterval(i) + resolve() + } + }, 50) + }) +} From d7c7b98b8209b833db03b5bc5354bfd336b01282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Szabo?= Date: Sat, 10 Feb 2024 02:42:54 -0300 Subject: [PATCH 03/11] Unifying both stores --- src/state-store.js | 143 ++------------------------------------------- 1 file changed, 4 insertions(+), 139 deletions(-) diff --git a/src/state-store.js b/src/state-store.js index 840ec18319..1ceb0a61f7 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -1,141 +1,6 @@ 'use strict'; +const IndexedDB = require('./state-store/indexed-db'); +const SQL = require('./state-store/sql'); -module.exports = class StateStore { - constructor(databaseName, version) { - this.connected = false; - this.databaseName = databaseName; - this.version = version; - } - - get dbPromise() { - if (!this._dbPromise) { - this._dbPromise = new Promise(resolve => { - const dbOpenRequest = indexedDB.open(this.databaseName, this.version); - dbOpenRequest.onupgradeneeded = event => { - let db = event.target.result; - db.onerror = error => { - atom.notifications.addFatalError('Error loading database', { - stack: new Error('Error loading database').stack, - dismissable: true - }); - console.error('Error loading database', error); - }; - db.createObjectStore('states'); - }; - dbOpenRequest.onsuccess = () => { - this.connected = true; - resolve(dbOpenRequest.result); - }; - dbOpenRequest.onerror = error => { - atom.notifications.addFatalError('Could not connect to indexedDB', { - stack: new Error('Could not connect to indexedDB').stack, - dismissable: true - }); - console.error('Could not connect to indexedDB', error); - this.connected = false; - resolve(null); - }; - }); - } - - return this._dbPromise; - } - - isConnected() { - return this.connected; - } - - connect() { - return this.dbPromise.then(db => !!db); - } - - save(key, value) { - return new Promise((resolve, reject) => { - this.dbPromise.then(db => { - if (db == null) return resolve(); - - const request = db - .transaction(['states'], 'readwrite') - .objectStore('states') - .put({ value: value, storedAt: new Date().toString() }, key); - - request.onsuccess = resolve; - request.onerror = reject; - }); - }); - } - - load(key) { - return this.dbPromise.then(db => { - if (!db) return; - - return new Promise((resolve, reject) => { - const request = db - .transaction(['states']) - .objectStore('states') - .get(key); - - request.onsuccess = event => { - let result = event.target.result; - if (result && !result.isJSON) { - resolve(result.value); - } else { - resolve(null); - } - }; - - request.onerror = event => reject(event); - }); - }); - } - - delete(key) { - return new Promise((resolve, reject) => { - this.dbPromise.then(db => { - if (db == null) return resolve(); - - const request = db - .transaction(['states'], 'readwrite') - .objectStore('states') - .delete(key); - - request.onsuccess = resolve; - request.onerror = reject; - }); - }); - } - - clear() { - return this.dbPromise.then(db => { - if (!db) return; - - return new Promise((resolve, reject) => { - const request = db - .transaction(['states'], 'readwrite') - .objectStore('states') - .clear(); - - request.onsuccess = resolve; - request.onerror = reject; - }); - }); - } - - count() { - return this.dbPromise.then(db => { - if (!db) return; - - return new Promise((resolve, reject) => { - const request = db - .transaction(['states']) - .objectStore('states') - .count(); - - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = reject; - }); - }); - } -}; +// module.exports = IndexedDB; +module.exports = SQL; From 1bcae1869c5ec47f410e28b910fefdf05bb363f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Szabo?= Date: Sat, 10 Feb 2024 02:43:03 -0300 Subject: [PATCH 04/11] Adding SQLite3 to the dependencies --- package.json | 1 + yarn.lock | 86 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 95e40a76cf..4b1649478a 100644 --- a/package.json +++ b/package.json @@ -163,6 +163,7 @@ "solarized-dark-syntax": "file:packages/solarized-dark-syntax", "solarized-light-syntax": "file:packages/solarized-light-syntax", "spell-check": "file:packages/spell-check", + "sqlite3": "^5.1.7", "status-bar": "file:packages/status-bar", "styleguide": "file:./packages/styleguide", "superstring": "^2.4.4", diff --git a/yarn.lock b/yarn.lock index ca41b620fa..3dbeaa65af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2527,6 +2527,13 @@ binary-search@^1.3.3: resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.6.tgz#e32426016a0c5092f0f3598836a1c7da3560565c" integrity sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA== +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bintrees@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" @@ -3627,6 +3634,11 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== +detect-libc@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" + integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== + detect-node@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" @@ -4496,6 +4508,11 @@ file-set@^4.0.2: array-back "^5.0.0" glob "^7.1.6" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + filelist@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" @@ -6497,6 +6514,11 @@ loglevel@^1.6.0: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== +loophole@^1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loophole/-/loophole-1.1.0.tgz#37949fea453b6256acc725c320ce0c5a7f70a2bd" + integrity sha512-zgLykD4MuL9HJRsoeKuGRXz4L9t1C0oDZPjFeUg5MLQyZjxWdxIbPHgRijVGN98DieVWJuo2fi9nEUDcdDbEiQ== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -7085,6 +7107,13 @@ node-abi@^3.0.0: dependencies: semver "^7.3.5" +node-abi@^3.3.0: + version "3.54.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.54.0.tgz#f6386f7548817acac6434c6cba02999c9aebcc69" + integrity sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA== + dependencies: + semver "^7.3.5" + node-addon-api@*: version "5.0.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501" @@ -7100,6 +7129,11 @@ node-addon-api@^3.1.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== +node-addon-api@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.0.tgz#71f609369379c08e251c558527a107107b5e0fdb" + integrity sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g== + node-api-version@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/node-api-version/-/node-api-version-0.1.4.tgz#1ed46a485e462d55d66b5aa1fe2821720dedf080" @@ -7142,7 +7176,7 @@ node-gyp-build@^4.2.1: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== -node-gyp@^8.4.0: +node-gyp@8.x, node-gyp@^8.4.0: version "8.4.1" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== @@ -7588,10 +7622,10 @@ pause-stream@0.0.11: dependencies: through "~2.3" -pegjs@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/pegjs/-/pegjs-0.10.0.tgz#cf8bafae6eddff4b5a7efb185269eaaf4610ddbd" - integrity sha512-qI5+oFNEGi3L5HAxDwN2LA4Gg7irF70Zs25edhjld9QemOgp0CbvMtbFcMvFtEo1OityPrcCzkQFB8JP/hxgow== +pegjs@~0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/pegjs/-/pegjs-0.8.0.tgz#976f067da13e5c5b1501c017925668a253811561" + integrity sha512-GtAFD5WLxE0LjyhlpKwAnbi3NLJDrYsOvil95UCUQ6pzxlUtUGP/k0FnKGypTpM1WWdmoclfXb0dmMd5UUDkvA== pend@~1.2.0: version "1.2.0" @@ -7731,6 +7765,24 @@ prebuild-install@^6.0.0, prebuild-install@^6.0.1: tar-fs "^2.0.0" tunnel-agent "^0.6.0" +prebuild-install@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" + integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -8599,6 +8651,15 @@ simple-get@^3.0.3: once "^1.3.1" simple-concat "^1.0.0" +simple-get@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -8644,7 +8705,8 @@ smart-buffer@^4.0.2, smart-buffer@^4.2.0: dependencies: async "~0.2.6" atom-select-list "^0.7.0" - pegjs "^0.10.0" + loophole "^1" + pegjs "~0.8.0" scoped-property-store "^0.17.0" season "^6.0.2" temp "~0.8.0" @@ -8796,6 +8858,18 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +sqlite3@^5.1.7: + version "5.1.7" + resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.1.7.tgz#59ca1053c1ab38647396586edad019b1551041b7" + integrity sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog== + dependencies: + bindings "^1.5.0" + node-addon-api "^7.0.0" + prebuild-install "^7.1.1" + tar "^6.1.11" + optionalDependencies: + node-gyp "8.x" + sshpk@^1.7.0: version "1.17.0" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" From e479936b3c3178e60bcaba42a85f94235d0cff89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Szabo?= Date: Sat, 10 Feb 2024 03:08:55 -0300 Subject: [PATCH 05/11] Reviving" Buffers --- src/state-store/sql.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/state-store/sql.js b/src/state-store/sql.js index f0c7244a30..807d74b4f8 100644 --- a/src/state-store/sql.js +++ b/src/state-store/sql.js @@ -33,13 +33,13 @@ module.exports = class SQLStateStore { } save(key, value) { - return this.dbPromise.then(db => - getOne(db, + return this.dbPromise.then(db => { + return getOne(db, `REPLACE INTO ${this.tableName} VALUES (?, ?)`, key, JSON.stringify({ value: value, storedAt: new Date().toString() }) ) - ) + }) } load(key) { @@ -47,7 +47,7 @@ module.exports = class SQLStateStore { getOne(db, `SELECT value FROM ${this.tableName} WHERE key = ?`, key ) ).then(result => { if(result) { - const parsed = JSON.parse(result.value); + const parsed = JSON.parse(result.value, reviver); return parsed?.value } return null; @@ -95,3 +95,11 @@ function awaitForAtomGlobal() { }, 50) }) } + +function reviver(_, value) { + if(value?.type === 'Buffer') { + return Buffer.from(value.data); + } else { + return value; + } +} From 55c0ab219b85b012a7303aab9942e4ee56a96b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Szabo?= Date: Sun, 11 Feb 2024 01:12:32 -0300 Subject: [PATCH 06/11] Adding a config to toggle to SQLite3 --- spec/state-store-spec.js | 145 ++++++++++++++++++++++++++------------- src/state-store.js | 67 +++++++++++++++++- src/state-store/sql.js | 30 +++++--- 3 files changed, 184 insertions(+), 58 deletions(-) diff --git a/spec/state-store-spec.js b/spec/state-store-spec.js index 6bb3f11170..89679a7b8d 100644 --- a/spec/state-store-spec.js +++ b/spec/state-store-spec.js @@ -6,66 +6,117 @@ describe('StateStore', () => { let databaseName = `test-database-${Date.now()}`; let version = 1; - it('can save, load, and delete states', () => { - const store = new StateStore(databaseName, version); - return store - .save('key', { foo: 'bar' }) - .then(() => store.load('key')) - .then(state => { - expect(state).toEqual({ foo: 'bar' }); - }) - .then(() => store.delete('key')) - .then(() => store.load('key')) - .then(value => { + describe('with the default IndexedDB backend', () => { + beforeEach(() => { + atom.config.set('core.useLegacySessionStore', true) + }) + + it('can save, load, and delete states', () => { + const store = new StateStore(databaseName, version); + return store + .save('key', { foo: 'bar' }) + .then(() => store.load('key')) + .then(state => { + expect(state).toEqual({ foo: 'bar' }); + }) + .then(() => store.delete('key')) + .then(() => store.load('key')) + .then(value => { + expect(value).toBeNull(); + }) + .then(() => store.count()) + .then(count => { + expect(count).toBe(0); + }); + }); + + it('resolves with null when a non-existent key is loaded', () => { + const store = new StateStore(databaseName, version); + return store.load('no-such-key').then(value => { expect(value).toBeNull(); - }) - .then(() => store.count()) - .then(count => { - expect(count).toBe(0); }); - }); + }); - it('resolves with null when a non-existent key is loaded', () => { - const store = new StateStore(databaseName, version); - return store.load('no-such-key').then(value => { - expect(value).toBeNull(); + it('can clear the state object store', () => { + const store = new StateStore(databaseName, version); + return store + .save('key', { foo: 'bar' }) + .then(() => store.count()) + .then(count => expect(count).toBe(1)) + .then(() => store.clear()) + .then(() => store.count()) + .then(count => { + expect(count).toBe(0); + }); }); - }); - it('can clear the state object store', () => { - const store = new StateStore(databaseName, version); - return store - .save('key', { foo: 'bar' }) - .then(() => store.count()) - .then(count => expect(count).toBe(1)) - .then(() => store.clear()) - .then(() => store.count()) - .then(count => { - expect(count).toBe(0); + describe('when there is an error reading from the database', () => { + it('rejects the promise returned by load', () => { + const store = new StateStore(databaseName, version); + + const fakeErrorEvent = { + target: { errorCode: 'Something bad happened' } + }; + + spyOn(IDBObjectStore.prototype, 'get').andCallFake(key => { + let request = {}; + process.nextTick(() => request.onerror(fakeErrorEvent)); + return request; + }); + + return store + .load('nonexistentKey') + .then(() => { + throw new Error('Promise should have been rejected'); + }) + .catch(event => { + expect(event).toBe(fakeErrorEvent); + }); }); + }); }); - describe('when there is an error reading from the database', () => { - it('rejects the promise returned by load', () => { - const store = new StateStore(databaseName, version); + describe('with the new SQLite3 backend', () => { + beforeEach(() => { + atom.config.set('core.useLegacySessionStore', false) + }) - const fakeErrorEvent = { - target: { errorCode: 'Something bad happened' } - }; + it('can save, load, and delete states', () => { + const store = new StateStore(databaseName, version); + return store + .save('key', { foo: 'bar' }) + .then(() => store.load('key')) + .then(state => { + expect(state).toEqual({ foo: 'bar' }); + }) + .then(() => store.delete('key')) + .then(() => store.load('key')) + .then(value => { + expect(value).toBeNull(); + }) + .then(() => store.count()) + .then(count => { + expect(count).toBe(0); + }); + }); - spyOn(IDBObjectStore.prototype, 'get').andCallFake(key => { - let request = {}; - process.nextTick(() => request.onerror(fakeErrorEvent)); - return request; + it('resolves with null when a non-existent key is loaded', () => { + const store = new StateStore(databaseName, version); + return store.load('no-such-key').then(value => { + expect(value).toBeNull(); }); + }); + it('can clear the state object store', () => { + const store = new StateStore(databaseName, version); return store - .load('nonexistentKey') - .then(() => { - throw new Error('Promise should have been rejected'); - }) - .catch(event => { - expect(event).toBe(fakeErrorEvent); + .save('key', { foo: 'bar' }) + .then(() => store.count()) + .then(count => expect(count).toBe(1)) + .then(() => store.clear()) + .then(() => store.count()) + .then(count => { + expect(count).toBe(0); }); }); }); diff --git a/src/state-store.js b/src/state-store.js index 1ceb0a61f7..1b3482aedc 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -2,5 +2,68 @@ const IndexedDB = require('./state-store/indexed-db'); const SQL = require('./state-store/sql'); -// module.exports = IndexedDB; -module.exports = SQL; +module.exports = class StateStore { + constructor(databaseName, version) { + this.databaseName = databaseName; + this.version = version; + } + + isConnected() { + // We don't need to wait for atom global here because this isConnected + // is only called on closing the editor + if(atom.config.get('core.useLegacySessionStore')) { + if(!this.indexed) return false; + return this.indexed.isConnected(); + } else { + if(!this.sql) return false; + return this.sql.isConnected(); + } + } + + connect() { + return this._getCorrectImplementation().then(i => i.connect()); + } + + save(key, value) { + return this._getCorrectImplementation().then(i => i.save(key, value)); + } + + load(key) { + return this._getCorrectImplementation().then(i => i.load(key)); + } + + delete(key) { + return this._getCorrectImplementation().then(i => i.delete(key)); + } + + clear() { + return this._getCorrectImplementation().then(i => i.clear()); + } + + count() { + return this._getCorrectImplementation().then(i => i.count()); + } + + _getCorrectImplementation() { + return awaitForAtomGlobal().then(() => { + if(atom.config.get('core.useLegacySessionStore')) { + this.indexed ||= new IndexedDB(this.databaseName, this.version); + return this.indexed; + } else { + this.sql ||= new SQL(this.databaseName, this.version); + return this.sql; + } + }); + } +}; + +function awaitForAtomGlobal() { + return new Promise(resolve => { + const i = setInterval(() => { + if(atom) { + clearInterval(i) + resolve() + } + }, 50) + }) +} diff --git a/src/state-store/sql.js b/src/state-store/sql.js index 807d74b4f8..0ca503f632 100644 --- a/src/state-store/sql.js +++ b/src/state-store/sql.js @@ -10,7 +10,17 @@ module.exports = class SQLStateStore { this.dbPromise = (async () => { await awaitForAtomGlobal(); const dbPath = path.join(atom.getConfigDirPath(), 'session-store.db'); - const db = new sqlite3.Database(dbPath); + let db; + try { + db = new sqlite3.Database(dbPath); + } catch(error) { + atom.notifications.addFatalError('Error loading database', { + stack: new Error('Error loading SQLite database for state storage').stack, + dismissable: true + }); + console.error('Error loading SQLite database', error); + return null + } await getOne(db, `CREATE TABLE IF NOT EXISTS ${this.tableName} (key VARCHAR, value JSON)` ); @@ -19,9 +29,8 @@ module.exports = class SQLStateStore { ); return db; })(); - // this. this.connected = false; - this.dbPromise.then(_ => this.connected = true); + this.dbPromise.then(db => this.connected = !!db); } isConnected() { @@ -34,6 +43,7 @@ module.exports = class SQLStateStore { save(key, value) { return this.dbPromise.then(db => { + if(!db) return null; return getOne(db, `REPLACE INTO ${this.tableName} VALUES (?, ?)`, key, @@ -43,9 +53,10 @@ module.exports = class SQLStateStore { } load(key) { - return this.dbPromise.then(db => - getOne(db, `SELECT value FROM ${this.tableName} WHERE key = ?`, key ) - ).then(result => { + return this.dbPromise.then(db => { + if(!db) return null; + return getOne(db, `SELECT value FROM ${this.tableName} WHERE key = ?`, key ) + }).then(result => { if(result) { const parsed = JSON.parse(result.value, reviver); return parsed?.value @@ -67,9 +78,10 @@ module.exports = class SQLStateStore { } count() { - return this.dbPromise.then(db => - getOne(db, `SELECT COUNT(key) c FROM ${this.tableName}`).then(r => r.c) - ); + return this.dbPromise.then(db => { + if(!db) return null; + return getOne(db, `SELECT COUNT(key) c FROM ${this.tableName}`).then(r => r.c) + }); } }; From a264906a94a0114537d954efbf790e9396ce8d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Szabo?= Date: Sun, 11 Feb 2024 01:12:48 -0300 Subject: [PATCH 07/11] Added a new config for toggling to SQLite3 --- src/config-schema.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/config-schema.js b/src/config-schema.js index fd038634f9..24fdb42a28 100644 --- a/src/config-schema.js +++ b/src/config-schema.js @@ -369,6 +369,12 @@ const configSchema = { title: 'Use Legacy Tree-sitter Implementation', description: 'Opt into the legacy Atom Tree-sitter system instead of the modern system added by Pulsar. (We plan to remove this legacy system soon.) Has no effect unless “Use Tree-sitter Parsers” is also checked.' }, + useLegacySessionStore: { + type: 'boolean', + default: true, + title: 'Use Legacy Session Store', + description: 'Opt into the legacy Atom session store (IndexedDB) instead of the new SQLite backend (We plan to remove this legacy system soon).' + }, colorProfile: { description: "Specify whether Pulsar should use the operating system's color profile (recommended) or an alternative color profile.
Changing this setting will require a relaunch of Pulsar to take effect.", From e38dedb33f9d3606feb7be601ca1fa76b26c2ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Szabo?= Date: Sun, 11 Feb 2024 01:15:38 -0300 Subject: [PATCH 08/11] Fixed clear() --- src/state-store/sql.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state-store/sql.js b/src/state-store/sql.js index 0ca503f632..b1ecd86305 100644 --- a/src/state-store/sql.js +++ b/src/state-store/sql.js @@ -73,7 +73,7 @@ module.exports = class SQLStateStore { clear() { return this.dbPromise.then(db => - getOne(db, `DROP TABLE ${this.tableName}`) + getOne(db, `DELETE FROM ${this.tableName}`) ); } From ff14af4c009942fd07937079682aa74e1334dd5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Szabo?= Date: Wed, 17 Jul 2024 12:20:46 -0300 Subject: [PATCH 09/11] Replaced Sqlite3 with better-sqlite3 --- package.json | 2 +- src/state-store/sql.js | 31 +++++++++++++++--------------- yarn.lock | 43 ++++++++++++++---------------------------- 3 files changed, 30 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index 0d49a6cfb8..e3b2e5c5f0 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "background-tips": "file:packages/background-tips", "base16-tomorrow-dark-theme": "file:packages/base16-tomorrow-dark-theme", "base16-tomorrow-light-theme": "file:packages/base16-tomorrow-light-theme", + "better-sqlite3": "^11.1.2", "bookmarks": "file:packages/bookmarks", "bracket-matcher": "file:packages/bracket-matcher", "chai": "4.3.4", @@ -165,7 +166,6 @@ "solarized-dark-syntax": "file:packages/solarized-dark-syntax", "solarized-light-syntax": "file:packages/solarized-light-syntax", "spell-check": "file:packages/spell-check", - "sqlite3": "^5.1.7", "status-bar": "file:packages/status-bar", "styleguide": "file:./packages/styleguide", "superstring": "github:pulsar-edit/superstring#de97b496663fce40050bf2d66e1466ccfbd00943", diff --git a/src/state-store/sql.js b/src/state-store/sql.js index b1ecd86305..7724bd5472 100644 --- a/src/state-store/sql.js +++ b/src/state-store/sql.js @@ -1,6 +1,6 @@ 'use strict'; -const sqlite3 = require('sqlite3').verbose(); +const sqlite3 = require('better-sqlite3'); const path = require('path'); module.exports = class SQLStateStore { @@ -12,7 +12,7 @@ module.exports = class SQLStateStore { const dbPath = path.join(atom.getConfigDirPath(), 'session-store.db'); let db; try { - db = new sqlite3.Database(dbPath); + db = sqlite3(dbPath); } catch(error) { atom.notifications.addFatalError('Error loading database', { stack: new Error('Error loading SQLite database for state storage').stack, @@ -21,10 +21,11 @@ module.exports = class SQLStateStore { console.error('Error loading SQLite database', error); return null } - await getOne(db, + db.pragma('journal_mode = WAL'); + db.exec( `CREATE TABLE IF NOT EXISTS ${this.tableName} (key VARCHAR, value JSON)` ); - await getOne(db, + db.exec( `CREATE UNIQUE INDEX IF NOT EXISTS "${table}_index" ON ${this.tableName}(key)` ); return db; @@ -44,7 +45,7 @@ module.exports = class SQLStateStore { save(key, value) { return this.dbPromise.then(db => { if(!db) return null; - return getOne(db, + return exec(db, `REPLACE INTO ${this.tableName} VALUES (?, ?)`, key, JSON.stringify({ value: value, storedAt: new Date().toString() }) @@ -67,13 +68,13 @@ module.exports = class SQLStateStore { delete(key) { return this.dbPromise.then(db => - getOne(db, `DELETE FROM ${this.tableName} WHERE key = ?`, key ) + exec(db, `DELETE FROM ${this.tableName} WHERE key = ?`, key ) ); } clear() { return this.dbPromise.then(db => - getOne(db, `DELETE FROM ${this.tableName}`) + exec(db, `DELETE FROM ${this.tableName}`) ); } @@ -86,15 +87,13 @@ module.exports = class SQLStateStore { }; function getOne(db, sql, ...params) { - return new Promise((resolve, reject) => { - db.get(sql, ...params, (error, result) => { - if(error) { - reject(error); - } else { - resolve(result); - } - }); - }); + const stmt = db.prepare(sql); + return stmt.get(params) +} + +function exec(db, sql, ...params) { + const stmt = db.prepare(sql); + stmt.run(params) } function awaitForAtomGlobal() { diff --git a/yarn.lock b/yarn.lock index 2b121a0529..9f0ed94d7b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2517,6 +2517,14 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +better-sqlite3@^11.1.2: + version "11.1.2" + resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-11.1.2.tgz#6c9d064c9f1ff2a7f507477648ca0ba67bf564a3" + integrity sha512-gujtFwavWU4MSPT+h9B+4pkvZdyOUkH54zgLdIrMmmmd4ZqiBIrRNBzNzYVFO417xo882uP5HBu4GjOfaSrIQw== + dependencies: + bindings "^1.5.0" + prebuild-install "^7.1.1" + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" @@ -6524,11 +6532,6 @@ loglevel@^1.6.0: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== -loophole@^1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/loophole/-/loophole-1.1.0.tgz#37949fea453b6256acc725c320ce0c5a7f70a2bd" - integrity sha512-zgLykD4MuL9HJRsoeKuGRXz4L9t1C0oDZPjFeUg5MLQyZjxWdxIbPHgRijVGN98DieVWJuo2fi9nEUDcdDbEiQ== - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -7147,11 +7150,6 @@ node-addon-api@^3.1.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== -node-addon-api@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.0.tgz#71f609369379c08e251c558527a107107b5e0fdb" - integrity sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g== - node-api-version@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/node-api-version/-/node-api-version-0.1.4.tgz#1ed46a485e462d55d66b5aa1fe2821720dedf080" @@ -7194,7 +7192,7 @@ node-gyp-build@^4.2.1: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== -node-gyp@8.x, node-gyp@^8.4.0: +node-gyp@^8.4.0: version "8.4.1" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== @@ -7640,10 +7638,10 @@ pause-stream@0.0.11: dependencies: through "~2.3" -pegjs@~0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/pegjs/-/pegjs-0.8.0.tgz#976f067da13e5c5b1501c017925668a253811561" - integrity sha512-GtAFD5WLxE0LjyhlpKwAnbi3NLJDrYsOvil95UCUQ6pzxlUtUGP/k0FnKGypTpM1WWdmoclfXb0dmMd5UUDkvA== +pegjs@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/pegjs/-/pegjs-0.10.0.tgz#cf8bafae6eddff4b5a7efb185269eaaf4610ddbd" + integrity sha512-qI5+oFNEGi3L5HAxDwN2LA4Gg7irF70Zs25edhjld9QemOgp0CbvMtbFcMvFtEo1OityPrcCzkQFB8JP/hxgow== pend@~1.2.0: version "1.2.0" @@ -8722,8 +8720,7 @@ smart-buffer@^4.0.2, smart-buffer@^4.2.0: dependencies: async "~0.2.6" atom-select-list "^0.7.0" - loophole "^1" - pegjs "~0.8.0" + pegjs "^0.10.0" scoped-property-store "^0.17.0" season "^6.0.2" temp "~0.8.0" @@ -8875,18 +8872,6 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -sqlite3@^5.1.7: - version "5.1.7" - resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.1.7.tgz#59ca1053c1ab38647396586edad019b1551041b7" - integrity sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog== - dependencies: - bindings "^1.5.0" - node-addon-api "^7.0.0" - prebuild-install "^7.1.1" - tar "^6.1.11" - optionalDependencies: - node-gyp "8.x" - sshpk@^1.7.0: version "1.17.0" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" From 873cb844175533db888424b55ced7f46da5ef312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Szabo?= Date: Sun, 15 Sep 2024 01:35:27 -0300 Subject: [PATCH 10/11] Fixed treating sync code as async --- src/state-store/sql.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/state-store/sql.js b/src/state-store/sql.js index 7724bd5472..85b92bd4a7 100644 --- a/src/state-store/sql.js +++ b/src/state-store/sql.js @@ -19,7 +19,7 @@ module.exports = class SQLStateStore { dismissable: true }); console.error('Error loading SQLite database', error); - return null + return null; } db.pragma('journal_mode = WAL'); db.exec( @@ -49,18 +49,18 @@ module.exports = class SQLStateStore { `REPLACE INTO ${this.tableName} VALUES (?, ?)`, key, JSON.stringify({ value: value, storedAt: new Date().toString() }) - ) - }) + ); + }); } load(key) { return this.dbPromise.then(db => { if(!db) return null; - return getOne(db, `SELECT value FROM ${this.tableName} WHERE key = ?`, key ) + return getOne(db, `SELECT value FROM ${this.tableName} WHERE key = ?`, key); }).then(result => { if(result) { const parsed = JSON.parse(result.value, reviver); - return parsed?.value + return parsed?.value; } return null; }); @@ -68,7 +68,7 @@ module.exports = class SQLStateStore { delete(key) { return this.dbPromise.then(db => - exec(db, `DELETE FROM ${this.tableName} WHERE key = ?`, key ) + exec(db, `DELETE FROM ${this.tableName} WHERE key = ?`, key) ); } @@ -81,7 +81,8 @@ module.exports = class SQLStateStore { count() { return this.dbPromise.then(db => { if(!db) return null; - return getOne(db, `SELECT COUNT(key) c FROM ${this.tableName}`).then(r => r.c) + const r = getOne(db, `SELECT COUNT(key) c FROM ${this.tableName}`); + return r.c; }); } }; @@ -100,10 +101,10 @@ function awaitForAtomGlobal() { return new Promise(resolve => { const i = setInterval(() => { if(atom) { - clearInterval(i) - resolve() + clearInterval(i); + resolve(); } - }, 50) + }, 50); }) } From 58cdd5f6c36ae42ce3b4117421b932ffea42e22a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Szabo?= Date: Sun, 15 Sep 2024 16:21:52 -0300 Subject: [PATCH 11/11] Removed auto retry that was giving false-positives --- .github/workflows/editor-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/editor-tests.yml b/.github/workflows/editor-tests.yml index c40264941f..53af44d66d 100644 --- a/.github/workflows/editor-tests.yml +++ b/.github/workflows/editor-tests.yml @@ -42,8 +42,8 @@ jobs: - name: Run Tests if: runner.os != 'Linux' - run: node script/run-tests.js spec + run: yarn start --test spec - name: Run Tests with xvfb-run (Linux) if: runner.os == 'Linux' - run: xvfb-run --auto-servernum node script/run-tests.js spec + run: xvfb-run --auto-servernum yarn start --test spec