From f9e3416d7843fec5ab810a424b656ab4e18ae9a5 Mon Sep 17 00:00:00 2001 From: SamTV12345 <40429738+samtv12345@users.noreply.github.com> Date: Wed, 13 Mar 2024 20:31:29 +0100 Subject: [PATCH] Ported bin folder to typescript. --- bin/{checkAllPads.js => checkAllPads.ts} | 8 +- bin/{checkPad.js => checkPad.ts} | 5 +- ...ateUserSession.js => createUserSession.ts} | 43 +- ...pSessions.js => deleteAllGroupSessions.ts} | 41 +- bin/{deletePad.js => deletePad.ts} | 23 +- bin/doc/LICENSE | 18 - bin/doc/README.md | 76 --- bin/doc/generate.js | 122 ---- bin/doc/html.js | 165 ------ bin/doc/json.js | 556 ------------------ bin/etherpad-healthcheck | 26 - bin/{extractPadData.js => extractPadData.ts} | 12 +- bin/{importSqlFile.js => importSqlFile.ts} | 42 +- ...BtoRealDB.js => migrateDirtyDBtoRealDB.ts} | 34 +- bin/package.json | 37 ++ .../{checkPlugin.js => checkPlugin.ts} | 34 +- bin/plugins/stalePlugins.js | 20 - bin/plugins/stalePlugins.ts | 22 + bin/{rebuildPad.js => rebuildPad.ts} | 13 +- bin/{release.js => release.ts} | 50 +- bin/{repairPad.js => repairPad.ts} | 10 +- bin/tsconfig.json | 109 ++++ bin/updatePlugins.sh | 2 +- pnpm-workspace.yaml | 1 + 24 files changed, 346 insertions(+), 1123 deletions(-) rename bin/{checkAllPads.js => checkAllPads.ts} (81%) rename bin/{checkPad.js => checkPad.ts} (83%) rename bin/{createUserSession.js => createUserSession.ts} (50%) rename bin/{deleteAllGroupSessions.js => deleteAllGroupSessions.ts} (53%) rename bin/{deletePad.js => deletePad.ts} (60%) delete mode 100644 bin/doc/LICENSE delete mode 100644 bin/doc/README.md delete mode 100644 bin/doc/generate.js delete mode 100644 bin/doc/html.js delete mode 100644 bin/doc/json.js delete mode 100755 bin/etherpad-healthcheck rename bin/{extractPadData.js => extractPadData.ts} (86%) rename bin/{importSqlFile.js => importSqlFile.ts} (70%) rename bin/{migrateDirtyDBtoRealDB.js => migrateDirtyDBtoRealDB.ts} (65%) create mode 100644 bin/package.json rename bin/plugins/{checkPlugin.js => checkPlugin.ts} (94%) mode change 100755 => 100644 delete mode 100644 bin/plugins/stalePlugins.js create mode 100644 bin/plugins/stalePlugins.ts rename bin/{rebuildPad.js => rebuildPad.ts} (88%) rename bin/{release.js => release.ts} (87%) rename bin/{repairPad.js => repairPad.ts} (84%) create mode 100644 bin/tsconfig.json diff --git a/bin/checkAllPads.js b/bin/checkAllPads.ts similarity index 81% rename from bin/checkAllPads.js rename to bin/checkAllPads.ts index 69514f358..a192be794 100644 --- a/bin/checkAllPads.js +++ b/bin/checkAllPads.ts @@ -10,14 +10,14 @@ process.on('unhandledRejection', (err) => { throw err; }); if (process.argv.length !== 2) throw new Error('Use: node bin/checkAllPads.js'); (async () => { - const db = require('./src/node/db/DB'); + const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); - const padManager = require('./src/node/db/PadManager'); - await Promise.all((await padManager.listAllPads()).padIDs.map(async (padId) => { + const padManager = require('ep_etherpad-lite/node/db/PadManager'); + await Promise.all((await padManager.listAllPads()).padIDs.map(async (padId: string) => { const pad = await padManager.getPad(padId); try { await pad.check(); - } catch (err) { + } catch (err:any) { console.error(`Error in pad ${padId}: ${err.stack || err}`); return; } diff --git a/bin/checkPad.js b/bin/checkPad.ts similarity index 83% rename from bin/checkPad.js rename to bin/checkPad.ts index 104486371..1e411693f 100644 --- a/bin/checkPad.js +++ b/bin/checkPad.ts @@ -8,11 +8,12 @@ process.on('unhandledRejection', (err) => { throw err; }); if (process.argv.length !== 3) throw new Error('Use: node bin/checkPad.js $PADID'); +// @ts-ignore const padId = process.argv[2]; (async () => { - const db = require('./src/node/db/DB'); + const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); - const padManager = require('./src/node/db/PadManager'); + const padManager = require('ep_etherpad-lite/node/db/PadManager'); if (!await padManager.doesPadExists(padId)) throw new Error('Pad does not exist'); const pad = await padManager.getPad(padId); await pad.check(); diff --git a/bin/createUserSession.js b/bin/createUserSession.ts similarity index 50% rename from bin/createUserSession.js rename to bin/createUserSession.ts index 58ab1bba5..9f340ae9b 100644 --- a/bin/createUserSession.js +++ b/bin/createUserSession.ts @@ -7,16 +7,20 @@ // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an // unhandled rejection into an uncaught exception, which does cause Node.js to exit. +import fs from "fs"; + +import path from "path"; + +import querystring from "querystring"; + +import axios from 'axios' + + process.on('unhandledRejection', (err) => { throw err; }); - -const fs = require('fs'); -const path = require('path'); -const querystring = require('querystring'); -const settings = require('./src/node/utils/Settings'); -const supertest = require('supertest'); - +const settings = require('ep_etherpad-lite/node/utils/Settings'); (async () => { - const api = supertest(`http://${settings.ip}:${settings.port}`); + axios.defaults.baseURL = `http://${settings.ip}:${settings.port}`; + const api = axios; const filePath = path.join(__dirname, '../APIKEY.txt'); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); @@ -24,28 +28,29 @@ const supertest = require('supertest'); let res; res = await api.get('/api/'); - const apiVersion = res.body.currentVersion; + const apiVersion = res.data.currentVersion; if (!apiVersion) throw new Error('No version set in API'); - const uri = (cmd, args) => `/api/${apiVersion}/${cmd}?${querystring.stringify(args)}`; + console.log('apiVersion', apiVersion); + const uri = (cmd: string, args: querystring.ParsedUrlQueryInput ) => `/api/${apiVersion}/${cmd}?${querystring.stringify(args)}`; res = await api.post(uri('createGroup', {apikey})); - if (res.body.code === 1) throw new Error(`Error creating group: ${res.body}`); - const groupID = res.body.data.groupID; + if (res.data.code === 1) throw new Error(`Error creating group: ${res.data}`); + const groupID = res.data.data.groupID; console.log('groupID', groupID); res = await api.post(uri('createGroupPad', {apikey, groupID})); - if (res.body.code === 1) throw new Error(`Error creating group pad: ${res.body}`); - console.log('Test Pad ID ====> ', res.body.data.padID); + if (res.data.code === 1) throw new Error(`Error creating group pad: ${res.data}`); + console.log('Test Pad ID ====> ', res.data.data.padID); res = await api.post(uri('createAuthor', {apikey})); - if (res.body.code === 1) throw new Error(`Error creating author: ${res.body}`); - const authorID = res.body.data.authorID; + if (res.data.code === 1) throw new Error(`Error creating author: ${res.data}`); + const authorID = res.data.data.authorID; console.log('authorID', authorID); - const validUntil = Math.floor(new Date() / 1000) + 60000; + const validUntil = Math.floor(new Date().getTime() / 1000) + 60000; console.log('validUntil', validUntil); res = await api.post(uri('createSession', {apikey, groupID, authorID, validUntil})); - if (res.body.code === 1) throw new Error(`Error creating session: ${res.body}`); + if (res.data.code === 1) throw new Error(`Error creating session: ${JSON.stringify(res.data)}`); console.log('Session made: ====> create a cookie named sessionID and set the value to', - res.body.data.sessionID); + res.data.data.sessionID); })(); diff --git a/bin/deleteAllGroupSessions.js b/bin/deleteAllGroupSessions.ts similarity index 53% rename from bin/deleteAllGroupSessions.js rename to bin/deleteAllGroupSessions.ts index 4b619265f..d10943382 100644 --- a/bin/deleteAllGroupSessions.js +++ b/bin/deleteAllGroupSessions.ts @@ -1,5 +1,3 @@ -'use strict'; - /* * A tool for deleting ALL GROUP sessions Etherpad user sessions from the CLI, * because sometimes a brick is required to fix a face. @@ -7,12 +5,13 @@ // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an // unhandled rejection into an uncaught exception, which does cause Node.js to exit. +import path from "path"; + +import fs from "fs"; +import process from "process"; + process.on('unhandledRejection', (err) => { throw err; }); - -const path = require('path'); -const fs = require('fs'); -const supertest = require('supertest'); - +import axios from 'axios' // Set a delete counter which will increment on each delete attempt // TODO: Check delete is successful before incrementing let deleteCount = 0; @@ -20,27 +19,33 @@ let deleteCount = 0; // get the API Key const filePath = path.join(__dirname, '../APIKEY.txt'); console.log('Deleting all group sessions, please be patient.'); +const settings = require('ep_etherpad-lite/tests/container/loadSettings').loadSettings(); (async () => { - const settings = require('./src/tests/container/loadSettings').loadSettings(); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); - const api = supertest(`http://${settings.ip}:${settings.port}`); + axios.defaults.baseURL = `http://${settings.ip}:${settings.port}`; - const apiVersionResponse = await api.get('/api/'); - const apiVersion = apiVersionResponse.body.currentVersion; // 1.12.5 + const apiVersionResponse = await axios.get('/api/'); + const apiVersion = apiVersionResponse.data.currentVersion; // 1.12.5 + console.log('apiVersion', apiVersion); - const groupsResponse = await api.get(`/api/${apiVersion}/listAllGroups?apikey=${apikey}`); - const groups = groupsResponse.body.data.groupIDs; // ['whateverGroupID'] + const groupsResponse = await axios.get(`/api/${apiVersion}/listAllGroups?apikey=${apikey}`); + const groups = groupsResponse.data.data.groupIDs; // ['whateverGroupID'] for (const groupID of groups) { const sessionURI = `/api/${apiVersion}/listSessionsOfGroup?apikey=${apikey}&groupID=${groupID}`; - const sessionsResponse = await api.get(sessionURI); - const sessions = sessionsResponse.body.data; + const sessionsResponse = await axios.get(sessionURI); + const sessions = sessionsResponse.data.data; - for (const sessionID of Object.keys(sessions)) { + if(sessions == null) continue; + + for (const [sessionID, val] of Object.entries(sessions)) { + if(val == null) continue; const deleteURI = `/api/${apiVersion}/deleteSession?apikey=${apikey}&sessionID=${sessionID}`; - await api.post(deleteURI); // delete - deleteCount++; + await axios.post(deleteURI).then(c=>{ + console.log(c.data) + deleteCount++; + }); // delete } } console.log(`Deleted ${deleteCount} sessions`); diff --git a/bin/deletePad.js b/bin/deletePad.ts similarity index 60% rename from bin/deletePad.js rename to bin/deletePad.ts index 7823cbd8b..a9d60190e 100644 --- a/bin/deletePad.js +++ b/bin/deletePad.ts @@ -7,14 +7,17 @@ // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an // unhandled rejection into an uncaught exception, which does cause Node.js to exit. +import path from "path"; + +import fs from "fs"; +import process from "process"; +import axios from "axios"; + process.on('unhandledRejection', (err) => { throw err; }); -const settings = require('./src/tests/container/loadSettings').loadSettings(); -const path = require('path'); -const fs = require('fs'); -const supertest = require('supertest'); +const settings = require('ep_etherpad-lite/tests/container/loadSettings').loadSettings(); -const api = supertest(`http://${settings.ip}:${settings.port}`); +axios.defaults.baseURL = `http://${settings.ip}:${settings.port}`; if (process.argv.length !== 3) throw new Error('Use: node deletePad.js $PADID'); @@ -26,13 +29,13 @@ const filePath = path.join(__dirname, '../APIKEY.txt'); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); (async () => { - let apiVersion = await api.get('/api/'); - apiVersion = apiVersion.body.currentVersion; + let apiVersion = await axios.get('/api/'); + apiVersion = apiVersion.data.currentVersion; if (!apiVersion) throw new Error('No version set in API'); // Now we know the latest API version, let's delete pad const uri = `/api/${apiVersion}/deletePad?apikey=${apikey}&padID=${padId}`; - const deleteAttempt = await api.post(uri); - if (deleteAttempt.body.code === 1) throw new Error(`Error deleting pad ${deleteAttempt.body}`); - console.log('Deleted pad', deleteAttempt.body); + const deleteAttempt = await axios.post(uri); + if (deleteAttempt.data.code === 1) throw new Error(`Error deleting pad ${deleteAttempt.data}`); + console.log('Deleted pad', deleteAttempt.data); })(); diff --git a/bin/doc/LICENSE b/bin/doc/LICENSE deleted file mode 100644 index e3d4e695a..000000000 --- a/bin/doc/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright Joyent, Inc. and other Node contributors. All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. diff --git a/bin/doc/README.md b/bin/doc/README.md deleted file mode 100644 index 19a137fd7..000000000 --- a/bin/doc/README.md +++ /dev/null @@ -1,76 +0,0 @@ -Here's how the node docs work. - -Each type of heading has a description block. - - - ## module - - Stability: 3 - Stable - - description and examples. - - ### module.property - - * Type - - description of the property. - - ### module.someFunction(x, y, [z=100]) - - * `x` {String} the description of the string - * `y` {Boolean} Should I stay or should I go? - * `z` {Number} How many zebras to bring. - - A description of the function. - - ### Event: 'blerg' - - * Argument: SomeClass object. - - Modules don't usually raise events on themselves. `cluster` is the - only exception. - - ## Class: SomeClass - - description of the class. - - ### Class Method: SomeClass.classMethod(anArg) - - * `anArg` {Object} Just an argument - * `field` {String} anArg can have this field. - * `field2` {Boolean} Another field. Default: `false`. - * Return: {Boolean} `true` if it worked. - - Description of the method for humans. - - ### someClass.nextSibling() - - * Return: {SomeClass object | null} The next someClass in line. - - ### someClass.someProperty - - * String - - The indication of what someProperty is. - - ### Event: 'grelb' - - * `isBlerg` {Boolean} - - This event is emitted on instances of SomeClass, not on the module itself. - - -* Modules have (description, Properties, Functions, Classes, Examples) -* Properties have (type, description) -* Functions have (list of arguments, description) -* Classes have (description, Properties, Methods, Events) -* Events have (list of arguments, description) -* Methods have (list of arguments, description) -* Properties have (type, description) - -# CLI usage - -Run the following from the etherpad-lite root directory: -```sh -$ node src/bin/doc/generate doc/index.md --format=html --template=doc/template.html > out.html -``` diff --git a/bin/doc/generate.js b/bin/doc/generate.js deleted file mode 100644 index d04468a8b..000000000 --- a/bin/doc/generate.js +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env node - -'use strict'; - -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -const fs = require('fs'); -const path = require('path'); - -// parse the args. -// Don't use nopt or whatever for this. It's simple enough. - -const args = process.argv.slice(2); -let format = 'json'; -let template = null; -let inputFile = null; - -args.forEach((arg) => { - if (!arg.match(/^--/)) { - inputFile = arg; - } else if (arg.match(/^--format=/)) { - format = arg.replace(/^--format=/, ''); - } else if (arg.match(/^--template=/)) { - template = arg.replace(/^--template=/, ''); - } -}); - - -if (!inputFile) { - throw new Error('No input file specified'); -} - - -console.error('Input file = %s', inputFile); -fs.readFile(inputFile, 'utf8', (er, input) => { - if (er) throw er; - // process the input for @include lines - processIncludes(inputFile, input, next); -}); - - -const includeExpr = /^@include\s+([A-Za-z0-9-_/]+)(?:\.)?([a-zA-Z]*)$/gmi; -const includeData = {}; -const processIncludes = (inputFile, input, cb) => { - const includes = input.match(includeExpr); - if (includes == null) return cb(null, input); - let errState = null; - console.error(includes); - let incCount = includes.length; - if (incCount === 0) cb(null, input); - - includes.forEach((include) => { - let fname = include.replace(/^@include\s+/, ''); - if (!fname.match(/\.md$/)) fname += '.md'; - - if (Object.prototype.hasOwnProperty.call(includeData, fname)) { - input = input.split(include).join(includeData[fname]); - incCount--; - if (incCount === 0) { - return cb(null, input); - } - } - - const fullFname = path.resolve(path.dirname(inputFile), fname); - fs.readFile(fullFname, 'utf8', (er, inc) => { - if (errState) return; - if (er) return cb(errState = er); - processIncludes(fullFname, inc, (er, inc) => { - if (errState) return; - if (er) return cb(errState = er); - incCount--; - includeData[fname] = inc; - input = input.split(include).join(includeData[fname]); - if (incCount === 0) { - return cb(null, input); - } - }); - }); - }); -}; - - -const next = (er, input) => { - if (er) throw er; - switch (format) { - case 'json': - require('./json.js')(input, inputFile, (er, obj) => { - console.log(JSON.stringify(obj, null, 2)); - if (er) throw er; - }); - break; - - case 'html': - require('./html.js')(input, inputFile, template, (er, html) => { - if (er) throw er; - console.log(html); - }); - break; - - default: - throw new Error(`Invalid format: ${format}`); - } -}; diff --git a/bin/doc/html.js b/bin/doc/html.js deleted file mode 100644 index ff07ae7c8..000000000 --- a/bin/doc/html.js +++ /dev/null @@ -1,165 +0,0 @@ -'use strict'; - -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -const fs = require('fs'); -const marked = require('marked'); -const path = require('path'); - - -const toHTML = (input, filename, template, cb) => { - const lexed = marked.lexer(input); - fs.readFile(template, 'utf8', (er, template) => { - if (er) return cb(er); - render(lexed, filename, template, cb); - }); -}; -module.exports = toHTML; - -const render = (lexed, filename, template, cb) => { - // get the section - const section = getSection(lexed); - - filename = path.basename(filename, '.md'); - - lexed = parseLists(lexed); - - // generate the table of contents. - // this mutates the lexed contents in-place. - buildToc(lexed, filename, (er, toc) => { - if (er) return cb(er); - - template = template.replace(/__FILENAME__/g, filename); - template = template.replace(/__SECTION__/g, section); - template = template.replace(/__TOC__/g, toc); - - // content has to be the last thing we do with - // the lexed tokens, because it's destructive. - const content = marked.parser(lexed); - template = template.replace(/__CONTENT__/g, content); - - cb(null, template); - }); -}; - - -// just update the list item text in-place. -// lists that come right after a heading are what we're after. -const parseLists = (input) => { - let state = null; - let depth = 0; - const output = []; - output.links = input.links; - input.forEach((tok) => { - if (state == null) { - if (tok.type === 'heading') { - state = 'AFTERHEADING'; - } - output.push(tok); - return; - } - if (state === 'AFTERHEADING') { - if (tok.type === 'list_start') { - state = 'LIST'; - if (depth === 0) { - output.push({type: 'html', text: '
'}); - } - depth++; - output.push(tok); - return; - } - state = null; - output.push(tok); - return; - } - if (state === 'LIST') { - if (tok.type === 'list_start') { - depth++; - output.push(tok); - return; - } - if (tok.type === 'list_end') { - depth--; - if (depth === 0) { - state = null; - output.push({type: 'html', text: '
'}); - } - output.push(tok); - return; - } - if (tok.text) { - tok.text = parseListItem(tok.text); - } - } - output.push(tok); - }); - - return output; -}; - - -const parseListItem = (text) => { - text = text.replace(/\{([^}]+)\}/, '$1'); - // XXX maybe put more stuff here? - return text; -}; - - -// section is just the first heading -const getSection = (lexed) => { - for (let i = 0, l = lexed.length; i < l; i++) { - const tok = lexed[i]; - if (tok.type === 'heading') return tok.text; - } - return ''; -}; - - -const buildToc = (lexed, filename, cb) => { - let toc = []; - let depth = 0; - - marked.setOptions({ - headerIds: true, - headerPrefix: `${filename}_`, - }); - - lexed.forEach((tok) => { - if (tok.type !== 'heading') return; - if (tok.depth - depth > 1) { - return cb(new Error(`Inappropriate heading level\n${JSON.stringify(tok)}`)); - } - - depth = tok.depth; - - const slugger = new marked.Slugger(); - const id = slugger.slug(`${filename}_${tok.text.trim()}`); - - toc.push(`${new Array((depth - 1) * 2 + 1).join(' ')}* ${tok.text}`); - - tok.text += `#`; - }); - - toc = marked.parse(toc.join('\n')); - cb(null, toc); -}; diff --git a/bin/doc/json.js b/bin/doc/json.js deleted file mode 100644 index 1a5ecb1d8..000000000 --- a/bin/doc/json.js +++ /dev/null @@ -1,556 +0,0 @@ -'use strict'; -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -module.exports = doJSON; - -// Take the lexed input, and return a JSON-encoded object -// A module looks like this: https://gist.github.com/1777387 - -const marked = require('marked'); - -const doJSON = (input, filename, cb) => { - const root = {source: filename}; - const stack = [root]; - let depth = 0; - let current = root; - let state = null; - const lexed = marked.lexer(input); - lexed.forEach((tok) => { - const type = tok.type; - let text = tok.text; - - // - // This is for cases where the markdown semantic structure is lacking. - if (type === 'paragraph' || type === 'html') { - const metaExpr = /\n*/g; - text = text.replace(metaExpr, (_0, k, v) => { - current[k.trim()] = v.trim(); - return ''; - }); - text = text.trim(); - if (!text) return; - } - - if (type === 'heading' && - !text.trim().match(/^example/i)) { - if (tok.depth - depth > 1) { - return cb(new Error(`Inappropriate heading level\n${ - JSON.stringify(tok)}`)); - } - - // Sometimes we have two headings with a single - // blob of description. Treat as a clone. - if (current && - state === 'AFTERHEADING' && - depth === tok.depth) { - const clone = current; - current = newSection(tok); - current.clone = clone; - // don't keep it around on the stack. - stack.pop(); - } else { - // if the level is greater than the current depth, - // then it's a child, so we should just leave the stack - // as it is. - // However, if it's a sibling or higher, then it implies - // the closure of the other sections that came before. - // root is always considered the level=0 section, - // and the lowest heading is 1, so this should always - // result in having a valid parent node. - let d = tok.depth; - while (d <= depth) { - finishSection(stack.pop(), stack[stack.length - 1]); - d++; - } - current = newSection(tok); - } - - depth = tok.depth; - stack.push(current); - state = 'AFTERHEADING'; - return; - } // heading - - // Immediately after a heading, we can expect the following - // - // { type: 'code', text: 'Stability: ...' }, - // - // a list: starting with list_start, ending with list_end, - // maybe containing other nested lists in each item. - // - // If one of these isn't found, then anything that comes between - // here and the next heading should be parsed as the desc. - let stability; - if (state === 'AFTERHEADING') { - if (type === 'code' && - (stability = text.match(/^Stability: ([0-5])(?:\s*-\s*)?(.*)$/))) { - current.stability = parseInt(stability[1], 10); - current.stabilityText = stability[2].trim(); - return; - } else if (type === 'list_start' && !tok.ordered) { - state = 'AFTERHEADING_LIST'; - current.list = current.list || []; - current.list.push(tok); - current.list.level = 1; - } else { - current.desc = current.desc || []; - if (!Array.isArray(current.desc)) { - current.shortDesc = current.desc; - current.desc = []; - } - current.desc.push(tok); - state = 'DESC'; - } - return; - } - - if (state === 'AFTERHEADING_LIST') { - current.list.push(tok); - if (type === 'list_start') { - current.list.level++; - } else if (type === 'list_end') { - current.list.level--; - } - if (current.list.level === 0) { - state = 'AFTERHEADING'; - processList(current); - } - return; - } - - current.desc = current.desc || []; - current.desc.push(tok); - }); - - // finish any sections left open - while (root !== (current = stack.pop())) { - finishSection(current, stack[stack.length - 1]); - } - - return cb(null, root); -}; - - -// go from something like this: -// [ { type: 'list_item_start' }, -// { type: 'text', -// text: '`settings` Object, Optional' }, -// { type: 'list_start', ordered: false }, -// { type: 'list_item_start' }, -// { type: 'text', -// text: 'exec: String, file path to worker file. Default: `__filename`' }, -// { type: 'list_item_end' }, -// { type: 'list_item_start' }, -// { type: 'text', -// text: 'args: Array, string arguments passed to worker.' }, -// { type: 'text', -// text: 'Default: `process.argv.slice(2)`' }, -// { type: 'list_item_end' }, -// { type: 'list_item_start' }, -// { type: 'text', -// text: 'silent: Boolean, whether or not to send output to parent\'s stdio.' }, -// { type: 'text', text: 'Default: `false`' }, -// { type: 'space' }, -// { type: 'list_item_end' }, -// { type: 'list_end' }, -// { type: 'list_item_end' }, -// { type: 'list_end' } ] -// to something like: -// [ { name: 'settings', -// type: 'object', -// optional: true, -// settings: -// [ { name: 'exec', -// type: 'string', -// desc: 'file path to worker file', -// default: '__filename' }, -// { name: 'args', -// type: 'array', -// default: 'process.argv.slice(2)', -// desc: 'string arguments passed to worker.' }, -// { name: 'silent', -// type: 'boolean', -// desc: 'whether or not to send output to parent\'s stdio.', -// default: 'false' } ] } ] - -const processList = (section) => { - const list = section.list; - const values = []; - let current; - const stack = []; - - // for now, *just* build the hierarchical list - list.forEach((tok) => { - const type = tok.type; - if (type === 'space') return; - if (type === 'list_item_start') { - if (!current) { - const n = {}; - values.push(n); - current = n; - } else { - current.options = current.options || []; - stack.push(current); - const n = {}; - current.options.push(n); - current = n; - } - return; - } else if (type === 'list_item_end') { - if (!current) { - throw new Error(`invalid list - end without current item\n${ - JSON.stringify(tok)}\n${ - JSON.stringify(list)}`); - } - current = stack.pop(); - } else if (type === 'text') { - if (!current) { - throw new Error(`invalid list - text without current item\n${ - JSON.stringify(tok)}\n${ - JSON.stringify(list)}`); - } - current.textRaw = current.textRaw || ''; - current.textRaw += `${tok.text} `; - } - }); - - // shove the name in there for properties, since they are always - // just going to be the value etc. - if (section.type === 'property' && values[0]) { - values[0].textRaw = `\`${section.name}\` ${values[0].textRaw}`; - } - - // now pull the actual values out of the text bits. - values.forEach(parseListItem); - - // Now figure out what this list actually means. - // depending on the section type, the list could be different things. - - switch (section.type) { - case 'ctor': - case 'classMethod': - case 'method': { - // each item is an argument, unless the name is 'return', - // in which case it's the return value. - section.signatures = section.signatures || []; - const sig = {}; - section.signatures.push(sig); - sig.params = values.filter((v) => { - if (v.name === 'return') { - sig.return = v; - return false; - } - return true; - }); - parseSignature(section.textRaw, sig); - break; - } - case 'property': { - // there should be only one item, which is the value. - // copy the data up to the section. - const value = values[0] || {}; - delete value.name; - section.typeof = value.type; - delete value.type; - Object.keys(value).forEach((k) => { - section[k] = value[k]; - }); - break; - } - - case 'event': { - // event: each item is an argument. - section.params = values; - break; - } - } - - delete section.list; -}; - - -// textRaw = "someobject.someMethod(a, [b=100], [c])" -const parseSignature = (text, sig) => { - let params = text.match(paramExpr); - if (!params) return; - params = params[1]; - // the ] is irrelevant. [ indicates optionalness. - params = params.replace(/\]/g, ''); - params = params.split(/,/); - params.forEach((p, i, _) => { - p = p.trim(); - if (!p) return; - let param = sig.params[i]; - let optional = false; - let def; - // [foo] -> optional - if (p.charAt(0) === '[') { - optional = true; - p = p.substr(1); - } - const eq = p.indexOf('='); - if (eq !== -1) { - def = p.substr(eq + 1); - p = p.substr(0, eq); - } - if (!param) { - param = sig.params[i] = {name: p}; - } - // at this point, the name should match. - if (p !== param.name) { - console.error('Warning: invalid param "%s"', p); - console.error(` > ${JSON.stringify(param)}`); - console.error(` > ${text}`); - } - if (optional) param.optional = true; - if (def !== undefined) param.default = def; - }); -}; - - -const parseListItem = (item) => { - if (item.options) item.options.forEach(parseListItem); - if (!item.textRaw) return; - - // the goal here is to find the name, type, default, and optional. - // anything left over is 'desc' - let text = item.textRaw.trim(); - // text = text.replace(/^(Argument|Param)s?\s*:?\s*/i, ''); - - text = text.replace(/^, /, '').trim(); - const retExpr = /^returns?\s*:?\s*/i; - const ret = text.match(retExpr); - if (ret) { - item.name = 'return'; - text = text.replace(retExpr, ''); - } else { - const nameExpr = /^['`"]?([^'`": {]+)['`"]?\s*:?\s*/; - const name = text.match(nameExpr); - if (name) { - item.name = name[1]; - text = text.replace(nameExpr, ''); - } - } - - text = text.trim(); - const defaultExpr = /\(default\s*[:=]?\s*['"`]?([^, '"`]*)['"`]?\)/i; - const def = text.match(defaultExpr); - if (def) { - item.default = def[1]; - text = text.replace(defaultExpr, ''); - } - - text = text.trim(); - const typeExpr = /^\{([^}]+)\}/; - const type = text.match(typeExpr); - if (type) { - item.type = type[1]; - text = text.replace(typeExpr, ''); - } - - text = text.trim(); - const optExpr = /^Optional\.|(?:, )?Optional$/; - const optional = text.match(optExpr); - if (optional) { - item.optional = true; - text = text.replace(optExpr, ''); - } - - text = text.replace(/^\s*-\s*/, ''); - text = text.trim(); - if (text) item.desc = text; -}; - - -const finishSection = (section, parent) => { - if (!section || !parent) { - throw new Error(`Invalid finishSection call\n${ - JSON.stringify(section)}\n${ - JSON.stringify(parent)}`); - } - - if (!section.type) { - section.type = 'module'; - if (parent && (parent.type === 'misc')) { - section.type = 'misc'; - } - section.displayName = section.name; - section.name = section.name.toLowerCase() - .trim().replace(/\s+/g, '_'); - } - - if (section.desc && Array.isArray(section.desc)) { - section.desc.links = section.desc.links || []; - section.desc = marked.parser(section.desc); - } - - if (!section.list) section.list = []; - processList(section); - - // classes sometimes have various 'ctor' children - // which are actually just descriptions of a constructor - // class signature. - // Merge them into the parent. - if (section.type === 'class' && section.ctors) { - section.signatures = section.signatures || []; - const sigs = section.signatures; - section.ctors.forEach((ctor) => { - ctor.signatures = ctor.signatures || [{}]; - ctor.signatures.forEach((sig) => { - sig.desc = ctor.desc; - }); - sigs.push(...ctor.signatures); - }); - delete section.ctors; - } - - // properties are a bit special. - // their "type" is the type of object, not "property" - if (section.properties) { - section.properties.forEach((p) => { - if (p.typeof) p.type = p.typeof; - else delete p.type; - delete p.typeof; - }); - } - - // handle clones - if (section.clone) { - const clone = section.clone; - delete section.clone; - delete clone.clone; - deepCopy(section, clone); - finishSection(clone, parent); - } - - let plur; - if (section.type.slice(-1) === 's') { - plur = `${section.type}es`; - } else if (section.type.slice(-1) === 'y') { - plur = section.type.replace(/y$/, 'ies'); - } else { - plur = `${section.type}s`; - } - - // if the parent's type is 'misc', then it's just a random - // collection of stuff, like the "globals" section. - // Make the children top-level items. - if (section.type === 'misc') { - Object.keys(section).forEach((k) => { - switch (k) { - case 'textRaw': - case 'name': - case 'type': - case 'desc': - case 'miscs': - return; - default: - if (parent.type === 'misc') { - return; - } - if (Array.isArray(k) && parent[k]) { - parent[k] = parent[k].concat(section[k]); - } else if (!parent[k]) { - parent[k] = section[k]; - } else { - // parent already has, and it's not an array. - return; - } - } - }); - } - - parent[plur] = parent[plur] || []; - parent[plur].push(section); -}; - - -// Not a general purpose deep copy. -// But sufficient for these basic things. -const deepCopy = (src, dest) => { - Object.keys(src).filter((k) => !Object.prototype.hasOwnProperty.call(dest, k)).forEach((k) => { - dest[k] = deepCopy_(src[k]); - }); -}; - -const deepCopy_ = (src) => { - if (!src) return src; - if (Array.isArray(src)) { - const c = new Array(src.length); - src.forEach((v, i) => { - c[i] = deepCopy_(v); - }); - return c; - } - if (typeof src === 'object') { - const c = {}; - Object.keys(src).forEach((k) => { - c[k] = deepCopy_(src[k]); - }); - return c; - } - return src; -}; - - -// these parse out the contents of an H# tag -const eventExpr = /^Event(?::|\s)+['"]?([^"']+).*$/i; -const classExpr = /^Class:\s*([^ ]+).*?$/i; -const propExpr = /^(?:property:?\s*)?[^.]+\.([^ .()]+)\s*?$/i; -const braceExpr = /^(?:property:?\s*)?[^.[]+(\[[^\]]+\])\s*?$/i; -const classMethExpr = - /^class\s*method\s*:?[^.]+\.([^ .()]+)\([^)]*\)\s*?$/i; -const methExpr = - /^(?:method:?\s*)?(?:[^.]+\.)?([^ .()]+)\([^)]*\)\s*?$/i; -const newExpr = /^new ([A-Z][a-z]+)\([^)]*\)\s*?$/; -const paramExpr = /\((.*)\);?$/; - -const newSection = (tok) => { - const section = {}; - // infer the type from the text. - const text = section.textRaw = tok.text; - if (text.match(eventExpr)) { - section.type = 'event'; - section.name = text.replace(eventExpr, '$1'); - } else if (text.match(classExpr)) { - section.type = 'class'; - section.name = text.replace(classExpr, '$1'); - } else if (text.match(braceExpr)) { - section.type = 'property'; - section.name = text.replace(braceExpr, '$1'); - } else if (text.match(propExpr)) { - section.type = 'property'; - section.name = text.replace(propExpr, '$1'); - } else if (text.match(classMethExpr)) { - section.type = 'classMethod'; - section.name = text.replace(classMethExpr, '$1'); - } else if (text.match(methExpr)) { - section.type = 'method'; - section.name = text.replace(methExpr, '$1'); - } else if (text.match(newExpr)) { - section.type = 'ctor'; - section.name = text.replace(newExpr, '$1'); - } else { - section.name = text; - } - return section; -}; diff --git a/bin/etherpad-healthcheck b/bin/etherpad-healthcheck deleted file mode 100755 index 59105d38a..000000000 --- a/bin/etherpad-healthcheck +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env node - -// Checks the health of Etherpad by visiting http://localhost:9001/health. Returns 0 on success, 1 -// on error as required by the Dockerfile HEALTHCHECK instruction. - -'use strict'; - -// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an -// unhandled rejection into an uncaught exception, which does cause Node.js to exit. -process.on('unhandledRejection', (err) => { throw err; }); - -const assert = require('assert').strict; -const superagent = require('superagent'); - -(async () => { - const res = await superagent.get('http://localhost:9001/health') - .accept('application/health+json') - .buffer(true) - .parse(superagent.parse['application/json']); - assert(res.ok, `Unexpected HTTP status: ${res.status}`); - assert.equal(res.type, 'application/health+json'); - const {body: {status} = {}} = res; - assert(status != null); - assert.equal(typeof status, 'string'); - assert(['pass', 'ok', 'up'].includes(status.toLowerCase()), `Unexpected status: ${status}`); -})(); diff --git a/bin/extractPadData.js b/bin/extractPadData.ts similarity index 86% rename from bin/extractPadData.js rename to bin/extractPadData.ts index 51c3abcdc..5c64cf0b4 100644 --- a/bin/extractPadData.js +++ b/bin/extractPadData.ts @@ -8,10 +8,10 @@ // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an // unhandled rejection into an uncaught exception, which does cause Node.js to exit. +import util from "util"; +import process from "process"; +import dirtyDB from "ueberdb2"; process.on('unhandledRejection', (err) => { throw err; }); - -const util = require('util'); - if (process.argv.length !== 3) throw new Error('Use: node extractPadData.js $PADID'); // get the padID @@ -19,13 +19,13 @@ const padId = process.argv[2]; (async () => { // initialize database - require('./src/node/utils/Settings'); - const db = require('./src/node/db/DB'); + require('ep_etherpad-lite/node/utils/Settings'); + const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); // load extra modules const dirtyDB = require('dirty'); - const padManager = require('./src/node/db/PadManager'); + const padManager = require('ep_etherpad-lite/node/db/PadManager'); // initialize output database const dirty = dirtyDB(`${padId}.db`); diff --git a/bin/importSqlFile.js b/bin/importSqlFile.ts similarity index 70% rename from bin/importSqlFile.js rename to bin/importSqlFile.ts index 145e19bf7..5d137dd92 100644 --- a/bin/importSqlFile.js +++ b/bin/importSqlFile.ts @@ -2,20 +2,24 @@ // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an // unhandled rejection into an uncaught exception, which does cause Node.js to exit. +import util from "util"; +const fs = require('fs'); +import log4js from 'log4js'; +import readline from 'readline'; +import ueberDB from "ueberdb2"; + +const settings = require('ep_etherpad-lite/node/utils/Settings'); process.on('unhandledRejection', (err) => { throw err; }); - -const util = require('util'); - const startTime = Date.now(); -const log = (str) => { +const log = (str:string) => { console.log(`${(Date.now() - startTime) / 1000}\t${str}`); }; -const unescape = (val) => { +const unescape = (val: string) => { // value is a string - if (val.substr(0, 1) === "'") { - val = val.substr(0, val.length - 1).substr(1); + if (val.substring(0, 1) === "'") { + val = val.substring(0, val.length - 1).substring(1); return val.replace(/\\[0nrbtZ\\'"]/g, (s) => { switch (s) { @@ -25,7 +29,7 @@ const unescape = (val) => { case '\\b': return '\b'; case '\\t': return '\t'; case '\\Z': return '\x1a'; - default: return s.substr(1); + default: return s.substring(1); } }); } @@ -46,18 +50,13 @@ const unescape = (val) => { }; (async () => { - const fs = require('fs'); - const log4js = require('log4js'); - const readline = require('readline'); - const settings = require('./src/node/utils/Settings'); - const ueberDB = require('ueberdb2'); const dbWrapperSettings = { cache: 0, writeInterval: 100, json: false, // data is already json encoded }; - const db = new ueberDB.database( // eslint-disable-line new-cap + const db = new ueberDB.Database( // eslint-disable-line new-cap settings.dbType, settings.dbSettings, dbWrapperSettings, @@ -69,7 +68,8 @@ const unescape = (val) => { if (!sqlFile) throw new Error('Use: node importSqlFile.js $SQLFILE'); log('initializing db'); - await util.promisify(db.init.bind(db))(); + const initDb = await util.promisify(db.init.bind(db)); + await initDb(null); log('done'); log(`Opening ${sqlFile}...`); @@ -78,13 +78,14 @@ const unescape = (val) => { log(`Reading ${sqlFile}...`); let keyNo = 0; for await (const l of readline.createInterface({input: stream, crlfDelay: Infinity})) { - if (l.substr(0, 27) === 'REPLACE INTO store VALUES (') { + if (l.substring(0, 27) === 'REPLACE INTO store VALUES (') { const pos = l.indexOf("', '"); - const key = l.substr(28, pos - 28); - let value = l.substr(pos + 3); - value = value.substr(0, value.length - 2); + const key = l.substring(28, pos - 28); + let value = l.substring(pos + 3); + value = value.substring(0, value.length - 2); console.log(`key: ${key} val: ${value}`); console.log(`unval: ${unescape(value)}`); + // @ts-ignore db.set(key, unescape(value), null); keyNo++; if (keyNo % 1000 === 0) log(` ${keyNo}`); @@ -94,6 +95,7 @@ const unescape = (val) => { process.stdout.write('done. waiting for db to finish transaction. ' + 'depended on dbms this may take some time..\n'); - await util.promisify(db.close.bind(db))(); + const closeDB = util.promisify(db.close.bind(db)); + await closeDB(null); log(`finished, imported ${keyNo} keys.`); })(); diff --git a/bin/migrateDirtyDBtoRealDB.js b/bin/migrateDirtyDBtoRealDB.ts similarity index 65% rename from bin/migrateDirtyDBtoRealDB.js rename to bin/migrateDirtyDBtoRealDB.ts index 37a95212a..144f6e88c 100644 --- a/bin/migrateDirtyDBtoRealDB.js +++ b/bin/migrateDirtyDBtoRealDB.ts @@ -1,5 +1,11 @@ 'use strict'; +import process from 'node:process'; +import ueberDB from "ueberdb2"; +import log4js from 'log4js'; +import util from 'util'; +const settings = require('ep_etherpad-lite/node/utils/Settings'); + // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); @@ -12,17 +18,12 @@ process.on('unhandledRejection', (err) => { throw err; }); // It might be necessary to run the script using more memory: // `node --max-old-space-size=4096 src/bin/migrateDirtyDBtoRealDB.js` - const dirtyDb = require('dirty'); - const log4js = require('log4js'); - const settings = require('./src/node/utils/Settings'); - const ueberDB = require('ueberdb2'); - const util = require('util'); const dbWrapperSettings = { cache: '0', // The cache slows things down when you're mostly writing. writeInterval: 0, // Write directly to the database, don't buffer }; - const db = new ueberDB.database( // eslint-disable-line new-cap + const db = new ueberDB.Database( // eslint-disable-line new-cap settings.dbType, settings.dbSettings, dbWrapperSettings, @@ -30,27 +31,30 @@ process.on('unhandledRejection', (err) => { throw err; }); await db.init(); console.log('Waiting for dirtyDB to parse its file.'); - const dirty = dirtyDb(`${__dirname}/../var/dirty.db`); - const length = await new Promise((resolve) => { dirty.once('load', resolve); }); + const dirty = await new ueberDB.Database('dirty',`${__dirname}/../var/dirty.db`); + await dirty.init(); + const keys = await dirty.findKeys('*', '') - console.log(`Found ${length} records, processing now.`); - const p = []; + console.log(`Found ${keys.length} records, processing now.`); + const p: Promise[] = []; let numWritten = 0; - dirty.forEach((key, value) => { + for (const key of keys) { + let value = await dirty.get(key); let bcb, wcb; p.push(new Promise((resolve, reject) => { - bcb = (err) => { if (err != null) return reject(err); }; - wcb = (err) => { + bcb = (err:any) => { if (err != null) return reject(err); }; + wcb = (err:any) => { if (err != null) return reject(err); if (++numWritten % 100 === 0) console.log(`Wrote record ${numWritten} of ${length}`); resolve(); }; })); db.set(key, value, bcb, wcb); - }); + } await Promise.all(p); console.log(`Wrote all ${numWritten} records`); - await util.promisify(db.close.bind(db))(); + await db.close(null); + await dirty.close(null); console.log('Finished.'); })(); diff --git a/bin/package.json b/bin/package.json new file mode 100644 index 000000000..de6714251 --- /dev/null +++ b/bin/package.json @@ -0,0 +1,37 @@ +{ + "name": "bin", + "version": "1.0.0", + "description": "", + "main": "checkAllPads.js", + "directories": { + "doc": "doc" + }, + "dependencies": { + "axios": "^1.6.7", + "ep_etherpad-lite": "workspace:../src", + "log4js": "^6.9.1", + "semver": "^7.6.0", + "tsx": "^4.7.1", + "ueberdb2": "^4.2.63" + }, + "devDependencies": { + "@types/node": "^20.11.27", + "@types/semver": "^7.5.8", + "typescript": "^5.4.2" + }, + "scripts": { + "checkPad": "node --import tsx checkPad.ts", + "checkAllPads": "node --import tsx checkAllPads.ts", + "createUserSession": "node --import tsx createUserSession.ts", + "deletePad": "node --import tsx deletePad.ts", + "repairPad": "node --import tsx repairPad.ts", + "deleteAllGroupSessions": "node --import tsx deleteAllGroupSessions.ts", + "importSqlFile": "node --import tsx importSqlFile.ts", + "migrateDirtyDBtoRealDB": "node --import tsx migrateDirtyDBtoRealDB.ts", + "rebuildPad": "node --import tsx rebuildPad.ts", + "stalePlugins": "node --import tsx ./plugins/stalePlugins.ts", + "checkPlugins": "node --import tsx ./plugins/checkPlugins.ts" + }, + "author": "", + "license": "ISC" +} diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.ts old mode 100755 new mode 100644 similarity index 94% rename from bin/plugins/checkPlugin.js rename to bin/plugins/checkPlugin.ts index a42beed0d..d5d22c0e2 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.ts @@ -10,18 +10,18 @@ * node bin/plugins/checkPlugin.js ep_whatever autopush */ -const process = require('process'); +import process from 'process'; // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); -const assert = require('assert').strict; -const fs = require('fs'); +import {strict as assert} from 'assert'; +import fs from 'fs'; const fsp = fs.promises; -const childProcess = require('child_process'); -const log4js = require('log4js'); -const path = require('path'); +import childProcess from 'child_process'; +import log4js from 'log4js'; +import path from 'path'; const logger = log4js.getLogger('checkPlugin'); @@ -44,23 +44,23 @@ const logger = log4js.getLogger('checkPlugin'); const autoCommit = autoPush || optArgs.includes('autocommit'); const autoFix = autoCommit || optArgs.includes('autofix'); - const execSync = (cmd, opts = {}) => (childProcess.execSync(cmd, { + const execSync = (cmd:string, opts = {}) => (childProcess.execSync(cmd, { cwd: `${pluginPath}/`, ...opts, }) || '').toString().replace(/\n+$/, ''); - const writePackageJson = async (obj) => { + const writePackageJson = async (obj: object) => { let s = JSON.stringify(obj, null, 2); if (s.length && s.slice(s.length - 1) !== '\n') s += '\n'; return await fsp.writeFile(`${pluginPath}/package.json`, s); }; - const checkEntries = (got, want) => { + const checkEntries = (got: any, want:any) => { let changed = false; for (const [key, val] of Object.entries(want)) { try { assert.deepEqual(got[key], val); - } catch (err) { + } catch (err:any) { logger.warn(`${key} possibly outdated.`); logger.warn(err.message); if (autoFix) { @@ -72,7 +72,9 @@ const logger = log4js.getLogger('checkPlugin'); return changed; }; - const updateDeps = async (parsedPackageJson, key, wantDeps) => { + const updateDeps = async (parsedPackageJson: any, key: string, wantDeps: { + [key: string]: string | {ver?: string, overwrite?: boolean}|null + }) => { const {[key]: deps = {}} = parsedPackageJson; let changed = false; for (const [pkg, verInfo] of Object.entries(wantDeps)) { @@ -115,14 +117,14 @@ const logger = log4js.getLogger('checkPlugin'); execSync('git config --get user.email'); } if (autoPush) { - if (!['master', 'main'].includes(br)) throw new Error('master/main not checked out'); + if (!['master', 'main'].includes(br!)) throw new Error('master/main not checked out'); execSync('git rev-parse --verify @{u}'); execSync('git pull --ff-only', {stdio: 'inherit'}); if (execSync('git rev-list @{u}...') !== '') throw new Error('repo contains unpushed commits'); } }; - const checkFile = async (srcFn, dstFn, overwrite = true) => { + const checkFile = async (srcFn: string, dstFn:string, overwrite = true) => { const outFn = path.join(pluginPath, dstFn); const wantContents = await fsp.readFile(srcFn, {encoding: 'utf8'}); let gotContents = null; @@ -131,7 +133,7 @@ const logger = log4js.getLogger('checkPlugin'); } catch (err) { /* treat as if the file doesn't exist */ } try { assert.equal(gotContents, wantContents); - } catch (err) { + } catch (err:any) { logger.warn(`File ${dstFn} does not match the default`); logger.warn(err.message); if (!overwrite && gotContents != null) { @@ -238,11 +240,11 @@ const logger = log4js.getLogger('checkPlugin'); } } - const fillTemplate = async (templateFilename, outputFilename) => { + const fillTemplate = async (templateFilename: string, outputFilename: string) => { const contents = (await fsp.readFile(templateFilename, 'utf8')) .replace(/\[name of copyright owner\]/g, execSync('git config user.name')) .replace(/\[plugin_name\]/g, pluginName) - .replace(/\[yyyy\]/g, new Date().getFullYear()); + .replace(/\[yyyy\]/g, new Date().getFullYear().toString()); await fsp.writeFile(outputFilename, contents); }; diff --git a/bin/plugins/stalePlugins.js b/bin/plugins/stalePlugins.js deleted file mode 100644 index 7e1817c66..000000000 --- a/bin/plugins/stalePlugins.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -// Returns a list of stale plugins and their authors email - -const superagent = require('superagent'); -const currentTime = new Date(); - -(async () => { - const res = await superagent.get('https://static.etherpad.org/plugins.full.json'); - const plugins = JSON.parse(res.text); - for (const plugin of Object.keys(plugins)) { - const name = plugins[plugin].data.name; - const date = new Date(plugins[plugin].time); - const diffTime = Math.abs(currentTime - date); - const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); - if (diffDays > (365 * 2)) { - console.log(`${name}, ${plugins[plugin].data.maintainers[0].email}`); - } - } -})(); diff --git a/bin/plugins/stalePlugins.ts b/bin/plugins/stalePlugins.ts new file mode 100644 index 000000000..ce2b876ed --- /dev/null +++ b/bin/plugins/stalePlugins.ts @@ -0,0 +1,22 @@ +'use strict'; + +// Returns a list of stale plugins and their authors email + +import axios from 'axios' +const currentTime = new Date(); + +(async () => { + const res = await axios.get('https://static.etherpad.org/plugins.full.json'); + for (const plugin of Object.keys(res.data)) { + // @ts-ignore + const name = res.data[plugin].data.name; + // @ts-ignore + const date = new Date(res.data[plugin].time); + const diffTime = Math.abs(currentTime.getTime() - date.getTime()); + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + if (diffDays > (365 * 2)) { + // @ts-ignore + console.log(`${name}, ${res.data[plugin].data.maintainers[0].email}`); + } + } +})(); diff --git a/bin/rebuildPad.js b/bin/rebuildPad.ts similarity index 88% rename from bin/rebuildPad.js rename to bin/rebuildPad.ts index 52194c9a3..0d77940c0 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.ts @@ -13,16 +13,17 @@ if (process.argv.length !== 4 && process.argv.length !== 5) { throw new Error('Use: node bin/repairPad.js $PADID $REV [$NEWPADID]'); } +// @ts-ignore const padId = process.argv[2]; -const newRevHead = process.argv[3]; +const newRevHead = Number(process.argv[3]); const newPadId = process.argv[4] || `${padId}-rebuilt`; (async () => { - const db = require('./src/node/db/DB'); + const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); - const PadManager = require('./src/node/db/PadManager'); - const Pad = require('./src/node/db/Pad').Pad; + const PadManager = require('ep_etherpad-lite/node/db/PadManager'); + const Pad = require('ep_etherpad-lite/node/db/Pad').Pad; // Validate the newPadId if specified and that a pad with that ID does // not already exist to avoid overwriting it. if (!PadManager.isValidPadId(newPadId)) { @@ -43,8 +44,8 @@ const newPadId = process.argv[4] || `${padId}-rebuilt`; })); // Rebuild Pad from revisions up to and including the new revision head - const AuthorManager = require('./src/node/db/AuthorManager'); - const Changeset = require('./src/static/js/Changeset'); + const AuthorManager = require('ep_etherpad-lite/node/db/AuthorManager'); + const Changeset = require('ep_etherpad-lite/static/js/Changeset'); // Author attributes are derived from changesets, but there can also be // non-author attributes with specific mappings that changesets depend on // and, AFAICT, cannot be recreated any other way diff --git a/bin/release.js b/bin/release.ts similarity index 87% rename from bin/release.js rename to bin/release.ts index 47a74a41f..580f56e9e 100644 --- a/bin/release.js +++ b/bin/release.ts @@ -4,12 +4,12 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); -const fs = require('fs'); -const childProcess = require('child_process'); -const log4js = require('log4js'); -const path = require('path'); -const semver = require('semver'); -const {exec} = require('child_process'); +import fs from 'fs'; +import childProcess from 'child_process'; +import log4js from 'log4js'; +import path from 'path'; +import semver from 'semver'; +import {exec} from 'child_process'; log4js.configure({appenders: {console: {type: 'console'}}, categories: { @@ -33,23 +33,31 @@ if (!release) { throw new Error('No release type included'); } +if (release !== 'patch' && release !== 'minor' && release !== 'major') { + console.log(usage); + throw new Error('Invalid release type'); +} + + const cwd = path.join(fs.realpathSync(__dirname), '../'); process.chdir(cwd); // Run command capturing stdout. Trailing newlines are stripped (like the shell does). const runc = - (cmd, opts = {}) => childProcess.execSync(cmd, {encoding: 'utf8', ...opts}).replace(/\n+$/, ''); + (cmd:string, opts = {}) => childProcess.execSync(cmd, {encoding: 'utf8', ...opts}).replace(/\n+$/, ''); // Run command without capturing stdout. -const run = (cmd, opts = {}) => childProcess.execSync(cmd, {stdio: 'inherit', ...opts}); +const run = (cmd: string, opts = {}) => childProcess.execSync(cmd, {stdio: 'inherit', ...opts}); -const readJson = (filename) => JSON.parse(fs.readFileSync(filename, {encoding: 'utf8', flag: 'r'})); -const writeJson = (filename, obj) => { +const readJson = (filename: string) => JSON.parse(fs.readFileSync(filename, {encoding: 'utf8', flag: 'r'})); +const writeJson = (filename: string, obj:object) => { let json = JSON.stringify(obj, null, 2); if (json !== '' && !json.endsWith('\n')) json += '\n'; fs.writeFileSync(filename, json); }; -const assertWorkDirClean = (opts = {}) => { +const assertWorkDirClean = (opts:{ + cwd?: string; +} = {}) => { opts.cwd = runc('git rev-parse --show-cdup', opts) || cwd; const m = runc('git diff-files --name-status', opts); if (m !== '') throw new Error(`modifications in working directory ${opts.cwd}:\n${m}`); @@ -59,7 +67,9 @@ const assertWorkDirClean = (opts = {}) => { if (s !== '') throw new Error(`uncommitted changes in working directory ${opts.cwd}:\n${s}`); }; -const assertBranchCheckedOut = (branch, opts = {}) => { +const assertBranchCheckedOut = (branch: string, opts:{ + cwd?: string; +} = {}) => { const b = runc('git symbolic-ref HEAD', opts); if (b !== `refs/heads/${branch}`) { const d = opts.cwd ? path.resolve(cwd, opts.cwd) : cwd; @@ -67,21 +77,23 @@ const assertBranchCheckedOut = (branch, opts = {}) => { } }; -const assertUpstreamOk = (branch, opts = {}) => { +const assertUpstreamOk = (branch: string, opts:{ + cwd?: string; +} = {}) => { const upstream = runc(`git rev-parse --symbolic-full-name ${branch}@{u}`, opts); if (!(new RegExp(`^refs/remotes/[^/]+/${branch}`)).test(upstream)) { throw new Error(`${branch} should track origin/${branch}; see git branch --set-upstream-to`); } try { run(`git merge-base --is-ancestor ${branch} ${branch}@{u}`); - } catch (err) { + } catch (err:any) { if (err.status !== 1) throw err; throw new Error(`${branch} is ahead of origin/${branch}; do you need to push?`); } }; // Check if asciidoctor is installed -exec('asciidoctor -v', (err, stdout) => { +exec('asciidoctor -v', (err) => { if (err) { console.log('Please install asciidoctor'); console.log('https://asciidoctor.org/docs/install-toolchain/'); @@ -89,10 +101,10 @@ exec('asciidoctor -v', (err, stdout) => { } }); -const dirExists = (dir) => { +const dirExists = (dir: string) => { try { return fs.statSync(dir).isDirectory(); - } catch (err) { + } catch (err:any) { if (err.code !== 'ENOENT') throw err; return false; } @@ -165,7 +177,7 @@ try { run('git checkout develop'); console.log('Merging master into develop...'); run('git merge --no-ff --no-edit master'); -} catch (err) { +} catch (err:any) { console.error(err.toString()); console.warn('Resetting repository...'); console.warn('Resetting master...'); @@ -192,7 +204,7 @@ try { run(`npm version ${newVersion}`, {cwd: '../ether.github.com'}); run('git add .', {cwd: '../ether.github.com/'}); run(`git commit -m '${newVersion} docs'`, {cwd: '../ether.github.com/'}); -} catch (err) { +} catch (err:any) { console.error(err.toString()); console.warn('Resetting repository...'); console.warn('Resetting master...'); diff --git a/bin/repairPad.js b/bin/repairPad.ts similarity index 84% rename from bin/repairPad.js rename to bin/repairPad.ts index bf346c8e7..f1a55238e 100644 --- a/bin/repairPad.js +++ b/bin/repairPad.ts @@ -1,5 +1,7 @@ 'use strict'; +import process from "process"; + /* * This is a repair tool. It extracts all datas of a pad, removes and inserts them again. */ @@ -19,19 +21,19 @@ let valueCount = 0; (async () => { // initialize database - require('./src/node/utils/Settings'); - const db = require('./src/node/db/DB'); + require('ep_etherpad-lite/node/utils/Settings'); + const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); // get the pad - const padManager = require('./src/node/db/PadManager'); + const padManager = require('ep_etherpad-lite/node/db/PadManager'); const pad = await padManager.getPad(padId); // accumulate the required keys const neededDBValues = [`pad:${padId}`]; // add all authors - neededDBValues.push(...pad.getAllAuthors().map((author) => `globalAuthor:${author}`)); + neededDBValues.push(...pad.getAllAuthors().map((author: string) => `globalAuthor:${author}`)); // add all revisions for (let rev = 0; rev <= pad.head; ++rev) { diff --git a/bin/tsconfig.json b/bin/tsconfig.json new file mode 100644 index 000000000..e075f973c --- /dev/null +++ b/bin/tsconfig.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/bin/updatePlugins.sh b/bin/updatePlugins.sh index 55879e6bf..8d0f43fd5 100755 --- a/bin/updatePlugins.sh +++ b/bin/updatePlugins.sh @@ -8,4 +8,4 @@ OUTDATED=$(npm outdated --depth=0 | awk '{print $1}' | grep '^ep_') || { } set -- ${OUTDATED} echo "Updating plugins: $*" -exec npm install --no-save "$@" +exec pnpm install "$@" diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3d3e285e0..dd590d517 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,4 @@ packages: - src - admin + - bin