Moved path_exists and promises to es6

This commit is contained in:
SamTV12345 2024-08-18 19:52:21 +02:00
parent 6ed711a4d8
commit 4ff00e278a
8 changed files with 59 additions and 31 deletions

View file

@ -25,7 +25,8 @@ const randomString = require('../utils/randomstring');
const hooks = require('../../static/js/pluginfw/hooks'); const hooks = require('../../static/js/pluginfw/hooks');
import pad_utils from "../../static/js/pad_utils"; import pad_utils from "../../static/js/pad_utils";
import {SmartOpAssembler} from "../../static/js/SmartOpAssembler"; import {SmartOpAssembler} from "../../static/js/SmartOpAssembler";
const promises = require('../utils/promises'); import {} from '../utils/promises';
import {timesLimit} from "async";
/** /**
* Copied from the Etherpad source code. It converts Windows line breaks to Unix * Copied from the Etherpad source code. It converts Windows line breaks to Unix
@ -586,12 +587,14 @@ class Pad {
p.push(db.remove(`pad2readonly:${padID}`)); p.push(db.remove(`pad2readonly:${padID}`));
// delete all chat messages // delete all chat messages
p.push(promises.timesLimit(this.chatHead + 1, 500, async (i: string) => { // @ts-ignore
p.push(timesLimit(this.chatHead + 1, 500, async (i: string) => {
await this.db.remove(`pad:${this.id}:chat:${i}`, null); await this.db.remove(`pad:${this.id}:chat:${i}`, null);
})); }));
// delete all revisions // delete all revisions
p.push(promises.timesLimit(this.head + 1, 500, async (i: string) => { // @ts-ignore
p.push(timesLimit(this.head + 1, 500, async (i: string) => {
await this.db.remove(`pad:${this.id}:revs:${i}`, null); await this.db.remove(`pad:${this.id}:revs:${i}`, null);
})); }));

View file

@ -21,7 +21,7 @@
*/ */
const CustomError = require('../utils/customError'); const CustomError = require('../utils/customError');
const promises = require('../utils/promises'); import {firstSatisfies} from '../utils/promises';
const randomString = require('../utils/randomstring'); const randomString = require('../utils/randomstring');
const db = require('./DB'); const db = require('./DB');
const groupManager = require('./GroupManager'); const groupManager = require('./GroupManager');
@ -79,7 +79,7 @@ exports.findAuthorID = async (groupID:string, sessionCookie: string) => {
groupID: string; groupID: string;
validUntil: number; validUntil: number;
}|null) => (si != null && si.groupID === groupID && now < si.validUntil); }|null) => (si != null && si.groupID === groupID && now < si.validUntil);
const sessionInfo = await promises.firstSatisfies(sessionInfoPromises, isMatch); const sessionInfo = await firstSatisfies(sessionInfoPromises, isMatch) as any;
if (sessionInfo == null) return undefined; if (sessionInfo == null) return undefined;
return sessionInfo.authorID; return sessionInfo.authorID;
}; };

View file

@ -8,7 +8,7 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const _ = require('underscore'); const _ = require('underscore');
const pluginDefs = require('../../static/js/pluginfw/plugin_defs'); const pluginDefs = require('../../static/js/pluginfw/plugin_defs');
const existsSync = require('../utils/path_exists'); import existsSync from '../utils/path_exists';
const settings = require('../utils/Settings'); const settings = require('../utils/Settings');
// returns all existing messages merged together and grouped by langcode // returns all existing messages merged together and grouped by langcode

View file

@ -74,7 +74,7 @@ const express = require('./hooks/express');
const hooks = require('../static/js/pluginfw/hooks'); const hooks = require('../static/js/pluginfw/hooks');
const pluginDefs = require('../static/js/pluginfw/plugin_defs'); const pluginDefs = require('../static/js/pluginfw/plugin_defs');
const plugins = require('../static/js/pluginfw/plugins'); const plugins = require('../static/js/pluginfw/plugins');
const {Gate} = require('./utils/promises'); import {Gate} from './utils/promises';
const stats = require('./stats') const stats = require('./stats')
const logger = log4js.getLogger('server'); const logger = log4js.getLogger('server');
@ -100,7 +100,7 @@ const removeSignalListener = (signal: NodeJS.Signals, listener: NodeJS.SignalsLi
}; };
let startDoneGate: { resolve: () => void; } let startDoneGate: Gate<unknown>
exports.start = async () => { exports.start = async () => {
switch (state) { switch (state) {
case State.INITIAL: case State.INITIAL:
@ -181,12 +181,14 @@ exports.start = async () => {
} catch (err) { } catch (err) {
logger.error('Error occurred while starting Etherpad'); logger.error('Error occurred while starting Etherpad');
state = State.STATE_TRANSITION_FAILED; state = State.STATE_TRANSITION_FAILED;
// @ts-ignore
startDoneGate.resolve(); startDoneGate.resolve();
return await exports.exit(err); return await exports.exit(err);
} }
logger.info('Etherpad is running'); logger.info('Etherpad is running');
state = State.RUNNING; state = State.RUNNING;
// @ts-ignore
startDoneGate.resolve(); startDoneGate.resolve();
// Return the HTTP server to make it easier to write tests. // Return the HTTP server to make it easier to write tests.
@ -228,11 +230,13 @@ exports.stop = async () => {
} catch (err) { } catch (err) {
logger.error('Error occurred while stopping Etherpad'); logger.error('Error occurred while stopping Etherpad');
state = State.STATE_TRANSITION_FAILED; state = State.STATE_TRANSITION_FAILED;
// @ts-ignore
stopDoneGate.resolve(); stopDoneGate.resolve();
return await exports.exit(err); return await exports.exit(err);
} }
logger.info('Etherpad stopped'); logger.info('Etherpad stopped');
state = State.STOPPED; state = State.STOPPED;
// @ts-ignore
stopDoneGate.resolve(); stopDoneGate.resolve();
}; };

View file

@ -1,5 +1,5 @@
'use strict'; 'use strict';
const fs = require('fs'); import fs from 'node:fs';
const check = (path:string) => { const check = (path:string) => {
const existsSync = fs.statSync || fs.existsSync; const existsSync = fs.statSync || fs.existsSync;
@ -13,4 +13,4 @@ const check = (path:string) => {
return result; return result;
}; };
module.exports = check; export default check;

View file

@ -7,7 +7,7 @@
// `predicate`. Resolves to `undefined` if none of the Promises satisfy `predicate`, or if // `predicate`. Resolves to `undefined` if none of the Promises satisfy `predicate`, or if
// `promises` is empty. If `predicate` is nullish, the truthiness of the resolved value is used as // `promises` is empty. If `predicate` is nullish, the truthiness of the resolved value is used as
// the predicate. // the predicate.
exports.firstSatisfies = <T>(promises: Promise<T>[], predicate: null|Function) => { export const firstSatisfies = <T>(promises: Promise<T>[], predicate: null|Function) => {
if (predicate == null) { if (predicate == null) {
predicate = (x: any) => x; predicate = (x: any) => x;
} }
@ -44,7 +44,7 @@ exports.firstSatisfies = <T>(promises: Promise<T>[], predicate: null|Function) =
// `total` is greater than `concurrency`, then `concurrency` Promises will be created right away, // `total` is greater than `concurrency`, then `concurrency` Promises will be created right away,
// and each remaining Promise will be created once one of the earlier Promises resolves.) This async // and each remaining Promise will be created once one of the earlier Promises resolves.) This async
// function resolves once all `total` Promises have resolved. // function resolves once all `total` Promises have resolved.
exports.timesLimit = async (total: number, concurrency: number, promiseCreator: Function) => { export const timesLimit = async (total: number, concurrency: number, promiseCreator: Function) => {
if (total > 0 && concurrency <= 0) throw new RangeError('concurrency must be positive'); if (total > 0 && concurrency <= 0) throw new RangeError('concurrency must be positive');
let next = 0; let next = 0;
const addAnother = () => promiseCreator(next++).finally(() => { const addAnother = () => promiseCreator(next++).finally(() => {
@ -61,7 +61,7 @@ exports.timesLimit = async (total: number, concurrency: number, promiseCreator:
* An ordinary Promise except the `resolve` and `reject` executor functions are exposed as * An ordinary Promise except the `resolve` and `reject` executor functions are exposed as
* properties. * properties.
*/ */
class Gate<T> extends Promise<T> { export class Gate<T> extends Promise<T> {
// Coax `.then()` into returning an ordinary Promise, not a Gate. See // Coax `.then()` into returning an ordinary Promise, not a Gate. See
// https://stackoverflow.com/a/65669070 for the rationale. // https://stackoverflow.com/a/65669070 for the rationale.
static get [Symbol.species]() { return Promise; } static get [Symbol.species]() { return Promise; }
@ -75,4 +75,3 @@ class Gate<T> extends Promise<T> {
Object.assign(this, props); Object.assign(this, props);
} }
} }
exports.Gate = Gate;

View file

@ -0,0 +1,22 @@
import check from "../../../node/utils/path_exists";
import {expect, describe, it} from "vitest";
describe('Test path exists', function () {
it('should return true if the path exists - directory', function () {
const path = './locales';
const result = check(path);
expect(result).toBeTruthy();
});
it('should return true if the path exists - file', function () {
const path = './locales/en.json';
const result = check(path);
expect(result).toBeTruthy();
})
it('should return false if the path does not exist', function () {
const path = './path_not_exists.ts';
const result = check(path);
expect(result).toEqual(false);
});
})

View file

@ -1,7 +1,7 @@
import {MapArrayType} from "../../../node/types/MapType"; import {MapArrayType} from "../../../node/types/MapType";
const assert = require('assert').strict; import {timesLimit} from '../../../node/utils/promises';
const promises = require('../../../node/utils/promises'); import {describe, it, expect} from "vitest";
describe(__filename, function () { describe(__filename, function () {
describe('promises.timesLimit', function () { describe('promises.timesLimit', function () {
@ -15,7 +15,7 @@ describe(__filename, function () {
const testPromises: TestPromise[] = []; const testPromises: TestPromise[] = [];
const makePromise = (index: number) => { const makePromise = (index: number) => {
// Make sure index increases by one each time. // Make sure index increases by one each time.
assert.equal(index, wantIndex++); expect(index).toEqual(wantIndex++);
// Save the resolve callback (so the test can trigger resolution) // Save the resolve callback (so the test can trigger resolution)
// and the promise itself (to wait for resolve to take effect). // and the promise itself (to wait for resolve to take effect).
const p:TestPromise = {}; const p:TestPromise = {};
@ -28,17 +28,17 @@ describe(__filename, function () {
const total = 11; const total = 11;
const concurrency = 7; const concurrency = 7;
const timesLimitPromise = promises.timesLimit(total, concurrency, makePromise); const timesLimitPromise = timesLimit(total, concurrency, makePromise);
it('honors concurrency', async function () { it('honors concurrency', async function () {
assert.equal(wantIndex, concurrency); expect(wantIndex).toEqual(concurrency);
}); });
it('creates another when one completes', async function () { it('creates another when one completes', async function () {
const {promise, resolve} = testPromises.shift()!; const {promise, resolve} = testPromises.shift()!;
resolve!(); resolve!();
await promise; await promise;
assert.equal(wantIndex, concurrency + 1); expect(wantIndex).toEqual(concurrency + 1);
}); });
it('creates the expected total number of promises', async function () { it('creates the expected total number of promises', async function () {
@ -49,7 +49,7 @@ describe(__filename, function () {
resolve!(); resolve!();
await promise; await promise;
} }
assert.equal(wantIndex, total); expect(wantIndex).toEqual(total);
}); });
it('resolves', async function () { it('resolves', async function () {
@ -58,35 +58,35 @@ describe(__filename, function () {
it('does not create too many promises if total < concurrency', async function () { it('does not create too many promises if total < concurrency', async function () {
wantIndex = 0; wantIndex = 0;
assert.equal(testPromises.length, 0); expect(testPromises.length).toEqual(0);
const total = 7; const total = 7;
const concurrency = 11; const concurrency = 11;
const timesLimitPromise = promises.timesLimit(total, concurrency, makePromise); const timesLimitPromise = timesLimit(total, concurrency, makePromise);
while (testPromises.length > 0) { while (testPromises.length > 0) {
const {promise, resolve} = testPromises.pop()!; const {promise, resolve} = testPromises.pop()!;
resolve!(); resolve!();
await promise; await promise;
} }
await timesLimitPromise; await timesLimitPromise;
assert.equal(wantIndex, total); expect(wantIndex).toEqual(total);
}); });
it('accepts total === 0, concurrency > 0', async function () { it('accepts total === 0, concurrency > 0', async function () {
wantIndex = 0; wantIndex = 0;
assert.equal(testPromises.length, 0); expect(testPromises.length).toEqual(0);
await promises.timesLimit(0, concurrency, makePromise); await timesLimit(0, concurrency, makePromise);
assert.equal(wantIndex, 0); expect(wantIndex).toEqual(0);
}); });
it('accepts total === 0, concurrency === 0', async function () { it('accepts total === 0, concurrency === 0', async function () {
wantIndex = 0; wantIndex = 0;
assert.equal(testPromises.length, 0); expect(testPromises.length).toEqual(0);
await promises.timesLimit(0, 0, makePromise); await timesLimit(0, 0, makePromise);
assert.equal(wantIndex, 0); expect(wantIndex).toEqual(0);
}); });
it('rejects total > 0, concurrency === 0', async function () { it('rejects total > 0, concurrency === 0', async function () {
await assert.rejects(promises.timesLimit(total, 0, makePromise), RangeError); expect(timesLimit(total, 0, makePromise)).rejects.toThrow(RangeError);
}); });
}); });
}); });