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