diff --git a/libraries/object-persistor/test/unit/FSPersistorTests.js b/libraries/object-persistor/test/unit/FSPersistorTests.js index 220c7f95e4..804de81849 100644 --- a/libraries/object-persistor/test/unit/FSPersistorTests.js +++ b/libraries/object-persistor/test/unit/FSPersistorTests.js @@ -114,9 +114,8 @@ describe('FSPersistorTests', function () { }) it('should not write the target file', async function () { - await expect( - fsPromises.access(scenario.fsPath(files.wombat)) - ).to.be.rejected + await expect(fsPromises.access(scenario.fsPath(files.wombat))).to + .be.rejected }) it('should delete the temporary file', async function () { @@ -135,9 +134,8 @@ describe('FSPersistorTests', function () { const contents = await fsPromises.readFile( scenario.fsPath(files.wombat) ) - expect( - contents.equals(localFiles['/uploads/info.txt']) - ).to.be.true + expect(contents.equals(localFiles['/uploads/info.txt'])).to.be + .true }) }) @@ -151,9 +149,8 @@ describe('FSPersistorTests', function () { }) it('should not write the target file', async function () { - await expect( - fsPromises.access(scenario.fsPath(files.wombat)) - ).to.be.rejected + await expect(fsPromises.access(scenario.fsPath(files.wombat))).to + .be.rejected }) it('should delete the temporary file', async function () { @@ -203,9 +200,8 @@ describe('FSPersistorTests', function () { const contents = await fsPromises.readFile( scenario.fsPath(files.wombat) ) - expect( - contents.equals(localFiles['/uploads/info.txt']) - ).to.be.true + expect(contents.equals(localFiles['/uploads/info.txt'])).to.be + .true }) it('should delete the temporary file', async function () { @@ -224,9 +220,8 @@ describe('FSPersistorTests', function () { const contents = await fsPromises.readFile( scenario.fsPath(files.wombat) ) - expect( - contents.equals(localFiles['/uploads/other.txt']) - ).to.be.true + expect(contents.equals(localFiles['/uploads/other.txt'])).to.be + .true }) }) @@ -243,9 +238,8 @@ describe('FSPersistorTests', function () { const contents = await fsPromises.readFile( scenario.fsPath(files.wombat) ) - expect( - contents.equals(localFiles['/uploads/info.txt']) - ).to.be.true + expect(contents.equals(localFiles['/uploads/info.txt'])).to.be + .true }) it('should delete the temporary file', async function () { @@ -280,9 +274,8 @@ describe('FSPersistorTests', function () { ) const contents = await streamToBuffer(stream) // end is inclusive in ranges, but exclusive in slice() - expect( - contents.equals(localFiles['/uploads/info.txt'].slice(5, 17)) - ).to.be.true + expect(contents.equals(localFiles['/uploads/info.txt'].slice(5, 17))) + .to.be.true }) it('should give a NotFoundError if the file does not exist', async function () { @@ -332,9 +325,8 @@ describe('FSPersistorTests', function () { it('should delete the file', async function () { await persistor.deleteObject(location, files.wombat) - await expect( - fsPromises.access(scenario.fsPath(files.wombat)) - ).to.be.rejected + await expect(fsPromises.access(scenario.fsPath(files.wombat))).to.be + .rejected }) it("should ignore files that don't exist", async function () { diff --git a/libraries/overleaf-editor-core/lib/operation/text_operation.js b/libraries/overleaf-editor-core/lib/operation/text_operation.js index 7efe7fb1d8..8b0b41a971 100644 --- a/libraries/overleaf-editor-core/lib/operation/text_operation.js +++ b/libraries/overleaf-editor-core/lib/operation/text_operation.js @@ -792,8 +792,8 @@ function getSimpleOp(operation) { return ops[0] instanceof RetainOp ? ops[1] : ops[1] instanceof RetainOp - ? ops[0] - : null + ? ops[0] + : null case 3: if (ops[0] instanceof RetainOp && ops[2] instanceof RetainOp) { return ops[1] diff --git a/package-lock.json b/package-lock.json index e943887650..079633451e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^6.0.0", - "prettier": "2.5.1", + "prettier": "3.2.5", "resolve-url-loader": "^5.0.0", "sass": "^1.69.5", "sass-loader": "^13.3.2", @@ -35248,15 +35248,18 @@ } }, "node_modules/prettier": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", - "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/prettier-linter-helpers": { @@ -44299,7 +44302,6 @@ "postcss-loader": "^7.3.0", "postcss-preset-env": "^8.3.2", "postcss-reporter": "^7.0.5", - "prettier": "^2.5.1", "react-test-renderer": "^16.7.0", "react-transform-hmr": "^1.0.4", "redux-mock-store": "1.5.0", @@ -72046,7 +72048,6 @@ "postcss-loader": "^7.3.0", "postcss-preset-env": "^8.3.2", "postcss-reporter": "^7.0.5", - "prettier": "^2.5.1", "prop-types": "^15.8.1", "react": "^16.8.6", "react-bootstrap": "^0.31.5", @@ -77090,9 +77091,9 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, "prettier": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", - "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true }, "prettier-linter-helpers": { diff --git a/package.json b/package.json index f868562347..f092472caf 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^6.0.0", - "prettier": "2.5.1", + "prettier": "3.2.5", "resolve-url-loader": "^5.0.0", "sass": "^1.69.5", "sass-loader": "^13.3.2", diff --git a/server-ce/config/settings.js b/server-ce/config/settings.js index b70b9226fa..12e6296353 100644 --- a/server-ce/config/settings.js +++ b/server-ce/config/settings.js @@ -382,9 +382,7 @@ if (process.env.OVERLEAF_EMAIL_FROM_ADDRESS != null) { // i18n if (process.env.OVERLEAF_LANG_DOMAIN_MAPPING != null) { - settings.i18n.subdomainLang = parse( - process.env.OVERLEAF_LANG_DOMAIN_MAPPING - ) + settings.i18n.subdomainLang = parse(process.env.OVERLEAF_LANG_DOMAIN_MAPPING) } // Password Settings diff --git a/server-ce/services.js b/server-ce/services.js index a58892d5ed..e91e252ea9 100644 --- a/server-ce/services.js +++ b/server-ce/services.js @@ -1,39 +1,39 @@ module.exports = [ { - name: 'web' + name: 'web', }, { - name: 'real-time' + name: 'real-time', }, { - name: 'document-updater' + name: 'document-updater', }, { - name: 'clsi' + name: 'clsi', }, { - name: 'filestore' + name: 'filestore', }, { - name: 'docstore' + name: 'docstore', }, { - name: 'chat' + name: 'chat', }, { - name: 'spelling' + name: 'spelling', }, { - name: 'contacts' + name: 'contacts', }, { - name: 'notifications' + name: 'notifications', }, { - name: 'project-history' + name: 'project-history', }, { - name: 'history-v1' + name: 'history-v1', }, ] diff --git a/services/chat/test/acceptance/js/DestroyingAProjectTests.js b/services/chat/test/acceptance/js/DestroyingAProjectTests.js index 0a92181994..95c6c35033 100644 --- a/services/chat/test/acceptance/js/DestroyingAProjectTests.js +++ b/services/chat/test/acceptance/js/DestroyingAProjectTests.js @@ -47,9 +47,8 @@ describe('Destroying a project', async function () { const globalThreadMessage = await getMessage(this.globalThreadMessageId) expect(globalThreadMessage).to.exist - const { response: responseDestroy } = await ChatClient.destroyProject( - projectId - ) + const { response: responseDestroy } = + await ChatClient.destroyProject(projectId) expect(responseDestroy.statusCode).to.equal(204) }) diff --git a/services/chat/test/acceptance/js/EditingAMessageTests.js b/services/chat/test/acceptance/js/EditingAMessageTests.js index df17a32432..39f7edb28c 100644 --- a/services/chat/test/acceptance/js/EditingAMessageTests.js +++ b/services/chat/test/acceptance/js/EditingAMessageTests.js @@ -43,9 +43,8 @@ describe('Editing a message', async function () { }) it('should then list the updated message in the threads', async function () { - const { response, body: threads } = await ChatClient.getThreads( - projectId - ) + const { response, body: threads } = + await ChatClient.getThreads(projectId) expect(response.statusCode).to.equal(200) expect(threads[threadId].messages.length).to.equal(1) expect(threads[threadId].messages[0].content).to.equal(newContent) @@ -65,9 +64,8 @@ describe('Editing a message', async function () { }) it('should then list the updated message in the threads', async function () { - const { response, body: threads } = await ChatClient.getThreads( - projectId - ) + const { response, body: threads } = + await ChatClient.getThreads(projectId) expect(response.statusCode).to.equal(200) expect(threads[threadId].messages.length).to.equal(1) expect(threads[threadId].messages[0].content).to.equal(newContent) @@ -87,9 +85,8 @@ describe('Editing a message', async function () { }) it('should then list the old message in the threads', async function () { - const { response, body: threads } = await ChatClient.getThreads( - projectId - ) + const { response, body: threads } = + await ChatClient.getThreads(projectId) expect(response.statusCode).to.equal(200) expect(threads[threadId].messages.length).to.equal(1) expect(threads[threadId].messages[0].content).to.equal(content) diff --git a/services/chat/test/acceptance/js/GettingMessagesTests.js b/services/chat/test/acceptance/js/GettingMessagesTests.js index d53428901f..af6a0624a9 100644 --- a/services/chat/test/acceptance/js/GettingMessagesTests.js +++ b/services/chat/test/acceptance/js/GettingMessagesTests.js @@ -46,9 +46,8 @@ describe('Getting messages', async function () { }) it('should contain the messages and populated users when getting the messages', async function () { - const { response, body: messages } = await ChatClient.getGlobalMessages( - projectId - ) + const { response, body: messages } = + await ChatClient.getGlobalMessages(projectId) expect(response.statusCode).to.equal(200) expect(messages.length).to.equal(2) messages.reverse() diff --git a/services/chat/test/acceptance/js/SendingAMessageTests.js b/services/chat/test/acceptance/js/SendingAMessageTests.js index 27a06780af..7dbc1df3a5 100644 --- a/services/chat/test/acceptance/js/SendingAMessageTests.js +++ b/services/chat/test/acceptance/js/SendingAMessageTests.js @@ -26,9 +26,8 @@ describe('Sending a message', async function () { }) it('should then list the message in the project messages', async function () { - const { response, body: messages } = await ChatClient.getGlobalMessages( - projectId - ) + const { response, body: messages } = + await ChatClient.getGlobalMessages(projectId) expect(response.statusCode).to.equal(200) expect(messages.length).to.equal(1) expect(messages[0].content).to.equal(content) @@ -61,9 +60,8 @@ describe('Sending a message', async function () { }) it('should not appear in the global messages', async function () { - const { response, body: messages } = await ChatClient.getGlobalMessages( - projectId - ) + const { response, body: messages } = + await ChatClient.getGlobalMessages(projectId) expect(response.statusCode).to.equal(200) expect(messages.length).to.equal(0) }) diff --git a/services/clsi/app/js/ProjectPersistenceManager.js b/services/clsi/app/js/ProjectPersistenceManager.js index 52f61840c7..4dbe8b636e 100644 --- a/services/clsi/app/js/ProjectPersistenceManager.js +++ b/services/clsi/app/js/ProjectPersistenceManager.js @@ -88,15 +88,18 @@ module.exports = ProjectPersistenceManager = { }) }, () => { - setInterval(() => { - ProjectPersistenceManager.refreshExpiryTimeout(() => { - ProjectPersistenceManager.clearExpiredProjects(err => { - if (err) { - logger.error({ err }, 'clearing expired projects failed') - } + setInterval( + () => { + ProjectPersistenceManager.refreshExpiryTimeout(() => { + ProjectPersistenceManager.clearExpiredProjects(err => { + if (err) { + logger.error({ err }, 'clearing expired projects failed') + } + }) }) - }) - }, 10 * 60 * 1000) + }, + 10 * 60 * 1000 + ) } ) }) @@ -111,39 +114,38 @@ module.exports = ProjectPersistenceManager = { if (callback == null) { callback = function () {} } - return ProjectPersistenceManager._findExpiredProjectIds(function ( - error, - projectIds - ) { - if (error != null) { - return callback(error) - } - logger.debug({ projectIds }, 'clearing expired projects') - const jobs = Array.from(projectIds || []).map(projectId => - ( - projectId => callback => - ProjectPersistenceManager.clearProjectFromCache( - projectId, - { reason: 'expired' }, - function (err) { - if (err != null) { - logger.error({ err, projectId }, 'error clearing project') - } - return callback() - } - ) - )(projectId) - ) - return async.series(jobs, function (error) { + return ProjectPersistenceManager._findExpiredProjectIds( + function (error, projectIds) { if (error != null) { return callback(error) } - return CompileManager.clearExpiredProjects( - ProjectPersistenceManager.EXPIRY_TIMEOUT, - error => callback(error) + logger.debug({ projectIds }, 'clearing expired projects') + const jobs = Array.from(projectIds || []).map(projectId => + ( + projectId => callback => + ProjectPersistenceManager.clearProjectFromCache( + projectId, + { reason: 'expired' }, + function (err) { + if (err != null) { + logger.error({ err, projectId }, 'error clearing project') + } + return callback() + } + ) + )(projectId) ) - }) - }) + return async.series(jobs, function (error) { + if (error != null) { + return callback(error) + } + return CompileManager.clearExpiredProjects( + ProjectPersistenceManager.EXPIRY_TIMEOUT, + error => callback(error) + ) + }) + } + ) }, // ignore any errors from deleting directories clearProject(projectId, userId, callback) { diff --git a/services/document-updater/app/js/UpdateManager.js b/services/document-updater/app/js/UpdateManager.js index a086cb62e4..fc71e74b63 100644 --- a/services/document-updater/app/js/UpdateManager.js +++ b/services/document-updater/app/js/UpdateManager.js @@ -74,9 +74,8 @@ const UpdateManager = { doc_id: docId, }) - const updates = await RealTimeRedisManager.promises.getPendingUpdatesForDoc( - docId - ) + const updates = + await RealTimeRedisManager.promises.getPendingUpdatesForDoc(docId) logger.debug( { projectId, docId, count: updates.length }, 'processing updates' diff --git a/services/document-updater/app/js/sharejs/types/text-tp2.js b/services/document-updater/app/js/sharejs/types/text-tp2.js index e2a40cf6bc..c3ff9ecf40 100644 --- a/services/document-updater/app/js/sharejs/types/text-tp2.js +++ b/services/document-updater/app/js/sharejs/types/text-tp2.js @@ -129,8 +129,8 @@ type._takeDoc = takeDoc = function ( ? part.slice(position.offset, position.offset + maxlength) : part.slice(position.offset) : maxlength === undefined || tombsIndivisible - ? part - position.offset - : Math.min(maxlength, part - position.offset) + ? part - position.offset + : Math.min(maxlength, part - position.offset) const resultLen = result.length || result diff --git a/services/document-updater/test/stress/js/run.js b/services/document-updater/test/stress/js/run.js index cbf9369ef9..4d2614eac4 100644 --- a/services/document-updater/test/stress/js/run.js +++ b/services/document-updater/test/stress/js/run.js @@ -162,9 +162,12 @@ class StressTestClient { continue() { if (this.updateCount > 0) { this.updateCount-- - return setTimeout(() => { - return this.sendUpdate() - }, this.options.updateDelay * (0.5 + Math.random())) + return setTimeout( + () => { + return this.sendUpdate() + }, + this.options.updateDelay * (0.5 + Math.random()) + ) } else { return this.updateCallback() } diff --git a/services/history-v1/storage/lib/blob_hash.js b/services/history-v1/storage/lib/blob_hash.js index 52dc8cb3e2..5c2edac14f 100644 --- a/services/history-v1/storage/lib/blob_hash.js +++ b/services/history-v1/storage/lib/blob_hash.js @@ -26,25 +26,24 @@ function getBlobHash(byteLength) { * @param {stream.Readable} stream * @return {Promise.} hexadecimal SHA-1 hash */ -exports.fromStream = BPromise.method(function blobHashFromStream( - byteLength, - stream -) { - assert.integer(byteLength, 'blobHash: bad byteLength') - assert.object(stream, 'blobHash: bad stream') +exports.fromStream = BPromise.method( + function blobHashFromStream(byteLength, stream) { + assert.integer(byteLength, 'blobHash: bad byteLength') + assert.object(stream, 'blobHash: bad stream') - const hash = getBlobHash(byteLength) - return new BPromise(function (resolve, reject) { - pipeline(stream, hash, function (err) { - if (err) { - reject(err) - } else { - hash.end() - resolve(hash.read()) - } + const hash = getBlobHash(byteLength) + return new BPromise(function (resolve, reject) { + pipeline(stream, hash, function (err) { + if (err) { + reject(err) + } else { + hash.end() + resolve(hash.read()) + } + }) }) - }) -}) + } +) /** * Compute the git blob hash for a blob with the given string content. diff --git a/services/project-history/app/js/ErrorRecorder.js b/services/project-history/app/js/ErrorRecorder.js index 9530b86857..ab04eee063 100644 --- a/services/project-history/app/js/ErrorRecorder.js +++ b/services/project-history/app/js/ErrorRecorder.js @@ -194,111 +194,113 @@ export function getFailures(callback) { if (callback == null) { callback = function () {} } - return getFailuresByType(function ( - error, - failureCounts, - failureAttempts, - failureRequests, - maxQueueSize - ) { - let attempts, failureType, label, requests - if (error != null) { - return callback(OError.tag(error)) + return getFailuresByType( + function ( + error, + failureCounts, + failureAttempts, + failureRequests, + maxQueueSize + ) { + let attempts, failureType, label, requests + if (error != null) { + return callback(OError.tag(error)) + } + + const shortNames = { + 'Error: bad response from filestore: 404': 'filestore-404', + 'Error: bad response from filestore: 500': 'filestore-500', + 'NotFoundError: got a 404 from web api': 'web-api-404', + 'Error: history store a non-success status code: 413': + 'history-store-413', + 'Error: history store a non-success status code: 422': + 'history-store-422', + 'Error: history store a non-success status code: 500': + 'history-store-500', + 'Error: history store a non-success status code: 503': + 'history-store-503', + 'Error: web returned a non-success status code: 500 (attempts: 2)': + 'web-500', + 'Error: ESOCKETTIMEDOUT': 'socket-timeout', + 'Error: no project found': 'no-project-found', + 'OpsOutOfOrderError: project structure version out of order on incoming updates': + 'incoming-project-version-out-of-order', + 'OpsOutOfOrderError: doc version out of order on incoming updates': + 'incoming-doc-version-out-of-order', + 'OpsOutOfOrderError: project structure version out of order': + 'chunk-project-version-out-of-order', + 'OpsOutOfOrderError: doc version out of order': + 'chunk-doc-version-out-of-order', + 'Error: failed to extend lock': 'lock-overrun', + 'Error: tried to release timed out lock': 'lock-overrun', + 'Error: Timeout': 'lock-overrun', + 'Error: sync ongoing': 'sync-ongoing', + 'SyncError: unexpected resyncProjectStructure update': 'sync-error', + '[object Error]': 'unknown-error-object', + 'UpdateWithUnknownFormatError: update with unknown format': + 'unknown-format', + 'Error: update with unknown format': 'unknown-format', + 'TextOperationError: The base length of the second operation has to be the target length of the first operation': + 'text-op-error', + 'Error: ENOSPC: no space left on device, write': 'ENOSPC', + '*': 'other', + } + + // set all the known errors to zero if not present (otherwise gauges stay on their last value) + const summaryCounts = {} + const summaryAttempts = {} + const summaryRequests = {} + const summaryMaxQueueSize = {} + + for (failureType in shortNames) { + label = shortNames[failureType] + summaryCounts[label] = 0 + summaryAttempts[label] = 0 + summaryRequests[label] = 0 + summaryMaxQueueSize[label] = 0 + } + + // record a metric for each type of failure + for (failureType in failureCounts) { + const failureCount = failureCounts[failureType] + label = shortNames[failureType] || shortNames['*'] + summaryCounts[label] += failureCount + summaryAttempts[label] += failureAttempts[failureType] + summaryRequests[label] += failureRequests[failureType] + summaryMaxQueueSize[label] = Math.max( + maxQueueSize[failureType], + summaryMaxQueueSize[label] + ) + } + + for (label in summaryCounts) { + const count = summaryCounts[label] + metrics.globalGauge('failed', count, 1, { status: label }) + } + + for (label in summaryAttempts) { + attempts = summaryAttempts[label] + metrics.globalGauge('attempts', attempts, 1, { status: label }) + } + + for (label in summaryRequests) { + requests = summaryRequests[label] + metrics.globalGauge('requests', requests, 1, { status: label }) + } + + for (label in summaryMaxQueueSize) { + const queueSize = summaryMaxQueueSize[label] + metrics.globalGauge('max-queue-size', queueSize, 1, { status: label }) + } + + return callback(null, { + counts: summaryCounts, + attempts: summaryAttempts, + requests: summaryRequests, + maxQueueSize: summaryMaxQueueSize, + }) } - - const shortNames = { - 'Error: bad response from filestore: 404': 'filestore-404', - 'Error: bad response from filestore: 500': 'filestore-500', - 'NotFoundError: got a 404 from web api': 'web-api-404', - 'Error: history store a non-success status code: 413': - 'history-store-413', - 'Error: history store a non-success status code: 422': - 'history-store-422', - 'Error: history store a non-success status code: 500': - 'history-store-500', - 'Error: history store a non-success status code: 503': - 'history-store-503', - 'Error: web returned a non-success status code: 500 (attempts: 2)': - 'web-500', - 'Error: ESOCKETTIMEDOUT': 'socket-timeout', - 'Error: no project found': 'no-project-found', - 'OpsOutOfOrderError: project structure version out of order on incoming updates': - 'incoming-project-version-out-of-order', - 'OpsOutOfOrderError: doc version out of order on incoming updates': - 'incoming-doc-version-out-of-order', - 'OpsOutOfOrderError: project structure version out of order': - 'chunk-project-version-out-of-order', - 'OpsOutOfOrderError: doc version out of order': - 'chunk-doc-version-out-of-order', - 'Error: failed to extend lock': 'lock-overrun', - 'Error: tried to release timed out lock': 'lock-overrun', - 'Error: Timeout': 'lock-overrun', - 'Error: sync ongoing': 'sync-ongoing', - 'SyncError: unexpected resyncProjectStructure update': 'sync-error', - '[object Error]': 'unknown-error-object', - 'UpdateWithUnknownFormatError: update with unknown format': - 'unknown-format', - 'Error: update with unknown format': 'unknown-format', - 'TextOperationError: The base length of the second operation has to be the target length of the first operation': - 'text-op-error', - 'Error: ENOSPC: no space left on device, write': 'ENOSPC', - '*': 'other', - } - - // set all the known errors to zero if not present (otherwise gauges stay on their last value) - const summaryCounts = {} - const summaryAttempts = {} - const summaryRequests = {} - const summaryMaxQueueSize = {} - - for (failureType in shortNames) { - label = shortNames[failureType] - summaryCounts[label] = 0 - summaryAttempts[label] = 0 - summaryRequests[label] = 0 - summaryMaxQueueSize[label] = 0 - } - - // record a metric for each type of failure - for (failureType in failureCounts) { - const failureCount = failureCounts[failureType] - label = shortNames[failureType] || shortNames['*'] - summaryCounts[label] += failureCount - summaryAttempts[label] += failureAttempts[failureType] - summaryRequests[label] += failureRequests[failureType] - summaryMaxQueueSize[label] = Math.max( - maxQueueSize[failureType], - summaryMaxQueueSize[label] - ) - } - - for (label in summaryCounts) { - const count = summaryCounts[label] - metrics.globalGauge('failed', count, 1, { status: label }) - } - - for (label in summaryAttempts) { - attempts = summaryAttempts[label] - metrics.globalGauge('attempts', attempts, 1, { status: label }) - } - - for (label in summaryRequests) { - requests = summaryRequests[label] - metrics.globalGauge('requests', requests, 1, { status: label }) - } - - for (label in summaryMaxQueueSize) { - const queueSize = summaryMaxQueueSize[label] - metrics.globalGauge('max-queue-size', queueSize, 1, { status: label }) - } - - return callback(null, { - counts: summaryCounts, - attempts: summaryAttempts, - requests: summaryRequests, - maxQueueSize: summaryMaxQueueSize, - }) - }) + ) } export const promises = { diff --git a/services/project-history/app/js/FlushManager.js b/services/project-history/app/js/FlushManager.js index c8e413d56c..7f8514afc0 100644 --- a/services/project-history/app/js/FlushManager.js +++ b/services/project-history/app/js/FlushManager.js @@ -59,79 +59,81 @@ export function flushOldOps(options, callback) { if (error != null) { return callback(OError.tag(error)) } - return ErrorRecorder.getFailedProjects(function ( - error, - projectHistoryFailures - ) { - if (error != null) { - return callback(OError.tag(error)) - } - // exclude failed projects already in projectHistoryFailures - const failedProjects = new Set() - for (const entry of Array.from(projectHistoryFailures)) { - failedProjects.add(entry.project_id) - } - // randomise order so we get different projects if there is a limit - projectIds = _.shuffle(projectIds) - const maxAge = options.maxAge || 6 * 3600 // default to 6 hours - const cutoffTime = new Date(Date.now() - maxAge * 1000) - const startTime = new Date() - let count = 0 - const jobs = projectIds.map( - projectId => - function (cb) { - const timeTaken = new Date() - startTime - count++ - if ( - (options != null ? options.timeout : undefined) && - timeTaken > options.timeout - ) { - // finish early due to timeout, return an error to bail out of the async iteration - logger.debug('background retries timed out') - return cb(new OError('retries timed out')) - } - if ( - (options != null ? options.limit : undefined) && - count > options.limit - ) { - // finish early due to reaching limit, return an error to bail out of the async iteration - logger.debug({ count }, 'background retries hit limit') - return cb(new OError('hit limit')) - } - if (failedProjects.has(projectId)) { - // skip failed projects - return setTimeout(cb, options.queueDelay || 100) // pause between flushes - } - return flushIfOld(projectId, cutoffTime, function (err) { - if (err != null) { - logger.warn( - { projectId, flushErr: err }, - 'error flushing old project' - ) + return ErrorRecorder.getFailedProjects( + function (error, projectHistoryFailures) { + if (error != null) { + return callback(OError.tag(error)) + } + // exclude failed projects already in projectHistoryFailures + const failedProjects = new Set() + for (const entry of Array.from(projectHistoryFailures)) { + failedProjects.add(entry.project_id) + } + // randomise order so we get different projects if there is a limit + projectIds = _.shuffle(projectIds) + const maxAge = options.maxAge || 6 * 3600 // default to 6 hours + const cutoffTime = new Date(Date.now() - maxAge * 1000) + const startTime = new Date() + let count = 0 + const jobs = projectIds.map( + projectId => + function (cb) { + const timeTaken = new Date() - startTime + count++ + if ( + (options != null ? options.timeout : undefined) && + timeTaken > options.timeout + ) { + // finish early due to timeout, return an error to bail out of the async iteration + logger.debug('background retries timed out') + return cb(new OError('retries timed out')) + } + if ( + (options != null ? options.limit : undefined) && + count > options.limit + ) { + // finish early due to reaching limit, return an error to bail out of the async iteration + logger.debug({ count }, 'background retries hit limit') + return cb(new OError('hit limit')) + } + if (failedProjects.has(projectId)) { + // skip failed projects + return setTimeout(cb, options.queueDelay || 100) // pause between flushes + } + return flushIfOld(projectId, cutoffTime, function (err) { + if (err != null) { + logger.warn( + { projectId, flushErr: err }, + 'error flushing old project' + ) + } + return setTimeout(cb, options.queueDelay || 100) + }) + } + ) // pause between flushes + return async.series( + async.reflectAll(jobs), + function (error, results) { + const success = [] + const failure = [] + results.forEach((result, i) => { + if ( + result.error != null && + !['retries timed out', 'hit limit'].includes( + result?.error?.message + ) + ) { + // ignore expected errors + return failure.push(projectIds[i]) + } else { + return success.push(projectIds[i]) } - return setTimeout(cb, options.queueDelay || 100) }) + return callback(error, { success, failure, failedProjects }) } - ) // pause between flushes - return async.series(async.reflectAll(jobs), function (error, results) { - const success = [] - const failure = [] - results.forEach((result, i) => { - if ( - result.error != null && - !['retries timed out', 'hit limit'].includes( - result?.error?.message - ) - ) { - // ignore expected errors - return failure.push(projectIds[i]) - } else { - return success.push(projectIds[i]) - } - }) - return callback(error, { success, failure, failedProjects }) - }) - }) + ) + } + ) } ) } diff --git a/services/project-history/app/js/RetryManager.js b/services/project-history/app/js/RetryManager.js index 4acefa5ea2..5c52931000 100644 --- a/services/project-history/app/js/RetryManager.js +++ b/services/project-history/app/js/RetryManager.js @@ -175,9 +175,8 @@ async function checkProjectHasHistoryId(projectId) { async function waitUntilRedisQueueIsEmpty(projectId) { for (let attempts = 0; attempts < 30; attempts++) { - const updatesCount = await RedisManager.promises.countUnprocessedUpdates( - projectId - ) + const updatesCount = + await RedisManager.promises.countUnprocessedUpdates(projectId) if (updatesCount === 0) { return } diff --git a/services/project-history/app/js/UpdateCompressor.js b/services/project-history/app/js/UpdateCompressor.js index 7d801a0cd7..d6d6867b1d 100644 --- a/services/project-history/app/js/UpdateCompressor.js +++ b/services/project-history/app/js/UpdateCompressor.js @@ -252,12 +252,12 @@ function _concatTwoUpdates(firstUpdate, secondUpdate) { firstOp.p === secondOp.p ) { offset = firstOp.p - const diffUpdates = diffAsShareJsOps(firstOp.d, secondOp.i).map(function ( - op - ) { - op.p += offset - return mergeUpdatesWithOp(firstUpdate, secondUpdate, op) - }) + const diffUpdates = diffAsShareJsOps(firstOp.d, secondOp.i).map( + function (op) { + op.p += offset + return mergeUpdatesWithOp(firstUpdate, secondUpdate, op) + } + ) // Doing a diff like this loses track of the doc lengths for each // update, so recalculate them diff --git a/services/project-history/scripts/clear_dangling_timestamps.js b/services/project-history/scripts/clear_dangling_timestamps.js index 8779006495..e83eb1d7fa 100644 --- a/services/project-history/scripts/clear_dangling_timestamps.js +++ b/services/project-history/scripts/clear_dangling_timestamps.js @@ -25,9 +25,8 @@ async function main() { let clearedTimestamps = 0 let processed = 0 for (const projectId of projectIdsWithFirstOpTimestamps) { - const result = await RedisManager.promises.clearDanglingFirstOpTimestamp( - projectId - ) + const result = + await RedisManager.promises.clearDanglingFirstOpTimestamp(projectId) processed++ clearedTimestamps += result if (processed % 1000 === 0) { diff --git a/services/real-time/app/js/RedisClientManager.js b/services/real-time/app/js/RedisClientManager.js index d818f43a05..63eccef106 100644 --- a/services/real-time/app/js/RedisClientManager.js +++ b/services/real-time/app/js/RedisClientManager.js @@ -8,10 +8,10 @@ module.exports = { const redisType = x.cluster ? 'cluster' : x.sentinels - ? 'sentinel' - : x.host - ? 'single' - : 'unknown' + ? 'sentinel' + : x.host + ? 'single' + : 'unknown' logger.debug({ redis: redisType }, 'creating redis client') return redis.createClient(x) }) diff --git a/services/web/app/src/Features/Authentication/AuthenticationManager.js b/services/web/app/src/Features/Authentication/AuthenticationManager.js index 937e1d67b1..3d1b491f3a 100644 --- a/services/web/app/src/Features/Authentication/AuthenticationManager.js +++ b/services/web/app/src/Features/Authentication/AuthenticationManager.js @@ -166,9 +166,8 @@ const AuthenticationManager = { let isPasswordReused try { - isPasswordReused = await HaveIBeenPwned.promises.checkPasswordForReuse( - password - ) + isPasswordReused = + await HaveIBeenPwned.promises.checkPasswordForReuse(password) } catch (err) { logger.err({ err }, 'cannot check password for re-use') } @@ -333,9 +332,8 @@ const AuthenticationManager = { let isPasswordReused try { - isPasswordReused = await HaveIBeenPwned.promises.checkPasswordForReuse( - password - ) + isPasswordReused = + await HaveIBeenPwned.promises.checkPasswordForReuse(password) } catch (error) { logger.err({ error }, 'cannot check password for re-use') } diff --git a/services/web/app/src/Features/Collaborators/CollaboratorsInviteController.js b/services/web/app/src/Features/Collaborators/CollaboratorsInviteController.js index 3546972099..cc457d7309 100644 --- a/services/web/app/src/Features/Collaborators/CollaboratorsInviteController.js +++ b/services/web/app/src/Features/Collaborators/CollaboratorsInviteController.js @@ -34,9 +34,8 @@ const CollaboratorsInviteController = { async getAllInvites(req, res) { const projectId = req.params.Project_id logger.debug({ projectId }, 'getting all active invites for project') - const invites = await CollaboratorsInviteHandler.promises.getAllInvites( - projectId - ) + const invites = + await CollaboratorsInviteHandler.promises.getAllInvites(projectId) res.json({ invites }) }, @@ -115,9 +114,8 @@ const CollaboratorsInviteController = { return res.status(400).json({ errorReason: 'invalid_email' }) } - const underRateLimit = await CollaboratorsInviteController._checkRateLimit( - sendingUserId - ) + const underRateLimit = + await CollaboratorsInviteController._checkRateLimit(sendingUserId) if (!underRateLimit) { return res.sendStatus(429) } diff --git a/services/web/app/src/Features/Editor/EditorHttpController.js b/services/web/app/src/Features/Editor/EditorHttpController.js index bd0ca113ea..6e820c2fe4 100644 --- a/services/web/app/src/Features/Editor/EditorHttpController.js +++ b/services/web/app/src/Features/Editor/EditorHttpController.js @@ -152,17 +152,15 @@ async function joinProject(req, res, next) { } async function _buildJoinProjectView(req, projectId, userId) { - const project = await ProjectGetter.promises.getProjectWithoutDocLines( - projectId - ) + const project = + await ProjectGetter.promises.getProjectWithoutDocLines(projectId) if (project == null) { throw new Errors.NotFoundError('project not found') } let deletedDocsFromDocstore = [] try { - deletedDocsFromDocstore = await DocstoreManager.promises.getAllDeletedDocs( - projectId - ) + deletedDocsFromDocstore = + await DocstoreManager.promises.getAllDeletedDocs(projectId) } catch (err) { // The query in docstore is not optimized at this time and fails for // projects with many very large, deleted documents. @@ -187,9 +185,8 @@ async function _buildJoinProjectView(req, projectId, userId) { if (privilegeLevel == null || privilegeLevel === PrivilegeLevels.NONE) { return { project: null, privilegeLevel: null, isRestrictedUser: false } } - const invites = await CollaboratorsInviteHandler.promises.getAllInvites( - projectId - ) + const invites = + await CollaboratorsInviteHandler.promises.getAllInvites(projectId) const isTokenMember = await CollaboratorsHandler.promises.userIsTokenMember( userId, projectId diff --git a/services/web/app/src/Features/Email/Layouts/BaseWithHeaderEmailLayout.js b/services/web/app/src/Features/Email/Layouts/BaseWithHeaderEmailLayout.js index 9be1b73595..11546e74c0 100644 --- a/services/web/app/src/Features/Email/Layouts/BaseWithHeaderEmailLayout.js +++ b/services/web/app/src/Features/Email/Layouts/BaseWithHeaderEmailLayout.js @@ -375,10 +375,10 @@ module.exports = _.template(`\ ? `${settings.email.template.customFooter}
` : '' }${settings.appName} • ${ - settings.siteUrl -} + settings.siteUrl + }" style="Margin: 0; color: #0F7A06; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: left; text-decoration: none;">${ + settings.siteUrl + }

diff --git a/services/web/app/src/Features/History/HistoryManager.js b/services/web/app/src/Features/History/HistoryManager.js index e1f20f1a8d..b59ba5e8dd 100644 --- a/services/web/app/src/Features/History/HistoryManager.js +++ b/services/web/app/src/Features/History/HistoryManager.js @@ -138,8 +138,8 @@ async function injectUserDetails(data) { const entries = Array.isArray(data.diff) ? data.diff : Array.isArray(data.updates) - ? data.updates - : [] + ? data.updates + : [] for (const entry of entries) { for (const user of (entry.meta && entry.meta.users) || []) { if (typeof user === 'string') { diff --git a/services/web/app/src/Features/Institutions/InstitutionsManager.js b/services/web/app/src/Features/Institutions/InstitutionsManager.js index f7c1fc6c52..116837c7d8 100644 --- a/services/web/app/src/Features/Institutions/InstitutionsManager.js +++ b/services/web/app/src/Features/Institutions/InstitutionsManager.js @@ -105,9 +105,8 @@ const InstitutionsManager = { async refreshInstitutionUsers(institutionId, notify) { const refreshFunction = notify ? refreshFeaturesAndNotify : refreshFeatures - const { institution, affiliations } = await fetchInstitutionAndAffiliations( - institutionId - ) + const { institution, affiliations } = + await fetchInstitutionAndAffiliations(institutionId) for (const affiliation of affiliations) { affiliation.institutionName = institution.name @@ -316,9 +315,8 @@ async function refreshFeaturesAndNotify(affiliation) { const getUserInfo = async userId => { const user = await UserGetter.promises.getUser(userId, { _id: 1 }) - const subscription = await SubscriptionLocator.promises.getUsersSubscription( - user - ) + const subscription = + await SubscriptionLocator.promises.getUsersSubscription(user) return { user, subscription } } diff --git a/services/web/app/src/Features/PasswordReset/PasswordResetController.js b/services/web/app/src/Features/PasswordReset/PasswordResetController.js index 739217b997..f9ea0cc7a7 100644 --- a/services/web/app/src/Features/PasswordReset/PasswordResetController.js +++ b/services/web/app/src/Features/PasswordReset/PasswordResetController.js @@ -114,9 +114,8 @@ async function requestReset(req, res, next) { let status try { - status = await PasswordResetHandler.promises.generateAndEmailResetToken( - email - ) + status = + await PasswordResetHandler.promises.generateAndEmailResetToken(email) } catch (err) { OError.tag(err, 'failed to generate and email password reset token', { email, diff --git a/services/web/app/src/Features/Project/ProjectController.js b/services/web/app/src/Features/Project/ProjectController.js index 4ba5fad2c5..dc2e054d71 100644 --- a/services/web/app/src/Features/Project/ProjectController.js +++ b/services/web/app/src/Features/Project/ProjectController.js @@ -751,8 +751,8 @@ const ProjectController = { ? // TODO: Create React version of detached page 'project/editor_detached' : idePageReact - ? 'project/ide-react' - : 'project/editor' + ? 'project/ide-react' + : 'project/editor' res.render(template, { title: project.name, diff --git a/services/web/app/src/Features/Project/ProjectDeleter.js b/services/web/app/src/Features/Project/ProjectDeleter.js index 91b742befb..5e8df133bd 100644 --- a/services/web/app/src/Features/Project/ProjectDeleter.js +++ b/services/web/app/src/Features/Project/ProjectDeleter.js @@ -418,9 +418,8 @@ let deletedFilesProjectIdIndexExist async function doesDeletedFilesProjectIdIndexExist() { if (typeof deletedFilesProjectIdIndexExist !== 'boolean') { // Resolve this about once. No need for locking or retry handling. - deletedFilesProjectIdIndexExist = await db.deletedFiles.indexExists( - 'projectId_1' - ) + deletedFilesProjectIdIndexExist = + await db.deletedFiles.indexExists('projectId_1') } return deletedFilesProjectIdIndexExist } diff --git a/services/web/app/src/Features/Project/ProjectEntityMongoUpdateHandler.js b/services/web/app/src/Features/Project/ProjectEntityMongoUpdateHandler.js index baf7cf047c..cf543a3a4e 100644 --- a/services/web/app/src/Features/Project/ProjectEntityMongoUpdateHandler.js +++ b/services/web/app/src/Features/Project/ProjectEntityMongoUpdateHandler.js @@ -265,9 +265,8 @@ async function mkdirp(projectId, path, options = {}) { // to make matching case-sensitive const folders = path.split('/').filter(folder => folder.length !== 0) - const project = await ProjectGetter.promises.getProjectWithOnlyFolders( - projectId - ) + const project = + await ProjectGetter.promises.getProjectWithOnlyFolders(projectId) if (path === '/') { return { newFolders: [], folder: project.rootFolder[0] } } diff --git a/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js b/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js index 9e7b52460a..a332397b11 100644 --- a/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js +++ b/services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js @@ -645,153 +645,159 @@ const ProjectEntityUpdateHandler = { ) }, - upsertDoc: wrapWithLock(function ( - projectId, - folderId, - docName, - docLines, - source, - userId, - callback - ) { - if (!SafePath.isCleanFilename(docName)) { - return callback(new Errors.InvalidNameError('invalid element name')) - } - ProjectLocator.findElement( - { project_id: projectId, element_id: folderId, type: 'folder' }, - (error, folder, folderPath) => { - if (error != null) { - if (error instanceof Errors.NotFoundError && folder == null) { - return callback(new Error('folder_not_found')) - } - return callback(error) - } - if (folder == null) { - return callback(new Error("Couldn't find folder")) - } - const existingDoc = folder.docs.find(({ name }) => name === docName) - const existingFile = folder.fileRefs.find( - ({ name }) => name === docName - ) - if (existingFile) { - const doc = new Doc({ name: docName }) - const filePath = `${folderPath.fileSystem}/${existingFile.name}` - DocstoreManager.updateDoc( - projectId.toString(), - doc._id.toString(), - docLines, - 0, - {}, - (err, modified, rev) => { - if (err != null) { - return callback(err) - } - doc.rev = rev - ProjectEntityMongoUpdateHandler.replaceFileWithDoc( - projectId, - existingFile._id, - doc, - (err, project) => { - if (err) { - return callback(err) - } - TpdsUpdateSender.addDoc( - { - projectId, - docId: doc._id, - path: filePath, - projectName: project.name, - rev: existingFile.rev + 1, - folderId, - }, - err => { - if (err) { - return callback(err) - } - const projectHistoryId = - project.overleaf && - project.overleaf.history && - project.overleaf.history.id - const newDocs = [ - { - doc, - path: filePath, - docLines: docLines.join('\n'), - }, - ] - const oldFiles = [ - { - file: existingFile, - path: filePath, - }, - ] - DocumentUpdaterHandler.updateProjectStructure( - projectId, - projectHistoryId, - userId, - { oldFiles, newDocs, newProject: project }, - source, - error => { - if (error != null) { - return callback(error) - } - EditorRealTimeController.emitToRoom( - projectId, - 'removeEntity', - existingFile._id, - 'convertFileToDoc' - ) - callback(null, doc, true) - } - ) - } - ) - } - ) - } - ) - } else if (existingDoc) { - DocumentUpdaterHandler.setDocument( - projectId, - existingDoc._id, - userId, - docLines, - source, - (err, result) => { - if (err != null) { - return callback(err) - } - logger.debug( - { projectId, docId: existingDoc._id }, - 'notifying users that the document has been updated' - ) - // there is no need to flush the doc to mongo at this point as docupdater - // flushes it as part of setDoc. - // - // combine rev from response with existing doc metadata - callback(null, { ...existingDoc, ...result }, existingDoc == null) - } - ) - } else { - ProjectEntityUpdateHandler.addDocWithRanges.withoutLock( - projectId, - folderId, - docName, - docLines, - {}, - userId, - source, - (err, doc) => { - if (err != null) { - return callback(err) - } - callback(null, doc, existingDoc == null) - } - ) - } + upsertDoc: wrapWithLock( + function ( + projectId, + folderId, + docName, + docLines, + source, + userId, + callback + ) { + if (!SafePath.isCleanFilename(docName)) { + return callback(new Errors.InvalidNameError('invalid element name')) } - ) - }), + ProjectLocator.findElement( + { project_id: projectId, element_id: folderId, type: 'folder' }, + (error, folder, folderPath) => { + if (error != null) { + if (error instanceof Errors.NotFoundError && folder == null) { + return callback(new Error('folder_not_found')) + } + return callback(error) + } + if (folder == null) { + return callback(new Error("Couldn't find folder")) + } + const existingDoc = folder.docs.find(({ name }) => name === docName) + const existingFile = folder.fileRefs.find( + ({ name }) => name === docName + ) + if (existingFile) { + const doc = new Doc({ name: docName }) + const filePath = `${folderPath.fileSystem}/${existingFile.name}` + DocstoreManager.updateDoc( + projectId.toString(), + doc._id.toString(), + docLines, + 0, + {}, + (err, modified, rev) => { + if (err != null) { + return callback(err) + } + doc.rev = rev + ProjectEntityMongoUpdateHandler.replaceFileWithDoc( + projectId, + existingFile._id, + doc, + (err, project) => { + if (err) { + return callback(err) + } + TpdsUpdateSender.addDoc( + { + projectId, + docId: doc._id, + path: filePath, + projectName: project.name, + rev: existingFile.rev + 1, + folderId, + }, + err => { + if (err) { + return callback(err) + } + const projectHistoryId = + project.overleaf && + project.overleaf.history && + project.overleaf.history.id + const newDocs = [ + { + doc, + path: filePath, + docLines: docLines.join('\n'), + }, + ] + const oldFiles = [ + { + file: existingFile, + path: filePath, + }, + ] + DocumentUpdaterHandler.updateProjectStructure( + projectId, + projectHistoryId, + userId, + { oldFiles, newDocs, newProject: project }, + source, + error => { + if (error != null) { + return callback(error) + } + EditorRealTimeController.emitToRoom( + projectId, + 'removeEntity', + existingFile._id, + 'convertFileToDoc' + ) + callback(null, doc, true) + } + ) + } + ) + } + ) + } + ) + } else if (existingDoc) { + DocumentUpdaterHandler.setDocument( + projectId, + existingDoc._id, + userId, + docLines, + source, + (err, result) => { + if (err != null) { + return callback(err) + } + logger.debug( + { projectId, docId: existingDoc._id }, + 'notifying users that the document has been updated' + ) + // there is no need to flush the doc to mongo at this point as docupdater + // flushes it as part of setDoc. + // + // combine rev from response with existing doc metadata + callback( + null, + { ...existingDoc, ...result }, + existingDoc == null + ) + } + ) + } else { + ProjectEntityUpdateHandler.addDocWithRanges.withoutLock( + projectId, + folderId, + docName, + docLines, + {}, + userId, + source, + (err, doc) => { + if (err != null) { + return callback(err) + } + callback(null, doc, existingDoc == null) + } + ) + } + } + ) + } + ), upsertFile: wrapWithLock({ beforeLock(next) { @@ -983,43 +989,38 @@ const ProjectEntityUpdateHandler = { }, }), - upsertDocWithPath: wrapWithLock(function ( - projectId, - elementPath, - docLines, - source, - userId, - callback - ) { - if (!SafePath.isCleanPath(elementPath)) { - return callback(new Errors.InvalidNameError('invalid element name')) - } - const docName = Path.basename(elementPath) - const folderPath = Path.dirname(elementPath) - ProjectEntityUpdateHandler.mkdirp.withoutLock( - projectId, - folderPath, - (err, newFolders, folder) => { - if (err != null) { - return callback(err) - } - ProjectEntityUpdateHandler.upsertDoc.withoutLock( - projectId, - folder._id, - docName, - docLines, - source, - userId, - (err, doc, isNewDoc) => { - if (err != null) { - return callback(err) - } - callback(null, doc, isNewDoc, newFolders, folder) - } - ) + upsertDocWithPath: wrapWithLock( + function (projectId, elementPath, docLines, source, userId, callback) { + if (!SafePath.isCleanPath(elementPath)) { + return callback(new Errors.InvalidNameError('invalid element name')) } - ) - }), + const docName = Path.basename(elementPath) + const folderPath = Path.dirname(elementPath) + ProjectEntityUpdateHandler.mkdirp.withoutLock( + projectId, + folderPath, + (err, newFolders, folder) => { + if (err != null) { + return callback(err) + } + ProjectEntityUpdateHandler.upsertDoc.withoutLock( + projectId, + folder._id, + docName, + docLines, + source, + userId, + (err, doc, isNewDoc) => { + if (err != null) { + return callback(err) + } + callback(null, doc, isNewDoc, newFolders, folder) + } + ) + } + ) + } + ), upsertFileWithPath: wrapWithLock({ beforeLock(next) { @@ -1115,64 +1116,62 @@ const ProjectEntityUpdateHandler = { }, }), - deleteEntity: wrapWithLock(function ( - projectId, - entityId, - entityType, - userId, - source, - callback - ) { - logger.debug({ entityId, entityType, projectId }, 'deleting project entity') - if (entityType == null) { - logger.warn({ err: 'No entityType set', projectId, entityId }) - return callback(new Error('No entityType set')) - } - entityType = entityType.toLowerCase() - ProjectEntityMongoUpdateHandler.deleteEntity( - projectId, - entityId, - entityType, - (error, entity, path, projectBeforeDeletion, newProject) => { - if (error != null) { - return callback(error) - } - ProjectEntityUpdateHandler._cleanUpEntity( - projectBeforeDeletion, - newProject, - entity, - entityType, - path.fileSystem, - userId, - source, - (error, subtreeListing) => { - if (error != null) { - return callback(error) - } - const subtreeEntityIds = subtreeListing.map(entry => - entry.entity._id.toString() - ) - TpdsUpdateSender.deleteEntity( - { - projectId, - path: path.fileSystem, - projectName: projectBeforeDeletion.name, - entityId, - entityType, - subtreeEntityIds, - }, - error => { - if (error != null) { - return callback(error) - } - callback(null, entityId) - } - ) - } - ) + deleteEntity: wrapWithLock( + function (projectId, entityId, entityType, userId, source, callback) { + logger.debug( + { entityId, entityType, projectId }, + 'deleting project entity' + ) + if (entityType == null) { + logger.warn({ err: 'No entityType set', projectId, entityId }) + return callback(new Error('No entityType set')) } - ) - }), + entityType = entityType.toLowerCase() + ProjectEntityMongoUpdateHandler.deleteEntity( + projectId, + entityId, + entityType, + (error, entity, path, projectBeforeDeletion, newProject) => { + if (error != null) { + return callback(error) + } + ProjectEntityUpdateHandler._cleanUpEntity( + projectBeforeDeletion, + newProject, + entity, + entityType, + path.fileSystem, + userId, + source, + (error, subtreeListing) => { + if (error != null) { + return callback(error) + } + const subtreeEntityIds = subtreeListing.map(entry => + entry.entity._id.toString() + ) + TpdsUpdateSender.deleteEntity( + { + projectId, + path: path.fileSystem, + projectName: projectBeforeDeletion.name, + entityId, + entityType, + subtreeEntityIds, + }, + error => { + if (error != null) { + return callback(error) + } + callback(null, entityId) + } + ) + } + ) + } + ) + } + ), deleteEntityWithPath: wrapWithLock( (projectId, path, userId, source, callback) => @@ -1225,166 +1224,167 @@ const ProjectEntityUpdateHandler = { ) }), - addFolder: wrapWithLock(function ( - projectId, - parentFolderId, - folderName, - callback - ) { - if (!SafePath.isCleanFilename(folderName)) { - return callback(new Errors.InvalidNameError('invalid element name')) + addFolder: wrapWithLock( + function (projectId, parentFolderId, folderName, callback) { + if (!SafePath.isCleanFilename(folderName)) { + return callback(new Errors.InvalidNameError('invalid element name')) + } + ProjectEntityMongoUpdateHandler.addFolder( + projectId, + parentFolderId, + folderName, + callback + ) } - ProjectEntityMongoUpdateHandler.addFolder( + ), + + moveEntity: wrapWithLock( + function ( projectId, - parentFolderId, - folderName, + entityId, + destFolderId, + entityType, + userId, + source, callback - ) - }), - - moveEntity: wrapWithLock(function ( - projectId, - entityId, - destFolderId, - entityType, - userId, - source, - callback - ) { - logger.debug( - { entityType, entityId, projectId, destFolderId }, - 'moving entity' - ) - if (entityType == null) { - logger.warn({ err: 'No entityType set', projectId, entityId }) - return callback(new Error('No entityType set')) - } - entityType = entityType.toLowerCase() - DocumentUpdaterHandler.flushProjectToMongo(projectId, err => { - if (err) { - return callback(err) - } - ProjectEntityMongoUpdateHandler.moveEntity( - projectId, - entityId, - destFolderId, - entityType, - (err, project, startPath, endPath, rev, changes) => { - if (err != null) { - return callback(err) - } - const projectHistoryId = - project.overleaf && - project.overleaf.history && - project.overleaf.history.id - TpdsUpdateSender.moveEntity( - { - projectId, - projectName: project.name, - startPath, - endPath, - rev, - entityId, - entityType, - folderId: destFolderId, - }, - err => { - if (err) { - logger.error({ err }, 'error sending tpds update') - } - DocumentUpdaterHandler.updateProjectStructure( - projectId, - projectHistoryId, - userId, - changes, - source, - callback - ) - } - ) - } + ) { + logger.debug( + { entityType, entityId, projectId, destFolderId }, + 'moving entity' ) - }) - }), - - renameEntity: wrapWithLock(function ( - projectId, - entityId, - entityType, - newName, - userId, - source, - callback - ) { - if (!newName || typeof newName !== 'string') { - const err = new OError('invalid newName value', { - value: newName, - type: typeof newName, - projectId, - entityId, - entityType, - userId, - source, + if (entityType == null) { + logger.warn({ err: 'No entityType set', projectId, entityId }) + return callback(new Error('No entityType set')) + } + entityType = entityType.toLowerCase() + DocumentUpdaterHandler.flushProjectToMongo(projectId, err => { + if (err) { + return callback(err) + } + ProjectEntityMongoUpdateHandler.moveEntity( + projectId, + entityId, + destFolderId, + entityType, + (err, project, startPath, endPath, rev, changes) => { + if (err != null) { + return callback(err) + } + const projectHistoryId = + project.overleaf && + project.overleaf.history && + project.overleaf.history.id + TpdsUpdateSender.moveEntity( + { + projectId, + projectName: project.name, + startPath, + endPath, + rev, + entityId, + entityType, + folderId: destFolderId, + }, + err => { + if (err) { + logger.error({ err }, 'error sending tpds update') + } + DocumentUpdaterHandler.updateProjectStructure( + projectId, + projectHistoryId, + userId, + changes, + source, + callback + ) + } + ) + } + ) }) - logger.error({ err }, 'Invalid newName passed to renameEntity') - return callback(err) } - if (!SafePath.isCleanFilename(newName)) { - return callback(new Errors.InvalidNameError('invalid element name')) - } - logger.debug({ entityId, projectId }, `renaming ${entityType}`) - if (entityType == null) { - logger.warn({ err: 'No entityType set', projectId, entityId }) - return callback(new Error('No entityType set')) - } - entityType = entityType.toLowerCase() + ), - DocumentUpdaterHandler.flushProjectToMongo(projectId, err => { - if (err) { + renameEntity: wrapWithLock( + function ( + projectId, + entityId, + entityType, + newName, + userId, + source, + callback + ) { + if (!newName || typeof newName !== 'string') { + const err = new OError('invalid newName value', { + value: newName, + type: typeof newName, + projectId, + entityId, + entityType, + userId, + source, + }) + logger.error({ err }, 'Invalid newName passed to renameEntity') return callback(err) } - ProjectEntityMongoUpdateHandler.renameEntity( - projectId, - entityId, - entityType, - newName, - (err, project, startPath, endPath, rev, changes) => { - if (err != null) { - return callback(err) - } - const projectHistoryId = - project.overleaf && - project.overleaf.history && - project.overleaf.history.id - TpdsUpdateSender.moveEntity( - { - projectId, - projectName: project.name, - startPath, - endPath, - rev, - entityId, - entityType, - folderId: null, // this means the folder has not changed - }, - err => { - if (err) { - logger.error({ err }, 'error sending tpds update') - } - DocumentUpdaterHandler.updateProjectStructure( - projectId, - projectHistoryId, - userId, - changes, - source, - callback - ) - } - ) + if (!SafePath.isCleanFilename(newName)) { + return callback(new Errors.InvalidNameError('invalid element name')) + } + logger.debug({ entityId, projectId }, `renaming ${entityType}`) + if (entityType == null) { + logger.warn({ err: 'No entityType set', projectId, entityId }) + return callback(new Error('No entityType set')) + } + entityType = entityType.toLowerCase() + + DocumentUpdaterHandler.flushProjectToMongo(projectId, err => { + if (err) { + return callback(err) } - ) - }) - }), + ProjectEntityMongoUpdateHandler.renameEntity( + projectId, + entityId, + entityType, + newName, + (err, project, startPath, endPath, rev, changes) => { + if (err != null) { + return callback(err) + } + const projectHistoryId = + project.overleaf && + project.overleaf.history && + project.overleaf.history.id + TpdsUpdateSender.moveEntity( + { + projectId, + projectName: project.name, + startPath, + endPath, + rev, + entityId, + entityType, + folderId: null, // this means the folder has not changed + }, + err => { + if (err) { + logger.error({ err }, 'error sending tpds update') + } + DocumentUpdaterHandler.updateProjectStructure( + projectId, + projectHistoryId, + userId, + changes, + source, + callback + ) + } + ) + } + ) + }) + } + ), // This doesn't directly update project structure but we need to take the lock // to prevent anything else being queued before the resync update @@ -1471,8 +1471,8 @@ const ProjectEntityUpdateHandler = { const originalName = entity.folder ? entity.folder.name : entity.doc - ? entity.doc.name - : entity.file.name + ? entity.doc.name + : entity.file.name let newPath = entity.path let newName = originalName @@ -1540,8 +1540,8 @@ const ProjectEntityUpdateHandler = { const entityId = entity.folder ? entity.folder._id : entity.doc - ? entity.doc._id - : entity.file._id + ? entity.doc._id + : entity.file._id const entityType = entity.folder ? 'folder' : entity.doc ? 'doc' : 'file' ProjectEntityMongoUpdateHandler.renameEntity( projectId, diff --git a/services/web/app/src/Features/ServerAdmin/AdminController.js b/services/web/app/src/Features/ServerAdmin/AdminController.js index c0920dbb82..f3c5a01431 100644 --- a/services/web/app/src/Features/ServerAdmin/AdminController.js +++ b/services/web/app/src/Features/ServerAdmin/AdminController.js @@ -93,19 +93,18 @@ const AdminController = { })() } - return SystemMessageManager.getMessagesFromDB(function ( - error, - systemMessages - ) { - if (error != null) { - return next(error) + return SystemMessageManager.getMessagesFromDB( + function (error, systemMessages) { + if (error != null) { + return next(error) + } + return res.render('admin/index', { + title: 'System Admin', + openSockets, + systemMessages, + }) } - return res.render('admin/index', { - title: 'System Admin', - openSockets, - systemMessages, - }) - }) + ) }, disconnectAllUsers: (req, res) => { diff --git a/services/web/app/src/Features/Subscription/FeaturesUpdater.js b/services/web/app/src/Features/Subscription/FeaturesUpdater.js index 4c74c19247..725b176885 100644 --- a/services/web/app/src/Features/Subscription/FeaturesUpdater.js +++ b/services/web/app/src/Features/Subscription/FeaturesUpdater.js @@ -114,16 +114,14 @@ async function computeFeatures(userId) { } async function _getIndividualFeatures(userId) { - const sub = await SubscriptionLocator.promises.getUserIndividualSubscription( - userId - ) + const sub = + await SubscriptionLocator.promises.getUserIndividualSubscription(userId) return _subscriptionToFeatures(sub) } async function _getGroupFeatureSets(userId) { - const subs = await SubscriptionLocator.promises.getGroupSubscriptionsMemberOf( - userId - ) + const subs = + await SubscriptionLocator.promises.getGroupSubscriptionsMemberOf(userId) return (subs || []).map(_subscriptionToFeatures) } diff --git a/services/web/app/src/Features/Subscription/SubscriptionGroupHandler.js b/services/web/app/src/Features/Subscription/SubscriptionGroupHandler.js index 91d30bffaa..40fe5cc5ba 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionGroupHandler.js +++ b/services/web/app/src/Features/Subscription/SubscriptionGroupHandler.js @@ -28,9 +28,8 @@ async function isUserPartOfGroup(userId, subscriptionId) { } async function getTotalConfirmedUsersInGroup(subscriptionId) { - const subscription = await SubscriptionLocator.promises.getSubscription( - subscriptionId - ) + const subscription = + await SubscriptionLocator.promises.getSubscription(subscriptionId) return subscription?.member_ids?.length } diff --git a/services/web/app/src/Features/Subscription/SubscriptionHandler.js b/services/web/app/src/Features/Subscription/SubscriptionHandler.js index 2849f49500..4ccbfde9e4 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionHandler.js +++ b/services/web/app/src/Features/Subscription/SubscriptionHandler.js @@ -207,9 +207,8 @@ async function syncSubscription(recurlySubscription, requesterData) { // This is used because Recurly doesn't always attempt collection of paast due // invoices after Paypal billing info were updated. async function attemptPaypalInvoiceCollection(recurlyAccountCode) { - const billingInfo = await RecurlyWrapper.promises.getBillingInfo( - recurlyAccountCode - ) + const billingInfo = + await RecurlyWrapper.promises.getBillingInfo(recurlyAccountCode) if (!billingInfo.paypal_billing_agreement_id) { // this is not a Paypal user diff --git a/services/web/app/src/Features/Subscription/SubscriptionUpdater.js b/services/web/app/src/Features/Subscription/SubscriptionUpdater.js index a9bf0e44b9..297fd0c4ee 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionUpdater.js +++ b/services/web/app/src/Features/Subscription/SubscriptionUpdater.js @@ -47,9 +47,8 @@ async function syncSubscription( adminUserId, requesterData = {} ) { - let subscription = await SubscriptionLocator.promises.getUsersSubscription( - adminUserId - ) + let subscription = + await SubscriptionLocator.promises.getUsersSubscription(adminUserId) if (subscription == null) { subscription = await _createNewSubscription(adminUserId) } diff --git a/services/web/app/src/Features/Subscription/TeamInvitesController.js b/services/web/app/src/Features/Subscription/TeamInvitesController.js index a3766ef9e4..4a8c5e379f 100644 --- a/services/web/app/src/Features/Subscription/TeamInvitesController.js +++ b/services/web/app/src/Features/Subscription/TeamInvitesController.js @@ -66,9 +66,8 @@ async function viewInvite(req, res, next) { const { token } = req.params const userId = SessionManager.getLoggedInUserId(req.session) - const { invite, subscription } = await TeamInvitesHandler.promises.getInvite( - token - ) + const { invite, subscription } = + await TeamInvitesHandler.promises.getInvite(token) if (!invite) { return ErrorController.notFound(req, res) } diff --git a/services/web/app/src/Features/Subscription/TeamInvitesHandler.js b/services/web/app/src/Features/Subscription/TeamInvitesHandler.js index 4540581ce4..da93ff2b7f 100644 --- a/services/web/app/src/Features/Subscription/TeamInvitesHandler.js +++ b/services/web/app/src/Features/Subscription/TeamInvitesHandler.js @@ -124,9 +124,8 @@ async function revokeInvite(teamManagerId, subscription, email) { // email is in Subscription.invited_emails when they join. We'll remove this // after a short while. async function createTeamInvitesForLegacyInvitedEmail(email) { - const teams = await SubscriptionLocator.promises.getGroupsWithEmailInvite( - email - ) + const teams = + await SubscriptionLocator.promises.getGroupsWithEmailInvite(email) return Promise.all( teams.map(team => createInvite(team.admin_id, team, email)) ) diff --git a/services/web/app/src/Features/ThirdPartyDataStore/UpdateMerger.js b/services/web/app/src/Features/ThirdPartyDataStore/UpdateMerger.js index 07cd1755af..e073ccdd54 100644 --- a/services/web/app/src/Features/ThirdPartyDataStore/UpdateMerger.js +++ b/services/web/app/src/Features/ThirdPartyDataStore/UpdateMerger.js @@ -43,9 +43,8 @@ async function writeUpdateToDisk(projectId, updateStream) { } async function _findExistingFileType(projectId, path) { - const { docs, files } = await ProjectEntityHandler.promises.getAllEntities( - projectId - ) + const { docs, files } = + await ProjectEntityHandler.promises.getAllEntities(projectId) if (_.some(docs, d => d.path === path)) { return 'doc' } diff --git a/services/web/app/src/Features/Uploads/ProjectUploadManager.js b/services/web/app/src/Features/Uploads/ProjectUploadManager.js index 98e1d38bb9..b96f5bd850 100644 --- a/services/web/app/src/Features/Uploads/ProjectUploadManager.js +++ b/services/web/app/src/Features/Uploads/ProjectUploadManager.js @@ -119,12 +119,10 @@ async function _initializeProjectWithZipContents( project, contentsPath ) { - const topLevelDir = await ArchiveManager.promises.findTopLevelDirectory( - contentsPath - ) - const importEntries = await FileSystemImportManager.promises.importDir( - topLevelDir - ) + const topLevelDir = + await ArchiveManager.promises.findTopLevelDirectory(contentsPath) + const importEntries = + await FileSystemImportManager.promises.importDir(topLevelDir) const { fileEntries, docEntries } = await _createEntriesFromImports( project._id, importEntries diff --git a/services/web/app/src/Features/User/SAMLIdentityManager.js b/services/web/app/src/Features/User/SAMLIdentityManager.js index 5bfb4a8ddd..ab71950087 100644 --- a/services/web/app/src/Features/User/SAMLIdentityManager.js +++ b/services/web/app/src/Features/User/SAMLIdentityManager.js @@ -32,9 +32,8 @@ async function _ensureCanAddIdentifier(userId, institutionEmail, providerId) { throw new Errors.SAMLAlreadyLinkedError() } - const userWithEmail = await UserGetter.promises.getUserByAnyEmail( - institutionEmail - ) + const userWithEmail = + await UserGetter.promises.getUserByAnyEmail(institutionEmail) if (!userWithEmail) { // email doesn't exist; all good diff --git a/services/web/app/src/Features/User/UserDeleter.js b/services/web/app/src/Features/User/UserDeleter.js index 27019ab3b5..49d0f0b264 100644 --- a/services/web/app/src/Features/User/UserDeleter.js +++ b/services/web/app/src/Features/User/UserDeleter.js @@ -103,9 +103,8 @@ async function expireDeletedUsersAfterDuration() { } async function ensureCanDeleteUser(user) { - const subscription = await SubscriptionLocator.promises.getUsersSubscription( - user - ) + const subscription = + await SubscriptionLocator.promises.getUsersSubscription(user) if (subscription) { throw new Errors.SubscriptionAdminDeletionError({}) } diff --git a/services/web/app/src/Features/User/UserGetter.js b/services/web/app/src/Features/User/UserGetter.js index 447c9b1c4f..2d68f3c24d 100644 --- a/services/web/app/src/Features/User/UserGetter.js +++ b/services/web/app/src/Features/User/UserGetter.js @@ -68,9 +68,8 @@ async function getUserFullEmails(userId) { return decorateFullEmails(user.email, user.emails, [], []) } - const affiliationsData = await InstitutionsAPIPromises.getUserAffiliations( - userId - ) + const affiliationsData = + await InstitutionsAPIPromises.getUserAffiliations(userId) return decorateFullEmails( user.email, diff --git a/services/web/app/src/Features/User/UserPostRegistrationAnalyticsManager.js b/services/web/app/src/Features/User/UserPostRegistrationAnalyticsManager.js index deb51c28e2..26dbf86e26 100644 --- a/services/web/app/src/Features/User/UserPostRegistrationAnalyticsManager.js +++ b/services/web/app/src/Features/User/UserPostRegistrationAnalyticsManager.js @@ -24,9 +24,8 @@ async function postRegistrationAnalytics(userId) { } async function checkAffiliations(userId) { - const affiliationsData = await InstitutionsAPIPromises.getUserAffiliations( - userId - ) + const affiliationsData = + await InstitutionsAPIPromises.getUserAffiliations(userId) const hasCommonsAccountAffiliation = affiliationsData.some( affiliationData => affiliationData.institution && affiliationData.institution.commonsAccount diff --git a/services/web/app/src/Features/User/UserUpdater.js b/services/web/app/src/Features/User/UserUpdater.js index 1bd4e056f7..e9613f6811 100644 --- a/services/web/app/src/Features/User/UserUpdater.js +++ b/services/web/app/src/Features/User/UserUpdater.js @@ -354,9 +354,8 @@ async function maybeCreateRedundantSubscriptionNotification(userId, email) { return } - const affiliations = await InstitutionsAPI.promises.getUserAffiliations( - userId - ) + const affiliations = + await InstitutionsAPI.promises.getUserAffiliations(userId) const confirmedAffiliation = affiliations.find(a => a.email === email) if (!confirmedAffiliation || confirmedAffiliation.licence === 'free') { return diff --git a/services/web/app/src/infrastructure/Modules.js b/services/web/app/src/infrastructure/Modules.js index 112d051ee8..cabfbefb10 100644 --- a/services/web/app/src/infrastructure/Modules.js +++ b/services/web/app/src/infrastructure/Modules.js @@ -30,11 +30,9 @@ function loadModules() { } for (const moduleName of Settings.moduleImportSequence || []) { - const loadedModule = require(Path.join( - MODULE_BASE_PATH, - moduleName, - 'index.js' - )) + const loadedModule = require( + Path.join(MODULE_BASE_PATH, moduleName, 'index.js') + ) loadedModule.name = moduleName _modules.push(loadedModule) } diff --git a/services/web/frontend/fonts/font-awesome.css b/services/web/frontend/fonts/font-awesome.css index 5cbae8e590..0c31964a4c 100644 --- a/services/web/frontend/fonts/font-awesome.css +++ b/services/web/frontend/fonts/font-awesome.css @@ -4,7 +4,8 @@ */ @font-face { font-family: 'FontAwesome'; - src: url('font-awesome-v470.woff2') format('woff2'), + src: + url('font-awesome-v470.woff2') format('woff2'), url('font-awesome-v470.woff') format('woff'); font-weight: normal; font-style: normal; diff --git a/services/web/frontend/fonts/lato.css b/services/web/frontend/fonts/lato.css index 2d616c9206..16d2dd953b 100644 --- a/services/web/frontend/fonts/lato.css +++ b/services/web/frontend/fonts/lato.css @@ -2,7 +2,8 @@ font-family: 'Lato'; font-style: normal; font-weight: 400; - src: url('lato/lato-v2-latin-ext-regular.woff2') format('woff2'), + src: + url('lato/lato-v2-latin-ext-regular.woff2') format('woff2'), url('lato/lato-v2-latin-ext-regular.woff') format('woff'); } @@ -10,7 +11,8 @@ font-family: 'Lato'; font-style: italic; font-weight: 400; - src: url('lato/lato-v2-latin-ext-italic.woff2') format('woff2'), + src: + url('lato/lato-v2-latin-ext-italic.woff2') format('woff2'), url('lato/lato-v2-latin-ext-italic.woff') format('woff'); } @@ -18,7 +20,8 @@ font-family: 'Lato'; font-style: normal; font-weight: 700; - src: url('lato/lato-v2-latin-ext-700.woff2') format('woff2'), + src: + url('lato/lato-v2-latin-ext-700.woff2') format('woff2'), url('lato/lato-v2-latin-ext-700.woff') format('woff'); } @@ -26,6 +29,7 @@ font-family: 'Lato'; font-style: italic; font-weight: 700; - src: url('lato/lato-v2-latin-ext-700italic.woff2') format('woff2'), + src: + url('lato/lato-v2-latin-ext-700italic.woff2') format('woff2'), url('lato/lato-v2-latin-ext-700italic.woff') format('woff'); } diff --git a/services/web/frontend/fonts/material-symbols.css b/services/web/frontend/fonts/material-symbols.css index 5244dc73ed..df48c82f46 100644 --- a/services/web/frontend/fonts/material-symbols.css +++ b/services/web/frontend/fonts/material-symbols.css @@ -15,7 +15,11 @@ font-weight: normal; font-style: normal; font-size: 20px; - font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 20; + font-variation-settings: + 'FILL' 1, + 'wght' 400, + 'GRAD' 0, + 'opsz' 20; line-height: 1; letter-spacing: normal; text-transform: none; diff --git a/services/web/frontend/fonts/merriweather.css b/services/web/frontend/fonts/merriweather.css index 61787faee8..aa53e02420 100644 --- a/services/web/frontend/fonts/merriweather.css +++ b/services/web/frontend/fonts/merriweather.css @@ -2,7 +2,9 @@ font-family: 'Merriweather'; font-style: normal; font-weight: 400; - src: local('Merriweather Regular'), local('Merriweather-Regular'), + src: + local('Merriweather Regular'), + local('Merriweather-Regular'), url('merriweather-v21-latin-regular.woff2') format('woff2'), url('merriweather-v21-latin-regular.woff') format('woff'); } @@ -10,7 +12,9 @@ font-family: 'Merriweather'; font-style: italic; font-weight: 400; - src: local('Merriweather Italic'), local('Merriweather-Italic'), + src: + local('Merriweather Italic'), + local('Merriweather-Italic'), url('merriweather-v21-latin-italic.woff2') format('woff2'), url('merriweather-v21-latin-italic.woff') format('woff'); } @@ -18,7 +22,9 @@ font-family: 'Merriweather'; font-style: normal; font-weight: 700; - src: local('Merriweather Bold'), local('Merriweather-Bold'), + src: + local('Merriweather Bold'), + local('Merriweather-Bold'), url('merriweather-v21-latin-700.woff2') format('woff2'), url('merriweather-v21-latin-700.woff') format('woff'); } @@ -26,7 +32,9 @@ font-family: 'Merriweather'; font-style: italic; font-weight: 700; - src: local('Merriweather Bold Italic'), local('Merriweather-BoldItalic'), + src: + local('Merriweather Bold Italic'), + local('Merriweather-BoldItalic'), url('merriweather-v21-latin-700italic.woff2') format('woff2'), url('merriweather-v21-latin-700italic.woff') format('woff'); } diff --git a/services/web/frontend/fonts/open-sans.css b/services/web/frontend/fonts/open-sans.css index ae38b38bb8..236dbf63d8 100644 --- a/services/web/frontend/fonts/open-sans.css +++ b/services/web/frontend/fonts/open-sans.css @@ -2,7 +2,9 @@ font-family: 'Open Sans'; font-style: normal; font-weight: 300; - src: local('Open Sans Light'), local('OpenSans-Light'), + src: + local('Open Sans Light'), + local('OpenSans-Light'), url('open-sans-v17-latin-300.woff2') format('woff2'), url('open-sans-v17-latin-300.woff') format('woff'); } @@ -10,7 +12,9 @@ font-family: 'Open Sans'; font-style: normal; font-weight: 400; - src: local('Open Sans Regular'), local('OpenSans-Regular'), + src: + local('Open Sans Regular'), + local('OpenSans-Regular'), url('open-sans-v17-latin-regular.woff2') format('woff2'), url('open-sans-v17-latin-regular.woff') format('woff'); } @@ -18,7 +22,9 @@ font-family: 'Open Sans'; font-style: normal; font-weight: 600; - src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), + src: + local('Open Sans SemiBold'), + local('OpenSans-SemiBold'), url('open-sans-v17-latin-600.woff2') format('woff2'), url('open-sans-v17-latin-600.woff') format('woff'); } @@ -26,7 +32,9 @@ font-family: 'Open Sans'; font-style: normal; font-weight: 700; - src: local('Open Sans Bold'), local('OpenSans-Bold'), + src: + local('Open Sans Bold'), + local('OpenSans-Bold'), url('open-sans-v17-latin-700.woff2') format('woff2'), url('open-sans-v17-latin-700.woff') format('woff'); } diff --git a/services/web/frontend/fonts/source-code-pro.css b/services/web/frontend/fonts/source-code-pro.css index be0d2ff03b..f52eb466af 100644 --- a/services/web/frontend/fonts/source-code-pro.css +++ b/services/web/frontend/fonts/source-code-pro.css @@ -2,7 +2,9 @@ font-family: 'Source Code Pro'; font-style: normal; font-weight: 400; - src: local('Source Code Pro Regular'), local('SourceCodePro-Regular'), + src: + local('Source Code Pro Regular'), + local('SourceCodePro-Regular'), url('source-code-pro-v13-latin-regular.woff2') format('woff2'), url('source-code-pro-v13-latin-regular.woff') format('woff'); } diff --git a/services/web/frontend/js/features/history/utils/auto-select-file.ts b/services/web/frontend/js/features/history/utils/auto-select-file.ts index 5d309e8866..add1c5a27d 100644 --- a/services/web/frontend/js/features/history/utils/auto-select-file.ts +++ b/services/web/frontend/js/features/history/utils/auto-select-file.ts @@ -75,16 +75,19 @@ function getFilesWithOps( return filesWithOps } else { - const filesWithOps = files.reduce((curFilesWithOps, file) => { - if ('operation' in file) { - curFilesWithOps.push({ - pathname: file.pathname, - editable: isFileEditable(file), - operation: file.operation, - }) - } - return curFilesWithOps - }, []) + const filesWithOps = files.reduce( + (curFilesWithOps, file) => { + if ('operation' in file) { + curFilesWithOps.push({ + pathname: file.pathname, + editable: isFileEditable(file), + operation: file.operation, + }) + } + return curFilesWithOps + }, + [] + ) return filesWithOps } diff --git a/services/web/frontend/js/features/ide-react/context/review-panel/hooks/use-review-panel-state.ts b/services/web/frontend/js/features/ide-react/context/review-panel/hooks/use-review-panel-state.ts index 82059ad3db..ef990876fc 100644 --- a/services/web/frontend/js/features/ide-react/context/review-panel/hooks/use-review-panel-state.ts +++ b/services/web/frontend/js/features/ide-react/context/review-panel/hooks/use-review-panel-state.ts @@ -226,8 +226,8 @@ function useReviewPanelState(): ReviewPanel.ReviewPanelState { MergeAndOverride< ReviewPanelCommentThread, ReviewPanelCommentThreadsApi[ThreadId] - > - ] + >, + ], ] for (const [threadId, thread] of threadsEntries) { for (const comment of thread.messages) { @@ -565,8 +565,8 @@ function useReviewPanelState(): ReviewPanel.ReviewPanelState { [ UserId, NonNullable< - typeof trackChangesState[keyof typeof trackChangesState] - > + (typeof trackChangesState)[keyof typeof trackChangesState] + >, ] > for (const [userId, { value }] of entries) { @@ -605,7 +605,7 @@ function useReviewPanelState(): ReviewPanel.ReviewPanelState { } const state = newTrackChangesState[userId] ?? - ({} as NonNullable) + ({} as NonNullable<(typeof newTrackChangesState)[UserId]>) newTrackChangesState[userId] = state if (state.syncState == null || state.syncState === 'synced') { diff --git a/services/web/frontend/js/features/ide-react/hooks/use-editing-session-heartbeat.ts b/services/web/frontend/js/features/ide-react/hooks/use-editing-session-heartbeat.ts index 9d17905215..521596bcd4 100644 --- a/services/web/frontend/js/features/ide-react/hooks/use-editing-session-heartbeat.ts +++ b/services/web/frontend/js/features/ide-react/hooks/use-editing-session-heartbeat.ts @@ -69,8 +69,8 @@ export function useEditingSessionHeartbeat() { heartbeatsSent <= 2 ? 30 : heartbeatsSent <= 6 - ? (heartbeatsSent - 2) * 60 - : 300 + ? (heartbeatsSent - 2) * 60 + : 300 setNextHeartbeatAt(moment().add(backoffSecs, 'seconds').toDate()) }, [getEditorType, heartbeatsSent, nextHeartbeatAt, projectId]) diff --git a/services/web/frontend/js/features/pdf-preview/components/faster-compiles-feedback.tsx b/services/web/frontend/js/features/pdf-preview/components/faster-compiles-feedback.tsx index 48b05337b4..da89efc4d4 100644 --- a/services/web/frontend/js/features/pdf-preview/components/faster-compiles-feedback.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/faster-compiles-feedback.tsx @@ -111,8 +111,8 @@ function FasterCompilesFeedbackContent() { {feedback === 'faster' ? t('faster_compiles_feedback_seems_faster') : feedback === 'same' - ? t('faster_compiles_feedback_seems_same') - : t('faster_compiles_feedback_seems_slower')} + ? t('faster_compiles_feedback_seems_same') + : t('faster_compiles_feedback_seems_slower')} ))} diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-viewer.jsx b/services/web/frontend/js/features/pdf-preview/components/pdf-viewer.jsx index 500ae2ff72..c4d79243b4 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-viewer.jsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-viewer.jsx @@ -1,8 +1,8 @@ import { lazy, memo } from 'react' import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' -const PdfJsViewer = lazy(() => - import(/* webpackChunkName: "pdf-js-viewer" */ './pdf-js-viewer') +const PdfJsViewer = lazy( + () => import(/* webpackChunkName: "pdf-js-viewer" */ './pdf-js-viewer') ) function PdfViewer() { diff --git a/services/web/frontend/js/features/project-list/components/notifications/groups-and-enterprise-banner.tsx b/services/web/frontend/js/features/project-list/components/notifications/groups-and-enterprise-banner.tsx index f728fc7bfc..6be16fccb2 100644 --- a/services/web/frontend/js/features/project-list/components/notifications/groups-and-enterprise-banner.tsx +++ b/services/web/frontend/js/features/project-list/components/notifications/groups-and-enterprise-banner.tsx @@ -7,7 +7,7 @@ import { useProjectListContext } from '../../context/project-list-context' import { useTranslation } from 'react-i18next' const variants = ['did-you-know', 'on-premise', 'people', 'FOMO'] as const -type GroupsAndEnterpriseBannerVariant = typeof variants[number] +type GroupsAndEnterpriseBannerVariant = (typeof variants)[number] let viewEventSent = false diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/tags-dropdown.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/tags-dropdown.tsx index b597e4746d..c129c1582f 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/tags-dropdown.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/tags-dropdown.tsx @@ -116,8 +116,8 @@ function TagsDropdown() { containsAllSelectedProjects(tag) ? 'check-square-o' : containsSomeSelectedProjects(tag) - ? 'minus-square-o' - : 'square-o' + ? 'minus-square-o' + : 'square-o' } className="tag-checkbox" />{' '} diff --git a/services/web/frontend/js/features/settings/components/emails/add-email/country-input.tsx b/services/web/frontend/js/features/settings/components/emails/add-email/country-input.tsx index b676f56ddf..f90fdb6152 100644 --- a/services/web/frontend/js/features/settings/components/emails/add-email/country-input.tsx +++ b/services/web/frontend/js/features/settings/components/emails/add-email/country-input.tsx @@ -9,7 +9,8 @@ type CountryInputProps = { inputRef?: React.ForwardedRef } & React.InputHTMLAttributes -const itemToString = (item: typeof countries[number] | null) => item?.name ?? '' +const itemToString = (item: (typeof countries)[number] | null) => + item?.name ?? '' function Downshift({ setValue, inputRef }: CountryInputProps) { const { t } = useTranslation() diff --git a/services/web/frontend/js/features/settings/domain-blocklist.ts b/services/web/frontend/js/features/settings/domain-blocklist.ts index 32b4bcb6de..4e31a5cfd0 100644 --- a/services/web/frontend/js/features/settings/domain-blocklist.ts +++ b/services/web/frontend/js/features/settings/domain-blocklist.ts @@ -58,4 +58,6 @@ for (const domain of commonDomains) { } } -export default domainBlocklist as ReadonlyArray +export default domainBlocklist as ReadonlyArray< + (typeof domainBlocklist)[number] +> diff --git a/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx b/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx index 929c73f543..d77cdf3db9 100644 --- a/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx +++ b/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx @@ -268,8 +268,8 @@ const FigureModalContent = () => { {helpShown ? t('help') : sourcePickerShown - ? t('replace_figure') - : getTitle(source)}{' '} + ? t('replace_figure') + : getTitle(source)}{' '} { uploading ? FileUploadStatus.UPLOADING : uploadError - ? FileUploadStatus.ERROR - : FileUploadStatus.NOT_ATTEMPTED + ? FileUploadStatus.ERROR + : FileUploadStatus.NOT_ATTEMPTED } onDelete={() => { uppy.removeFile(file.id) diff --git a/services/web/frontend/js/features/source-editor/components/review-panel/utils/compare-props-with-shallow-array-compare.ts b/services/web/frontend/js/features/source-editor/components/review-panel/utils/compare-props-with-shallow-array-compare.ts index 07ad6def04..c50a5edd73 100644 --- a/services/web/frontend/js/features/source-editor/components/review-panel/utils/compare-props-with-shallow-array-compare.ts +++ b/services/web/frontend/js/features/source-editor/components/review-panel/utils/compare-props-with-shallow-array-compare.ts @@ -4,7 +4,7 @@ const shallowEqual = (arr1: unknown[], arr2: unknown[]) => // Compares props for a component, but comparing the specified props using // shallow array comparison rather than identity export default function comparePropsWithShallowArrayCompare< - T extends Record + T extends Record, >(...args: Array) { return (prevProps: T, nextProps: T) => { for (const k in prevProps) { diff --git a/services/web/frontend/js/features/source-editor/components/table-generator/toolbar/commands.ts b/services/web/frontend/js/features/source-editor/components/table-generator/toolbar/commands.ts index b715fc02fe..0d8798f275 100644 --- a/services/web/frontend/js/features/source-editor/components/table-generator/toolbar/commands.ts +++ b/services/web/frontend/js/features/source-editor/components/table-generator/toolbar/commands.ts @@ -747,9 +747,8 @@ export const setColumnWidth = ( const alignmentCharacter = getParagraphAlignmentCharacter( columnSpecification[i] ) - columnSpecification[ - i - ].content = `${alignmentCharacter}{${widthValue}${suffix}}` + columnSpecification[i].content = + `${alignmentCharacter}{${widthValue}${suffix}}` } } const newSpecification = generateColumnSpecification(columnSpecification) diff --git a/services/web/frontend/js/features/source-editor/extensions/browser.ts b/services/web/frontend/js/features/source-editor/extensions/browser.ts index 52da810b49..465846f1c7 100644 --- a/services/web/frontend/js/features/source-editor/extensions/browser.ts +++ b/services/web/frontend/js/features/source-editor/extensions/browser.ts @@ -28,10 +28,10 @@ export default { ie_version: ieUpTo10 ? doc.documentMode || 6 : ie11Up - ? +ie11Up[1] - : ieEdge - ? +ieEdge[1] - : 0, + ? +ie11Up[1] + : ieEdge + ? +ieEdge[1] + : 0, gecko, gecko_version: gecko ? +(/Firefox\/(\d+)/.exec(nav.userAgent) || [0, 0])[1] diff --git a/services/web/frontend/js/features/source-editor/extensions/track-changes.ts b/services/web/frontend/js/features/source-editor/extensions/track-changes.ts index cee664ecc0..461b8f78ca 100644 --- a/services/web/frontend/js/features/source-editor/extensions/track-changes.ts +++ b/services/web/frontend/js/features/source-editor/extensions/track-changes.ts @@ -221,7 +221,10 @@ class ChangeDeletedWidget extends WidgetType { } class ChangeCalloutWidget extends WidgetType { - constructor(public change: Change, public opType: string) { + constructor( + public change: Change, + public opType: string + ) { super() } diff --git a/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts b/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts index 7e9f2966c0..d4d75cdcf8 100644 --- a/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts +++ b/services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts @@ -542,7 +542,7 @@ const createSelector = < T extends string, E extends HTMLElement = T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] - : HTMLElement + : HTMLElement, >({ selector, ...elementSelector diff --git a/services/web/frontend/js/features/source-editor/extensions/visual/pasted-content.tsx b/services/web/frontend/js/features/source-editor/extensions/visual/pasted-content.tsx index e6628ae699..061c77d315 100644 --- a/services/web/frontend/js/features/source-editor/extensions/visual/pasted-content.tsx +++ b/services/web/frontend/js/features/source-editor/extensions/visual/pasted-content.tsx @@ -13,8 +13,10 @@ import { SplitTestProvider } from '../../../../shared/context/split-test-context export type PastedContent = { latex: string; text: string } -const pastedContentEffect = - StateEffect.define<{ content: PastedContent; formatted: boolean }>() +const pastedContentEffect = StateEffect.define<{ + content: PastedContent + formatted: boolean +}>() export const insertPastedContent = ( view: EditorView, diff --git a/services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/environment-line.ts b/services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/environment-line.ts index 1d3ef080f6..47d30812e9 100644 --- a/services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/environment-line.ts +++ b/services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/environment-line.ts @@ -1,7 +1,10 @@ import { EditorView, WidgetType } from '@codemirror/view' export class EnvironmentLineWidget extends WidgetType { - constructor(public environment: string, public line?: 'begin' | 'end') { + constructor( + public environment: string, + public line?: 'begin' | 'end' + ) { super() } diff --git a/services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js b/services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js index 2c9c8f8cd5..ba622f71e2 100644 --- a/services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js +++ b/services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js @@ -371,9 +371,8 @@ export default App.controller('ReviewPanelController', [ ide.$scope.reviewPanel.overview.docsCollapsedState[doc.id] == null ) { - ide.$scope.reviewPanel.overview.docsCollapsedState[ - doc.id - ] = false + ide.$scope.reviewPanel.overview.docsCollapsedState[doc.id] = + false } if (doc.id !== $scope.editor.open_doc_id) { // this is kept up to date in real-time, don't overwrite diff --git a/services/web/frontend/js/main/event.js b/services/web/frontend/js/main/event.js index d85999b175..b3e64cf7c3 100644 --- a/services/web/frontend/js/main/event.js +++ b/services/web/frontend/js/main/event.js @@ -93,8 +93,8 @@ App.factory('eventTracking', [ heartbeatsSent <= 2 ? 30 : heartbeatsSent <= 6 - ? (heartbeatsSent - 2) * 60 - : 300 + ? (heartbeatsSent - 2) * 60 + : 300 nextHeartbeat = moment().add(backoffSecs, 'seconds').toDate() }, diff --git a/services/web/frontend/js/shared/context/project-context.tsx b/services/web/frontend/js/shared/context/project-context.tsx index 36f79b45b4..f016bc54b4 100644 --- a/services/web/frontend/js/shared/context/project-context.tsx +++ b/services/web/frontend/js/shared/context/project-context.tsx @@ -94,8 +94,8 @@ export const ProjectProvider: FC = ({ children }) => { forceNewCompileTimeout === 'active' ? 'active' : forceNewCompileTimeout === 'changing' - ? 'changing' - : undefined + ? 'changing' + : undefined const value = useMemo(() => { return { diff --git a/services/web/frontend/js/utils/functions.ts b/services/web/frontend/js/utils/functions.ts index 3cdbf0d6c6..dfc46d8cad 100644 --- a/services/web/frontend/js/utils/functions.ts +++ b/services/web/frontend/js/utils/functions.ts @@ -1,6 +1,6 @@ export function callFnsInSequence< Args, - Fn extends ((...args: Args[]) => void) | void + Fn extends ((...args: Args[]) => void) | void, >(...fns: Fn[]) { return (...args: Args[]) => fns.forEach(fn => fn?.(...args)) } diff --git a/services/web/frontend/stylesheets/app/editor/logs.less b/services/web/frontend/stylesheets/app/editor/logs.less index 5074321770..32573a2d56 100644 --- a/services/web/frontend/stylesheets/app/editor/logs.less +++ b/services/web/frontend/stylesheets/app/editor/logs.less @@ -155,7 +155,9 @@ position: relative; height: 40px; margin-top: 0; - transition: margin 0.15s ease-in-out, opacity 0.15s ease-in-out; + transition: + margin 0.15s ease-in-out, + opacity 0.15s ease-in-out; padding-bottom: @padding-sm; text-align: center; background-image: linear-gradient(0, @ol-blue-gray-1, transparent); diff --git a/services/web/frontend/stylesheets/app/editor/pdf.less b/services/web/frontend/stylesheets/app/editor/pdf.less index 7a1ba0bb84..880a7a7027 100644 --- a/services/web/frontend/stylesheets/app/editor/pdf.less +++ b/services/web/frontend/stylesheets/app/editor/pdf.less @@ -47,7 +47,11 @@ } .toolbar-pdf-hybrid { - .btn:not(.detach-compile-button):not(.btn-orphan):not(.detach-synctex-control):not(.switch-to-editor-btn):not(.split-menu-dropdown-toggle):not(.split-menu-button) { + .btn:not(.detach-compile-button):not(.btn-orphan):not( + .detach-synctex-control + ):not(.switch-to-editor-btn):not(.split-menu-dropdown-toggle):not( + .split-menu-button + ) { display: inline-block; color: @toolbar-btn-color; background-color: transparent; @@ -208,7 +212,9 @@ z-index: 10; // above the PDF viewer .btn-group { - transition: opacity 0.5s ease, visibility 0 linear 0.5s; + transition: + opacity 0.5s ease, + visibility 0 linear 0.5s; visibility: hidden; opacity: 0; } diff --git a/services/web/frontend/stylesheets/app/editor/review-panel.less b/services/web/frontend/stylesheets/app/editor/review-panel.less index 80d7e2b394..ad7afe8a69 100644 --- a/services/web/frontend/stylesheets/app/editor/review-panel.less +++ b/services/web/frontend/stylesheets/app/editor/review-panel.less @@ -231,10 +231,15 @@ border-radius: 3px; color: #fff; cursor: pointer; - transition: top @rp-entry-animation-speed, left 0.1s, right 0.1s; + transition: + top @rp-entry-animation-speed, + left 0.1s, + right 0.1s; .no-animate & { - transition: left 0.1s, right 0.1s; + transition: + left 0.1s, + right 0.1s; } &-focused { @@ -381,10 +386,15 @@ border-left: solid @rp-entry-ribbon-width transparent; border-radius: 3px; background-color: #fff; - transition: top @rp-entry-animation-speed, left 0.1s, right 0.1s; + transition: + top @rp-entry-animation-speed, + left 0.1s, + right 0.1s; .no-animate & { - transition: left 0.1s, right 0.1s; + transition: + left 0.1s, + right 0.1s; } &-insert, @@ -407,8 +417,11 @@ z-index: 3; transform: scale(0.1); transform-origin: 0 0; - transition: top 0.35s ease-out, left 0.35s ease-out, - transform 0.35s ease-out, opacity 0.35s ease-out 0.2s; + transition: + top 0.35s ease-out, + left 0.35s ease-out, + transform 0.35s ease-out, + opacity 0.35s ease-out 0.2s; } &-comment-resolved { @@ -654,7 +667,9 @@ } .rp-entry-callout { - transition: top @rp-entry-animation-speed, height @rp-entry-animation-speed; + transition: + top @rp-entry-animation-speed, + height @rp-entry-animation-speed; .rp-state-current-file & { position: absolute; diff --git a/services/web/frontend/stylesheets/app/editor/toolbar.less b/services/web/frontend/stylesheets/app/editor/toolbar.less index 31d535d3d1..a27532b7ef 100644 --- a/services/web/frontend/stylesheets/app/editor/toolbar.less +++ b/services/web/frontend/stylesheets/app/editor/toolbar.less @@ -252,7 +252,9 @@ user-select: none; color: @text-color; border-radius: @btn-border-radius-base; - transition: color 0.12s ease-out, background-color 0.12s ease-out, + transition: + color 0.12s ease-out, + background-color 0.12s ease-out, box-shadow 0.12s ease-out; overflow: hidden; diff --git a/services/web/frontend/stylesheets/app/plans/plans-v2.less b/services/web/frontend/stylesheets/app/plans/plans-v2.less index 8251446779..6d2f3f969a 100644 --- a/services/web/frontend/stylesheets/app/plans/plans-v2.less +++ b/services/web/frontend/stylesheets/app/plans/plans-v2.less @@ -590,7 +590,9 @@ span.plans-v2-license-picker-educational-discount-learn-more-container { background-clip: padding-box; /* needed for firefox when there is bg color */ text-align: center; - &:not(.plans-v2-table-cell-before-green-highlighted-column):not(.plans-v2-table-green-highlighted):not(.plans-v2-table-divider-highlighted) { + &:not(.plans-v2-table-cell-before-green-highlighted-column):not( + .plans-v2-table-green-highlighted + ):not(.plans-v2-table-divider-highlighted) { border-right: 1px solid @ol-blue-gray-0; @media (max-width: @screen-xs-max) { @@ -623,7 +625,9 @@ span.plans-v2-license-picker-educational-discount-learn-more-container { vertical-align: middle; height: 100%; - &:last-child:not(.plans-v2-table-green-highlighted):not(.plans-v2-table-divider-highlighted) { + &:last-child:not(.plans-v2-table-green-highlighted):not( + .plans-v2-table-divider-highlighted + ) { border-right: 0; } } @@ -649,7 +653,9 @@ span.plans-v2-license-picker-educational-discount-learn-more-container { } } - &:not(.plans-v2-table-row-last-row-per-section):not(.plans-v2-table-divider):not(:last-of-type) { + &:not(.plans-v2-table-row-last-row-per-section):not( + .plans-v2-table-divider + ):not(:last-of-type) { th > .plans-v2-table-th > .plans-v2-table-th-content, td > .plans-v2-table-feature-name, td > .plans-v2-table-cell > .plans-v2-table-cell-content { diff --git a/services/web/frontend/stylesheets/app/project-list-react.less b/services/web/frontend/stylesheets/app/project-list-react.less index 4174eebade..76bc4bfbf4 100644 --- a/services/web/frontend/stylesheets/app/project-list-react.less +++ b/services/web/frontend/stylesheets/app/project-list-react.less @@ -923,7 +923,10 @@ vertical-align: middle; &:focus-visible { - box-shadow: 0 0 0 2px @white, 0 0 0 3px @blue-50, 0 0 0 5px @blue-30; + box-shadow: + 0 0 0 2px @white, + 0 0 0 3px @blue-50, + 0 0 0 5px @blue-30; } &.more-button { diff --git a/services/web/frontend/stylesheets/app/review-features-page.less b/services/web/frontend/stylesheets/app/review-features-page.less index 6144e67abf..35e62f2d7c 100644 --- a/services/web/frontend/stylesheets/app/review-features-page.less +++ b/services/web/frontend/stylesheets/app/review-features-page.less @@ -214,7 +214,9 @@ display: inline-block; transform: translate(150px, 0); opacity: 0; - transition: transform 0.8s ease 0s, opacity 0.8s ease 0s; + transition: + transform 0.8s ease 0s, + opacity 0.8s ease 0s; &:nth-child(2) { transition-delay: 0.5s, 0.5s; } @@ -244,7 +246,9 @@ margin-bottom: 2em; transform: translate(0, 100px); opacity: 0; - transition: transform 0.8s ease 1s, opacity 0.8s ease 1s; + transition: + transform 0.8s ease 1s, + opacity 0.8s ease 1s; box-shadow: none; max-width: none; @@ -271,7 +275,9 @@ } } .rfp-video-anim { - transition: transform 0.8s ease, opacity 0.8s ease; + transition: + transform 0.8s ease, + opacity 0.8s ease; transform: translate(100%, 0); opacity: 0; } @@ -294,7 +300,7 @@ border-left: 0; max-width: 30em; font-size: @rfp-lead-size; - quotes: '\201C''\201D'; + quotes: '\201C' '\201D'; box-shadow: @rfp-card-shadow; border-radius: @rfp-border-radius; background-color: #fff; @@ -333,7 +339,7 @@ border-left: 0; margin: 0 auto; padding: 0; - quotes: '\201C''\201D'; + quotes: '\201C' '\201D'; font-size: @rfp-lead-size; @media (min-width: @screen-md-min) { display: flex; @@ -454,7 +460,9 @@ position: absolute; left: 50%; text-transform: uppercase; - transition: opacity 0.25s, transform 0.25s; + transition: + opacity 0.25s, + transform 0.25s; transform: translate(-50%, 100%); opacity: 0; font-size: 0.5em; diff --git a/services/web/frontend/stylesheets/app/website-redesign.less b/services/web/frontend/stylesheets/app/website-redesign.less index 2fdd4ad1f5..bcefe47e20 100644 --- a/services/web/frontend/stylesheets/app/website-redesign.less +++ b/services/web/frontend/stylesheets/app/website-redesign.less @@ -140,7 +140,9 @@ .info-card { border-radius: 8px; height: 100%; - box-shadow: 0px 2px 4px 0px #1e253014, 0px 4px 12px 0px #1e25301f; + box-shadow: + 0px 2px 4px 0px #1e253014, + 0px 4px 12px 0px #1e25301f; border-top: 8px solid var(--sapphire-blue); padding: 32px 40px 32px 40px; @@ -345,7 +347,8 @@ } video { - box-shadow: 0px 4px 6px 0px rgba(30, 37, 48, 0.12), + box-shadow: + 0px 4px 6px 0px rgba(30, 37, 48, 0.12), 0px 8px 16px 0px rgba(30, 37, 48, 0.12); max-height: 100%; width: auto; @@ -998,7 +1001,7 @@ font-size: 1.875rem; line-height: 1.333; font-weight: 600; - quotes: '\201C''\201D'; // override default quotes + quotes: '\201C' '\201D'; // override default quotes padding: unset; margin: unset; font-family: 'Noto Sans', sans-serif; diff --git a/services/web/frontend/stylesheets/bootstrap-5/foundations/elevation.scss b/services/web/frontend/stylesheets/bootstrap-5/foundations/elevation.scss index c507cb3e64..94ca9c6f93 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/foundations/elevation.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/foundations/elevation.scss @@ -5,12 +5,14 @@ // Tooltips, Callouts, Dropdowns, etc. @mixin shadow-md { - box-shadow: 0px 4px 24px rgba(30, 37, 48, 0.12), + box-shadow: + 0px 4px 24px rgba(30, 37, 48, 0.12), 0px 1px 4px rgba(30, 37, 48, 0.08); } // Modals, drawers @mixin shadow-lg { - box-shadow: 0px 8px 24px rgba(30, 37, 48, 0.16), + box-shadow: + 0px 8px 24px rgba(30, 37, 48, 0.16), 0px 4px 8px rgba(30, 37, 48, 0.16); } diff --git a/services/web/frontend/stylesheets/components/expand-collapse.less b/services/web/frontend/stylesheets/components/expand-collapse.less index 1a5b6e3eb5..c2f8fa60b5 100644 --- a/services/web/frontend/stylesheets/components/expand-collapse.less +++ b/services/web/frontend/stylesheets/components/expand-collapse.less @@ -1,4 +1,6 @@ .expand-collapse-container { overflow: hidden; - transition: height 0.15s ease-in-out, width 0.15s ease-in-out; + transition: + height 0.15s ease-in-out, + width 0.15s ease-in-out; } diff --git a/services/web/frontend/stylesheets/components/forms.less b/services/web/frontend/stylesheets/components/forms.less index 5a04165714..220aa35be8 100755 --- a/services/web/frontend/stylesheets/components/forms.less +++ b/services/web/frontend/stylesheets/components/forms.less @@ -331,7 +331,8 @@ input[type='checkbox'], ); // Redeclare so transitions work &:focus { border-color: darken(@state-danger-text, 10%); - @shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), + @shadow: + inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px lighten(@state-danger-text, 20%); .box-shadow(@shadow); } diff --git a/services/web/frontend/stylesheets/components/input-switch.less b/services/web/frontend/stylesheets/components/input-switch.less index bc6133f4fc..ecdfa632e7 100644 --- a/services/web/frontend/stylesheets/components/input-switch.less +++ b/services/web/frontend/stylesheets/components/input-switch.less @@ -19,7 +19,9 @@ padding: 1px; background-color: @rp-highlight-blue; border-radius: 0.875em; - transition: background 0.15s ease, border-color 0.15s ease; + transition: + background 0.15s ease, + border-color 0.15s ease; &::before { content: ''; @@ -31,7 +33,10 @@ top: 1px; background-color: #fff; border-radius: 0.875em; - transition: background-color 0.15s ease, color 0.15s ease, left 0.15s ease; + transition: + background-color 0.15s ease, + color 0.15s ease, + left 0.15s ease; } } diff --git a/services/web/frontend/stylesheets/components/navbar.less b/services/web/frontend/stylesheets/components/navbar.less index d8839911dd..8a89126768 100755 --- a/services/web/frontend/stylesheets/components/navbar.less +++ b/services/web/frontend/stylesheets/components/navbar.less @@ -324,7 +324,8 @@ padding: 10px @navbar-padding-horizontal; border-top: 1px solid transparent; border-bottom: 1px solid transparent; - @shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), + @shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); .box-shadow(@shadow); diff --git a/services/web/frontend/stylesheets/components/ui-select.less b/services/web/frontend/stylesheets/components/ui-select.less index b6231d8b26..562fdfb58d 100644 --- a/services/web/frontend/stylesheets/components/ui-select.less +++ b/services/web/frontend/stylesheets/components/ui-select.less @@ -34,7 +34,8 @@ background-color: transparent; > .btn { border-color: @input-border-focus; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), + box-shadow: + inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px fade(@input-border-focus, 60%); padding-top: @input-suggestion-v-offset; } diff --git a/services/web/frontend/stylesheets/core/type.less b/services/web/frontend/stylesheets/core/type.less index 15672ec46e..036813d2e0 100755 --- a/services/web/frontend/stylesheets/core/type.less +++ b/services/web/frontend/stylesheets/core/type.less @@ -328,7 +328,7 @@ blockquote { padding: (@line-height-computed / 2) @line-height-computed; margin: 0 0 @line-height-computed; font-size: @blockquote-font-size; - quotes: '\201C''\201D''\2018''\2019'; + quotes: '\201C' '\201D' '\2018' '\2019'; border-left: 5px solid @blockquote-border-color; &:before { content: open-quote; diff --git a/services/web/frontend/stylesheets/core/variables.less b/services/web/frontend/stylesheets/core/variables.less index 84afac0c27..ada828214a 100644 --- a/services/web/frontend/stylesheets/core/variables.less +++ b/services/web/frontend/stylesheets/core/variables.less @@ -962,7 +962,8 @@ @btn-border-bottom-width: 0; // Shadows -@box-shadow: 0px 4px 12px rgba(30, 37, 48, 0.12), +@box-shadow: + 0px 4px 12px rgba(30, 37, 48, 0.12), 0px 2px 4px rgba(30, 37, 48, 0.08); // Cards diff --git a/services/web/frontend/stylesheets/variables/all.less b/services/web/frontend/stylesheets/variables/all.less index 3975d38eed..dd5e9b0d3b 100644 --- a/services/web/frontend/stylesheets/variables/all.less +++ b/services/web/frontend/stylesheets/variables/all.less @@ -787,7 +787,8 @@ @btn-secondary-hover-bg-color: @neutral-20; // Shadows -@box-shadow: 0px 4px 12px rgba(30, 37, 48, 0.12), +@box-shadow: + 0px 4px 12px rgba(30, 37, 48, 0.12), 0px 2px 4px rgba(30, 37, 48, 0.08); // Cards diff --git a/services/web/frontend/stylesheets/vendor/select/select.css b/services/web/frontend/stylesheets/vendor/select/select.css index 60e1675b94..ce8a9cdc51 100755 --- a/services/web/frontend/stylesheets/vendor/select/select.css +++ b/services/web/frontend/stylesheets/vendor/select/select.css @@ -156,7 +156,8 @@ body > .select2-container.open { text-decoration: none; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), + box-shadow: + inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); } diff --git a/services/web/scripts/analytics/sync_group_subscription_memberships.js b/services/web/scripts/analytics/sync_group_subscription_memberships.js index 8249177dbb..691a3ec451 100644 --- a/services/web/scripts/analytics/sync_group_subscription_memberships.js +++ b/services/web/scripts/analytics/sync_group_subscription_memberships.js @@ -45,9 +45,8 @@ async function checkActiveSubscriptions() { if (subscriptions.length) { const groupIds = subscriptions.map(sub => sub._id) - const bigQueryGroupMemberships = await fetchBigQueryMembershipStatuses( - groupIds - ) + const bigQueryGroupMemberships = + await fetchBigQueryMembershipStatuses(groupIds) const membershipsByGroupId = _.groupBy( bigQueryGroupMemberships, 'group_id' @@ -85,9 +84,8 @@ async function checkDeletedSubscriptions() { if (deletedSubscriptions.length) { const groupIds = deletedSubscriptions.map(sub => sub._id.toString()) - const bigQueryGroupMemberships = await fetchBigQueryMembershipStatuses( - groupIds - ) + const bigQueryGroupMemberships = + await fetchBigQueryMembershipStatuses(groupIds) const membershipsByGroupId = _.groupBy( bigQueryGroupMemberships, diff --git a/services/web/scripts/delete_orphaned_docs_online_check.js b/services/web/scripts/delete_orphaned_docs_online_check.js index c0a7789d51..a94ff1cce5 100644 --- a/services/web/scripts/delete_orphaned_docs_online_check.js +++ b/services/web/scripts/delete_orphaned_docs_online_check.js @@ -68,9 +68,8 @@ async function main() { new Set(docs.map(doc => doc.project_id.toString())) ).map(id => new ObjectId(id)) console.log('Checking projects', JSON.stringify(projectIds)) - const { nProjectsWithOrphanedDocs, nDeletedDocs } = await processBatch( - projectIds - ) + const { nProjectsWithOrphanedDocs, nDeletedDocs } = + await processBatch(projectIds) nProjectsProcessedTotal += projectIds.length nProjectsWithOrphanedDocsTotal += nProjectsWithOrphanedDocs nDeletedDocsTotal += nDeletedDocs diff --git a/services/web/scripts/restore_soft_deleted_docs.js b/services/web/scripts/restore_soft_deleted_docs.js index 5ab31f3bef..841407b00f 100644 --- a/services/web/scripts/restore_soft_deleted_docs.js +++ b/services/web/scripts/restore_soft_deleted_docs.js @@ -8,9 +8,8 @@ const PROJECT_ID = ARGV.shift() const FILE_NAMES_TO_RESTORE = ARGV async function main() { - const deletedDocs = await DocstoreManager.promises.getAllDeletedDocs( - PROJECT_ID - ) + const deletedDocs = + await DocstoreManager.promises.getAllDeletedDocs(PROJECT_ID) const docsToRestore = deletedDocs.filter(doc => FILE_NAMES_TO_RESTORE.includes(doc.name) ) diff --git a/services/web/scripts/sso_id_migration_check.js b/services/web/scripts/sso_id_migration_check.js index 822150a91a..20adc37325 100644 --- a/services/web/scripts/sso_id_migration_check.js +++ b/services/web/scripts/sso_id_migration_check.js @@ -19,9 +19,8 @@ waitForDb() }) async function main() { - const result = await SAMLUserIdMigrationHandler.promises.checkMigration( - institutionId - ) + const result = + await SAMLUserIdMigrationHandler.promises.checkMigration(institutionId) if (emitUsers) { console.log( diff --git a/services/web/scripts/sso_id_remove_not_migrated.js b/services/web/scripts/sso_id_remove_not_migrated.js index 031728fa8f..a63c5c2104 100644 --- a/services/web/scripts/sso_id_remove_not_migrated.js +++ b/services/web/scripts/sso_id_remove_not_migrated.js @@ -19,9 +19,8 @@ waitForDb() }) async function main() { - const result = await SAMLUserIdMigrationHandler.promises.removeNotMigrated( - institutionId - ) + const result = + await SAMLUserIdMigrationHandler.promises.removeNotMigrated(institutionId) if (emitUsers) { console.log( diff --git a/services/web/test/frontend/components/shared/select.spec.tsx b/services/web/test/frontend/components/shared/select.spec.tsx index 7231a1bd11..594fd3bb5c 100644 --- a/services/web/test/frontend/components/shared/select.spec.tsx +++ b/services/web/test/frontend/components/shared/select.spec.tsx @@ -11,7 +11,7 @@ const testData = [1, 2, 3].map(index => ({ sub: `Subtitle ${index}`, })) -type RenderProps = Partial> & { +type RenderProps = Partial> & { onSubmit?: (formData: object) => void } diff --git a/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx b/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx index 1ce5e43ed6..b65464315c 100644 --- a/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx +++ b/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx @@ -803,9 +803,8 @@ describe('', function () { fireEvent.click(allCheckboxes[1]) // select a project owned by the current user const actionsToolbar = screen.getAllByRole('toolbar')[0] - const moreDropdown = await within( - actionsToolbar - ).findByText('More') + const moreDropdown = + await within(actionsToolbar).findByText('More') fireEvent.click(moreDropdown) const editButton = @@ -851,9 +850,8 @@ describe('', function () { status: 200, } ) - const moreDropdown = await within( - actionsToolbar - ).findByText('More') + const moreDropdown = + await within(actionsToolbar).findByText('More') fireEvent.click(moreDropdown) const renameButton = @@ -888,9 +886,8 @@ describe('', function () { within(table).getByText(newProjectName) expect(within(table).queryByText(oldName)).to.be.null - const allCheckboxesInTable = await within( - table - ).findAllByRole('checkbox') + const allCheckboxesInTable = + await within(table).findAllByRole('checkbox') const allCheckboxesChecked = allCheckboxesInTable.filter( c => c.checked ) diff --git a/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual.spec.tsx b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual.spec.tsx index e732c7f6ef..eb13ffacec 100644 --- a/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual.spec.tsx +++ b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual.spec.tsx @@ -209,16 +209,19 @@ describe(' in Visual mode', function () { cy.get('@first-line').should('have.text', `${icon}key `) }) - forEach([['ref', '🏷']]).it('handles \\%s commands', function (command, icon) { - cy.get('@first-line').type(`\\${command}{} `) - cy.get('@first-line').should('have.text', `${icon} `) - cy.get('@first-line').type('{Backspace}{leftArrow}key') - cy.get('@first-line').should('have.text', `${icon}{key}`) - cy.get('@first-line').type('{rightArrow}') - cy.get('@first-line').should('have.text', `${icon}{key}`) - cy.get('@first-line').type(' ') - cy.get('@first-line').should('have.text', `${icon}key `) - }) + forEach([['ref', '🏷']]).it( + 'handles \\%s commands', + function (command, icon) { + cy.get('@first-line').type(`\\${command}{} `) + cy.get('@first-line').should('have.text', `${icon} `) + cy.get('@first-line').type('{Backspace}{leftArrow}key') + cy.get('@first-line').should('have.text', `${icon}{key}`) + cy.get('@first-line').type('{rightArrow}') + cy.get('@first-line').should('have.text', `${icon}{key}`) + cy.get('@first-line').type(' ') + cy.get('@first-line').should('have.text', `${icon}key `) + } + ) it('handles \\href command', function () { cy.get('@first-line').type('\\href{{}https://overleaf.com} ') diff --git a/services/web/test/unit/src/Institutions/InstitutionHelperTests.js b/services/web/test/unit/src/Institutions/InstitutionHelperTests.js index edd6669ca1..9de43c4be6 100644 --- a/services/web/test/unit/src/Institutions/InstitutionHelperTests.js +++ b/services/web/test/unit/src/Institutions/InstitutionHelperTests.js @@ -1,9 +1,11 @@ const { expect } = require('chai') const path = require('path') -const InstitutionsHelper = require(path.join( - __dirname, - '/../../../../app/src/Features/Institutions/InstitutionsHelper' -)) +const InstitutionsHelper = require( + path.join( + __dirname, + '/../../../../app/src/Features/Institutions/InstitutionsHelper' + ) +) describe('InstitutionsHelper', function () { describe('emailHasLicence', function () { diff --git a/services/web/test/unit/src/Project/ProjectGetterTests.js b/services/web/test/unit/src/Project/ProjectGetterTests.js index 10d8de6000..c2e3512ed9 100644 --- a/services/web/test/unit/src/Project/ProjectGetterTests.js +++ b/services/web/test/unit/src/Project/ProjectGetterTests.js @@ -440,9 +440,8 @@ describe('ProjectGetter', function () { }) it('should pass the found projects to the callback', async function () { - const docs = await this.ProjectGetter.promises.getUsersDeletedProjects( - 'giraffe' - ) + const docs = + await this.ProjectGetter.promises.getUsersDeletedProjects('giraffe') expect(docs).to.deep.equal([this.deletedProject]) }) }) diff --git a/services/web/test/unit/src/Subscription/SubscriptionGroupHandlerTests.js b/services/web/test/unit/src/Subscription/SubscriptionGroupHandlerTests.js index 81d898101b..d9f81045a5 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionGroupHandlerTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionGroupHandlerTests.js @@ -160,9 +160,8 @@ describe('SubscriptionGroupHandler', function () { }) describe('for nonexistent subscriptions', function () { it('should return undefined', async function () { - const count = await this.Handler.promises.getTotalConfirmedUsersInGroup( - 'fake-id' - ) + const count = + await this.Handler.promises.getTotalConfirmedUsersInGroup('fake-id') expect(count).not.to.exist }) }) diff --git a/services/web/test/unit/src/Uploads/FileSystemImportManagerTests.js b/services/web/test/unit/src/Uploads/FileSystemImportManagerTests.js index fb0a2ad2be..d42388bf04 100644 --- a/services/web/test/unit/src/Uploads/FileSystemImportManagerTests.js +++ b/services/web/test/unit/src/Uploads/FileSystemImportManagerTests.js @@ -64,9 +64,8 @@ describe('FileSystemImportManager', function () { }, symlink: mockFs.symlink({ path: 'import-test' }), }) - this.entries = await this.FileSystemImportManager.promises.importDir( - 'import-test' - ) + this.entries = + await this.FileSystemImportManager.promises.importDir('import-test') this.projectPaths = this.entries.map(x => x.projectPath) }) diff --git a/services/web/types/utils.ts b/services/web/types/utils.ts index 4bdb852937..52c505dbac 100644 --- a/services/web/types/utils.ts +++ b/services/web/types/utils.ts @@ -10,10 +10,10 @@ type DeepReadonlyObject = { export type DeepReadonly = T extends (infer R)[] ? DeepReadonlyArray : T extends (...args: any[]) => void - ? T - : T extends object - ? DeepReadonlyObject - : T + ? T + : T extends object + ? DeepReadonlyObject + : T export type DeepPartial = Partial<{ [P in keyof T]: DeepPartial }>