From 07b3597fd3a6cb6b925d0e471cfd5ca89fdd7674 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 20:40:37 +0200 Subject: [PATCH 01/49] Bump the dev-dependencies group with 2 updates (#6573) Bumps the dev-dependencies group with 2 updates: [mocha](https://github.com/mochajs/mocha) and [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react). Updates `mocha` from 10.7.0 to 10.7.3 - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v10.7.0...v10.7.3) Updates `lucide-react` from 0.426.0 to 0.427.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/0.427.0/packages/lucide-react) --- updated-dependencies: - dependency-name: mocha dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies - dependency-name: lucide-react dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- admin/package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- src/package.json | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/admin/package.json b/admin/package.json index 4850b48d108..558275c2d3d 100644 --- a/admin/package.json +++ b/admin/package.json @@ -26,7 +26,7 @@ "eslint-plugin-react-refresh": "^0.4.9", "i18next": "^23.12.2", "i18next-browser-languagedetector": "^8.0.0", - "lucide-react": "^0.426.0", + "lucide-react": "^0.427.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.52.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 671c26577b7..031e0856333 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,8 +65,8 @@ importers: specifier: ^8.0.0 version: 8.0.0 lucide-react: - specifier: ^0.426.0 - version: 0.426.0(react@18.3.1) + specifier: ^0.427.0 + version: 0.427.0(react@18.3.1) react: specifier: ^18.2.0 version: 18.3.1 @@ -334,8 +334,8 @@ importers: specifier: ^3.0.2 version: 3.0.2 mocha: - specifier: ^10.7.0 - version: 10.7.0 + specifier: ^10.7.3 + version: 10.7.3 mocha-froth: specifier: ^0.2.10 version: 0.2.10 @@ -3287,8 +3287,8 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - lucide-react@0.426.0: - resolution: {integrity: sha512-aby5G+Zt+LIIEU0n9900XQNJFJUcs7/S+jOEgIhkV08NX3kGx1zxALKh1JvAKcYqutWLg07exbnYvh66szhrRA==} + lucide-react@0.427.0: + resolution: {integrity: sha512-lv9s6c5BDF/ccuA0EgTdskTxIe11qpwBDmzRZHJAKtp8LTewAvDvOM+pTES9IpbBuTqkjiMhOmGpJ/CB+mKjFw==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc @@ -3409,8 +3409,8 @@ packages: mocha-froth@0.2.10: resolution: {integrity: sha512-xyJqAYtm2zjrkG870hjeSVvGgS4Dc9tRokmN6R7XLgBKhdtAJ1ytU6zL045djblfHaPyTkSerQU4wqcjsv7Aew==} - mocha@10.7.0: - resolution: {integrity: sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==} + mocha@10.7.3: + resolution: {integrity: sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==} engines: {node: '>= 14.0.0'} hasBin: true @@ -7723,7 +7723,7 @@ snapshots: lru-cache@7.18.3: {} - lucide-react@0.426.0(react@18.3.1): + lucide-react@0.427.0(react@18.3.1): dependencies: react: 18.3.1 @@ -7829,7 +7829,7 @@ snapshots: mocha-froth@0.2.10: {} - mocha@10.7.0: + mocha@10.7.3: dependencies: ansi-colors: 4.1.3 browser-stdout: 1.3.1 diff --git a/src/package.json b/src/package.json index b961da164a2..939c6d9a530 100644 --- a/src/package.json +++ b/src/package.json @@ -100,7 +100,7 @@ "eslint": "^9.8.0", "eslint-config-etherpad": "^4.0.4", "etherpad-cli-client": "^3.0.2", - "mocha": "^10.7.0", + "mocha": "^10.7.3", "mocha-froth": "^0.2.10", "nodeify": "^1.0.1", "openapi-schema-validation": "^0.4.2", From dafb158de996213e8e5795da05a9e0ce90a31696 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 20:40:47 +0200 Subject: [PATCH 02/49] Bump actions/cache from 3 to 4 (#6572) Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/frontend-admin-tests.yml | 2 +- .github/workflows/frontend-tests.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/frontend-admin-tests.yml b/.github/workflows/frontend-admin-tests.yml index a306d561d95..9231cb6ff25 100644 --- a/.github/workflows/frontend-admin-tests.yml +++ b/.github/workflows/frontend-admin-tests.yml @@ -50,7 +50,7 @@ jobs: restore-keys: | ${{ runner.os }}-pnpm-store- - name: Cache playwright binaries - uses: actions/cache@v3 + uses: actions/cache@v4 id: playwright-cache with: path: | diff --git a/.github/workflows/frontend-tests.yml b/.github/workflows/frontend-tests.yml index 0b15bf3a973..c11058b240c 100644 --- a/.github/workflows/frontend-tests.yml +++ b/.github/workflows/frontend-tests.yml @@ -57,7 +57,7 @@ jobs: name: Create settings.json run: cp ./src/tests/settings.json settings.json - name: Cache playwright binaries - uses: actions/cache@v3 + uses: actions/cache@v4 id: playwright-cache with: path: | @@ -127,7 +127,7 @@ jobs: - name: Create settings.json run: cp ./src/tests/settings.json settings.json - name: Cache playwright binaries - uses: actions/cache@v3 + uses: actions/cache@v4 id: playwright-cache with: path: | @@ -175,7 +175,7 @@ jobs: with: node-version: 22 - name: Cache playwright binaries - uses: actions/cache@v3 + uses: actions/cache@v4 id: playwright-cache with: path: | From 642aa250ba956d871d04220385d07d8823e01b31 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 12 Aug 2024 14:04:03 +0200 Subject: [PATCH 03/49] Localisation updates from https://translatewiki.net. --- src/locales/th.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/locales/th.json b/src/locales/th.json index 7de1e4fdce4..8b44b2f20f0 100644 --- a/src/locales/th.json +++ b/src/locales/th.json @@ -3,6 +3,7 @@ "authors": [ "Aefgh39622", "Andibecker", + "Ekminarin", "Patsagorn Y.", "Trisorn Triboon" ] @@ -121,7 +122,7 @@ "pad.share.readonly": "อ่านเท่านั้น", "pad.share.link": "ลิงก์", "pad.share.emebdcode": "URL แบบฝังตัว", - "pad.chat": "แชท", + "pad.chat": "แชต", "pad.chat.title": "เปิดการแชทสำหรับแผ่นจดบันทึกนี้", "pad.chat.loadmessages": "โหลดข้อความเพิ่มเติม", "pad.chat.stick.title": "ปักการสนทนาไว้บนหน้าจอ", From dd1231cc55603cb28a1366321ac9e53a74a67d8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 20:06:20 +0200 Subject: [PATCH 04/49] Bump the dev-dependencies group with 2 updates (#6577) Bumps the dev-dependencies group with 2 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [eslint](https://github.com/eslint/eslint). Updates `@types/node` from 22.1.0 to 22.2.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `eslint` from 9.8.0 to 9.9.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v9.8.0...v9.9.0) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- admin/package.json | 2 +- bin/package.json | 2 +- pnpm-lock.yaml | 281 +++++++++++++++++++++++---------------------- src/package.json | 4 +- 4 files changed, 147 insertions(+), 142 deletions(-) diff --git a/admin/package.json b/admin/package.json index 558275c2d3d..04195c1125e 100644 --- a/admin/package.json +++ b/admin/package.json @@ -21,7 +21,7 @@ "@typescript-eslint/eslint-plugin": "^8.0.1", "@typescript-eslint/parser": "^8.0.1", "@vitejs/plugin-react-swc": "^3.5.0", - "eslint": "^9.8.0", + "eslint": "^9.9.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.9", "i18next": "^23.12.2", diff --git a/bin/package.json b/bin/package.json index 9021e0d8c57..e598fc31194 100644 --- a/bin/package.json +++ b/bin/package.json @@ -15,7 +15,7 @@ "ueberdb2": "^4.2.92" }, "devDependencies": { - "@types/node": "^22.1.0", + "@types/node": "^22.2.0", "@types/semver": "^7.5.8", "typescript": "^5.5.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 031e0856333..3ccad1aa54f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,22 +42,22 @@ importers: version: 18.3.0 '@typescript-eslint/eslint-plugin': specifier: ^8.0.1 - version: 8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4) + version: 8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4) '@typescript-eslint/parser': specifier: ^8.0.1 - version: 8.0.1(eslint@9.8.0)(typescript@5.5.4) + version: 8.0.1(eslint@9.9.0)(typescript@5.5.4) '@vitejs/plugin-react-swc': specifier: ^3.5.0 - version: 3.7.0(vite@5.4.0(@types/node@22.1.0)) + version: 3.7.0(vite@5.4.0(@types/node@22.2.0)) eslint: - specifier: ^9.8.0 - version: 9.8.0 + specifier: ^9.9.0 + version: 9.9.0 eslint-plugin-react-hooks: specifier: ^4.6.0 - version: 4.6.2(eslint@9.8.0) + version: 4.6.2(eslint@9.9.0) eslint-plugin-react-refresh: specifier: ^0.4.9 - version: 0.4.9(eslint@9.8.0) + version: 0.4.9(eslint@9.9.0) i18next: specifier: ^23.12.2 version: 23.12.2 @@ -90,13 +90,13 @@ importers: version: 5.5.4 vite: specifier: ^5.4.0 - version: 5.4.0(@types/node@22.1.0) + version: 5.4.0(@types/node@22.2.0) vite-plugin-static-copy: specifier: ^1.0.6 - version: 1.0.6(vite@5.4.0(@types/node@22.1.0)) + version: 1.0.6(vite@5.4.0(@types/node@22.2.0)) vite-plugin-svgr: specifier: ^4.2.0 - version: 4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.0(@types/node@22.1.0)) + version: 4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.0(@types/node@22.2.0)) zustand: specifier: ^4.5.4 version: 4.5.4(@types/react@18.3.3)(react@18.3.1) @@ -123,8 +123,8 @@ importers: version: 4.2.92 devDependencies: '@types/node': - specifier: ^22.1.0 - version: 22.1.0 + specifier: ^22.2.0 + version: 22.2.0 '@types/semver': specifier: ^7.5.8 version: 7.5.8 @@ -136,7 +136,7 @@ importers: devDependencies: vitepress: specifier: ^1.3.2 - version: 1.3.2(@algolia/client-search@4.23.3)(@types/node@22.1.0)(@types/react@18.3.3)(axios@1.7.3)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4) + version: 1.3.2(@algolia/client-search@4.23.3)(@types/node@22.2.0)(@types/react@18.3.3)(axios@1.7.3)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4) src: dependencies: @@ -304,8 +304,8 @@ importers: specifier: ^10.0.7 version: 10.0.7 '@types/node': - specifier: ^22.1.0 - version: 22.1.0 + specifier: ^22.2.0 + version: 22.2.0 '@types/oidc-provider': specifier: ^8.5.1 version: 8.5.1 @@ -325,11 +325,11 @@ importers: specifier: ^3.6.0 version: 3.6.0 eslint: - specifier: ^9.8.0 - version: 9.8.0 + specifier: ^9.9.0 + version: 9.9.0 eslint-config-etherpad: specifier: ^4.0.4 - version: 4.0.4(eslint@9.8.0)(typescript@5.5.4) + version: 4.0.4(eslint@9.9.0)(typescript@5.5.4) etherpad-cli-client: specifier: ^3.0.2 version: 3.0.2 @@ -371,7 +371,7 @@ importers: version: 5.5.4 vite: specifier: ^5.4.0 - version: 5.4.0(@types/node@22.1.0) + version: 5.4.0(@types/node@22.2.0) packages: @@ -866,8 +866,8 @@ packages: resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.8.0': - resolution: {integrity: sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==} + '@eslint/js@9.9.0': + resolution: {integrity: sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.4': @@ -1549,8 +1549,8 @@ packages: '@types/node-fetch@2.6.11': resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} - '@types/node@22.1.0': - resolution: {integrity: sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==} + '@types/node@22.2.0': + resolution: {integrity: sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==} '@types/oidc-provider@8.5.1': resolution: {integrity: sha512-NS8tBPOj9GG6SxyrUHWBzglOtAYNDX41J4cRE45oeK0iSqI6V6tDW70aPWg25pJFNSC1evccXFm9evfwjxm7HQ==} @@ -2525,10 +2525,15 @@ packages: resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.8.0: - resolution: {integrity: sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==} + eslint@9.9.0: + resolution: {integrity: sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true esm@3.2.25: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} @@ -4934,9 +4939,9 @@ snapshots: '@esbuild/win32-x64@0.23.0': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.8.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.9.0)': dependencies: - eslint: 9.8.0 + eslint: 9.9.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.11.0': {} @@ -4963,7 +4968,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.8.0': {} + '@eslint/js@9.9.0': {} '@eslint/object-schema@2.1.4': {} @@ -5459,18 +5464,18 @@ snapshots: '@types/accepts@1.3.7': dependencies: - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/async@3.2.24': {} '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/connect@3.4.38': dependencies: - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/content-disposition@0.5.8': {} @@ -5483,11 +5488,11 @@ snapshots: '@types/connect': 3.4.38 '@types/express': 4.17.21 '@types/keygrip': 1.0.6 - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/cors@2.8.17': dependencies: - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/debug@4.1.12': dependencies: @@ -5497,7 +5502,7 @@ snapshots: '@types/express-serve-static-core@4.19.5': dependencies: - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -5511,11 +5516,11 @@ snapshots: '@types/formidable@3.4.5': dependencies: - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/fs-extra@9.0.13': dependencies: - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/hast@3.0.4': dependencies: @@ -5533,7 +5538,7 @@ snapshots: '@types/jsdom@21.1.7': dependencies: - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 @@ -5543,7 +5548,7 @@ snapshots: '@types/jsonwebtoken@9.0.6': dependencies: - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/keygrip@1.0.6': {} @@ -5560,7 +5565,7 @@ snapshots: '@types/http-errors': 2.0.4 '@types/keygrip': 1.0.6 '@types/koa-compose': 3.2.8 - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/linkify-it@5.0.0': {} @@ -5587,17 +5592,17 @@ snapshots: '@types/node-fetch@2.6.11': dependencies: - '@types/node': 22.1.0 + '@types/node': 22.2.0 form-data: 4.0.0 - '@types/node@22.1.0': + '@types/node@22.2.0': dependencies: undici-types: 6.13.0 '@types/oidc-provider@8.5.1': dependencies: '@types/koa': 2.15.0 - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/prop-types@15.7.12': {} @@ -5619,12 +5624,12 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/send': 0.17.4 '@types/sinon@17.0.3': @@ -5639,7 +5644,7 @@ snapshots: dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 22.1.0 + '@types/node': 22.2.0 '@types/supertest@6.0.2': dependencies: @@ -5648,7 +5653,7 @@ snapshots: '@types/tar@6.1.13': dependencies: - '@types/node': 22.1.0 + '@types/node': 22.2.0 minipass: 4.2.8 '@types/tough-cookie@4.0.5': {} @@ -5661,15 +5666,15 @@ snapshots: '@types/web-bluetooth@0.0.20': {} - '@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 7.17.0(eslint@9.8.0)(typescript@5.5.4) + '@typescript-eslint/parser': 7.17.0(eslint@9.9.0)(typescript@5.5.4) '@typescript-eslint/scope-manager': 7.17.0 - '@typescript-eslint/type-utils': 7.17.0(eslint@9.8.0)(typescript@5.5.4) - '@typescript-eslint/utils': 7.17.0(eslint@9.8.0)(typescript@5.5.4) + '@typescript-eslint/type-utils': 7.17.0(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/utils': 7.17.0(eslint@9.9.0)(typescript@5.5.4) '@typescript-eslint/visitor-keys': 7.17.0 - eslint: 9.8.0 + eslint: 9.9.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 @@ -5679,15 +5684,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.0.1(eslint@9.8.0)(typescript@5.5.4) + '@typescript-eslint/parser': 8.0.1(eslint@9.9.0)(typescript@5.5.4) '@typescript-eslint/scope-manager': 8.0.1 - '@typescript-eslint/type-utils': 8.0.1(eslint@9.8.0)(typescript@5.5.4) - '@typescript-eslint/utils': 8.0.1(eslint@9.8.0)(typescript@5.5.4) + '@typescript-eslint/type-utils': 8.0.1(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/utils': 8.0.1(eslint@9.9.0)(typescript@5.5.4) '@typescript-eslint/visitor-keys': 8.0.1 - eslint: 9.8.0 + eslint: 9.9.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 @@ -5697,27 +5702,27 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4)': + '@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: '@typescript-eslint/scope-manager': 7.17.0 '@typescript-eslint/types': 7.17.0 '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) '@typescript-eslint/visitor-keys': 7.17.0 debug: 4.3.5(supports-color@8.1.1) - eslint: 9.8.0 + eslint: 9.9.0 optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.0.1(eslint@9.8.0)(typescript@5.5.4)': + '@typescript-eslint/parser@8.0.1(eslint@9.9.0)(typescript@5.5.4)': dependencies: '@typescript-eslint/scope-manager': 8.0.1 '@typescript-eslint/types': 8.0.1 '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4) '@typescript-eslint/visitor-keys': 8.0.1 debug: 4.3.5(supports-color@8.1.1) - eslint: 9.8.0 + eslint: 9.9.0 optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -5733,22 +5738,22 @@ snapshots: '@typescript-eslint/types': 8.0.1 '@typescript-eslint/visitor-keys': 8.0.1 - '@typescript-eslint/type-utils@7.17.0(eslint@9.8.0)(typescript@5.5.4)': + '@typescript-eslint/type-utils@7.17.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) - '@typescript-eslint/utils': 7.17.0(eslint@9.8.0)(typescript@5.5.4) + '@typescript-eslint/utils': 7.17.0(eslint@9.9.0)(typescript@5.5.4) debug: 4.3.5(supports-color@8.1.1) - eslint: 9.8.0 + eslint: 9.9.0 ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.0.1(eslint@9.8.0)(typescript@5.5.4)': + '@typescript-eslint/type-utils@8.0.1(eslint@9.9.0)(typescript@5.5.4)': dependencies: '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4) - '@typescript-eslint/utils': 8.0.1(eslint@9.8.0)(typescript@5.5.4) + '@typescript-eslint/utils': 8.0.1(eslint@9.9.0)(typescript@5.5.4) debug: 4.3.5(supports-color@8.1.1) ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: @@ -5791,24 +5796,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.17.0(eslint@9.8.0)(typescript@5.5.4)': + '@typescript-eslint/utils@7.17.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) '@typescript-eslint/scope-manager': 7.17.0 '@typescript-eslint/types': 7.17.0 '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) - eslint: 9.8.0 + eslint: 9.9.0 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/utils@8.0.1(eslint@9.8.0)(typescript@5.5.4)': + '@typescript-eslint/utils@8.0.1(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) '@typescript-eslint/scope-manager': 8.0.1 '@typescript-eslint/types': 8.0.1 '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4) - eslint: 9.8.0 + eslint: 9.9.0 transitivePeerDependencies: - supports-color - typescript @@ -5825,16 +5830,16 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react-swc@3.7.0(vite@5.4.0(@types/node@22.1.0))': + '@vitejs/plugin-react-swc@3.7.0(vite@5.4.0(@types/node@22.2.0))': dependencies: '@swc/core': 1.5.28 - vite: 5.4.0(@types/node@22.1.0) + vite: 5.4.0(@types/node@22.2.0) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-vue@5.0.5(vite@5.4.0(@types/node@22.1.0))(vue@3.4.31(typescript@5.5.4))': + '@vitejs/plugin-vue@5.0.5(vite@5.4.0(@types/node@22.2.0))(vue@3.4.31(typescript@5.5.4))': dependencies: - vite: 5.4.0(@types/node@22.1.0) + vite: 5.4.0(@types/node@22.2.0) vue: 3.4.31(typescript@5.5.4) '@vue/compiler-core@3.4.31': @@ -6458,7 +6463,7 @@ snapshots: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.17 - '@types/node': 22.1.0 + '@types/node': 22.2.0 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.4.2 @@ -6626,24 +6631,24 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-compat-utils@0.5.1(eslint@9.8.0): + eslint-compat-utils@0.5.1(eslint@9.9.0): dependencies: - eslint: 9.8.0 + eslint: 9.9.0 semver: 7.6.3 - eslint-config-etherpad@4.0.4(eslint@9.8.0)(typescript@5.5.4): + eslint-config-etherpad@4.0.4(eslint@9.9.0)(typescript@5.5.4): dependencies: '@rushstack/eslint-patch': 1.10.3 - '@typescript-eslint/eslint-plugin': 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4) - '@typescript-eslint/parser': 7.17.0(eslint@9.8.0)(typescript@5.5.4) - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.8.0) - eslint-plugin-cypress: 2.15.2(eslint@9.8.0) - eslint-plugin-eslint-comments: 3.2.0(eslint@9.8.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.8.0) - eslint-plugin-mocha: 10.4.3(eslint@9.8.0) - eslint-plugin-n: 16.6.2(eslint@9.8.0) - eslint-plugin-prefer-arrow: 1.2.3(eslint@9.8.0) - eslint-plugin-promise: 6.6.0(eslint@9.8.0) + '@typescript-eslint/eslint-plugin': 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/parser': 7.17.0(eslint@9.9.0)(typescript@5.5.4) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0) + eslint-plugin-cypress: 2.15.2(eslint@9.9.0) + eslint-plugin-eslint-comments: 3.2.0(eslint@9.9.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.9.0) + eslint-plugin-mocha: 10.4.3(eslint@9.9.0) + eslint-plugin-n: 16.6.2(eslint@9.9.0) + eslint-plugin-prefer-arrow: 1.2.3(eslint@9.9.0) + eslint-plugin-promise: 6.6.0(eslint@9.9.0) eslint-plugin-you-dont-need-lodash-underscore: 6.14.0 transitivePeerDependencies: - eslint @@ -6660,13 +6665,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.8.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0): dependencies: debug: 4.3.5(supports-color@8.1.1) enhanced-resolve: 5.17.0 - eslint: 9.8.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.8.0))(eslint@9.8.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.8.0) + eslint: 9.9.0 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0))(eslint@9.9.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.9.0) fast-glob: 3.3.2 get-tsconfig: 4.7.6 is-core-module: 2.15.0 @@ -6677,36 +6682,36 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.8.0))(eslint@9.8.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0))(eslint@9.9.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 7.17.0(eslint@9.8.0)(typescript@5.5.4) - eslint: 9.8.0 + '@typescript-eslint/parser': 7.17.0(eslint@9.9.0)(typescript@5.5.4) + eslint: 9.9.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.8.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0) transitivePeerDependencies: - supports-color - eslint-plugin-cypress@2.15.2(eslint@9.8.0): + eslint-plugin-cypress@2.15.2(eslint@9.9.0): dependencies: - eslint: 9.8.0 + eslint: 9.9.0 globals: 13.24.0 - eslint-plugin-es-x@7.8.0(eslint@9.8.0): + eslint-plugin-es-x@7.8.0(eslint@9.9.0): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) '@eslint-community/regexpp': 4.11.0 - eslint: 9.8.0 - eslint-compat-utils: 0.5.1(eslint@9.8.0) + eslint: 9.9.0 + eslint-compat-utils: 0.5.1(eslint@9.9.0) - eslint-plugin-eslint-comments@3.2.0(eslint@9.8.0): + eslint-plugin-eslint-comments@3.2.0(eslint@9.9.0): dependencies: escape-string-regexp: 1.0.5 - eslint: 9.8.0 + eslint: 9.9.0 ignore: 5.3.1 - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.8.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.9.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -6714,9 +6719,9 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.8.0 + eslint: 9.9.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.8.0))(eslint@9.8.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.17.0(eslint@9.9.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@9.9.0))(eslint@9.9.0) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -6727,25 +6732,25 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.17.0(eslint@9.8.0)(typescript@5.5.4) + '@typescript-eslint/parser': 7.17.0(eslint@9.9.0)(typescript@5.5.4) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-mocha@10.4.3(eslint@9.8.0): + eslint-plugin-mocha@10.4.3(eslint@9.9.0): dependencies: - eslint: 9.8.0 - eslint-utils: 3.0.0(eslint@9.8.0) + eslint: 9.9.0 + eslint-utils: 3.0.0(eslint@9.9.0) globals: 13.24.0 rambda: 7.5.0 - eslint-plugin-n@16.6.2(eslint@9.8.0): + eslint-plugin-n@16.6.2(eslint@9.9.0): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) builtins: 5.1.0 - eslint: 9.8.0 - eslint-plugin-es-x: 7.8.0(eslint@9.8.0) + eslint: 9.9.0 + eslint-plugin-es-x: 7.8.0(eslint@9.9.0) get-tsconfig: 4.7.6 globals: 13.24.0 ignore: 5.3.1 @@ -6755,21 +6760,21 @@ snapshots: resolve: 1.22.8 semver: 7.6.3 - eslint-plugin-prefer-arrow@1.2.3(eslint@9.8.0): + eslint-plugin-prefer-arrow@1.2.3(eslint@9.9.0): dependencies: - eslint: 9.8.0 + eslint: 9.9.0 - eslint-plugin-promise@6.6.0(eslint@9.8.0): + eslint-plugin-promise@6.6.0(eslint@9.9.0): dependencies: - eslint: 9.8.0 + eslint: 9.9.0 - eslint-plugin-react-hooks@4.6.2(eslint@9.8.0): + eslint-plugin-react-hooks@4.6.2(eslint@9.9.0): dependencies: - eslint: 9.8.0 + eslint: 9.9.0 - eslint-plugin-react-refresh@0.4.9(eslint@9.8.0): + eslint-plugin-react-refresh@0.4.9(eslint@9.9.0): dependencies: - eslint: 9.8.0 + eslint: 9.9.0 eslint-plugin-you-dont-need-lodash-underscore@6.14.0: dependencies: @@ -6780,9 +6785,9 @@ snapshots: esrecurse: 4.3.0 estraverse: 5.3.0 - eslint-utils@3.0.0(eslint@9.8.0): + eslint-utils@3.0.0(eslint@9.9.0): dependencies: - eslint: 9.8.0 + eslint: 9.9.0 eslint-visitor-keys: 2.1.0 eslint-visitor-keys@2.1.0: {} @@ -6791,13 +6796,13 @@ snapshots: eslint-visitor-keys@4.0.0: {} - eslint@9.8.0: + eslint@9.9.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) '@eslint-community/regexpp': 4.11.0 '@eslint/config-array': 0.17.1 '@eslint/eslintrc': 3.1.0 - '@eslint/js': 9.8.0 + '@eslint/js': 9.9.0 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.3.0 '@nodelib/fs.walk': 1.2.8 @@ -8857,42 +8862,42 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-plugin-static-copy@1.0.6(vite@5.4.0(@types/node@22.1.0)): + vite-plugin-static-copy@1.0.6(vite@5.4.0(@types/node@22.2.0)): dependencies: chokidar: 3.6.0 fast-glob: 3.3.2 fs-extra: 11.2.0 picocolors: 1.0.1 - vite: 5.4.0(@types/node@22.1.0) + vite: 5.4.0(@types/node@22.2.0) - vite-plugin-svgr@4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.0(@types/node@22.1.0)): + vite-plugin-svgr@4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.0(@types/node@22.2.0)): dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.18.0) '@svgr/core': 8.1.0(typescript@5.5.4) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.5.4)) - vite: 5.4.0(@types/node@22.1.0) + vite: 5.4.0(@types/node@22.2.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite@5.4.0(@types/node@22.1.0): + vite@5.4.0(@types/node@22.2.0): dependencies: esbuild: 0.21.5 postcss: 8.4.41 rollup: 4.18.0 optionalDependencies: - '@types/node': 22.1.0 + '@types/node': 22.2.0 fsevents: 2.3.3 - vitepress@1.3.2(@algolia/client-search@4.23.3)(@types/node@22.1.0)(@types/react@18.3.3)(axios@1.7.3)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4): + vitepress@1.3.2(@algolia/client-search@4.23.3)(@types/node@22.2.0)(@types/react@18.3.3)(axios@1.7.3)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4): dependencies: '@docsearch/css': 3.6.0 '@docsearch/js': 3.6.0(@algolia/client-search@4.23.3)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@shikijs/core': 1.10.3 '@shikijs/transformers': 1.10.3 '@types/markdown-it': 14.1.1 - '@vitejs/plugin-vue': 5.0.5(vite@5.4.0(@types/node@22.1.0))(vue@3.4.31(typescript@5.5.4)) + '@vitejs/plugin-vue': 5.0.5(vite@5.4.0(@types/node@22.2.0))(vue@3.4.31(typescript@5.5.4)) '@vue/devtools-api': 7.3.5 '@vue/shared': 3.4.31 '@vueuse/core': 10.11.0(vue@3.4.31(typescript@5.5.4)) @@ -8901,7 +8906,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.0.0 shiki: 1.10.3 - vite: 5.4.0(@types/node@22.1.0) + vite: 5.4.0(@types/node@22.2.0) vue: 3.4.31(typescript@5.5.4) optionalDependencies: postcss: 8.4.41 diff --git a/src/package.json b/src/package.json index 939c6d9a530..7228363145b 100644 --- a/src/package.json +++ b/src/package.json @@ -90,14 +90,14 @@ "@types/jsdom": "^21.1.7", "@types/jsonwebtoken": "^9.0.6", "@types/mocha": "^10.0.7", - "@types/node": "^22.1.0", + "@types/node": "^22.2.0", "@types/oidc-provider": "^8.5.1", "@types/semver": "^7.5.8", "@types/sinon": "^17.0.3", "@types/supertest": "^6.0.2", "@types/underscore": "^1.11.15", "chokidar": "^3.6.0", - "eslint": "^9.8.0", + "eslint": "^9.9.0", "eslint-config-etherpad": "^4.0.4", "etherpad-cli-client": "^3.0.2", "mocha": "^10.7.3", From 44024e8a5fbf65ff698ff251894f835de2d2cc71 Mon Sep 17 00:00:00 2001 From: Zuo Zongyuan Date: Tue, 13 Aug 2024 02:07:02 +0800 Subject: [PATCH 05/49] fix: correct the transport protocol check (#6576) --- src/node/utils/Settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/utils/Settings.ts b/src/node/utils/Settings.ts index 3936cf4e1d7..d798e10bffc 100644 --- a/src/node/utils/Settings.ts +++ b/src/node/utils/Settings.ts @@ -837,7 +837,7 @@ exports.reloadSettings = () => { exports.skinName = 'colibris'; } - if (!exports.socketTransportProtocols.includes("websocket") || exports.socketTransportProtocols.includes("polling")) { + if (!exports.socketTransportProtocols.includes("websocket") || !exports.socketTransportProtocols.includes("polling")) { logger.warn("Invalid socketTransportProtocols setting. Please check out settings.json.template and update your settings.json. Falling back to the default ['websocket', 'polling']."); exports.socketTransportProtocols = ['websocket', 'polling']; } From f78cb4a3a87b82a1cf3f0a2eeee74b1f98d7fd7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 21:07:56 +0200 Subject: [PATCH 06/49] Bump superagent from 9.0.2 to 10.0.1 (#6579) Bumps [superagent](https://github.com/ladjs/superagent) from 9.0.2 to 10.0.1. - [Release notes](https://github.com/ladjs/superagent/releases) - [Changelog](https://github.com/ladjs/superagent/blob/master/HISTORY.md) - [Commits](https://github.com/ladjs/superagent/compare/v9.0.2...v10.0.1) --- updated-dependencies: - dependency-name: superagent dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pnpm-lock.yaml | 22 ++++++++++++++++++++-- src/package.json | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3ccad1aa54f..7617d1a97d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -252,8 +252,8 @@ importers: specifier: ^4.7.5 version: 4.7.5 superagent: - specifier: 9.0.2 - version: 9.0.2 + specifier: 10.0.1 + version: 10.0.1 threads: specifier: ^1.7.0 version: 1.7.0 @@ -4037,6 +4037,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + superagent@10.0.1: + resolution: {integrity: sha512-kG7dZ4Z6s6VbCVxd0PJpkYND0X+SW+iIAuboIQyHE7eFSNVprFVTpG1uID3UsVS7Jw47tdPvSiCSGzgXDhFcGQ==} + engines: {node: '>=14.18.0'} + superagent@8.1.2: resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} engines: {node: '>=6.4.0 <13 || >=14'} @@ -8561,6 +8565,20 @@ snapshots: strip-json-comments@3.1.1: {} + superagent@10.0.1: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.3.5(supports-color@8.1.1) + fast-safe-stringify: 2.1.1 + form-data: 4.0.0 + formidable: 3.5.1 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.12.3 + transitivePeerDependencies: + - supports-color + superagent@8.1.2: dependencies: component-emitter: 1.3.1 diff --git a/src/package.json b/src/package.json index 7228363145b..6d7031ef4af 100644 --- a/src/package.json +++ b/src/package.json @@ -67,7 +67,7 @@ "semver": "^7.6.3", "socket.io": "^4.7.5", "socket.io-client": "^4.7.5", - "superagent": "9.0.2", + "superagent": "10.0.1", "threads": "^1.7.0", "tinycon": "0.6.8", "tsx": "4.17.0", From 3fdb3f7838884e7cd332abc7d943cf93806d170f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 21:08:03 +0200 Subject: [PATCH 07/49] Bump the dev-dependencies group with 3 updates (#6578) Bumps the dev-dependencies group with 3 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [i18next](https://github.com/i18next/i18next). Updates `@typescript-eslint/eslint-plugin` from 8.0.1 to 8.1.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.1.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.0.1 to 8.1.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.1.0/packages/parser) Updates `i18next` from 23.12.2 to 23.12.3 - [Release notes](https://github.com/i18next/i18next/releases) - [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md) - [Commits](https://github.com/i18next/i18next/compare/v23.12.2...v23.12.3) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: i18next dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- admin/package.json | 6 +-- pnpm-lock.yaml | 110 ++++++++++++++++++++++----------------------- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/admin/package.json b/admin/package.json index 04195c1125e..566bd743b0c 100644 --- a/admin/package.json +++ b/admin/package.json @@ -18,13 +18,13 @@ "@radix-ui/react-toast": "^1.2.1", "@types/react": "^18.3.2", "@types/react-dom": "^18.2.25", - "@typescript-eslint/eslint-plugin": "^8.0.1", - "@typescript-eslint/parser": "^8.0.1", + "@typescript-eslint/eslint-plugin": "^8.1.0", + "@typescript-eslint/parser": "^8.1.0", "@vitejs/plugin-react-swc": "^3.5.0", "eslint": "^9.9.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.9", - "i18next": "^23.12.2", + "i18next": "^23.12.3", "i18next-browser-languagedetector": "^8.0.0", "lucide-react": "^0.427.0", "react": "^18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7617d1a97d1..6b7f8fe9cda 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,11 +41,11 @@ importers: specifier: ^18.2.25 version: 18.3.0 '@typescript-eslint/eslint-plugin': - specifier: ^8.0.1 - version: 8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4) + specifier: ^8.1.0 + version: 8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^8.0.1 - version: 8.0.1(eslint@9.9.0)(typescript@5.5.4) + specifier: ^8.1.0 + version: 8.1.0(eslint@9.9.0)(typescript@5.5.4) '@vitejs/plugin-react-swc': specifier: ^3.5.0 version: 3.7.0(vite@5.4.0(@types/node@22.2.0)) @@ -59,8 +59,8 @@ importers: specifier: ^0.4.9 version: 0.4.9(eslint@9.9.0) i18next: - specifier: ^23.12.2 - version: 23.12.2 + specifier: ^23.12.3 + version: 23.12.3 i18next-browser-languagedetector: specifier: ^8.0.0 version: 8.0.0 @@ -78,7 +78,7 @@ importers: version: 7.52.2(react@18.3.1) react-i18next: specifier: ^15.0.1 - version: 15.0.1(i18next@23.12.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 15.0.1(i18next@23.12.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-router-dom: specifier: ^6.26.0 version: 6.26.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1623,8 +1623,8 @@ packages: typescript: optional: true - '@typescript-eslint/eslint-plugin@8.0.1': - resolution: {integrity: sha512-5g3Y7GDFsJAnY4Yhvk8sZtFfV6YNF2caLzjrRPUBzewjPCaj0yokePB4LJSobyCzGMzjZZYFbwuzbfDHlimXbQ==} + '@typescript-eslint/eslint-plugin@8.1.0': + resolution: {integrity: sha512-LlNBaHFCEBPHyD4pZXb35mzjGkuGKXU5eeCA1SxvHfiRES0E82dOounfVpL4DCqYvJEKab0bZIA0gCRpdLKkCw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 @@ -1644,8 +1644,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.0.1': - resolution: {integrity: sha512-5IgYJ9EO/12pOUwiBKFkpU7rS3IU21mtXzB81TNwq2xEybcmAZrE9qwDtsb5uQd9aVO9o0fdabFyAmKveXyujg==} + '@typescript-eslint/parser@8.1.0': + resolution: {integrity: sha512-U7iTAtGgJk6DPX9wIWPPOlt1gO57097G06gIcl0N0EEnNw8RGD62c+2/DiP/zL7KrkqnnqF7gtFGR7YgzPllTA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1658,8 +1658,8 @@ packages: resolution: {integrity: sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/scope-manager@8.0.1': - resolution: {integrity: sha512-NpixInP5dm7uukMiRyiHjRKkom5RIFA4dfiHvalanD2cF0CLUuQqxfg8PtEUo9yqJI2bBhF+pcSafqnG3UBnRQ==} + '@typescript-eslint/scope-manager@8.1.0': + resolution: {integrity: sha512-DsuOZQji687sQUjm4N6c9xABJa7fjvfIdjqpSIIVOgaENf2jFXiM9hIBZOL3hb6DHK9Nvd2d7zZnoMLf9e0OtQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/type-utils@7.17.0': @@ -1672,8 +1672,8 @@ packages: typescript: optional: true - '@typescript-eslint/type-utils@8.0.1': - resolution: {integrity: sha512-+/UT25MWvXeDX9YaHv1IS6KI1fiuTto43WprE7pgSMswHbn1Jm9GEM4Txp+X74ifOWV8emu2AWcbLhpJAvD5Ng==} + '@typescript-eslint/type-utils@8.1.0': + resolution: {integrity: sha512-oLYvTxljVvsMnldfl6jIKxTaU7ok7km0KDrwOt1RHYu6nxlhN3TIx8k5Q52L6wR33nOwDgM7VwW1fT1qMNfFIA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -1685,8 +1685,8 @@ packages: resolution: {integrity: sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/types@8.0.1': - resolution: {integrity: sha512-PpqTVT3yCA/bIgJ12czBuE3iBlM3g4inRSC5J0QOdQFAn07TYrYEQBBKgXH1lQpglup+Zy6c1fxuwTk4MTNKIw==} + '@typescript-eslint/types@8.1.0': + resolution: {integrity: sha512-q2/Bxa0gMOu/2/AKALI0tCKbG2zppccnRIRCW6BaaTlRVaPKft4oVYPp7WOPpcnsgbr0qROAVCVKCvIQ0tbWog==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@7.17.0': @@ -1698,8 +1698,8 @@ packages: typescript: optional: true - '@typescript-eslint/typescript-estree@8.0.1': - resolution: {integrity: sha512-8V9hriRvZQXPWU3bbiUV4Epo7EvgM6RTs+sUmxp5G//dBGy402S7Fx0W0QkB2fb4obCF8SInoUzvTYtc3bkb5w==} + '@typescript-eslint/typescript-estree@8.1.0': + resolution: {integrity: sha512-NTHhmufocEkMiAord/g++gWKb0Fr34e9AExBRdqgWdVBaKoei2dIyYKD9Q0jBnvfbEA5zaf8plUFMUH6kQ0vGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -1713,8 +1713,8 @@ packages: peerDependencies: eslint: ^8.56.0 - '@typescript-eslint/utils@8.0.1': - resolution: {integrity: sha512-CBFR0G0sCt0+fzfnKaciu9IBsKvEKYwN9UZ+eeogK1fYHg4Qxk1yf/wLQkLXlq8wbU2dFlgAesxt8Gi76E8RTA==} + '@typescript-eslint/utils@8.1.0': + resolution: {integrity: sha512-ypRueFNKTIFwqPeJBfeIpxZ895PQhNyH4YID6js0UoBImWYoSjBsahUn9KMiJXh94uOjVBgHD9AmkyPsPnFwJA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1723,8 +1723,8 @@ packages: resolution: {integrity: sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/visitor-keys@8.0.1': - resolution: {integrity: sha512-W5E+o0UfUcK5EgchLZsyVWqARmsM7v54/qEq6PY3YI5arkgmCzHiuk0zKSJJbm71V0xdRna4BGomkCTXz2/LkQ==} + '@typescript-eslint/visitor-keys@8.1.0': + resolution: {integrity: sha512-ba0lNI19awqZ5ZNKh6wCModMwoZs457StTebQ0q1NP58zSi2F6MOZRXwfKZy+jB78JNJ/WH8GSh2IQNzXX8Nag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.2.0': @@ -2924,8 +2924,8 @@ packages: i18next-browser-languagedetector@8.0.0: resolution: {integrity: sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==} - i18next@23.12.2: - resolution: {integrity: sha512-XIeh5V+bi8SJSWGL3jqbTEBW5oD6rbP5L+E7dVQh1MNTxxYef0x15rhJVcRb7oiuq4jLtgy2SD8eFlf6P2cmqg==} + i18next@23.12.3: + resolution: {integrity: sha512-DyigQmrR10V9U2N6pjhbfahW13GY7n8BQD9swN09JuRRropgsksWVi4vRLeex0Qf7zCPnBfIqQfhcBzdZBQBYw==} iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} @@ -5688,14 +5688,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.0.1(eslint@9.9.0)(typescript@5.5.4) - '@typescript-eslint/scope-manager': 8.0.1 - '@typescript-eslint/type-utils': 8.0.1(eslint@9.9.0)(typescript@5.5.4) - '@typescript-eslint/utils': 8.0.1(eslint@9.9.0)(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 8.0.1 + '@typescript-eslint/parser': 8.1.0(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.1.0 + '@typescript-eslint/type-utils': 8.1.0(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/utils': 8.1.0(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.1.0 eslint: 9.9.0 graphemer: 1.4.0 ignore: 5.3.1 @@ -5719,12 +5719,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.0.1(eslint@9.9.0)(typescript@5.5.4)': + '@typescript-eslint/parser@8.1.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@typescript-eslint/scope-manager': 8.0.1 - '@typescript-eslint/types': 8.0.1 - '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 8.0.1 + '@typescript-eslint/scope-manager': 8.1.0 + '@typescript-eslint/types': 8.1.0 + '@typescript-eslint/typescript-estree': 8.1.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.1.0 debug: 4.3.5(supports-color@8.1.1) eslint: 9.9.0 optionalDependencies: @@ -5737,10 +5737,10 @@ snapshots: '@typescript-eslint/types': 7.17.0 '@typescript-eslint/visitor-keys': 7.17.0 - '@typescript-eslint/scope-manager@8.0.1': + '@typescript-eslint/scope-manager@8.1.0': dependencies: - '@typescript-eslint/types': 8.0.1 - '@typescript-eslint/visitor-keys': 8.0.1 + '@typescript-eslint/types': 8.1.0 + '@typescript-eslint/visitor-keys': 8.1.0 '@typescript-eslint/type-utils@7.17.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: @@ -5754,10 +5754,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.0.1(eslint@9.9.0)(typescript@5.5.4)': + '@typescript-eslint/type-utils@8.1.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: - '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4) - '@typescript-eslint/utils': 8.0.1(eslint@9.9.0)(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 8.1.0(typescript@5.5.4) + '@typescript-eslint/utils': 8.1.0(eslint@9.9.0)(typescript@5.5.4) debug: 4.3.5(supports-color@8.1.1) ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: @@ -5768,7 +5768,7 @@ snapshots: '@typescript-eslint/types@7.17.0': {} - '@typescript-eslint/types@8.0.1': {} + '@typescript-eslint/types@8.1.0': {} '@typescript-eslint/typescript-estree@7.17.0(typescript@5.5.4)': dependencies: @@ -5785,10 +5785,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.0.1(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@8.1.0(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 8.0.1 - '@typescript-eslint/visitor-keys': 8.0.1 + '@typescript-eslint/types': 8.1.0 + '@typescript-eslint/visitor-keys': 8.1.0 debug: 4.3.5(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 @@ -5811,12 +5811,12 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@8.0.1(eslint@9.9.0)(typescript@5.5.4)': + '@typescript-eslint/utils@8.1.0(eslint@9.9.0)(typescript@5.5.4)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) - '@typescript-eslint/scope-manager': 8.0.1 - '@typescript-eslint/types': 8.0.1 - '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.1.0 + '@typescript-eslint/types': 8.1.0 + '@typescript-eslint/typescript-estree': 8.1.0(typescript@5.5.4) eslint: 9.9.0 transitivePeerDependencies: - supports-color @@ -5827,9 +5827,9 @@ snapshots: '@typescript-eslint/types': 7.17.0 eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@8.0.1': + '@typescript-eslint/visitor-keys@8.1.0': dependencies: - '@typescript-eslint/types': 8.0.1 + '@typescript-eslint/types': 8.1.0 eslint-visitor-keys: 3.4.3 '@ungap/structured-clone@1.2.0': {} @@ -7336,7 +7336,7 @@ snapshots: dependencies: '@babel/runtime': 7.24.7 - i18next@23.12.2: + i18next@23.12.3: dependencies: '@babel/runtime': 7.24.8 @@ -8179,11 +8179,11 @@ snapshots: dependencies: react: 18.3.1 - react-i18next@15.0.1(i18next@23.12.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-i18next@15.0.1(i18next@23.12.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.24.8 html-parse-stringify: 3.0.1 - i18next: 23.12.2 + i18next: 23.12.3 react: 18.3.1 optionalDependencies: react-dom: 18.3.1(react@18.3.1) From 221f6f200208e796f64487bb2abd77fa49903780 Mon Sep 17 00:00:00 2001 From: SamTV12345 <40429738+SamTV12345@users.noreply.github.com> Date: Tue, 13 Aug 2024 21:32:40 +0200 Subject: [PATCH 08/49] Added sorting for plugins (#6580) --- admin/src/index.css | 9 +++++ admin/src/pages/HomePage.tsx | 76 ++++++++++++++++++++++++++++++------ admin/src/pages/PadPage.tsx | 4 +- admin/src/pages/Plugin.ts | 2 +- 4 files changed, 75 insertions(+), 16 deletions(-) diff --git a/admin/src/index.css b/admin/src/index.css index a038888d750..99a406ee70d 100644 --- a/admin/src/index.css +++ b/admin/src/index.css @@ -802,3 +802,12 @@ input, button, select, optgroup, textarea { background-color: var(--etherpad-color); color: white } + +.search-pads{ + text-align: center; +} + +.search-pads-body tr td:last-child { + display: flex; + justify-content: center; +} diff --git a/admin/src/pages/HomePage.tsx b/admin/src/pages/HomePage.tsx index c96b034245c..c0d5913eefa 100644 --- a/admin/src/pages/HomePage.tsx +++ b/admin/src/pages/HomePage.tsx @@ -6,14 +6,51 @@ import {Trans, useTranslation} from "react-i18next"; import {SearchField} from "../components/SearchField.tsx"; import {Download, Trash} from "lucide-react"; import {IconButton} from "../components/IconButton.tsx"; +import {determineSorting} from "../utils/sorting.ts"; export const HomePage = () => { const pluginsSocket = useStore(state=>state.pluginsSocket) const [plugins,setPlugins] = useState([]) const [installedPlugins, setInstalledPlugins] = useState([]) + const [searchParams, setSearchParams] = useState({ + offset: 0, + limit: 99999, + sortBy: 'name', + sortDir: 'asc', + searchTerm: '' + }) + + const filteredInstallablePlugins = useMemo(()=>{ + return plugins.sort((a, b)=>{ + if(searchParams.sortBy === "version"){ + if(searchParams.sortDir === "asc"){ + return a.version.localeCompare(b.version) + } + return b.version.localeCompare(a.version) + } + + if(searchParams.sortBy === "last-updated"){ + if(searchParams.sortDir === "asc"){ + return a.time.localeCompare(b.time) + } + return b.time.localeCompare(a.time) + } + + + if (searchParams.sortBy === "name") { + if(searchParams.sortDir === "asc"){ + return a.name.localeCompare(b.name) + } + return b.name.localeCompare(a.name) + } + return 0 + }) + }, [plugins, searchParams]) + const sortedInstalledPlugins = useMemo(()=>{ return installedPlugins.sort((a, b)=>{ + if(a.name < b.name){ return -1 } @@ -23,14 +60,8 @@ export const HomePage = () => { return 0 }) - } ,[installedPlugins]) - const [searchParams, setSearchParams] = useState({ - offset: 0, - limit: 99999, - sortBy: 'name', - sortDir: 'asc', - searchTerm: '' - }) + } ,[installedPlugins, searchParams]) + const [searchTerm, setSearchTerm] = useState('') const {t} = useTranslation() @@ -165,16 +196,35 @@ export const HomePage = () => { - + - - + + - {(plugins.length > 0) ? - plugins.map((plugin) => { + {(filteredInstallablePlugins.length > 0) ? + filteredInstallablePlugins.map((plugin) => { return diff --git a/admin/src/pages/PadPage.tsx b/admin/src/pages/PadPage.tsx index 2de3ba5f9ab..e663603cdd9 100644 --- a/admin/src/pages/PadPage.tsx +++ b/admin/src/pages/PadPage.tsx @@ -104,7 +104,7 @@ export const PadPage = ()=>{ setSearchTerm(v.target.value)} placeholder={t('ep_admin_pads:ep_adminpads2_search-heading')}/>
{ + setSearchParams({ + ...searchParams, + sortBy: 'name', + sortDir: searchParams.sortDir === "asc"? "desc": "asc" + }) + }}> + { + setSearchParams({ + ...searchParams, + sortBy: 'version', + sortDir: searchParams.sortDir === "asc"? "desc": "asc" + }) + }}>{ + setSearchParams({ + ...searchParams, + sortBy: 'last-updated', + sortDir: searchParams.sortDir === "asc"? "desc": "asc" + }) + }}>
{plugin.name} {plugin.description}
- + - + { pads?.results?.map((pad)=>{ return diff --git a/admin/src/pages/Plugin.ts b/admin/src/pages/Plugin.ts index 3188c247f0a..f5563863b53 100644 --- a/admin/src/pages/Plugin.ts +++ b/admin/src/pages/Plugin.ts @@ -20,7 +20,7 @@ export type SearchParams = { searchTerm: string, offset: number, limit: number, - sortBy: 'name'|'version', + sortBy: 'name'|'version'|'last-updated', sortDir: 'asc'|'desc' } From c7b295f8dd2ff8d06880c76e16da921ac9a02e01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:31:20 +0200 Subject: [PATCH 09/49] Bump the dev-dependencies group across 1 directory with 7 updates (#6590) Bumps the dev-dependencies group with 7 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `22.2.0` | `22.3.0` | | [@types/oidc-provider](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/oidc-provider) | `8.5.1` | `8.5.2` | | [i18next](https://github.com/i18next/i18next) | `23.12.3` | `23.13.0` | | [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `0.427.0` | `0.428.0` | | [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) | `6.26.0` | `6.26.1` | | [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `5.4.0` | `5.4.1` | | [zustand](https://github.com/pmndrs/zustand) | `4.5.4` | `4.5.5` | Updates `@types/node` from 22.2.0 to 22.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@types/oidc-provider` from 8.5.1 to 8.5.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/oidc-provider) Updates `i18next` from 23.12.3 to 23.13.0 - [Release notes](https://github.com/i18next/i18next/releases) - [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md) - [Commits](https://github.com/i18next/i18next/compare/v23.12.3...v23.13.0) Updates `lucide-react` from 0.427.0 to 0.428.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/0.428.0/packages/lucide-react) Updates `react-router-dom` from 6.26.0 to 6.26.1 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.26.1/packages/react-router-dom) Updates `vite` from 5.4.0 to 5.4.1 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.4.1/packages/vite) Updates `zustand` from 4.5.4 to 4.5.5 - [Release notes](https://github.com/pmndrs/zustand/releases) - [Commits](https://github.com/pmndrs/zustand/compare/v4.5.4...v4.5.5) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: "@types/oidc-provider" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies - dependency-name: i18next dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: lucide-react dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: react-router-dom dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies - dependency-name: vite dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies - dependency-name: zustand dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- admin/package.json | 10 +-- bin/package.json | 2 +- pnpm-lock.yaml | 186 ++++++++++++++++++++++----------------------- src/package.json | 4 +- ui/package.json | 2 +- 5 files changed, 102 insertions(+), 102 deletions(-) diff --git a/admin/package.json b/admin/package.json index 566bd743b0c..5569150c345 100644 --- a/admin/package.json +++ b/admin/package.json @@ -24,19 +24,19 @@ "eslint": "^9.9.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.9", - "i18next": "^23.12.3", + "i18next": "^23.13.0", "i18next-browser-languagedetector": "^8.0.0", - "lucide-react": "^0.427.0", + "lucide-react": "^0.428.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.52.2", "react-i18next": "^15.0.1", - "react-router-dom": "^6.26.0", + "react-router-dom": "^6.26.1", "socket.io-client": "^4.7.5", "typescript": "^5.5.4", - "vite": "^5.4.0", + "vite": "^5.4.1", "vite-plugin-static-copy": "^1.0.6", "vite-plugin-svgr": "^4.2.0", - "zustand": "^4.5.4" + "zustand": "^4.5.5" } } diff --git a/bin/package.json b/bin/package.json index e598fc31194..49b418d3228 100644 --- a/bin/package.json +++ b/bin/package.json @@ -15,7 +15,7 @@ "ueberdb2": "^4.2.92" }, "devDependencies": { - "@types/node": "^22.2.0", + "@types/node": "^22.3.0", "@types/semver": "^7.5.8", "typescript": "^5.5.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b7f8fe9cda..8be0c2ab3a0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,7 +48,7 @@ importers: version: 8.1.0(eslint@9.9.0)(typescript@5.5.4) '@vitejs/plugin-react-swc': specifier: ^3.5.0 - version: 3.7.0(vite@5.4.0(@types/node@22.2.0)) + version: 3.7.0(vite@5.4.1(@types/node@22.3.0)) eslint: specifier: ^9.9.0 version: 9.9.0 @@ -59,14 +59,14 @@ importers: specifier: ^0.4.9 version: 0.4.9(eslint@9.9.0) i18next: - specifier: ^23.12.3 - version: 23.12.3 + specifier: ^23.13.0 + version: 23.13.0 i18next-browser-languagedetector: specifier: ^8.0.0 version: 8.0.0 lucide-react: - specifier: ^0.427.0 - version: 0.427.0(react@18.3.1) + specifier: ^0.428.0 + version: 0.428.0(react@18.3.1) react: specifier: ^18.2.0 version: 18.3.1 @@ -78,10 +78,10 @@ importers: version: 7.52.2(react@18.3.1) react-i18next: specifier: ^15.0.1 - version: 15.0.1(i18next@23.12.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 15.0.1(i18next@23.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-router-dom: - specifier: ^6.26.0 - version: 6.26.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^6.26.1 + version: 6.26.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) socket.io-client: specifier: ^4.7.5 version: 4.7.5 @@ -89,17 +89,17 @@ importers: specifier: ^5.5.4 version: 5.5.4 vite: - specifier: ^5.4.0 - version: 5.4.0(@types/node@22.2.0) + specifier: ^5.4.1 + version: 5.4.1(@types/node@22.3.0) vite-plugin-static-copy: specifier: ^1.0.6 - version: 1.0.6(vite@5.4.0(@types/node@22.2.0)) + version: 1.0.6(vite@5.4.1(@types/node@22.3.0)) vite-plugin-svgr: specifier: ^4.2.0 - version: 4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.0(@types/node@22.2.0)) + version: 4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)) zustand: - specifier: ^4.5.4 - version: 4.5.4(@types/react@18.3.3)(react@18.3.1) + specifier: ^4.5.5 + version: 4.5.5(@types/react@18.3.3)(react@18.3.1) bin: dependencies: @@ -123,8 +123,8 @@ importers: version: 4.2.92 devDependencies: '@types/node': - specifier: ^22.2.0 - version: 22.2.0 + specifier: ^22.3.0 + version: 22.3.0 '@types/semver': specifier: ^7.5.8 version: 7.5.8 @@ -136,7 +136,7 @@ importers: devDependencies: vitepress: specifier: ^1.3.2 - version: 1.3.2(@algolia/client-search@4.23.3)(@types/node@22.2.0)(@types/react@18.3.3)(axios@1.7.3)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4) + version: 1.3.2(@algolia/client-search@4.23.3)(@types/node@22.3.0)(@types/react@18.3.3)(axios@1.7.3)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4) src: dependencies: @@ -304,11 +304,11 @@ importers: specifier: ^10.0.7 version: 10.0.7 '@types/node': - specifier: ^22.2.0 - version: 22.2.0 + specifier: ^22.3.0 + version: 22.3.0 '@types/oidc-provider': - specifier: ^8.5.1 - version: 8.5.1 + specifier: ^8.5.2 + version: 8.5.2 '@types/semver': specifier: ^7.5.8 version: 7.5.8 @@ -370,8 +370,8 @@ importers: specifier: ^5.5.4 version: 5.5.4 vite: - specifier: ^5.4.0 - version: 5.4.0(@types/node@22.2.0) + specifier: ^5.4.1 + version: 5.4.1(@types/node@22.3.0) packages: @@ -1164,8 +1164,8 @@ packages: '@types/react-dom': optional: true - '@remix-run/router@1.19.0': - resolution: {integrity: sha512-zDICCLKEwbVYTS6TjYaWtHXxkdoUvD/QXvyVZjGCsWz5vyH7aFeONlPffPdW+Y/t6KT0MgXb2Mfjun9YpWN1dA==} + '@remix-run/router@1.19.1': + resolution: {integrity: sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==} engines: {node: '>=14.0.0'} '@rollup/pluginutils@5.1.0': @@ -1549,11 +1549,11 @@ packages: '@types/node-fetch@2.6.11': resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} - '@types/node@22.2.0': - resolution: {integrity: sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==} + '@types/node@22.3.0': + resolution: {integrity: sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g==} - '@types/oidc-provider@8.5.1': - resolution: {integrity: sha512-NS8tBPOj9GG6SxyrUHWBzglOtAYNDX41J4cRE45oeK0iSqI6V6tDW70aPWg25pJFNSC1evccXFm9evfwjxm7HQ==} + '@types/oidc-provider@8.5.2': + resolution: {integrity: sha512-NiD3VG49+cRCAAe8+uZLM4onOcX8y9+cwaml8JG1qlgc98rWoCRgsnOB4Ypx+ysays5jiwzfUgT0nWyXPB/9uQ==} '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} @@ -2924,8 +2924,8 @@ packages: i18next-browser-languagedetector@8.0.0: resolution: {integrity: sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==} - i18next@23.12.3: - resolution: {integrity: sha512-DyigQmrR10V9U2N6pjhbfahW13GY7n8BQD9swN09JuRRropgsksWVi4vRLeex0Qf7zCPnBfIqQfhcBzdZBQBYw==} + i18next@23.13.0: + resolution: {integrity: sha512-B+g0/KTKmN3+NeMKPljQxdrih6Q6lyDF5O2e/Ofd0JQsTLojJD/BSTTN04iw6OVc0yBiHeypu5hoBNV6ag44Zw==} iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} @@ -3292,8 +3292,8 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - lucide-react@0.427.0: - resolution: {integrity: sha512-lv9s6c5BDF/ccuA0EgTdskTxIe11qpwBDmzRZHJAKtp8LTewAvDvOM+pTES9IpbBuTqkjiMhOmGpJ/CB+mKjFw==} + lucide-react@0.428.0: + resolution: {integrity: sha512-rGrzslfEcgqwh+TLBC5qJ8wvVIXhLvAIXVFKNHndYyb1utSxxn9rXOC+1CNJLi6yNOooyPqIs6+3YCp6uSiEvg==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc @@ -3761,15 +3761,15 @@ packages: '@types/react': optional: true - react-router-dom@6.26.0: - resolution: {integrity: sha512-RRGUIiDtLrkX3uYcFiCIxKFWMcWQGMojpYZfcstc63A1+sSnVgILGIm9gNUA6na3Fm1QuPGSBQH2EMbAZOnMsQ==} + react-router-dom@6.26.1: + resolution: {integrity: sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' react-dom: '>=16.8' - react-router@6.26.0: - resolution: {integrity: sha512-wVQq0/iFYd3iZ9H2l3N3k4PL8EEHcb0XlU2Na8nEwmiXgIUElEH6gaJDtUQxJ+JFzmIXaQjfdpcGWaM6IoQGxg==} + react-router@6.26.1: + resolution: {integrity: sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' @@ -4204,8 +4204,8 @@ packages: underscore@1.13.7: resolution: {integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==} - undici-types@6.13.0: - resolution: {integrity: sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==} + undici-types@6.18.2: + resolution: {integrity: sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ==} unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -4280,8 +4280,8 @@ packages: '@types/react': optional: true - use-sync-external-store@1.2.0: - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + use-sync-external-store@1.2.2: + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -4313,8 +4313,8 @@ packages: peerDependencies: vite: ^2.6.0 || 3 || 4 || 5 - vite@5.4.0: - resolution: {integrity: sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==} + vite@5.4.1: + resolution: {integrity: sha512-1oE6yuNXssjrZdblI9AfBbHCC41nnyoVoEZxQnID6yvQZAFBzxxkqoFLtHUMkYunL8hwOLEjgTuxpkRxvba3kA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -4501,8 +4501,8 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zustand@4.5.4: - resolution: {integrity: sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg==} + zustand@4.5.5: + resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} engines: {node: '>=12.7.0'} peerDependencies: '@types/react': '>=16.8' @@ -5246,7 +5246,7 @@ snapshots: '@types/react': 18.3.3 '@types/react-dom': 18.3.0 - '@remix-run/router@1.19.0': {} + '@remix-run/router@1.19.1': {} '@rollup/pluginutils@5.1.0(rollup@4.18.0)': dependencies: @@ -5468,18 +5468,18 @@ snapshots: '@types/accepts@1.3.7': dependencies: - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/async@3.2.24': {} '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/connect@3.4.38': dependencies: - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/content-disposition@0.5.8': {} @@ -5492,11 +5492,11 @@ snapshots: '@types/connect': 3.4.38 '@types/express': 4.17.21 '@types/keygrip': 1.0.6 - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/cors@2.8.17': dependencies: - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/debug@4.1.12': dependencies: @@ -5506,7 +5506,7 @@ snapshots: '@types/express-serve-static-core@4.19.5': dependencies: - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -5520,11 +5520,11 @@ snapshots: '@types/formidable@3.4.5': dependencies: - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/fs-extra@9.0.13': dependencies: - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/hast@3.0.4': dependencies: @@ -5542,7 +5542,7 @@ snapshots: '@types/jsdom@21.1.7': dependencies: - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 @@ -5552,7 +5552,7 @@ snapshots: '@types/jsonwebtoken@9.0.6': dependencies: - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/keygrip@1.0.6': {} @@ -5569,7 +5569,7 @@ snapshots: '@types/http-errors': 2.0.4 '@types/keygrip': 1.0.6 '@types/koa-compose': 3.2.8 - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/linkify-it@5.0.0': {} @@ -5596,17 +5596,17 @@ snapshots: '@types/node-fetch@2.6.11': dependencies: - '@types/node': 22.2.0 + '@types/node': 22.3.0 form-data: 4.0.0 - '@types/node@22.2.0': + '@types/node@22.3.0': dependencies: - undici-types: 6.13.0 + undici-types: 6.18.2 - '@types/oidc-provider@8.5.1': + '@types/oidc-provider@8.5.2': dependencies: '@types/koa': 2.15.0 - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/prop-types@15.7.12': {} @@ -5628,12 +5628,12 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/send': 0.17.4 '@types/sinon@17.0.3': @@ -5648,7 +5648,7 @@ snapshots: dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 22.2.0 + '@types/node': 22.3.0 '@types/supertest@6.0.2': dependencies: @@ -5657,7 +5657,7 @@ snapshots: '@types/tar@6.1.13': dependencies: - '@types/node': 22.2.0 + '@types/node': 22.3.0 minipass: 4.2.8 '@types/tough-cookie@4.0.5': {} @@ -5834,16 +5834,16 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react-swc@3.7.0(vite@5.4.0(@types/node@22.2.0))': + '@vitejs/plugin-react-swc@3.7.0(vite@5.4.1(@types/node@22.3.0))': dependencies: '@swc/core': 1.5.28 - vite: 5.4.0(@types/node@22.2.0) + vite: 5.4.1(@types/node@22.3.0) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-vue@5.0.5(vite@5.4.0(@types/node@22.2.0))(vue@3.4.31(typescript@5.5.4))': + '@vitejs/plugin-vue@5.0.5(vite@5.4.1(@types/node@22.3.0))(vue@3.4.31(typescript@5.5.4))': dependencies: - vite: 5.4.0(@types/node@22.2.0) + vite: 5.4.1(@types/node@22.3.0) vue: 3.4.31(typescript@5.5.4) '@vue/compiler-core@3.4.31': @@ -6467,7 +6467,7 @@ snapshots: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.17 - '@types/node': 22.2.0 + '@types/node': 22.3.0 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.4.2 @@ -7336,7 +7336,7 @@ snapshots: dependencies: '@babel/runtime': 7.24.7 - i18next@23.12.3: + i18next@23.13.0: dependencies: '@babel/runtime': 7.24.8 @@ -7732,7 +7732,7 @@ snapshots: lru-cache@7.18.3: {} - lucide-react@0.427.0(react@18.3.1): + lucide-react@0.428.0(react@18.3.1): dependencies: react: 18.3.1 @@ -8179,11 +8179,11 @@ snapshots: dependencies: react: 18.3.1 - react-i18next@15.0.1(i18next@23.12.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-i18next@15.0.1(i18next@23.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.24.8 html-parse-stringify: 3.0.1 - i18next: 23.12.3 + i18next: 23.13.0 react: 18.3.1 optionalDependencies: react-dom: 18.3.1(react@18.3.1) @@ -8207,16 +8207,16 @@ snapshots: optionalDependencies: '@types/react': 18.3.3 - react-router-dom@6.26.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-router-dom@6.26.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@remix-run/router': 1.19.0 + '@remix-run/router': 1.19.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-router: 6.26.0(react@18.3.1) + react-router: 6.26.1(react@18.3.1) - react-router@6.26.0(react@18.3.1): + react-router@6.26.1(react@18.3.1): dependencies: - '@remix-run/router': 1.19.0 + '@remix-run/router': 1.19.1 react: 18.3.1 react-style-singleton@2.2.1(@types/react@18.3.3)(react@18.3.1): @@ -8779,7 +8779,7 @@ snapshots: underscore@1.13.7: {} - undici-types@6.13.0: {} + undici-types@6.18.2: {} unified@11.0.5: dependencies: @@ -8856,7 +8856,7 @@ snapshots: optionalDependencies: '@types/react': 18.3.3 - use-sync-external-store@1.2.0(react@18.3.1): + use-sync-external-store@1.2.2(react@18.3.1): dependencies: react: 18.3.1 @@ -8880,42 +8880,42 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-plugin-static-copy@1.0.6(vite@5.4.0(@types/node@22.2.0)): + vite-plugin-static-copy@1.0.6(vite@5.4.1(@types/node@22.3.0)): dependencies: chokidar: 3.6.0 fast-glob: 3.3.2 fs-extra: 11.2.0 picocolors: 1.0.1 - vite: 5.4.0(@types/node@22.2.0) + vite: 5.4.1(@types/node@22.3.0) - vite-plugin-svgr@4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.0(@types/node@22.2.0)): + vite-plugin-svgr@4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)): dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.18.0) '@svgr/core': 8.1.0(typescript@5.5.4) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.5.4)) - vite: 5.4.0(@types/node@22.2.0) + vite: 5.4.1(@types/node@22.3.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite@5.4.0(@types/node@22.2.0): + vite@5.4.1(@types/node@22.3.0): dependencies: esbuild: 0.21.5 postcss: 8.4.41 rollup: 4.18.0 optionalDependencies: - '@types/node': 22.2.0 + '@types/node': 22.3.0 fsevents: 2.3.3 - vitepress@1.3.2(@algolia/client-search@4.23.3)(@types/node@22.2.0)(@types/react@18.3.3)(axios@1.7.3)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4): + vitepress@1.3.2(@algolia/client-search@4.23.3)(@types/node@22.3.0)(@types/react@18.3.3)(axios@1.7.3)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4): dependencies: '@docsearch/css': 3.6.0 '@docsearch/js': 3.6.0(@algolia/client-search@4.23.3)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@shikijs/core': 1.10.3 '@shikijs/transformers': 1.10.3 '@types/markdown-it': 14.1.1 - '@vitejs/plugin-vue': 5.0.5(vite@5.4.0(@types/node@22.2.0))(vue@3.4.31(typescript@5.5.4)) + '@vitejs/plugin-vue': 5.0.5(vite@5.4.1(@types/node@22.3.0))(vue@3.4.31(typescript@5.5.4)) '@vue/devtools-api': 7.3.5 '@vue/shared': 3.4.31 '@vueuse/core': 10.11.0(vue@3.4.31(typescript@5.5.4)) @@ -8924,7 +8924,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.0.0 shiki: 1.10.3 - vite: 5.4.0(@types/node@22.2.0) + vite: 5.4.1(@types/node@22.3.0) vue: 3.4.31(typescript@5.5.4) optionalDependencies: postcss: 8.4.41 @@ -9066,9 +9066,9 @@ snapshots: yocto-queue@0.1.0: {} - zustand@4.5.4(@types/react@18.3.3)(react@18.3.1): + zustand@4.5.5(@types/react@18.3.3)(react@18.3.1): dependencies: - use-sync-external-store: 1.2.0(react@18.3.1) + use-sync-external-store: 1.2.2(react@18.3.1) optionalDependencies: '@types/react': 18.3.3 react: 18.3.1 diff --git a/src/package.json b/src/package.json index 6d7031ef4af..05449c555b3 100644 --- a/src/package.json +++ b/src/package.json @@ -90,8 +90,8 @@ "@types/jsdom": "^21.1.7", "@types/jsonwebtoken": "^9.0.6", "@types/mocha": "^10.0.7", - "@types/node": "^22.2.0", - "@types/oidc-provider": "^8.5.1", + "@types/node": "^22.3.0", + "@types/oidc-provider": "^8.5.2", "@types/semver": "^7.5.8", "@types/sinon": "^17.0.3", "@types/supertest": "^6.0.2", diff --git a/ui/package.json b/ui/package.json index 2112cdde4e7..03499bbae55 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,6 +12,6 @@ "devDependencies": { "ep_etherpad-lite": "workspace:../src", "typescript": "^5.5.4", - "vite": "^5.4.0" + "vite": "^5.4.1" } } From 04ba1f53f01d760617ca966bd8e28006c6174dd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:31:32 +0200 Subject: [PATCH 10/49] Bump superagent from 10.0.1 to 10.0.2 (#6589) Bumps [superagent](https://github.com/ladjs/superagent) from 10.0.1 to 10.0.2. - [Release notes](https://github.com/ladjs/superagent/releases) - [Changelog](https://github.com/ladjs/superagent/blob/master/HISTORY.md) - [Commits](https://github.com/ladjs/superagent/compare/v10.0.1...v10.0.2) --- updated-dependencies: - dependency-name: superagent dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pnpm-lock.yaml | 10 +++++----- src/package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8be0c2ab3a0..69261199c05 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -252,8 +252,8 @@ importers: specifier: ^4.7.5 version: 4.7.5 superagent: - specifier: 10.0.1 - version: 10.0.1 + specifier: 10.0.2 + version: 10.0.2 threads: specifier: ^1.7.0 version: 1.7.0 @@ -4037,8 +4037,8 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - superagent@10.0.1: - resolution: {integrity: sha512-kG7dZ4Z6s6VbCVxd0PJpkYND0X+SW+iIAuboIQyHE7eFSNVprFVTpG1uID3UsVS7Jw47tdPvSiCSGzgXDhFcGQ==} + superagent@10.0.2: + resolution: {integrity: sha512-rvcN5ciYmVRaZLyM/9thpmNSDGscx1FXNwT5LJjsl+2nhdE4ppd53L2c9iSQSM6FA1poBVwlw+0h2Ofxwr3DoQ==} engines: {node: '>=14.18.0'} superagent@8.1.2: @@ -8565,7 +8565,7 @@ snapshots: strip-json-comments@3.1.1: {} - superagent@10.0.1: + superagent@10.0.2: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 diff --git a/src/package.json b/src/package.json index 05449c555b3..e1849a9dd8b 100644 --- a/src/package.json +++ b/src/package.json @@ -67,7 +67,7 @@ "semver": "^7.6.3", "socket.io": "^4.7.5", "socket.io-client": "^4.7.5", - "superagent": "10.0.1", + "superagent": "10.0.2", "threads": "^1.7.0", "tinycon": "0.6.8", "tsx": "4.17.0", From 74a12e04c1d5a426ec367baa925c7c4b8a94a105 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:31:39 +0200 Subject: [PATCH 11/49] Bump ueberdb2 from 4.2.92 to 4.2.93 (#6585) Bumps [ueberdb2](https://github.com/ether/ueberDB) from 4.2.92 to 4.2.93. - [Changelog](https://github.com/ether/ueberDB/blob/main/CHANGELOG.md) - [Commits](https://github.com/ether/ueberDB/compare/v4.2.92...v4.2.93) --- updated-dependencies: - dependency-name: ueberdb2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- bin/package.json | 2 +- pnpm-lock.yaml | 14 +++++++------- src/package.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bin/package.json b/bin/package.json index 49b418d3228..4237b4ad1e9 100644 --- a/bin/package.json +++ b/bin/package.json @@ -12,7 +12,7 @@ "log4js": "^6.9.1", "semver": "^7.6.3", "tsx": "^4.17.0", - "ueberdb2": "^4.2.92" + "ueberdb2": "^4.2.93" }, "devDependencies": { "@types/node": "^22.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69261199c05..4fca9586043 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,8 +119,8 @@ importers: specifier: ^4.17.0 version: 4.17.0 ueberdb2: - specifier: ^4.2.92 - version: 4.2.92 + specifier: ^4.2.93 + version: 4.2.93 devDependencies: '@types/node': specifier: ^22.3.0 @@ -264,8 +264,8 @@ importers: specifier: 4.17.0 version: 4.17.0 ueberdb2: - specifier: ^4.2.92 - version: 4.2.92 + specifier: ^4.2.93 + version: 4.2.93 underscore: specifier: 1.13.7 version: 1.13.7 @@ -4190,8 +4190,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - ueberdb2@4.2.92: - resolution: {integrity: sha512-CD6X6B4NcDlF9VBaZ69Y57aFngw2qQoARK+fBUPCfMgH52TutR/Jh+xuRdZ6KHZE9E7sCXatGtUevjO40IsRMQ==} + ueberdb2@4.2.93: + resolution: {integrity: sha512-inWXazVuEJc33aRBm6kCT8/ymgSvHcXbakKf4nxVf8Wp86Le0cQd3URn3cCUhxRV05qP14kDdvAQho9LUiXWwg==} engines: {node: '>=16.20.1'} uid-safe@2.1.5: @@ -8764,7 +8764,7 @@ snapshots: typescript@5.5.4: {} - ueberdb2@4.2.92: {} + ueberdb2@4.2.93: {} uid-safe@2.1.5: dependencies: diff --git a/src/package.json b/src/package.json index e1849a9dd8b..e253d57759f 100644 --- a/src/package.json +++ b/src/package.json @@ -71,7 +71,7 @@ "threads": "^1.7.0", "tinycon": "0.6.8", "tsx": "4.17.0", - "ueberdb2": "^4.2.92", + "ueberdb2": "^4.2.93", "underscore": "1.13.7", "unorm": "1.6.0", "wtfnode": "^0.9.3" From babfaab4dfd4729ce11d15f61107194b0a6c9d46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:34:25 +0200 Subject: [PATCH 12/49] Bump axios from 1.7.3 to 1.7.4 (#6583) Bumps [axios](https://github.com/axios/axios) from 1.7.3 to 1.7.4. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.7.3...v1.7.4) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- bin/package.json | 2 +- pnpm-lock.yaml | 24 ++++++++++++------------ src/package.json | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bin/package.json b/bin/package.json index 4237b4ad1e9..05687af11ff 100644 --- a/bin/package.json +++ b/bin/package.json @@ -7,7 +7,7 @@ "doc": "doc" }, "dependencies": { - "axios": "^1.7.3", + "axios": "^1.7.4", "ep_etherpad-lite": "workspace:../src", "log4js": "^6.9.1", "semver": "^7.6.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4fca9586043..0421e26fc8d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -104,8 +104,8 @@ importers: bin: dependencies: axios: - specifier: ^1.7.3 - version: 1.7.3 + specifier: ^1.7.4 + version: 1.7.4 ep_etherpad-lite: specifier: workspace:../src version: link:../src @@ -136,7 +136,7 @@ importers: devDependencies: vitepress: specifier: ^1.3.2 - version: 1.3.2(@algolia/client-search@4.23.3)(@types/node@22.3.0)(@types/react@18.3.3)(axios@1.7.3)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4) + version: 1.3.2(@algolia/client-search@4.23.3)(@types/node@22.3.0)(@types/react@18.3.3)(axios@1.7.4)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4) src: dependencies: @@ -147,8 +147,8 @@ importers: specifier: ^3.2.5 version: 3.2.5 axios: - specifier: ^1.7.3 - version: 1.7.3 + specifier: ^1.7.4 + version: 1.7.4 cookie-parser: specifier: ^1.4.6 version: 1.4.6 @@ -1940,8 +1940,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axios@1.7.3: - resolution: {integrity: sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==} + axios@1.7.4: + resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -5928,13 +5928,13 @@ snapshots: - '@vue/composition-api' - vue - '@vueuse/integrations@10.11.0(axios@1.7.3)(focus-trap@7.5.4)(vue@3.4.31(typescript@5.5.4))': + '@vueuse/integrations@10.11.0(axios@1.7.4)(focus-trap@7.5.4)(vue@3.4.31(typescript@5.5.4))': dependencies: '@vueuse/core': 10.11.0(vue@3.4.31(typescript@5.5.4)) '@vueuse/shared': 10.11.0(vue@3.4.31(typescript@5.5.4)) vue-demi: 0.14.8(vue@3.4.31(typescript@5.5.4)) optionalDependencies: - axios: 1.7.3 + axios: 1.7.4 focus-trap: 7.5.4 transitivePeerDependencies: - '@vue/composition-api' @@ -6091,7 +6091,7 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 - axios@1.7.3: + axios@1.7.4: dependencies: follow-redirects: 1.15.6 form-data: 4.0.0 @@ -8908,7 +8908,7 @@ snapshots: '@types/node': 22.3.0 fsevents: 2.3.3 - vitepress@1.3.2(@algolia/client-search@4.23.3)(@types/node@22.3.0)(@types/react@18.3.3)(axios@1.7.3)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4): + vitepress@1.3.2(@algolia/client-search@4.23.3)(@types/node@22.3.0)(@types/react@18.3.3)(axios@1.7.4)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4): dependencies: '@docsearch/css': 3.6.0 '@docsearch/js': 3.6.0(@algolia/client-search@4.23.3)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -8919,7 +8919,7 @@ snapshots: '@vue/devtools-api': 7.3.5 '@vue/shared': 3.4.31 '@vueuse/core': 10.11.0(vue@3.4.31(typescript@5.5.4)) - '@vueuse/integrations': 10.11.0(axios@1.7.3)(focus-trap@7.5.4)(vue@3.4.31(typescript@5.5.4)) + '@vueuse/integrations': 10.11.0(axios@1.7.4)(focus-trap@7.5.4)(vue@3.4.31(typescript@5.5.4)) focus-trap: 7.5.4 mark.js: 8.11.1 minisearch: 7.0.0 diff --git a/src/package.json b/src/package.json index e253d57759f..02426efed40 100644 --- a/src/package.json +++ b/src/package.json @@ -32,7 +32,7 @@ "dependencies": { "@etherpad/express-session": "^1.18.2", "async": "^3.2.5", - "axios": "^1.7.3", + "axios": "^1.7.4", "cookie-parser": "^1.4.6", "cross-env": "^7.0.3", "cross-spawn": "^7.0.3", From c7a2dea4d1fbe8f18cdf0123db97fdb56dc1a549 Mon Sep 17 00:00:00 2001 From: SamTV12345 <40429738+SamTV12345@users.noreply.github.com> Date: Fri, 16 Aug 2024 22:55:42 +0200 Subject: [PATCH 13/49] Feat/frontend vitest (#6469) * Added vitest tests. * Added Settings tests to vitest - not working * Added attributes and attributemap to vitest. * Added more tests. * Also run the vitest tests. * Also run withoutPlugins * Fixed pnpm lock --- .github/workflows/backend-tests.yml | 18 +- pnpm-lock.yaml | 468 ++++++++++++++---- src/node/hooks/express/static.ts | 12 +- src/node/types/PadType.ts | 1 + src/node/utils/{Minify.js => Minify.ts} | 140 +++--- .../{MinifyWorker.js => MinifyWorker.ts} | 10 +- src/node/utils/caching_middleware.ts | 211 -------- src/node/utils/sanitizePathname.ts | 8 +- src/package.json | 8 +- .../specs/AttributeMap.ts} | 10 +- .../specs/attributes.ts} | 38 +- .../backend-new/specs/contentcollector.ts | 398 +++++++++++++++ .../specs/pad_utils.ts | 11 +- .../specs/sanitizePathname.ts | 7 +- src/tests/backend-new/specs/skiplist.ts | 55 ++ src/tests/backend/specs/lowerCasePadIds.ts | 6 +- src/tests/backend/specs/settings.ts | 140 +++--- ...sembler.js => easysync-assembler.spec.mjs} | 27 +- ...ysync-other.js => easysync-other.test.mjs} | 5 +- src/tests/frontend/specs/skiplist.js | 54 -- src/vitest.config.ts | 7 + 21 files changed, 1087 insertions(+), 547 deletions(-) rename src/node/utils/{Minify.js => Minify.ts} (69%) rename src/node/utils/{MinifyWorker.js => MinifyWorker.ts} (83%) delete mode 100644 src/node/utils/caching_middleware.ts rename src/tests/{frontend/specs/AttributeMap.js => backend-new/specs/AttributeMap.ts} (93%) rename src/tests/{frontend/specs/attributes.js => backend-new/specs/attributes.ts} (91%) create mode 100644 src/tests/backend-new/specs/contentcollector.ts rename src/tests/{backend => backend-new}/specs/pad_utils.ts (86%) rename src/tests/{backend => backend-new}/specs/sanitizePathname.ts (92%) create mode 100644 src/tests/backend-new/specs/skiplist.ts rename src/tests/frontend/specs/{easysync-assembler.js => easysync-assembler.spec.mjs} (92%) rename src/tests/frontend/specs/{easysync-other.js => easysync-other.test.mjs} (97%) delete mode 100644 src/tests/frontend/specs/skiplist.js create mode 100644 src/vitest.config.ts diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index fb6c366c91c..485cb5eedec 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -69,6 +69,9 @@ jobs: - name: Run the backend tests run: pnpm test + - name: Run the new vitest tests + working-directory: src + run: pnpm run test:vitest withpluginsLinux: # run on pushes to any branch @@ -142,6 +145,9 @@ jobs: - name: Run the backend tests run: pnpm test + - name: Run the new vitest tests + working-directory: src + run: pnpm run test:vitest withoutpluginsWindows: # run on pushes to any branch @@ -193,7 +199,11 @@ jobs: powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json" - name: Run the backend tests - run: cd src && pnpm test + working-directory: src + run: pnpm test + - name: Run the new vitest tests + working-directory: src + run: pnpm run test:vitest withpluginsWindows: # run on pushes to any branch @@ -273,4 +283,8 @@ jobs: powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json" - name: Run the backend tests - run: cd src && pnpm test + working-directory: src + run: pnpm test + - name: Run the new vitest tests + working-directory: src + run: pnpm run test:vitest diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0421e26fc8d..63d8a4794be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,7 +48,7 @@ importers: version: 8.1.0(eslint@9.9.0)(typescript@5.5.4) '@vitejs/plugin-react-swc': specifier: ^3.5.0 - version: 3.7.0(vite@5.4.1(@types/node@22.3.0)) + version: 3.7.0(vite@5.4.1(@types/node@22.4.0)) eslint: specifier: ^9.9.0 version: 9.9.0 @@ -90,13 +90,13 @@ importers: version: 5.5.4 vite: specifier: ^5.4.1 - version: 5.4.1(@types/node@22.3.0) + version: 5.4.1(@types/node@22.4.0) vite-plugin-static-copy: specifier: ^1.0.6 - version: 1.0.6(vite@5.4.1(@types/node@22.3.0)) + version: 1.0.6(vite@5.4.1(@types/node@22.4.0)) vite-plugin-svgr: specifier: ^4.2.0 - version: 4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)) + version: 4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.1(@types/node@22.4.0)) zustand: specifier: ^4.5.5 version: 4.5.5(@types/react@18.3.3)(react@18.3.1) @@ -124,7 +124,7 @@ importers: devDependencies: '@types/node': specifier: ^22.3.0 - version: 22.3.0 + version: 22.4.0 '@types/semver': specifier: ^7.5.8 version: 7.5.8 @@ -136,7 +136,7 @@ importers: devDependencies: vitepress: specifier: ^1.3.2 - version: 1.3.2(@algolia/client-search@4.23.3)(@types/node@22.3.0)(@types/react@18.3.3)(axios@1.7.4)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4) + version: 1.3.2(@algolia/client-search@4.23.3)(@types/node@22.4.0)(@types/react@18.3.3)(axios@1.7.4)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4) src: dependencies: @@ -254,9 +254,6 @@ importers: superagent: specifier: 10.0.2 version: 10.0.2 - threads: - specifier: ^1.7.0 - version: 1.7.0 tinycon: specifier: 0.6.8 version: 0.6.8 @@ -272,6 +269,9 @@ importers: unorm: specifier: 1.6.0 version: 1.6.0 + vitest: + specifier: ^2.0.5 + version: 2.0.5(@types/node@22.4.0)(jsdom@24.1.1) wtfnode: specifier: ^0.9.3 version: 0.9.3 @@ -300,12 +300,15 @@ importers: '@types/jsonwebtoken': specifier: ^9.0.6 version: 9.0.6 + '@types/mime-types': + specifier: ^2.1.4 + version: 2.1.4 '@types/mocha': specifier: ^10.0.7 version: 10.0.7 '@types/node': specifier: ^22.3.0 - version: 22.3.0 + version: 22.4.0 '@types/oidc-provider': specifier: ^8.5.2 version: 8.5.2 @@ -371,7 +374,7 @@ importers: version: 5.5.4 vite: specifier: ^5.4.1 - version: 5.4.1(@types/node@22.3.0) + version: 5.4.1(@types/node@22.4.0) packages: @@ -1537,6 +1540,9 @@ packages: '@types/methods@1.1.4': resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + '@types/mime-types@2.1.4': + resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==} + '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -1549,8 +1555,8 @@ packages: '@types/node-fetch@2.6.11': resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} - '@types/node@22.3.0': - resolution: {integrity: sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g==} + '@types/node@22.4.0': + resolution: {integrity: sha512-49AbMDwYUz7EXxKU/r7mXOsxwFr4BYbvB7tWYxVuLdb2ibd30ijjXINSMAHiEEZk5PCRBmW1gUeisn2VMKt3cQ==} '@types/oidc-provider@8.5.2': resolution: {integrity: sha512-NiD3VG49+cRCAAe8+uZLM4onOcX8y9+cwaml8JG1qlgc98rWoCRgsnOB4Ypx+ysays5jiwzfUgT0nWyXPB/9uQ==} @@ -1742,6 +1748,24 @@ packages: vite: ^5.0.0 vue: ^3.2.25 + '@vitest/expect@2.0.5': + resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} + + '@vitest/pretty-format@2.0.5': + resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==} + + '@vitest/runner@2.0.5': + resolution: {integrity: sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==} + + '@vitest/snapshot@2.0.5': + resolution: {integrity: sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==} + + '@vitest/spy@2.0.5': + resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} + + '@vitest/utils@2.0.5': + resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} + '@vue/compiler-core@3.4.31': resolution: {integrity: sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==} @@ -1926,6 +1950,10 @@ packages: asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + ast-types@0.13.4: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} @@ -2006,6 +2034,10 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + cache-content-type@1.0.1: resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==} engines: {node: '>= 6.0.0'} @@ -2036,6 +2068,10 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@5.1.1: + resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} + engines: {node: '>=12'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -2050,6 +2086,10 @@ packages: character-entities-legacy@3.0.0: resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -2225,6 +2265,10 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-equal@1.0.1: resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} @@ -2535,10 +2579,6 @@ packages: jiti: optional: true - esm@3.2.25: - resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} - engines: {node: '>=6'} - espree@10.1.0: resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2563,6 +2603,9 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -2580,6 +2623,10 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + express-rate-limit@7.4.0: resolution: {integrity: sha512-v1204w3cXu5gCDmAvgvzI6qjzZzoMWKnyVDk3ACgfswTQLYiGen+r8w0VnXnGMmzEN/g8fwIQ4JrFFd4ZP6ssg==} engines: {node: '>= 16'} @@ -2744,6 +2791,9 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} @@ -2756,6 +2806,10 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + get-symbol-description@1.0.2: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} @@ -2921,6 +2975,10 @@ packages: resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + i18next-browser-languagedetector@8.0.0: resolution: {integrity: sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==} @@ -3035,10 +3093,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-observable@2.1.0: - resolution: {integrity: sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw==} - engines: {node: '>=8'} - is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} @@ -3065,6 +3119,10 @@ packages: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} engines: {node: '>= 0.4'} + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -3274,6 +3332,9 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + loupe@3.1.1: + resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} + lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -3317,6 +3378,9 @@ packages: merge-descriptors@1.0.1: resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -3362,6 +3426,10 @@ packages: engines: {node: '>=4.0.0'} hasBin: true + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -3480,6 +3548,10 @@ packages: resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} engines: {node: '>=14.16'} + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + nwsapi@2.2.12: resolution: {integrity: sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==} @@ -3515,9 +3587,6 @@ packages: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} - observable-fns@0.6.1: - resolution: {integrity: sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==} - oidc-provider@8.5.1: resolution: {integrity: sha512-Bm3EyxN68/KS76IlciJ3+4pnVtfdRWL+NghWpIF0XQbiRT1gzc6Qf/cyFmpL9yieko/jXYZ/uLHUv77jD00qww==} @@ -3536,6 +3605,10 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + only@0.0.2: resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==} @@ -3602,6 +3675,10 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -3615,6 +3692,13 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} @@ -3934,9 +4018,16 @@ packages: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + sinon@18.0.0: resolution: {integrity: sha512-+dXDXzD1sBO6HlmZDd7mXZCR/y5ECiEiGCBSGuFD/kZ0bDTofPYc6JaeGmPSF+1j1MejGUWkORbYOLDyvqCWpA==} @@ -3995,6 +4086,9 @@ packages: sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@1.5.0: resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} @@ -4003,6 +4097,9 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + streamroller@3.1.5: resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} engines: {node: '>=8.0'} @@ -4033,6 +4130,10 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -4097,15 +4198,24 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - threads@1.7.0: - resolution: {integrity: sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==} - - tiny-worker@2.3.0: - resolution: {integrity: sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} tinycon@0.6.8: resolution: {integrity: sha512-bF8Lxm4JUXF6Cw0XlZdugJ44GV575OinZ0Pt8vQPr8ooNqd2yyNkoFdCHzmdpHlgoqfSLfcyk4HDP1EyllT+ug==} + tinypool@1.0.0: + resolution: {integrity: sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.0: + resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==} + engines: {node: '>=14.0.0'} + to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -4204,8 +4314,8 @@ packages: underscore@1.13.7: resolution: {integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==} - undici-types@6.18.2: - resolution: {integrity: sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ==} + undici-types@6.19.6: + resolution: {integrity: sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==} unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -4302,6 +4412,11 @@ packages: vfile@6.0.2: resolution: {integrity: sha512-zND7NlS8rJYb/sPqkb13ZvbbUoExdbi4w3SfRrMq6R3FvnLQmmfpajJNITuuYm6AZ5uao9vy4BAos3EXBPf2rg==} + vite-node@2.0.5: + resolution: {integrity: sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + vite-plugin-static-copy@1.0.6: resolution: {integrity: sha512-3uSvsMwDVFZRitqoWHj0t4137Kz7UynnJeq1EZlRW7e25h2068fyIZX4ORCCOAkfp1FklGxJNVJBkBOD+PZIew==} engines: {node: ^18.0.0 || >=20.0.0} @@ -4356,6 +4471,31 @@ packages: postcss: optional: true + vitest@2.0.5: + resolution: {integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.0.5 + '@vitest/ui': 2.0.5 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + void-elements@3.1.0: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} @@ -4418,6 +4558,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -5468,18 +5613,18 @@ snapshots: '@types/accepts@1.3.7': dependencies: - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/async@3.2.24': {} '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/connect@3.4.38': dependencies: - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/content-disposition@0.5.8': {} @@ -5492,11 +5637,11 @@ snapshots: '@types/connect': 3.4.38 '@types/express': 4.17.21 '@types/keygrip': 1.0.6 - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/cors@2.8.17': dependencies: - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/debug@4.1.12': dependencies: @@ -5506,7 +5651,7 @@ snapshots: '@types/express-serve-static-core@4.19.5': dependencies: - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -5520,11 +5665,11 @@ snapshots: '@types/formidable@3.4.5': dependencies: - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/fs-extra@9.0.13': dependencies: - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/hast@3.0.4': dependencies: @@ -5542,7 +5687,7 @@ snapshots: '@types/jsdom@21.1.7': dependencies: - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 @@ -5552,7 +5697,7 @@ snapshots: '@types/jsonwebtoken@9.0.6': dependencies: - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/keygrip@1.0.6': {} @@ -5569,7 +5714,7 @@ snapshots: '@types/http-errors': 2.0.4 '@types/keygrip': 1.0.6 '@types/koa-compose': 3.2.8 - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/linkify-it@5.0.0': {} @@ -5588,6 +5733,8 @@ snapshots: '@types/methods@1.1.4': {} + '@types/mime-types@2.1.4': {} + '@types/mime@1.3.5': {} '@types/mocha@10.0.7': {} @@ -5596,17 +5743,17 @@ snapshots: '@types/node-fetch@2.6.11': dependencies: - '@types/node': 22.3.0 + '@types/node': 22.4.0 form-data: 4.0.0 - '@types/node@22.3.0': + '@types/node@22.4.0': dependencies: - undici-types: 6.18.2 + undici-types: 6.19.6 '@types/oidc-provider@8.5.2': dependencies: '@types/koa': 2.15.0 - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/prop-types@15.7.12': {} @@ -5628,12 +5775,12 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/send': 0.17.4 '@types/sinon@17.0.3': @@ -5648,7 +5795,7 @@ snapshots: dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 22.3.0 + '@types/node': 22.4.0 '@types/supertest@6.0.2': dependencies: @@ -5657,7 +5804,7 @@ snapshots: '@types/tar@6.1.13': dependencies: - '@types/node': 22.3.0 + '@types/node': 22.4.0 minipass: 4.2.8 '@types/tough-cookie@4.0.5': {} @@ -5834,18 +5981,51 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react-swc@3.7.0(vite@5.4.1(@types/node@22.3.0))': + '@vitejs/plugin-react-swc@3.7.0(vite@5.4.1(@types/node@22.4.0))': dependencies: '@swc/core': 1.5.28 - vite: 5.4.1(@types/node@22.3.0) + vite: 5.4.1(@types/node@22.4.0) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-vue@5.0.5(vite@5.4.1(@types/node@22.3.0))(vue@3.4.31(typescript@5.5.4))': + '@vitejs/plugin-vue@5.0.5(vite@5.4.1(@types/node@22.4.0))(vue@3.4.31(typescript@5.5.4))': dependencies: - vite: 5.4.1(@types/node@22.3.0) + vite: 5.4.1(@types/node@22.4.0) vue: 3.4.31(typescript@5.5.4) + '@vitest/expect@2.0.5': + dependencies: + '@vitest/spy': 2.0.5 + '@vitest/utils': 2.0.5 + chai: 5.1.1 + tinyrainbow: 1.2.0 + + '@vitest/pretty-format@2.0.5': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.0.5': + dependencies: + '@vitest/utils': 2.0.5 + pathe: 1.1.2 + + '@vitest/snapshot@2.0.5': + dependencies: + '@vitest/pretty-format': 2.0.5 + magic-string: 0.30.10 + pathe: 1.1.2 + + '@vitest/spy@2.0.5': + dependencies: + tinyspy: 3.0.0 + + '@vitest/utils@2.0.5': + dependencies: + '@vitest/pretty-format': 2.0.5 + estree-walker: 3.0.3 + loupe: 3.1.1 + tinyrainbow: 1.2.0 + '@vue/compiler-core@3.4.31': dependencies: '@babel/parser': 7.24.7 @@ -6079,6 +6259,8 @@ snapshots: asap@2.0.6: {} + assertion-error@2.0.1: {} + ast-types@0.13.4: dependencies: tslib: 2.6.3 @@ -6164,6 +6346,8 @@ snapshots: bytes@3.1.2: {} + cac@6.7.14: {} + cache-content-type@1.0.1: dependencies: mime-types: 2.1.35 @@ -6197,6 +6381,14 @@ snapshots: ccount@2.0.1: {} + chai@5.1.1: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.1 + pathval: 2.0.0 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -6212,6 +6404,8 @@ snapshots: character-entities-legacy@3.0.0: {} + check-error@2.1.1: {} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -6369,6 +6563,8 @@ snapshots: dependencies: mimic-response: 3.1.0 + deep-eql@5.0.2: {} + deep-equal@1.0.1: {} deep-is@0.1.4: {} @@ -6467,7 +6663,7 @@ snapshots: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.17 - '@types/node': 22.3.0 + '@types/node': 22.4.0 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.4.2 @@ -6839,9 +7035,6 @@ snapshots: transitivePeerDependencies: - supports-color - esm@3.2.25: - optional: true - espree@10.1.0: dependencies: acorn: 8.12.0 @@ -6862,6 +7055,10 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.5 + esutils@2.0.3: {} eta@3.4.0: {} @@ -6878,6 +7075,18 @@ snapshots: - supports-color - utf-8-validate + execa@8.0.1: + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + express-rate-limit@7.4.0(express@4.19.2): dependencies: express: 4.19.2 @@ -7075,6 +7284,8 @@ snapshots: get-caller-file@2.0.5: {} + get-func-name@2.0.2: {} + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 @@ -7087,6 +7298,8 @@ snapshots: get-stream@6.0.1: {} + get-stream@8.0.1: {} + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 @@ -7332,6 +7545,8 @@ snapshots: transitivePeerDependencies: - supports-color + human-signals@5.0.0: {} + i18next-browser-languagedetector@8.0.0: dependencies: '@babel/runtime': 7.24.7 @@ -7439,8 +7654,6 @@ snapshots: is-number@7.0.0: {} - is-observable@2.1.0: {} - is-path-inside@3.0.3: {} is-plain-obj@2.1.0: {} @@ -7460,6 +7673,8 @@ snapshots: dependencies: call-bind: 1.0.7 + is-stream@3.0.0: {} + is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 @@ -7718,6 +7933,10 @@ snapshots: dependencies: js-tokens: 4.0.0 + loupe@3.1.1: + dependencies: + get-func-name: 2.0.2 + lower-case@2.0.2: dependencies: tslib: 2.6.3 @@ -7763,6 +7982,8 @@ snapshots: merge-descriptors@1.0.1: {} + merge-stream@2.0.0: {} + merge2@1.4.1: {} methods@1.1.2: {} @@ -7799,6 +8020,8 @@ snapshots: mime@2.6.0: {} + mimic-fn@4.0.0: {} + mimic-response@3.1.0: {} mimic-response@4.0.0: {} @@ -7912,6 +8135,10 @@ snapshots: normalize-url@8.0.1: {} + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + nwsapi@2.2.12: {} object-assign@4.1.1: {} @@ -7948,8 +8175,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 - observable-fns@0.6.1: {} - oidc-provider@8.5.1: dependencies: '@koa/cors': 5.0.0 @@ -7980,6 +8205,10 @@ snapshots: dependencies: wrappy: 1.0.2 + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + only@0.0.2: {} openapi-backend@5.10.6: @@ -8070,6 +8299,8 @@ snapshots: path-key@3.1.1: {} + path-key@4.0.0: {} + path-parse@1.0.7: {} path-to-regexp@0.1.7: {} @@ -8078,6 +8309,10 @@ snapshots: path-type@4.0.0: {} + pathe@1.1.2: {} + + pathval@2.0.0: {} + perfect-debounce@1.0.0: {} picocolors@1.0.1: {} @@ -8428,8 +8663,12 @@ snapshots: get-intrinsic: 1.2.4 object-inspect: 1.13.2 + siginfo@2.0.0: {} + signal-exit@3.0.7: {} + signal-exit@4.1.0: {} + sinon@18.0.0: dependencies: '@sinonjs/commons': 3.0.1 @@ -8515,10 +8754,14 @@ snapshots: sprintf-js@1.1.3: {} + stackback@0.0.2: {} + statuses@1.5.0: {} statuses@2.0.1: {} + std-env@3.7.0: {} + streamroller@3.1.5: dependencies: date-format: 4.0.14 @@ -8563,6 +8806,8 @@ snapshots: strip-bom@3.0.0: {} + strip-final-newline@3.0.0: {} + strip-json-comments@3.1.1: {} superagent@10.0.2: @@ -8654,24 +8899,16 @@ snapshots: text-table@0.2.0: {} - threads@1.7.0: - dependencies: - callsites: 3.1.0 - debug: 4.3.5(supports-color@8.1.1) - is-observable: 2.1.0 - observable-fns: 0.6.1 - optionalDependencies: - tiny-worker: 2.3.0 - transitivePeerDependencies: - - supports-color - - tiny-worker@2.3.0: - dependencies: - esm: 3.2.25 - optional: true + tinybench@2.9.0: {} tinycon@0.6.8: {} + tinypool@1.0.0: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.0: {} + to-fast-properties@2.0.0: {} to-regex-range@5.0.1: @@ -8779,7 +9016,7 @@ snapshots: underscore@1.13.7: {} - undici-types@6.18.2: {} + undici-types@6.19.6: {} unified@11.0.5: dependencies: @@ -8880,42 +9117,60 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-plugin-static-copy@1.0.6(vite@5.4.1(@types/node@22.3.0)): + vite-node@2.0.5(@types/node@22.4.0): + dependencies: + cac: 6.7.14 + debug: 4.3.5(supports-color@8.1.1) + pathe: 1.1.2 + tinyrainbow: 1.2.0 + vite: 5.4.1(@types/node@22.4.0) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite-plugin-static-copy@1.0.6(vite@5.4.1(@types/node@22.4.0)): dependencies: chokidar: 3.6.0 fast-glob: 3.3.2 fs-extra: 11.2.0 picocolors: 1.0.1 - vite: 5.4.1(@types/node@22.3.0) + vite: 5.4.1(@types/node@22.4.0) - vite-plugin-svgr@4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)): + vite-plugin-svgr@4.2.0(rollup@4.18.0)(typescript@5.5.4)(vite@5.4.1(@types/node@22.4.0)): dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.18.0) '@svgr/core': 8.1.0(typescript@5.5.4) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.5.4)) - vite: 5.4.1(@types/node@22.3.0) + vite: 5.4.1(@types/node@22.4.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite@5.4.1(@types/node@22.3.0): + vite@5.4.1(@types/node@22.4.0): dependencies: esbuild: 0.21.5 postcss: 8.4.41 rollup: 4.18.0 optionalDependencies: - '@types/node': 22.3.0 + '@types/node': 22.4.0 fsevents: 2.3.3 - vitepress@1.3.2(@algolia/client-search@4.23.3)(@types/node@22.3.0)(@types/react@18.3.3)(axios@1.7.4)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4): + vitepress@1.3.2(@algolia/client-search@4.23.3)(@types/node@22.4.0)(@types/react@18.3.3)(axios@1.7.4)(postcss@8.4.41)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4): dependencies: '@docsearch/css': 3.6.0 '@docsearch/js': 3.6.0(@algolia/client-search@4.23.3)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@shikijs/core': 1.10.3 '@shikijs/transformers': 1.10.3 '@types/markdown-it': 14.1.1 - '@vitejs/plugin-vue': 5.0.5(vite@5.4.1(@types/node@22.3.0))(vue@3.4.31(typescript@5.5.4)) + '@vitejs/plugin-vue': 5.0.5(vite@5.4.1(@types/node@22.4.0))(vue@3.4.31(typescript@5.5.4)) '@vue/devtools-api': 7.3.5 '@vue/shared': 3.4.31 '@vueuse/core': 10.11.0(vue@3.4.31(typescript@5.5.4)) @@ -8924,7 +9179,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.0.0 shiki: 1.10.3 - vite: 5.4.1(@types/node@22.3.0) + vite: 5.4.1(@types/node@22.4.0) vue: 3.4.31(typescript@5.5.4) optionalDependencies: postcss: 8.4.41 @@ -8956,6 +9211,40 @@ snapshots: - typescript - universal-cookie + vitest@2.0.5(@types/node@22.4.0)(jsdom@24.1.1): + dependencies: + '@ampproject/remapping': 2.3.0 + '@vitest/expect': 2.0.5 + '@vitest/pretty-format': 2.0.5 + '@vitest/runner': 2.0.5 + '@vitest/snapshot': 2.0.5 + '@vitest/spy': 2.0.5 + '@vitest/utils': 2.0.5 + chai: 5.1.1 + debug: 4.3.5(supports-color@8.1.1) + execa: 8.0.1 + magic-string: 0.30.10 + pathe: 1.1.2 + std-env: 3.7.0 + tinybench: 2.9.0 + tinypool: 1.0.0 + tinyrainbow: 1.2.0 + vite: 5.4.1(@types/node@22.4.0) + vite-node: 2.0.5(@types/node@22.4.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.4.0 + jsdom: 24.1.1 + transitivePeerDependencies: + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + void-elements@3.1.0: {} vue-demi@0.14.8(vue@3.4.31(typescript@5.5.4)): @@ -9013,6 +9302,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} workerpool@6.5.1: {} diff --git a/src/node/hooks/express/static.ts b/src/node/hooks/express/static.ts index 18ff8c76a16..07a7f3a21cf 100644 --- a/src/node/hooks/express/static.ts +++ b/src/node/hooks/express/static.ts @@ -4,11 +4,10 @@ import {MapArrayType} from "../../types/MapType"; import {PartType} from "../../types/PartType"; const fs = require('fs').promises; -const minify = require('../../utils/Minify'); -const path = require('path'); +import {minify} from '../../utils/Minify'; +import path from 'node:path'; const plugins = require('../../../static/js/pluginfw/plugin_defs'); const settings = require('../../utils/Settings'); -import CachingMiddleware from '../../utils/caching_middleware'; // Rewrite tar to include modules with no extensions and proper rooted paths. const getTar = async () => { @@ -32,15 +31,10 @@ const getTar = async () => { }; exports.expressPreSession = async (hookName:string, {app}:any) => { - // Cache both minified and static. - const assetCache = new CachingMiddleware(); - // Cache static assets - app.all(/\/js\/(.*)/, assetCache.handle.bind(assetCache)); - app.all(/\/css\/(.*)/, assetCache.handle.bind(assetCache)); // Minify will serve static files compressed (minify enabled). It also has // file-specific hacks for ace/require-kernel/etc. - app.all('/static/:filename(*)', minify.minify); + app.all('/static/:filename(*)', minify); // serve plugin definitions // not very static, but served here so that client can do diff --git a/src/node/types/PadType.ts b/src/node/types/PadType.ts index b344ed8c555..5b96ac5f615 100644 --- a/src/node/types/PadType.ts +++ b/src/node/types/PadType.ts @@ -35,6 +35,7 @@ export type APool = { clone: ()=>APool, check: ()=>Promise, eachAttrib: (callback: (key: string, value: any)=>void)=>void, + getAttrib: (key: number)=>any, } diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.ts similarity index 69% rename from src/node/utils/Minify.js rename to src/node/utils/Minify.ts index bc4bdce679e..9660c909866 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.ts @@ -21,20 +21,20 @@ * limitations under the License. */ +import {TransformResult} from "esbuild"; +import mime from 'mime-types'; +import log4js from 'log4js'; +import {compressCSS, compressJS} from './MinifyWorker' + const settings = require('./Settings'); -const fs = require('fs').promises; -const path = require('path'); +import {promises as fs} from 'fs'; +import path from 'node:path'; const plugins = require('../../static/js/pluginfw/plugin_defs'); -const mime = require('mime-types'); -const Threads = require('threads'); -const log4js = require('log4js'); -const sanitizePathname = require('./sanitizePathname'); - +import sanitizePathname from './sanitizePathname'; const logger = log4js.getLogger('Minify'); const ROOT_DIR = path.join(settings.root, 'src/static/'); -const threadsPool = new Threads.Pool(() => Threads.spawn(new Threads.Worker('./MinifyWorker')), 2); const LIBRARY_WHITELIST = [ 'async', @@ -48,10 +48,10 @@ const LIBRARY_WHITELIST = [ // What follows is a terrible hack to avoid loop-back within the server. // TODO: Serve files from another service, or directly from the file system. -const requestURI = async (url, method, headers) => { +const requestURI = async (url: string | URL, method: any, headers: { [x: string]: any; }) => { const parsedUrl = new URL(url); let status = 500; - const content = []; + const content: any[] = []; const mockRequest = { url, method, @@ -61,7 +61,7 @@ const requestURI = async (url, method, headers) => { let mockResponse; const p = new Promise((resolve) => { mockResponse = { - writeHead: (_status, _headers) => { + writeHead: (_status: number, _headers: { [x: string]: any; }) => { status = _status; for (const header in _headers) { if (Object.prototype.hasOwnProperty.call(_headers, header)) { @@ -69,37 +69,63 @@ const requestURI = async (url, method, headers) => { } } }, - setHeader: (header, value) => { + setHeader: (header: string, value: { toString: () => any; }) => { headers[header.toLowerCase()] = value.toString(); }, - header: (header, value) => { + header: (header: string, value: { toString: () => any; }) => { headers[header.toLowerCase()] = value.toString(); }, - write: (_content) => { + write: (_content: any) => { _content && content.push(_content); }, - end: (_content) => { + end: (_content: any) => { _content && content.push(_content); resolve([status, headers, content.join('')]); }, }; }); - await minify(mockRequest, mockResponse); + await _minify(mockRequest, mockResponse); return await p; }; -const requestURIs = (locations, method, headers, callback) => { +const _requestURIs = (locations: any[], method: any, headers: { + [x: string]: + /** + * This Module manages all /minified/* requests. It controls the + * minification && compression of Javascript and CSS. + */ + /* + * 2011 Peter 'Pita' Martischka (Primary Technology Ltd) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + any; +}, callback: (arg0: any[], arg1: any[], arg2: any[]) => void) => { Promise.all(locations.map(async (loc) => { try { return await requestURI(loc, method, headers); } catch (err) { logger.debug(`requestURI(${JSON.stringify(loc)}, ${JSON.stringify(method)}, ` + - `${JSON.stringify(headers)}) failed: ${err.stack || err}`); + // @ts-ignore + `${JSON.stringify(headers)}) failed: ${err.stack || err}`); return [500, headers, '']; } })).then((responses) => { + // @ts-ignore const statuss = responses.map((x) => x[0]); + // @ts-ignore const headerss = responses.map((x) => x[1]); + // @ts-ignore const contentss = responses.map((x) => x[2]); callback(statuss, headerss, contentss); }); @@ -119,11 +145,12 @@ const compatPaths = { * @param req the Express request * @param res the Express response */ -const minify = async (req, res) => { +const _minify = async (req:any, res:any) => { let filename = req.params.filename; try { filename = sanitizePathname(filename); } catch (err) { + // @ts-ignore logger.error(`sanitization of pathname "${filename}" failed: ${err.stack || err}`); res.writeHead(404, {}); res.end(); @@ -131,6 +158,7 @@ const minify = async (req, res) => { } // Backward compatibility for plugins that require() files from old paths. + // @ts-ignore const newLocation = compatPaths[filename.replace(/^plugins\/ep_etherpad-lite\/static\//, '')]; if (newLocation != null) { logger.warn(`request for deprecated path "${filename}", replacing with "${newLocation}"`); @@ -193,7 +221,7 @@ const minify = async (req, res) => { res.writeHead(200, {}); res.end(); } else if (req.method === 'GET') { - const content = await getFileCompressed(filename, contentType); + const content = await getFileCompressed(filename, contentType as string); res.header('Content-Type', contentType); res.writeHead(200, {}); res.write(content); @@ -205,7 +233,7 @@ const minify = async (req, res) => { }; // Check for the existance of the file and get the last modification date. -const statFile = async (filename, dirStatLimit) => { +const statFile = async (filename: string, dirStatLimit: number):Promise<(any | boolean)[]> => { /* * The only external call to this function provides an explicit value for * dirStatLimit: this check could be removed. @@ -221,6 +249,7 @@ const statFile = async (filename, dirStatLimit) => { try { stats = await fs.stat(path.resolve(ROOT_DIR, filename)); } catch (err) { + // @ts-ignore if (['ENOENT', 'ENOTDIR'].includes(err.code)) { // Stat the directory instead. const [date] = await statFile(path.dirname(filename), dirStatLimit - 1); @@ -234,69 +263,64 @@ const statFile = async (filename, dirStatLimit) => { let contentCache = new Map(); -const getFileCompressed = async (filename, contentType) => { +const getFileCompressed = async (filename: any, contentType: string) => { if (contentCache.has(filename)) { return contentCache.get(filename); } - let content = await getFile(filename); + let content: Buffer|string = await getFile(filename); if (!content || !settings.minify) { return content; } else if (contentType === 'application/javascript') { - return await new Promise((resolve) => { - threadsPool.queue(async ({compressJS}) => { - try { - logger.info('Compress JS file %s.', filename); - - content = content.toString(); - const compressResult = await compressJS(content); + return await new Promise(async (resolve) => { + try { + logger.info('Compress JS file %s.', filename); - if (compressResult.error) { - console.error(`Error compressing JS (${filename}) using terser`, compressResult.error); - } else { - content = compressResult.code.toString(); // Convert content obj code to string - } + content = content.toString(); + try { + let compressResult: TransformResult<{ minify: boolean }> + compressResult = await compressJS(content); + content = compressResult.code.toString(); // Convert content obj code to string } catch (error) { - console.error('getFile() returned an error in ' + - `getFileCompressed(${filename}, ${contentType}): ${error}`); + console.error(`Error compressing JS (${filename}) using esbuild`, error); } - contentCache.set(filename, content); - resolve(content); - }); + } catch (error) { + console.error('getFile() returned an error in ' + + `getFileCompressed(${filename}, ${contentType}): ${error}`); + } + contentCache.set(filename, content); + resolve(content); }); } else if (contentType === 'text/css') { - return await new Promise((resolve) => { - threadsPool.queue(async ({compressCSS}) => { - try { - logger.info('Compress CSS file %s.', filename); - - const compressResult = await compressCSS(path.resolve(ROOT_DIR,filename)); + return await new Promise(async (resolve) => { + try { + logger.info('Compress CSS file %s.', filename); - if (compressResult.error) { - console.error(`Error compressing CSS (${filename}) using terser`, compressResult.error); - } else { - content = compressResult - } + try { + content = await compressCSS(path.resolve(ROOT_DIR, filename)); } catch (error) { console.error(`CleanCSS.minify() returned an error on ${filename}: ${error}`); } contentCache.set(filename, content); resolve(content); - }); - }); + } catch (e) { + console.error('getFile() returned an error in ' + + `getFileCompressed(${filename}, ${contentType}): ${e}`); + } + }) } else { contentCache.set(filename, content); return content; } }; -const getFile = async (filename) => { +const getFile = async (filename: any) => { return await fs.readFile(path.resolve(ROOT_DIR, filename)); }; -exports.minify = (req, res, next) => minify(req, res).catch((err) => next(err || new Error(err))); +export const minify = (req:any, res:any, next:Function) => _minify(req, res).catch((err) => next(err || new Error(err))); -exports.requestURIs = requestURIs; +export const requestURIs = _requestURIs; -exports.shutdown = async (hookName, context) => { - await threadsPool.terminate(); +export const shutdown = async (hookName: string, context:any) => { + contentCache = new Map(); }; diff --git a/src/node/utils/MinifyWorker.js b/src/node/utils/MinifyWorker.ts similarity index 83% rename from src/node/utils/MinifyWorker.js rename to src/node/utils/MinifyWorker.ts index 507b4f00c76..cb278313c6f 100644 --- a/src/node/utils/MinifyWorker.js +++ b/src/node/utils/MinifyWorker.ts @@ -3,14 +3,13 @@ * Worker thread to minify JS & CSS files out of the main NodeJS thread */ -import {expose} from 'threads' import {build, transform} from 'esbuild'; /* * Minify JS content * @param {string} content - JS content to minify */ -const compressJS = async (content) => { +export const compressJS = async (content: string) => { return await transform(content, {minify: true}); } @@ -19,7 +18,7 @@ const compressJS = async (content) => { * @param {string} filename - name of the file * @param {string} ROOT_DIR - the root dir of Etherpad */ -const compressCSS = async (content) => { +export const compressCSS = async (content: string) => { const transformedCSS = await build( { entryPoints: [content], @@ -41,8 +40,3 @@ const compressCSS = async (content) => { ) return transformedCSS.outputFiles[0].text }; - -expose({ - compressJS: compressJS, - compressCSS, -}); diff --git a/src/node/utils/caching_middleware.ts b/src/node/utils/caching_middleware.ts deleted file mode 100644 index 74712871c67..00000000000 --- a/src/node/utils/caching_middleware.ts +++ /dev/null @@ -1,211 +0,0 @@ -'use strict'; - -/* - * 2011 Peter 'Pita' Martischka (Primary Technology Ltd) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS-IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import {Buffer} from 'node:buffer' -import fs from 'fs'; -const fsp = fs.promises; -import path from 'path'; -import zlib from 'zlib'; -const settings = require('./Settings'); -const existsSync = require('./path_exists'); -import util from 'util'; - -/* - * The crypto module can be absent on reduced node installations. - * - * Here we copy the approach TypeScript guys used for https://github.com/microsoft/TypeScript/issues/19100 - * If importing crypto fails at runtime, we replace sha256 with djb2, which is - * weaker, but works for our case. - * - * djb2 was written in 1991 by Daniel J. Bernstein. - * - */ - - -import _crypto from 'crypto'; - - -let CACHE_DIR: string|undefined = path.join(settings.root, 'var/'); -CACHE_DIR = existsSync(CACHE_DIR) ? CACHE_DIR : undefined; - -type Headers = { - [id: string]: string -} - -type ResponseCache = { - [id: string]: { - statusCode: number - headers: Headers - } -} - -const responseCache: ResponseCache = {}; - -const djb2Hash = (data: string) => { - const chars = data.split('').map((str) => str.charCodeAt(0)); - return `${chars.reduce((prev, curr) => ((prev << 5) + prev) + curr, 5381)}`; -}; - -const generateCacheKeyWithSha256 = - (path: string) => _crypto.createHash('sha256').update(path).digest('hex'); - -const generateCacheKeyWithDjb2 = - (path: string) => Buffer.from(djb2Hash(path)).toString('hex'); - -let generateCacheKey: (path: string)=>string; - -if (_crypto) { - generateCacheKey = generateCacheKeyWithSha256; -} else { - generateCacheKey = generateCacheKeyWithDjb2; - console.warn('No crypto support in this nodejs runtime. Djb2 (weaker) will be used.'); -} - -// MIMIC https://github.com/microsoft/TypeScript/commit/9677b0641cc5ba7d8b701b4f892ed7e54ceaee9a - END - -/* - This caches and compresses 200 and 404 responses to GET and HEAD requests. - TODO: Caching and compressing are solved problems, a middleware configuration - should replace this. -*/ - -export default class CachingMiddleware { - handle(req: any, res: any, next: any) { - this._handle(req, res, next).catch((err) => next(err || new Error(err))); - } - - async _handle(req: any, res: any, next: any) { - if (!(req.method === 'GET' || req.method === 'HEAD') || !CACHE_DIR) { - return next(undefined, req, res); - } - - const oldReq:ResponseCache = {}; - const oldRes:ResponseCache = {}; - - const supportsGzip = - (req.get('Accept-Encoding') || '').indexOf('gzip') !== -1; - - const url = new URL(req.url, 'http://localhost'); - const cacheKey = generateCacheKey(url.pathname + url.search); - - const stats = await fsp.stat(`${CACHE_DIR}minified_${cacheKey}`).catch(() => {}); - const modifiedSince = - req.headers['if-modified-since'] && new Date(req.headers['if-modified-since']); - if (stats != null && stats.mtime && responseCache[cacheKey]) { - req.headers['if-modified-since'] = stats.mtime.toUTCString(); - } else { - delete req.headers['if-modified-since']; - } - - // Always issue get to downstream. - oldReq.method = req.method; - req.method = 'GET'; - - // This handles read/write synchronization as well as its predecessor, - // which is to say, not at all. - // TODO: Implement locking on write or ditch caching of gzip and use - // existing middlewares. - const respond = () => { - req.method = oldReq.method || req.method; - res.write = oldRes.write || res.write; - res.end = oldRes.end || res.end; - - const headers: Headers = {}; - Object.assign(headers, (responseCache[cacheKey].headers || {})); - const statusCode = responseCache[cacheKey].statusCode; - - let pathStr = `${CACHE_DIR}minified_${cacheKey}`; - if (supportsGzip && /application\/javascript/.test(headers['content-type'])) { - pathStr += '.gz'; - headers['content-encoding'] = 'gzip'; - } - - const lastModified = headers['last-modified'] && new Date(headers['last-modified']); - - if (statusCode === 200 && lastModified <= modifiedSince) { - res.writeHead(304, headers); - res.end(); - } else if (req.method === 'GET') { - const readStream = fs.createReadStream(pathStr); - res.writeHead(statusCode, headers); - readStream.pipe(res); - } else { - res.writeHead(statusCode, headers); - res.end(); - } - }; - - const expirationDate = new Date(((responseCache[cacheKey] || {}).headers || {}).expires); - if (expirationDate > new Date()) { - // Our cached version is still valid. - return respond(); - } - - const _headers:Headers = {}; - oldRes.setHeader = res.setHeader; - res.setHeader = (key: string, value: string) => { - // Don't set cookies, see issue #707 - if (key.toLowerCase() === 'set-cookie') return; - - _headers[key.toLowerCase()] = value; - // @ts-ignore - oldRes.setHeader.call(res, key, value); - }; - - oldRes.writeHead = res.writeHead; - res.writeHead = (status: number, headers: Headers) => { - res.writeHead = oldRes.writeHead; - if (status === 200) { - // Update cache - let buffer = ''; - - Object.keys(headers || {}).forEach((key) => { - res.setHeader(key, headers[key]); - }); - headers = _headers; - - oldRes.write = res.write; - oldRes.end = res.end; - res.write = (data: number, encoding: number) => { - buffer += data.toString(encoding); - }; - res.end = async (data: number, encoding: number) => { - await Promise.all([ - fsp.writeFile(`${CACHE_DIR}minified_${cacheKey}`, buffer).catch(() => {}), - util.promisify(zlib.gzip)(buffer) - // @ts-ignore - .then((content: string) => fsp.writeFile(`${CACHE_DIR}minified_${cacheKey}.gz`, content)) - .catch(() => {}), - ]); - responseCache[cacheKey] = {statusCode: status, headers}; - respond(); - }; - } else if (status === 304) { - // Nothing new changed from the cached version. - oldRes.write = res.write; - oldRes.end = res.end; - res.write = (data: number, encoding: number) => {}; - res.end = (data: number, encoding: number) => { respond(); }; - } else { - res.writeHead(status, headers); - } - }; - - next(undefined, req, res); - } -}; diff --git a/src/node/utils/sanitizePathname.ts b/src/node/utils/sanitizePathname.ts index 2932b913d31..72ab88417b1 100644 --- a/src/node/utils/sanitizePathname.ts +++ b/src/node/utils/sanitizePathname.ts @@ -1,10 +1,8 @@ -'use strict'; - -const path = require('path'); +import path from 'path'; // Normalizes p and ensures that it is a relative path that does not reach outside. See // https://nvd.nist.gov/vuln/detail/CVE-2015-3297 for additional context. -module.exports = (p: string, pathApi = path) => { +const sanitizeRoot = (p: string, pathApi = path) => { // The documentation for path.normalize() says that it resolves '..' and '.' segments. The word // "resolve" implies that it examines the filesystem to resolve symbolic links, so 'a/../b' might // not be the same thing as 'b'. Most path normalization functions from other libraries (e.g., @@ -21,3 +19,5 @@ module.exports = (p: string, pathApi = path) => { if (pathApi.sep === '\\') p = p.replace(/\\/g, '/'); return p; }; + +export default sanitizeRoot diff --git a/src/package.json b/src/package.json index 02426efed40..4b674201d96 100644 --- a/src/package.json +++ b/src/package.json @@ -68,13 +68,13 @@ "socket.io": "^4.7.5", "socket.io-client": "^4.7.5", "superagent": "10.0.2", - "threads": "^1.7.0", "tinycon": "0.6.8", "tsx": "4.17.0", "ueberdb2": "^4.2.93", "underscore": "1.13.7", "unorm": "1.6.0", - "wtfnode": "^0.9.3" + "wtfnode": "^0.9.3", + "vitest": "^2.0.5" }, "bin": { "etherpad-healthcheck": "../bin/etherpad-healthcheck", @@ -89,6 +89,7 @@ "@types/jquery": "^3.5.30", "@types/jsdom": "^21.1.7", "@types/jsonwebtoken": "^9.0.6", + "@types/mime-types": "^2.1.4", "@types/mocha": "^10.0.7", "@types/node": "^22.3.0", "@types/oidc-provider": "^8.5.2", @@ -132,7 +133,8 @@ "test-ui:ui": "cross-env NODE_ENV=production npx playwright test tests/frontend-new/specs --ui", "test-admin": "cross-env NODE_ENV=production npx playwright test tests/frontend-new/admin-spec --workers 1", "test-admin:ui": "cross-env NODE_ENV=production npx playwright test tests/frontend-new/admin-spec --ui --workers 1", - "debug:socketio": "cross-env DEBUG=socket.io* node --require tsx/cjs node/server.ts" + "debug:socketio": "cross-env DEBUG=socket.io* node --require tsx/cjs node/server.ts", + "test:vitest": "vitest" }, "version": "2.2.2", "license": "Apache-2.0" diff --git a/src/tests/frontend/specs/AttributeMap.js b/src/tests/backend-new/specs/AttributeMap.ts similarity index 93% rename from src/tests/frontend/specs/AttributeMap.js rename to src/tests/backend-new/specs/AttributeMap.ts index 92ca6833463..3e43abf7c97 100644 --- a/src/tests/frontend/specs/AttributeMap.js +++ b/src/tests/backend-new/specs/AttributeMap.ts @@ -1,8 +1,9 @@ 'use strict'; -const AttributeMap = require('../../../static/js/AttributeMap'); +const AttributeMap = require('../../../static/js/AttributeMap.js'); const AttributePool = require('../../../static/js/AttributePool'); const attributes = require('../../../static/js/attributes'); +import {expect, describe, it, beforeEach} from 'vitest' describe('AttributeMap', function () { const attribs = [ @@ -10,7 +11,7 @@ describe('AttributeMap', function () { ['baz', 'bif'], ['emptyValue', ''], ]; - let pool; + let pool: { eachAttrib: (arg0: () => number) => void; putAttrib: (arg0: string[]) => any; getAttrib: (arg0: number) => any; }; const getPoolSize = () => { let n = 0; @@ -66,7 +67,7 @@ describe('AttributeMap', function () { ['number', 1, '1'], ]; for (const [desc, input, want] of testCases) { - describe(desc, function () { + describe(desc as string, function () { it('key is coerced to string', async function () { const m = new AttributeMap(pool); m.set(input, 'value'); @@ -116,8 +117,9 @@ describe('AttributeMap', function () { }); for (const funcName of ['update', 'updateFromString']) { - const callUpdateFn = (m, ...args) => { + const callUpdateFn = (m: any, ...args: (boolean | (string | null | undefined)[][])[]) => { if (funcName === 'updateFromString') { + // @ts-ignore args[0] = attributes.attribsToString(attributes.sort([...args[0]]), pool); } return AttributeMap.prototype[funcName].call(m, ...args); diff --git a/src/tests/frontend/specs/attributes.js b/src/tests/backend-new/specs/attributes.ts similarity index 91% rename from src/tests/frontend/specs/attributes.js rename to src/tests/backend-new/specs/attributes.ts index 13058dbe38c..5f33f52b3fb 100644 --- a/src/tests/frontend/specs/attributes.js +++ b/src/tests/backend-new/specs/attributes.ts @@ -1,11 +1,15 @@ 'use strict'; +import {APool} from "../../../node/types/PadType"; + const AttributePool = require('../../../static/js/AttributePool'); const attributes = require('../../../static/js/attributes'); +import {expect, describe, it, beforeEach} from 'vitest'; + describe('attributes', function () { const attribs = [['foo', 'bar'], ['baz', 'bif']]; - let pool; + let pool: APool; beforeEach(async function () { pool = new AttributePool(); @@ -14,7 +18,7 @@ describe('attributes', function () { describe('decodeAttribString', function () { it('is a generator function', async function () { - expect(attributes.decodeAttribString).to.be.a((function* () {}).constructor); + expect(attributes.decodeAttribString.constructor.name).to.equal('GeneratorFunction'); }); describe('rejects invalid attribute strings', function () { @@ -22,7 +26,7 @@ describe('attributes', function () { for (const tc of testCases) { it(JSON.stringify(tc), async function () { expect(() => [...attributes.decodeAttribString(tc)]) - .to.throwException(/invalid character/); + .toThrowError(/invalid character/); }); } }); @@ -56,7 +60,7 @@ describe('attributes', function () { ['set', new Set([0, 1])], ]; for (const [desc, input] of testCases) { - it(desc, async function () { + it(desc as string, async function () { expect(attributes.encodeAttribString(input)).to.equal('*0*1'); }); } @@ -74,7 +78,7 @@ describe('attributes', function () { ]; for (const [input, wantErr] of testCases) { it(JSON.stringify(input), async function () { - expect(() => attributes.encodeAttribString(input)).to.throwException(wantErr); + expect(() => attributes.encodeAttribString(input)).toThrowError(wantErr as RegExp); }); } }); @@ -101,7 +105,7 @@ describe('attributes', function () { describe('attribsFromNums', function () { it('is a generator function', async function () { - expect(attributes.attribsFromNums).to.be.a((function* () {}).constructor); + expect(attributes.attribsFromNums.constructor.name).to.equal("GeneratorFunction"); }); describe('accepts any kind of iterable', function () { @@ -112,7 +116,7 @@ describe('attributes', function () { ]; for (const [desc, input] of testCases) { - it(desc, async function () { + it(desc as string, async function () { const gotAttribs = [...attributes.attribsFromNums(input, pool)]; expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(attribs)); }); @@ -132,7 +136,7 @@ describe('attributes', function () { ]; for (const [input, wantErr] of testCases) { it(JSON.stringify(input), async function () { - expect(() => [...attributes.attribsFromNums(input, pool)]).to.throwException(wantErr); + expect(() => [...attributes.attribsFromNums(input, pool)]).toThrowError(wantErr as RegExp); }); } }); @@ -156,7 +160,7 @@ describe('attributes', function () { describe('attribsToNums', function () { it('is a generator function', async function () { - expect(attributes.attribsToNums).to.be.a((function* () {}).constructor); + expect(attributes.attribsToNums.constructor.name).to.equal("GeneratorFunction") }); describe('accepts any kind of iterable', function () { @@ -167,7 +171,7 @@ describe('attributes', function () { ]; for (const [desc, input] of testCases) { - it(desc, async function () { + it(desc as string, async function () { const gotNums = [...attributes.attribsToNums(input, pool)]; expect(JSON.stringify(gotNums)).to.equal(JSON.stringify([0, 1])); }); @@ -178,7 +182,7 @@ describe('attributes', function () { const testCases = [null, [null]]; for (const input of testCases) { it(JSON.stringify(input), async function () { - expect(() => [...attributes.attribsToNums(input, pool)]).to.throwException(); + expect(() => [...attributes.attribsToNums(input, pool)]).toThrowError(); }); } }); @@ -224,12 +228,12 @@ describe('attributes', function () { ['number', 1, '1'], ]; for (const [desc, inputVal, wantVal] of testCases) { - describe(desc, function () { + describe(desc as string, function () { for (const [desc, inputAttribs, wantAttribs] of [ ['key is coerced to string', [[inputVal, 'value']], [[wantVal, 'value']]], ['value is coerced to string', [['key', inputVal]], [['key', wantVal]]], ]) { - it(desc, async function () { + it(desc as string, async function () { const gotNums = [...attributes.attribsToNums(inputAttribs, pool)]; // Each attrib in inputAttribs is expected to be new to the pool. const wantNums = [...Array(attribs.length + 1).keys()].slice(attribs.length); @@ -245,7 +249,7 @@ describe('attributes', function () { describe('attribsFromString', function () { it('is a generator function', async function () { - expect(attributes.attribsFromString).to.be.a((function* () {}).constructor); + expect(attributes.attribsFromString.constructor.name).to.equal('GeneratorFunction'); }); describe('rejects invalid attribute strings', function () { @@ -261,7 +265,7 @@ describe('attributes', function () { ]; for (const [input, wantErr] of testCases) { it(JSON.stringify(input), async function () { - expect(() => [...attributes.attribsFromString(input, pool)]).to.throwException(wantErr); + expect(() => [...attributes.attribsFromString(input, pool)]).toThrowError(wantErr); }); } }); @@ -292,7 +296,7 @@ describe('attributes', function () { ]; for (const [desc, input] of testCases) { - it(desc, async function () { + it(desc as string, async function () { const got = attributes.attribsToString(input, pool); expect(got).to.equal('*0*1'); }); @@ -303,7 +307,7 @@ describe('attributes', function () { const testCases = [null, [null]]; for (const input of testCases) { it(JSON.stringify(input), async function () { - expect(() => attributes.attribsToString(input, pool)).to.throwException(); + expect(() => attributes.attribsToString(input, pool)).toThrowError(); }); } }); diff --git a/src/tests/backend-new/specs/contentcollector.ts b/src/tests/backend-new/specs/contentcollector.ts new file mode 100644 index 00000000000..81f0bc2ea7b --- /dev/null +++ b/src/tests/backend-new/specs/contentcollector.ts @@ -0,0 +1,398 @@ +'use strict'; + +/* + * While importexport tests target the `setHTML` API endpoint, which is nearly identical to what + * happens when a user manually imports a document via the UI, the contentcollector tests here don't + * use rehype to process the document. Rehype removes spaces and newĺines were applicable, so the + * expected results here can differ from importexport.js. + * + * If you add tests here, please also add them to importexport.js + */ + +import {APool} from "../../../node/types/PadType"; + +const AttributePool = require('../../../static/js/AttributePool'); +const Changeset = require('../../../static/js/Changeset'); +const assert = require('assert').strict; +const attributes = require('../../../static/js/attributes'); +const contentcollector = require('../../../static/js/contentcollector'); +const jsdom = require('jsdom'); + +import {describe, it, beforeAll, test} from 'vitest'; + +// All test case `wantAlines` values must only refer to attributes in this list so that the +// attribute numbers do not change due to changes in pool insertion order. +const knownAttribs = [ + ['insertorder', 'first'], + ['italic', 'true'], + ['list', 'bullet1'], + ['list', 'bullet2'], + ['list', 'number1'], + ['list', 'number2'], + ['lmkr', '1'], + ['start', '1'], + ['start', '2'], +]; + +const testCases = [ + { + description: 'Simple', + html: '

foo

', + wantAlines: ['+3'], + wantText: ['foo'], + }, + { + description: 'Line starts with asterisk', + html: '

*foo

', + wantAlines: ['+4'], + wantText: ['*foo'], + }, + { + description: 'Complex nested Li', + html: '
  1. one
    1. 1.1
  2. two
', + wantAlines: [ + '*0*4*6*7+1+3', + '*0*5*6*8+1+3', + '*0*4*6*8+1+3', + ], + wantText: [ + '*one', '*1.1', '*two', + ], + }, + { + description: 'Complex list of different types', + html: '
  • one
  • two
  • 0
  • 1
  • 2
    • 3
    • 4
  1. item
    1. item1
    2. item2
', + wantAlines: [ + '*0*2*6+1+3', + '*0*2*6+1+3', + '*0*2*6+1+1', + '*0*2*6+1+1', + '*0*2*6+1+1', + '*0*3*6+1+1', + '*0*3*6+1+1', + '*0*4*6*7+1+4', + '*0*5*6*8+1+5', + '*0*5*6*8+1+5', + ], + wantText: [ + '*one', + '*two', + '*0', + '*1', + '*2', + '*3', + '*4', + '*item', + '*item1', + '*item2', + ], + }, + { + description: 'Tests if uls properly get attributes', + html: '
  • a
  • b
div

foo

', + wantAlines: [ + '*0*2*6+1+1', + '*0*2*6+1+1', + '+3', + '+3', + ], + wantText: ['*a', '*b', 'div', 'foo'], + }, + { + description: 'Tests if indented uls properly get attributes', + html: '
  • a
    • b
  • a

foo

', + wantAlines: [ + '*0*2*6+1+1', + '*0*3*6+1+1', + '*0*2*6+1+1', + '+3', + ], + wantText: ['*a', '*b', '*a', 'foo'], + }, + { + description: 'Tests if ols properly get line numbers when in a normal OL', + html: '
  1. a
  2. b
  3. c

test

', + wantAlines: [ + '*0*4*6*7+1+1', + '*0*4*6*7+1+1', + '*0*4*6*7+1+1', + '+4', + ], + wantText: ['*a', '*b', '*c', 'test'], + noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?', + }, + { + description: 'A single completely empty line break within an ol should reset count if OL is closed off..', + html: '
  1. should be 1

hello

  1. should be 1
  2. should be 2

', + wantAlines: [ + '*0*4*6*7+1+b', + '+5', + '*0*4*6*8+1+b', + '*0*4*6*8+1+b', + '', + ], + wantText: ['*should be 1', 'hello', '*should be 1', '*should be 2', ''], + noteToSelf: "Shouldn't include attribute marker in the

line", + }, + { + description: 'A single

should create a new line', + html: '

', + wantAlines: ['', ''], + wantText: ['', ''], + noteToSelf: '

should create a line break but not break numbering', + }, + { + description: 'Tests if ols properly get line numbers when in a normal OL #2', + html: 'a
  1. b
    1. c
notlist

foo

', + wantAlines: [ + '+1', + '*0*4*6*7+1+1', + '*0*5*6*8+1+1', + '+7', + '+3', + ], + wantText: ['a', '*b', '*c', 'notlist', 'foo'], + noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?', + }, + { + description: 'First item being an UL then subsequent being OL will fail', + html: '
  • a
    1. b
    2. c
', + wantAlines: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1'], + wantText: ['a', '*b', '*c'], + noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?', + disabled: true, + }, + { + description: 'A single completely empty line break within an ol should NOT reset count', + html: '
  1. should be 1
  2. should be 2
  3. should be 3

', + wantAlines: [], + wantText: ['*should be 1', '*should be 2', '*should be 3'], + noteToSelf: "

should create a line break but not break numbering -- This is what I can't get working!", + disabled: true, + }, + { + description: 'Content outside body should be ignored', + html: 'titleempty
', + wantAlines: ['+5'], + wantText: ['empty'], + }, + { + description: 'Multiple spaces should be preserved', + html: 'Text with more than one space.
', + wantAlines: ['+10'], + wantText: ['Text with more than one space.'], + }, + { + description: 'non-breaking and normal space should be preserved', + html: 'Text with  more   than  one space.
', + wantAlines: ['+10'], + wantText: ['Text with more than one space.'], + }, + { + description: 'Multiple nbsp should be preserved', + html: '  
', + wantAlines: ['+2'], + wantText: [' '], + }, + { + description: 'Multiple nbsp between words ', + html: '  word1  word2   word3
', + wantAlines: ['+m'], + wantText: [' word1 word2 word3'], + }, + { + description: 'A non-breaking space preceded by a normal space', + html: '  word1  word2  word3
', + wantAlines: ['+l'], + wantText: [' word1 word2 word3'], + }, + { + description: 'A non-breaking space followed by a normal space', + html: '  word1  word2  word3
', + wantAlines: ['+l'], + wantText: [' word1 word2 word3'], + }, + { + description: 'Don\'t collapse spaces that follow a newline', + html: 'something
something
', + wantAlines: ['+9', '+m'], + wantText: ['something', ' something'], + }, + { + description: 'Don\'t collapse spaces that follow a empty paragraph', + html: 'something

something
', + wantAlines: ['+9', '', '+m'], + wantText: ['something', '', ' something'], + }, + { + description: 'Don\'t collapse spaces that preceed/follow a newline', + html: 'something
something
', + wantAlines: ['+l', '+m'], + wantText: ['something ', ' something'], + }, + { + description: 'Don\'t collapse spaces that preceed/follow a empty paragraph', + html: 'something

something
', + wantAlines: ['+l', '', '+m'], + wantText: ['something ', '', ' something'], + }, + { + description: 'Don\'t collapse non-breaking spaces that follow a newline', + html: 'something
   something
', + wantAlines: ['+9', '+c'], + wantText: ['something', ' something'], + }, + { + description: 'Don\'t collapse non-breaking spaces that follow a paragraph', + html: 'something

   something
', + wantAlines: ['+9', '', '+c'], + wantText: ['something', '', ' something'], + }, + { + description: 'Preserve all spaces when multiple are present', + html: 'Need more space s !
', + wantAlines: ['+h*1+4+2'], + wantText: ['Need more space s !'], + }, + { + description: 'Newlines and multiple spaces across newlines should be preserved', + html: ` + Need + more + space + s + !
`, + wantAlines: ['+19*1+4+b'], + wantText: ['Need more space s !'], + }, + { + description: 'Multiple new lines at the beginning should be preserved', + html: '

first line

second line
', + wantAlines: ['', '', '', '', '+a', '', '+b'], + wantText: ['', '', '', '', 'first line', '', 'second line'], + }, + { + description: 'A paragraph with multiple lines should not loose spaces when lines are combined', + html: `

+а б в г ґ д е є ж з и і ї й к л м н о +п р с т у ф х ц ч ш щ ю я ь

+`, + wantAlines: ['+1t'], + wantText: ['а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь'], + }, + { + description: 'lines in preformatted text should be kept intact', + html: `

+а б в г ґ д е є ж з и і ї й к л м н о

multiple
+lines
+in
+pre
+

п р с т у ф х ц ч ш щ ю я +ь

+`, + wantAlines: ['+11', '+8', '+5', '+2', '+3', '+r'], + wantText: [ + 'а б в г ґ д е є ж з и і ї й к л м н о', + 'multiple', + 'lines', + 'in', + 'pre', + 'п р с т у ф х ц ч ш щ ю я ь', + ], + }, + { + description: 'pre should be on a new line not preceded by a space', + html: `

+ 1 +

preline
+
`, + wantAlines: ['+6', '+7'], + wantText: [' 1 ', 'preline'], + }, + { + description: 'Preserve spaces on the beginning and end of a element', + html: 'Need more space s !
', + wantAlines: ['+f*1+3+1'], + wantText: ['Need more space s !'], + }, + { + description: 'Preserve spaces outside elements', + html: 'Need more space s !
', + wantAlines: ['+g*1+1+2'], + wantText: ['Need more space s !'], + }, + { + description: 'Preserve spaces at the end of an element', + html: 'Need more space s !
', + wantAlines: ['+g*1+2+1'], + wantText: ['Need more space s !'], + }, + { + description: 'Preserve spaces at the start of an element', + html: 'Need more space s !
', + wantAlines: ['+f*1+2+2'], + wantText: ['Need more space s !'], + }, +]; + +describe(__filename, function () { + for (const tc of testCases) { + describe(tc.description, function () { + let apool: APool; + let result: { + lines: string[], + lineAttribs: string[], + }; + if (tc.disabled) { + test.skip('If disabled we do not run the test'); + return; + } + + beforeAll(async function () { + + const {window: {document}} = new jsdom.JSDOM(tc.html); + apool = new AttributePool(); + // To reduce test fragility, the attribute pool is seeded with `knownAttribs`, and all + // attributes in `tc.wantAlines` must be in `knownAttribs`. (This guarantees that attribute + // numbers do not change if the attribute processing code changes.) + for (const attrib of knownAttribs) apool.putAttrib(attrib); + for (const aline of tc.wantAlines) { + for (const op of Changeset.deserializeOps(aline)) { + for (const n of attributes.decodeAttribString(op.attribs)) { + assert(n < knownAttribs.length); + } + } + } + const cc = contentcollector.makeContentCollector(true, null, apool); + cc.collectContent(document.body); + result = cc.finish(); + console.log(result); + }); + + it('text matches', async function () { + assert.deepEqual(result.lines, tc.wantText); + }); + + it('alines match', async function () { + assert.deepEqual(result.lineAttribs, tc.wantAlines); + }); + + it('attributes are sorted in canonical order', async function () { + const gotAttribs:string[][][] = []; + const wantAttribs = []; + for (const aline of result.lineAttribs) { + const gotAlineAttribs:string[][] = []; + gotAttribs.push(gotAlineAttribs); + const wantAlineAttribs:string[] = []; + wantAttribs.push(wantAlineAttribs); + for (const op of Changeset.deserializeOps(aline)) { + const gotOpAttribs:string[] = [...attributes.attribsFromString(op.attribs, apool)]; + gotAlineAttribs.push(gotOpAttribs); + wantAlineAttribs.push(attributes.sort([...gotOpAttribs])); + } + } + assert.deepEqual(gotAttribs, wantAttribs); + }); + }); + } +}); diff --git a/src/tests/backend/specs/pad_utils.ts b/src/tests/backend-new/specs/pad_utils.ts similarity index 86% rename from src/tests/backend/specs/pad_utils.ts rename to src/tests/backend-new/specs/pad_utils.ts index 3ca3c0858a7..bcccdf4496d 100644 --- a/src/tests/backend/specs/pad_utils.ts +++ b/src/tests/backend-new/specs/pad_utils.ts @@ -1,16 +1,13 @@ -'use strict'; - import {MapArrayType} from "../../../node/types/MapType"; - -import {strict as assert} from "assert"; const {padutils} = require('../../../static/js/pad_utils'); +import {describe, it, expect, afterEach, beforeAll} from "vitest"; describe(__filename, function () { describe('warnDeprecated', function () { const {warnDeprecated} = padutils; const backups:MapArrayType = {}; - before(async function () { + beforeAll(async function () { backups.logger = warnDeprecated.logger; }); @@ -36,10 +33,10 @@ describe(__filename, function () { for (const [now, want] of testCases) { // In a loop so that the stack trace is the same. warnDeprecated._rl.now = () => now; warnDeprecated(); - assert.equal(got, want); + expect(got).toEqual(want); } warnDeprecated(); // Should have a different stack trace. - assert.equal(got, testCases[testCases.length - 1][1] + 1); + expect(got).toEqual(testCases[testCases.length - 1][1] + 1); }); }); }); diff --git a/src/tests/backend/specs/sanitizePathname.ts b/src/tests/backend-new/specs/sanitizePathname.ts similarity index 92% rename from src/tests/backend/specs/sanitizePathname.ts rename to src/tests/backend-new/specs/sanitizePathname.ts index fd3cbb2e7f3..e841ae1551b 100644 --- a/src/tests/backend/specs/sanitizePathname.ts +++ b/src/tests/backend-new/specs/sanitizePathname.ts @@ -1,8 +1,7 @@ -'use strict'; - import {strict as assert} from "assert"; import path from 'path'; -const sanitizePathname = require('../../../node/utils/sanitizePathname'); +import sanitizePathname from '../../../node/utils/sanitizePathname'; +import {describe, it, expect} from 'vitest'; describe(__filename, function () { describe('absolute paths rejected', function () { @@ -21,7 +20,7 @@ describe(__filename, function () { for (const [platform, p] of testCases) { it(`${platform} ${p}`, async function () { // @ts-ignore - assert.throws(() => sanitizePathname(p, path[platform]), {message: /absolute path/}); + expect(() => sanitizePathname(p, path[platform] as any)).toThrowError(/absolute path/); }); } }); diff --git a/src/tests/backend-new/specs/skiplist.ts b/src/tests/backend-new/specs/skiplist.ts new file mode 100644 index 00000000000..c1b408e3a7c --- /dev/null +++ b/src/tests/backend-new/specs/skiplist.ts @@ -0,0 +1,55 @@ +'use strict'; + +const SkipList = require('ep_etherpad-lite/static/js/skiplist'); +import {expect, describe, it} from 'vitest'; + +describe('skiplist.js', function () { + it('rejects null keys', async function () { + const skiplist = new SkipList(); + for (const key of [undefined, null]) { + expect(() => skiplist.push({key})).toThrowError(); + } + }); + + it('rejects duplicate keys', async function () { + const skiplist = new SkipList(); + skiplist.push({key: 'foo'}); + expect(() => skiplist.push({key: 'foo'})).toThrowError(); + }); + + it('atOffset() returns last entry that touches offset', async function () { + const skiplist = new SkipList(); + const entries: { key: string; width: number; }[] = []; + let nextId = 0; + const makeEntry = (width: number) => { + const entry = {key: `id${nextId++}`, width}; + entries.push(entry); + return entry; + }; + + skiplist.push(makeEntry(5)); + expect(skiplist.atOffset(4)).toBe(entries[0]); + expect(skiplist.atOffset(5)).toBe(entries[0]); + expect(() => skiplist.atOffset(6)).toThrowError(); + + skiplist.push(makeEntry(0)); + expect(skiplist.atOffset(4)).toBe(entries[0]); + expect(skiplist.atOffset(5)).toBe(entries[1]); + expect(() => skiplist.atOffset(6)).toThrowError(); + + skiplist.push(makeEntry(0)); + expect(skiplist.atOffset(4)).toBe(entries[0]); + expect(skiplist.atOffset(5)).toBe(entries[2]); + expect(() => skiplist.atOffset(6)).toThrowError(); + + skiplist.splice(2, 0, [makeEntry(0)]); + expect(skiplist.atOffset(4)).toBe(entries[0]); + expect(skiplist.atOffset(5)).toBe(entries[2]); + expect(() => skiplist.atOffset(6)).toThrowError(); + + skiplist.push(makeEntry(3)); + expect(skiplist.atOffset(4)).toBe(entries[0]); + expect(skiplist.atOffset(5)).toBe(entries[4]); + expect(skiplist.atOffset(6)).toBe(entries[4]); + }); +}); diff --git a/src/tests/backend/specs/lowerCasePadIds.ts b/src/tests/backend/specs/lowerCasePadIds.ts index 359f85d2ce3..c85d16c3fc8 100644 --- a/src/tests/backend/specs/lowerCasePadIds.ts +++ b/src/tests/backend/specs/lowerCasePadIds.ts @@ -40,7 +40,7 @@ describe(__filename, function () { it('do nothing', async function () { await agent.get('/p/UPPERCASEpad') - .expect(200); + .expect(200); }); }); @@ -50,8 +50,8 @@ describe(__filename, function () { }); it('lowercase pad ids', async function () { await agent.get('/p/UPPERCASEpad') - .expect(302) - .expect('location', 'uppercasepad'); + .expect(302) + .expect('location', 'uppercasepad'); }); it('keeps old pads accessible', async function () { diff --git a/src/tests/backend/specs/settings.ts b/src/tests/backend/specs/settings.ts index d9bfe4f6d89..d6dcaf71ae9 100644 --- a/src/tests/backend/specs/settings.ts +++ b/src/tests/backend/specs/settings.ts @@ -6,87 +6,87 @@ import path from 'path'; import process from 'process'; describe(__filename, function () { - describe('parseSettings', function () { - let settings: any; - const envVarSubstTestCases = [ - {name: 'true', val: 'true', var: 'SET_VAR_TRUE', want: true}, - {name: 'false', val: 'false', var: 'SET_VAR_FALSE', want: false}, - {name: 'null', val: 'null', var: 'SET_VAR_NULL', want: null}, - {name: 'undefined', val: 'undefined', var: 'SET_VAR_UNDEFINED', want: undefined}, - {name: 'number', val: '123', var: 'SET_VAR_NUMBER', want: 123}, - {name: 'string', val: 'foo', var: 'SET_VAR_STRING', want: 'foo'}, - {name: 'empty string', val: '', var: 'SET_VAR_EMPTY_STRING', want: ''}, - ]; + describe('parseSettings', function () { + let settings: any; + const envVarSubstTestCases = [ + {name: 'true', val: 'true', var: 'SET_VAR_TRUE', want: true}, + {name: 'false', val: 'false', var: 'SET_VAR_FALSE', want: false}, + {name: 'null', val: 'null', var: 'SET_VAR_NULL', want: null}, + {name: 'undefined', val: 'undefined', var: 'SET_VAR_UNDEFINED', want: undefined}, + {name: 'number', val: '123', var: 'SET_VAR_NUMBER', want: 123}, + {name: 'string', val: 'foo', var: 'SET_VAR_STRING', want: 'foo'}, + {name: 'empty string', val: '', var: 'SET_VAR_EMPTY_STRING', want: ''}, + ]; - before(async function () { - for (const tc of envVarSubstTestCases) process.env[tc.var] = tc.val; - delete process.env.UNSET_VAR; - settings = parseSettings(path.join(__dirname, 'settings.json'), true); - assert(settings != null); - }); - - describe('environment variable substitution', function () { - describe('set', function () { - for (const tc of envVarSubstTestCases) { - it(tc.name, async function () { - const obj = settings['environment variable substitution'].set; - if (tc.name === 'undefined') { - assert(!(tc.name in obj)); - } else { - assert.equal(obj[tc.name], tc.want); - } - }); - } - }); + before(async function () { + for (const tc of envVarSubstTestCases) process.env[tc.var] = tc.val; + delete process.env.UNSET_VAR; + settings = parseSettings(path.join(__dirname, 'settings.json'), true); + assert(settings != null); + }); - describe('unset', function () { - it('no default', async function () { - const obj = settings['environment variable substitution'].unset; - assert.equal(obj['no default'], null); - }); + describe('environment variable substitution', function () { + describe('set', function () { + for (const tc of envVarSubstTestCases) { + it(tc.name, async function () { + const obj = settings['environment variable substitution'].set; + if (tc.name === 'undefined') { + assert(!(tc.name in obj)); + } else { + assert.equal(obj[tc.name], tc.want); + } + }); + } + }); - for (const tc of envVarSubstTestCases) { - it(tc.name, async function () { - const obj = settings['environment variable substitution'].unset; - if (tc.name === 'undefined') { - assert(!(tc.name in obj)); - } else { - assert.equal(obj[tc.name], tc.want); - } - }); - } - }); + describe('unset', function () { + it('no default', async function () { + const obj = settings['environment variable substitution'].unset; + assert.equal(obj['no default'], null); }); + + for (const tc of envVarSubstTestCases) { + it(tc.name, async function () { + const obj = settings['environment variable substitution'].unset; + if (tc.name === 'undefined') { + assert(!(tc.name in obj)); + } else { + assert.equal(obj[tc.name], tc.want); + } + }); + } + }); }); + }); - describe("Parse plugin settings", function () { + describe("Parse plugin settings", function () { - before(async function () { - process.env["EP__ADMIN__PASSWORD"] = "test" - }) + before(async function () { + process.env["EP__ADMIN__PASSWORD"] = "test" + }) - it('should parse plugin settings', async function () { - let settings = parseSettings(path.join(__dirname, 'settings.json'), true); - assert.equal(settings.ADMIN.PASSWORD, "test"); - }) + it('should parse plugin settings', async function () { + let settings = parseSettings(path.join(__dirname, 'settings.json'), true); + assert.equal(settings.ADMIN.PASSWORD, "test"); + }) - it('should bundle settings with same path', async function () { - process.env["EP__ADMIN__USERNAME"] = "test" - let settings = parseSettings(path.join(__dirname, 'settings.json'), true); - assert.deepEqual(settings.ADMIN, {PASSWORD: "test", USERNAME: "test"}); - }) + it('should bundle settings with same path', async function () { + process.env["EP__ADMIN__USERNAME"] = "test" + let settings = parseSettings(path.join(__dirname, 'settings.json'), true); + assert.deepEqual(settings.ADMIN, {PASSWORD: "test", USERNAME: "test"}); + }) - it("Can set the ep themes", async function () { - process.env["EP__ep_themes__default_theme"] = "hacker" - let settings = parseSettings(path.join(__dirname, 'settings.json'), true); - assert.deepEqual(settings.ep_themes, {"default_theme": "hacker"}); - }) + it("Can set the ep themes", async function () { + process.env["EP__ep_themes__default_theme"] = "hacker" + let settings = parseSettings(path.join(__dirname, 'settings.json'), true); + assert.deepEqual(settings.ep_themes, {"default_theme": "hacker"}); + }) - it("can set the ep_webrtc settings", async function () { - process.env["EP__ep_webrtc__enabled"] = "true" - let settings = parseSettings(path.join(__dirname, 'settings.json'), true); - assert.deepEqual(settings.ep_webrtc, {"enabled": true}); - }) + it("can set the ep_webrtc settings", async function () { + process.env["EP__ep_webrtc__enabled"] = "true" + let settings = parseSettings(path.join(__dirname, 'settings.json'), true); + assert.deepEqual(settings.ep_webrtc, {"enabled": true}); }) + }) }); diff --git a/src/tests/frontend/specs/easysync-assembler.js b/src/tests/frontend/specs/easysync-assembler.spec.mjs similarity index 92% rename from src/tests/frontend/specs/easysync-assembler.js rename to src/tests/frontend/specs/easysync-assembler.spec.mjs index 80a6636d223..ffccfc7e370 100644 --- a/src/tests/frontend/specs/easysync-assembler.js +++ b/src/tests/frontend/specs/easysync-assembler.spec.mjs @@ -4,11 +4,19 @@ const Changeset = require('../../../static/js/Changeset'); const {padutils} = require('../../../static/js/pad_utils'); const {poolOrArray} = require('../easysync-helper.js'); +import {describe, it, expect} from 'vitest' + + describe('easysync-assembler', function () { it('opAssembler', async function () { const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; const assem = Changeset.opAssembler(); - for (const op of Changeset.deserializeOps(x)) assem.append(op); + var opLength = 0 + for (const op of Changeset.deserializeOps(x)){ + console.log(op) + assem.append(op); + opLength++ + } expect(assem.toString()).to.equal(x); }); @@ -145,14 +153,23 @@ describe('easysync-assembler', function () { next() { const v = this._n.value; this._n = ops.next(); return v; }, }; const assem = Changeset.smartOpAssembler(); - assem.append(iter.next()); - assem.append(iter.next()); - assem.append(iter.next()); + var iter1 = iter.next() + assem.append(iter1); + var iter2 = iter.next() + assem.append(iter2); + var iter3 = iter.next() + assem.append(iter3); + console.log(assem.toString()); assem.clear(); assem.append(iter.next()); assem.append(iter.next()); + console.log(assem.toString()); assem.clear(); - while (iter.hasNext()) assem.append(iter.next()); + let counter = 0; + while (iter.hasNext()) { + console.log(counter++) + assem.append(iter.next()); + } assem.endDocument(); expect(assem.toString()).to.equal('-1+1*0+1=1-1+1|c=c-1'); }); diff --git a/src/tests/frontend/specs/easysync-other.js b/src/tests/frontend/specs/easysync-other.test.mjs similarity index 97% rename from src/tests/frontend/specs/easysync-other.js rename to src/tests/frontend/specs/easysync-other.test.mjs index af4580835c8..3c0bdff7c37 100644 --- a/src/tests/frontend/specs/easysync-other.js +++ b/src/tests/frontend/specs/easysync-other.test.mjs @@ -4,6 +4,8 @@ const Changeset = require('../../../static/js/Changeset'); const AttributePool = require('../../../static/js/AttributePool'); const {randomMultiline, poolOrArray} = require('../easysync-helper.js'); const {padutils} = require('../../../static/js/pad_utils'); +import {describe, it, expect} from 'vitest' + describe('easysync-other', function () { describe('filter attribute numbers', function () { @@ -66,7 +68,8 @@ describe('easysync-other', function () { it('testMakeSplice', async function () { const t = 'a\nb\nc\n'; - const t2 = Changeset.applyToText(Changeset.makeSplice(t, 5, 0, 'def'), t); + let splice = Changeset.makeSplice(t, 5, 0, 'def') + const t2 = Changeset.applyToText(splice, t); expect(t2).to.equal('a\nb\ncdef\n'); }); diff --git a/src/tests/frontend/specs/skiplist.js b/src/tests/frontend/specs/skiplist.js deleted file mode 100644 index 16b98561515..00000000000 --- a/src/tests/frontend/specs/skiplist.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict'; - -const SkipList = require('ep_etherpad-lite/static/js/skiplist'); - -describe('skiplist.js', function () { - it('rejects null keys', async function () { - const skiplist = new SkipList(); - for (const key of [undefined, null]) { - expect(() => skiplist.push({key})).to.throwError(); - } - }); - - it('rejects duplicate keys', async function () { - const skiplist = new SkipList(); - skiplist.push({key: 'foo'}); - expect(() => skiplist.push({key: 'foo'})).to.throwError(); - }); - - it('atOffset() returns last entry that touches offset', async function () { - const skiplist = new SkipList(); - const entries = []; - let nextId = 0; - const makeEntry = (width) => { - const entry = {key: `id${nextId++}`, width}; - entries.push(entry); - return entry; - }; - - skiplist.push(makeEntry(5)); - expect(skiplist.atOffset(4)).to.be(entries[0]); - expect(skiplist.atOffset(5)).to.be(entries[0]); - expect(() => skiplist.atOffset(6)).to.throwError(); - - skiplist.push(makeEntry(0)); - expect(skiplist.atOffset(4)).to.be(entries[0]); - expect(skiplist.atOffset(5)).to.be(entries[1]); - expect(() => skiplist.atOffset(6)).to.throwError(); - - skiplist.push(makeEntry(0)); - expect(skiplist.atOffset(4)).to.be(entries[0]); - expect(skiplist.atOffset(5)).to.be(entries[2]); - expect(() => skiplist.atOffset(6)).to.throwError(); - - skiplist.splice(2, 0, [makeEntry(0)]); - expect(skiplist.atOffset(4)).to.be(entries[0]); - expect(skiplist.atOffset(5)).to.be(entries[2]); - expect(() => skiplist.atOffset(6)).to.throwError(); - - skiplist.push(makeEntry(3)); - expect(skiplist.atOffset(4)).to.be(entries[0]); - expect(skiplist.atOffset(5)).to.be(entries[4]); - expect(skiplist.atOffset(6)).to.be(entries[4]); - }); -}); diff --git a/src/vitest.config.ts b/src/vitest.config.ts new file mode 100644 index 00000000000..945b62cf036 --- /dev/null +++ b/src/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ["tests/backend-new/**/*.ts"], + }, +}) From 870dbf6ce0b47dfb5f2cadd6657cc861cd91020f Mon Sep 17 00:00:00 2001 From: SamTV12345 <40429738+samtv12345@users.noreply.github.com> Date: Fri, 16 Aug 2024 22:57:25 +0200 Subject: [PATCH 14/49] Moved vitest to devDependencies. --- src/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/package.json b/src/package.json index 4b674201d96..ce80bd88e63 100644 --- a/src/package.json +++ b/src/package.json @@ -73,8 +73,7 @@ "ueberdb2": "^4.2.93", "underscore": "1.13.7", "unorm": "1.6.0", - "wtfnode": "^0.9.3", - "vitest": "^2.0.5" + "wtfnode": "^0.9.3" }, "bin": { "etherpad-healthcheck": "../bin/etherpad-healthcheck", @@ -109,7 +108,8 @@ "sinon": "^18.0.0", "split-grid": "^1.0.11", "supertest": "^7.0.0", - "typescript": "^5.5.4" + "typescript": "^5.5.4", + "vitest": "^2.0.5" }, "engines": { "node": ">=18.18.2", From 5ee2c4e7f8a91478da723235d33829f65741d8ca Mon Sep 17 00:00:00 2001 From: SamTV12345 <40429738+samtv12345@users.noreply.github.com> Date: Fri, 16 Aug 2024 23:17:13 +0200 Subject: [PATCH 15/49] Upgraded lockfile --- pnpm-lock.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63d8a4794be..bbc755ad3e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -269,9 +269,6 @@ importers: unorm: specifier: 1.6.0 version: 1.6.0 - vitest: - specifier: ^2.0.5 - version: 2.0.5(@types/node@22.4.0)(jsdom@24.1.1) wtfnode: specifier: ^0.9.3 version: 0.9.3 @@ -363,6 +360,9 @@ importers: typescript: specifier: ^5.5.4 version: 5.5.4 + vitest: + specifier: ^2.0.5 + version: 2.0.5(@types/node@22.4.0)(jsdom@24.1.1) ui: devDependencies: From 7e3ad03e2fe7bb487158a2116a53e63e949d0a13 Mon Sep 17 00:00:00 2001 From: SamTV12345 <40429738+SamTV12345@users.noreply.github.com> Date: Sat, 17 Aug 2024 20:14:36 +0200 Subject: [PATCH 16/49] Moved to ts (#6593) * Moved to ts * Fixed type check * Removed js suffixes * Migrated to ts * Fixed ts. * Fixed type check * Installed missing d ts --- pnpm-lock.yaml | 8 + src/node/db/API.ts | 2 +- src/node/db/AuthorManager.ts | 6 +- src/node/db/GroupManager.ts | 2 +- src/node/db/Pad.ts | 26 +- src/node/db/SecurityManager.ts | 4 +- src/node/eejs/index.ts | 2 +- src/node/handler/ImportHandler.ts | 2 +- src/node/handler/PadMessageHandler.ts | 48 +- src/node/hooks/i18n.ts | 2 +- src/node/utils/ExportHelper.ts | 5 +- src/node/utils/ExportHtml.ts | 2 +- src/node/utils/ImportEtherpad.ts | 4 +- src/node/utils/padDiff.ts | 2 +- src/package.json | 1 + ...ttributeManager.js => AttributeManager.ts} | 3 +- .../js/{AttributeMap.js => AttributeMap.ts} | 24 +- .../js/{AttributePool.js => AttributePool.ts} | 36 +- src/static/js/{Changeset.js => Changeset.ts} | 7 +- .../{ChangesetUtils.js => ChangesetUtils.ts} | 1 + .../js/{ChatMessage.js => ChatMessage.ts} | 35 +- src/static/js/{ace.js => ace.ts} | 1 + .../js/{ace2_common.js => ace2_common.ts} | 23 +- .../js/{ace2_inner.js => ace2_inner.ts} | 10 +- .../js/{attributes.js => attributes.ts} | 34 +- ...rror_handler.js => basic_error_handler.ts} | 1 + src/static/js/{broadcast.js => broadcast.ts} | 3 +- ...st_revisions.js => broadcast_revisions.ts} | 1 + ...roadcast_slider.js => broadcast_slider.ts} | 1 + ...hangesettracker.js => changesettracker.ts} | 5 +- src/static/js/{chat.js => chat.ts} | 5 +- .../js/{collab_client.js => collab_client.ts} | 1 + .../js/{colorutils.js => colorutils.ts} | 1 + ...ontentcollector.js => contentcollector.ts} | 3 +- .../js/{cssmanager.js => cssmanager.ts} | 1 + src/static/js/{domline.js => domline.ts} | 1 + ...{linestylefilter.js => linestylefilter.ts} | 3 +- src/static/js/{pad.js => pad.ts} | 8 +- ...econnect.js => pad_automatic_reconnect.ts} | 1 + ...ctionstatus.js => pad_connectionstatus.ts} | 1 + .../js/{pad_cookie.js => pad_cookie.ts} | 3 +- .../js/{pad_editbar.js => pad_editbar.ts} | 3 +- .../js/{pad_editor.js => pad_editor.ts} | 4 +- .../js/{pad_impexp.js => pad_impexp.ts} | 1 + .../js/{pad_modals.js => pad_modals.ts} | 1 + .../js/{pad_savedrevs.js => pad_savedrevs.ts} | 1 + .../js/{pad_userlist.js => pad_userlist.ts} | 3 +- src/static/js/{pad_utils.js => pad_utils.ts} | 413 ++++++++++-------- .../{client_plugins.js => client_plugins.ts} | 1 + src/static/js/pluginfw/{hooks.js => hooks.ts} | 1 + .../{plugin_defs.js => plugin_defs.ts} | 0 .../js/pluginfw/{plugins.js => plugins.ts} | 1 + .../js/pluginfw/{shared.js => shared.ts} | 1 + src/static/js/pluginfw/{tsort.js => tsort.ts} | 1 + src/static/js/{rjquery.js => rjquery.ts} | 1 + src/static/js/{security.js => security.ts} | 1 + .../js/{skin_variants.js => skin_variants.ts} | 1 + src/static/js/{skiplist.js => skiplist.ts} | 185 ++++---- src/static/js/{socketio.js => socketio.ts} | 1 + .../js/{timeslider.js => timeslider.ts} | 6 +- src/static/js/types/AText.ts | 4 + src/static/js/types/Attribute.ts | 1 + src/static/js/types/PadRevision.ts | 7 + src/static/js/types/SocketIOMessage.ts | 317 ++++++++++++++ .../js/{underscore.js => underscore.ts} | 1 + .../js/{undomodule.js => undomodule.ts} | 1 + .../js/vendors/{browser.js => browser.ts} | 1 + .../vendors/{farbtastic.js => farbtastic.ts} | 1 + .../js/vendors/{gritter.js => gritter.ts} | 1 + .../js/vendors/{jquery.js => jquery.ts} | 1 + .../{nice-select.js => nice-select.ts} | 1 + src/templates/timeslider.html | 1 - src/tests/backend-new/specs/AttributeMap.ts | 14 +- src/tests/backend-new/specs/attributes.ts | 27 +- .../backend-new/specs/contentcollector.ts | 398 ----------------- src/tests/backend-new/specs/pad_utils.ts | 16 +- src/tests/backend-new/specs/skiplist.ts | 3 +- src/tests/backend/common.ts | 5 +- src/tests/backend/specs/ImportEtherpad.ts | 2 +- src/tests/backend/specs/chat.ts | 16 +- src/tests/backend/specs/contentcollector.ts | 16 +- 81 files changed, 960 insertions(+), 829 deletions(-) rename src/static/js/{AttributeManager.js => AttributeManager.ts} (99%) rename src/static/js/{AttributeMap.js => AttributeMap.ts} (78%) rename src/static/js/{AttributePool.js => AttributePool.ts} (91%) rename src/static/js/{Changeset.js => Changeset.ts} (99%) rename src/static/js/{ChangesetUtils.js => ChangesetUtils.ts} (99%) rename src/static/js/{ChatMessage.js => ChatMessage.ts} (66%) rename src/static/js/{ace.js => ace.ts} (99%) rename src/static/js/{ace2_common.js => ace2_common.ts} (76%) rename src/static/js/{ace2_inner.js => ace2_inner.ts} (99%) rename src/static/js/{attributes.js => attributes.ts} (77%) rename src/static/js/{basic_error_handler.js => basic_error_handler.ts} (99%) rename src/static/js/{broadcast.js => broadcast.ts} (99%) rename src/static/js/{broadcast_revisions.js => broadcast_revisions.ts} (99%) rename src/static/js/{broadcast_slider.js => broadcast_slider.ts} (99%) rename src/static/js/{changesettracker.js => changesettracker.ts} (98%) rename src/static/js/{chat.js => chat.ts} (99%) mode change 100755 => 100644 rename src/static/js/{collab_client.js => collab_client.ts} (99%) rename src/static/js/{colorutils.js => colorutils.ts} (99%) rename src/static/js/{contentcollector.js => contentcollector.ts} (99%) rename src/static/js/{cssmanager.js => cssmanager.ts} (99%) rename src/static/js/{domline.js => domline.ts} (99%) rename src/static/js/{linestylefilter.js => linestylefilter.ts} (99%) rename src/static/js/{pad.js => pad.ts} (99%) rename src/static/js/{pad_automatic_reconnect.js => pad_automatic_reconnect.ts} (99%) rename src/static/js/{pad_connectionstatus.js => pad_connectionstatus.ts} (99%) rename src/static/js/{pad_cookie.js => pad_cookie.ts} (97%) rename src/static/js/{pad_editbar.js => pad_editbar.ts} (99%) rename src/static/js/{pad_editor.js => pad_editor.ts} (98%) rename src/static/js/{pad_impexp.js => pad_impexp.ts} (99%) rename src/static/js/{pad_modals.js => pad_modals.ts} (99%) rename src/static/js/{pad_savedrevs.js => pad_savedrevs.ts} (98%) rename src/static/js/{pad_userlist.js => pad_userlist.ts} (99%) rename src/static/js/{pad_utils.js => pad_utils.ts} (58%) rename src/static/js/pluginfw/{client_plugins.js => client_plugins.ts} (99%) rename src/static/js/pluginfw/{hooks.js => hooks.ts} (99%) rename src/static/js/pluginfw/{plugin_defs.js => plugin_defs.ts} (100%) rename src/static/js/pluginfw/{plugins.js => plugins.ts} (99%) rename src/static/js/pluginfw/{shared.js => shared.ts} (99%) rename src/static/js/pluginfw/{tsort.js => tsort.ts} (99%) rename src/static/js/{rjquery.js => rjquery.ts} (93%) rename src/static/js/{security.js => security.ts} (97%) rename src/static/js/{skin_variants.js => skin_variants.ts} (99%) rename src/static/js/{skiplist.js => skiplist.ts} (61%) rename src/static/js/{socketio.js => socketio.ts} (99%) rename src/static/js/{timeslider.js => timeslider.ts} (97%) create mode 100644 src/static/js/types/AText.ts create mode 100644 src/static/js/types/Attribute.ts create mode 100644 src/static/js/types/PadRevision.ts create mode 100644 src/static/js/types/SocketIOMessage.ts rename src/static/js/{underscore.js => underscore.ts} (78%) rename src/static/js/{undomodule.js => undomodule.ts} (99%) rename src/static/js/vendors/{browser.js => browser.ts} (99%) rename src/static/js/vendors/{farbtastic.js => farbtastic.ts} (99%) rename src/static/js/vendors/{gritter.js => gritter.ts} (99%) rename src/static/js/vendors/{jquery.js => jquery.ts} (99%) rename src/static/js/vendors/{nice-select.js => nice-select.ts} (99%) delete mode 100644 src/tests/backend-new/specs/contentcollector.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bbc755ad3e0..a5e4f96ae5b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -291,6 +291,9 @@ importers: '@types/jquery': specifier: ^3.5.30 version: 3.5.30 + '@types/js-cookie': + specifier: ^3.0.6 + version: 3.0.6 '@types/jsdom': specifier: ^21.1.7 version: 21.1.7 @@ -1501,6 +1504,9 @@ packages: '@types/jquery@3.5.30': resolution: {integrity: sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==} + '@types/js-cookie@3.0.6': + resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} + '@types/jsdom@21.1.7': resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} @@ -5685,6 +5691,8 @@ snapshots: dependencies: '@types/sizzle': 2.3.8 + '@types/js-cookie@3.0.6': {} + '@types/jsdom@21.1.7': dependencies: '@types/node': 22.4.0 diff --git a/src/node/db/API.ts b/src/node/db/API.ts index 9ce84fc5138..e9f445560ba 100644 --- a/src/node/db/API.ts +++ b/src/node/db/API.ts @@ -20,7 +20,7 @@ */ const Changeset = require('../../static/js/Changeset'); -const ChatMessage = require('../../static/js/ChatMessage'); +import ChatMessage from '../../static/js/ChatMessage'; const CustomError = require('../utils/customError'); const padManager = require('./PadManager'); const padMessageHandler = require('../handler/PadMessageHandler'); diff --git a/src/node/db/AuthorManager.ts b/src/node/db/AuthorManager.ts index 2f4e7d751af..4bcfa2c0d4a 100644 --- a/src/node/db/AuthorManager.ts +++ b/src/node/db/AuthorManager.ts @@ -21,8 +21,8 @@ const db = require('./DB'); const CustomError = require('../utils/customError'); -const hooks = require('../../static/js/pluginfw/hooks.js'); -const {randomString, padutils: {warnDeprecated}} = require('../../static/js/pad_utils'); +const hooks = require('../../static/js/pluginfw/hooks'); +import padutils, {randomString} from "../../static/js/pad_utils"; exports.getColorPalette = () => [ '#ffc7c7', @@ -169,7 +169,7 @@ exports.getAuthorId = async (token: string, user: object) => { * @param {String} token The token */ exports.getAuthor4Token = async (token: string) => { - warnDeprecated( + padutils.warnDeprecated( 'AuthorManager.getAuthor4Token() is deprecated; use AuthorManager.getAuthorId() instead'); return await getAuthor4Token(token); }; diff --git a/src/node/db/GroupManager.ts b/src/node/db/GroupManager.ts index 0524c4edaa0..b8cb6db02b4 100644 --- a/src/node/db/GroupManager.ts +++ b/src/node/db/GroupManager.ts @@ -20,7 +20,7 @@ */ const CustomError = require('../utils/customError'); -const randomString = require('../../static/js/pad_utils').randomString; +import {randomString} from "../../static/js/pad_utils"; const db = require('./DB'); const padManager = require('./PadManager'); const sessionManager = require('./SessionManager'); diff --git a/src/node/db/Pad.ts b/src/node/db/Pad.ts index fa4af994d57..e117bb3434d 100644 --- a/src/node/db/Pad.ts +++ b/src/node/db/Pad.ts @@ -7,10 +7,10 @@ import {MapArrayType} from "../types/MapType"; * The pad object, defined with joose */ -const AttributeMap = require('../../static/js/AttributeMap'); +import AttributeMap from '../../static/js/AttributeMap'; const Changeset = require('../../static/js/Changeset'); -const ChatMessage = require('../../static/js/ChatMessage'); -const AttributePool = require('../../static/js/AttributePool'); +import ChatMessage from '../../static/js/ChatMessage'; +import AttributePool from '../../static/js/AttributePool'; const Stream = require('../utils/Stream'); const assert = require('assert').strict; const db = require('./DB'); @@ -23,7 +23,7 @@ const CustomError = require('../utils/customError'); const readOnlyManager = require('./ReadOnlyManager'); const randomString = require('../utils/randomstring'); const hooks = require('../../static/js/pluginfw/hooks'); -const {padutils: {warnDeprecated}} = require('../../static/js/pad_utils'); +import pad_utils from "../../static/js/pad_utils"; const promises = require('../utils/promises'); /** @@ -40,7 +40,7 @@ exports.cleanText = (txt:string): string => txt.replace(/\r\n/g, '\n') class Pad { private db: Database; private atext: AText; - private pool: APool; + private pool: AttributePool; private head: number; private chatHead: number; private publicStatus: boolean; @@ -126,11 +126,11 @@ class Pad { pad: this, authorId, get author() { - warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`); + pad_utils.warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`); return this.authorId; }, set author(authorId) { - warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`); + pad_utils.warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`); this.authorId = authorId; }, ...this.head === 0 ? {} : { @@ -330,7 +330,7 @@ class Pad { * @param {?number} [time] - Message timestamp (milliseconds since epoch). Deprecated; use * `msgOrText.time` instead. */ - async appendChatMessage(msgOrText: string|typeof ChatMessage, authorId = null, time = null) { + async appendChatMessage(msgOrText: string| ChatMessage, authorId = null, time = null) { const msg = msgOrText instanceof ChatMessage ? msgOrText : new ChatMessage(msgOrText, authorId, time); this.chatHead++; @@ -437,11 +437,11 @@ class Pad { // let the plugins know the pad was copied await hooks.aCallAll('padCopy', { get originalPad() { - warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead'); + pad_utils.warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead'); return this.srcPad; }, get destinationID() { - warnDeprecated( + pad_utils.warnDeprecated( 'padCopy destinationID context property is deprecated; use dstPad.id instead'); return this.dstPad.id; }, @@ -538,11 +538,11 @@ class Pad { await hooks.aCallAll('padCopy', { get originalPad() { - warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead'); + pad_utils.warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead'); return this.srcPad; }, get destinationID() { - warnDeprecated( + pad_utils.warnDeprecated( 'padCopy destinationID context property is deprecated; use dstPad.id instead'); return this.dstPad.id; }, @@ -603,7 +603,7 @@ class Pad { p.push(padManager.removePad(padID)); p.push(hooks.aCallAll('padRemove', { get padID() { - warnDeprecated('padRemove padID context property is deprecated; use pad.id instead'); + pad_utils.warnDeprecated('padRemove padID context property is deprecated; use pad.id instead'); return this.pad.id; }, pad: this, diff --git a/src/node/db/SecurityManager.ts b/src/node/db/SecurityManager.ts index 326bf36595d..c2f209a01ae 100644 --- a/src/node/db/SecurityManager.ts +++ b/src/node/db/SecurityManager.ts @@ -22,7 +22,7 @@ import {UserSettingsObject} from "../types/UserSettingsObject"; const authorManager = require('./AuthorManager'); -const hooks = require('../../static/js/pluginfw/hooks.js'); +const hooks = require('../../static/js/pluginfw/hooks'); const padManager = require('./PadManager'); const readOnlyManager = require('./ReadOnlyManager'); const sessionManager = require('./SessionManager'); @@ -30,7 +30,7 @@ const settings = require('../utils/Settings'); const webaccess = require('../hooks/express/webaccess'); const log4js = require('log4js'); const authLogger = log4js.getLogger('auth'); -const {padutils} = require('../../static/js/pad_utils'); +import padutils from '../../static/js/pad_utils' const DENY = Object.freeze({accessStatus: 'deny'}); diff --git a/src/node/eejs/index.ts b/src/node/eejs/index.ts index b7f2cf99836..5d57e475132 100644 --- a/src/node/eejs/index.ts +++ b/src/node/eejs/index.ts @@ -22,7 +22,7 @@ const ejs = require('ejs'); const fs = require('fs'); -const hooks = require('../../static/js/pluginfw/hooks.js'); +const hooks = require('../../static/js/pluginfw/hooks'); const path = require('path'); const resolve = require('resolve'); const settings = require('../utils/Settings'); diff --git a/src/node/handler/ImportHandler.ts b/src/node/handler/ImportHandler.ts index 330a2d6f97b..286b4fb5699 100644 --- a/src/node/handler/ImportHandler.ts +++ b/src/node/handler/ImportHandler.ts @@ -31,7 +31,7 @@ import os from 'os'; const importHtml = require('../utils/ImportHtml'); const importEtherpad = require('../utils/ImportEtherpad'); import log4js from 'log4js'; -const hooks = require('../../static/js/pluginfw/hooks.js'); +const hooks = require('../../static/js/pluginfw/hooks'); const logger = log4js.getLogger('ImportHandler'); diff --git a/src/node/handler/PadMessageHandler.ts b/src/node/handler/PadMessageHandler.ts index 3909496075d..2976381bb1c 100644 --- a/src/node/handler/PadMessageHandler.ts +++ b/src/node/handler/PadMessageHandler.ts @@ -21,28 +21,29 @@ import {MapArrayType} from "../types/MapType"; -const AttributeMap = require('../../static/js/AttributeMap'); +import AttributeMap from '../../static/js/AttributeMap'; const padManager = require('../db/PadManager'); const Changeset = require('../../static/js/Changeset'); -const ChatMessage = require('../../static/js/ChatMessage'); -const AttributePool = require('../../static/js/AttributePool'); +import ChatMessage from '../../static/js/ChatMessage'; +import AttributePool from '../../static/js/AttributePool'; const AttributeManager = require('../../static/js/AttributeManager'); const authorManager = require('../db/AuthorManager'); -const {padutils} = require('../../static/js/pad_utils'); +import padutils from '../../static/js/pad_utils'; const readOnlyManager = require('../db/ReadOnlyManager'); const settings = require('../utils/Settings'); const securityManager = require('../db/SecurityManager'); -const plugins = require('../../static/js/pluginfw/plugin_defs.js'); +const plugins = require('../../static/js/pluginfw/plugin_defs'); import log4js from 'log4js'; const messageLogger = log4js.getLogger('message'); const accessLogger = log4js.getLogger('access'); -const hooks = require('../../static/js/pluginfw/hooks.js'); +const hooks = require('../../static/js/pluginfw/hooks'); const stats = require('../stats') const assert = require('assert').strict; import {RateLimiterMemory} from 'rate-limiter-flexible'; import {ChangesetRequest, PadUserInfo, SocketClientRequest} from "../types/SocketClientRequest"; import {APool, AText, PadAuthor, PadType} from "../types/PadType"; import {ChangeSet} from "../types/ChangeSet"; +import {ChatMessageMessage, ClientReadyMessage, ClientSaveRevisionMessage, ClientSuggestUserName, ClientUserChangesMessage, ClientVarMessage, CustomMessage, UserNewInfoMessage} from "../../static/js/types/SocketIOMessage"; const webaccess = require('../hooks/express/webaccess'); const { checkValidRev } = require('../utils/checkValidRev'); @@ -214,7 +215,7 @@ exports.handleDisconnect = async (socket:any) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -exports.handleMessage = async (socket:any, message:typeof ChatMessage) => { +exports.handleMessage = async (socket:any, message: ClientVarMessage) => { const env = process.env.NODE_ENV || 'development'; if (env === 'production') { @@ -348,15 +349,15 @@ exports.handleMessage = async (socket:any, message:typeof ChatMessage) => { stats.counter('pendingEdits').inc(); await padChannels.enqueue(thisSession.padId, {socket, message}); break; - case 'USERINFO_UPDATE': await handleUserInfoUpdate(socket, message); break; - case 'CHAT_MESSAGE': await handleChatMessage(socket, message); break; + case 'USERINFO_UPDATE': await handleUserInfoUpdate(socket, message as unknown as UserNewInfoMessage); break; + case 'CHAT_MESSAGE': await handleChatMessage(socket, message as unknown as ChatMessageMessage); break; case 'GET_CHAT_MESSAGES': await handleGetChatMessages(socket, message); break; - case 'SAVE_REVISION': await handleSaveRevisionMessage(socket, message); break; + case 'SAVE_REVISION': await handleSaveRevisionMessage(socket, message as unknown as ClientSaveRevisionMessage); break; case 'CLIENT_MESSAGE': { const {type} = message.data.payload; try { switch (type) { - case 'suggestUserName': handleSuggestUserName(socket, message); break; + case 'suggestUserName': handleSuggestUserName(socket, message as unknown as ClientSuggestUserName); break; default: throw new Error('unknown message type'); } } catch (err) { @@ -384,7 +385,7 @@ exports.handleMessage = async (socket:any, message:typeof ChatMessage) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleSaveRevisionMessage = async (socket:any, message: string) => { +const handleSaveRevisionMessage = async (socket:any, message: ClientSaveRevisionMessage) => { const {padId, author: authorId} = sessioninfos[socket.id]; const pad = await padManager.getPad(padId, null, authorId); await pad.addSavedRevision(pad.head, authorId); @@ -397,7 +398,7 @@ const handleSaveRevisionMessage = async (socket:any, message: string) => { * @param msg {Object} the message we're sending * @param sessionID {string} the socketIO session to which we're sending this message */ -exports.handleCustomObjectMessage = (msg: typeof ChatMessage, sessionID: string) => { +exports.handleCustomObjectMessage = (msg: CustomMessage, sessionID: string) => { if (msg.data.type === 'CUSTOM') { if (sessionID) { // a sessionID is targeted: directly to this sessionID @@ -432,7 +433,7 @@ exports.handleCustomMessage = (padID: string, msgString:string) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleChatMessage = async (socket:any, message: typeof ChatMessage) => { +const handleChatMessage = async (socket:any, message: ChatMessageMessage) => { const chatMessage = ChatMessage.fromObject(message.data.message); const {padId, author: authorId} = sessioninfos[socket.id]; // Don't trust the user-supplied values. @@ -452,7 +453,7 @@ const handleChatMessage = async (socket:any, message: typeof ChatMessage) => { * @param {string} [padId] - The destination pad ID. Deprecated; pass a chat message * object as the first argument and the destination pad ID as the second argument instead. */ -exports.sendChatMessageToPadClients = async (mt: typeof ChatMessage|number, puId: string, text:string|null = null, padId:string|null = null) => { +exports.sendChatMessageToPadClients = async (mt: ChatMessage|number, puId: string, text:string|null = null, padId:string|null = null) => { const message = mt instanceof ChatMessage ? mt : new ChatMessage(text, puId, mt); padId = mt instanceof ChatMessage ? puId : padId; const pad = await padManager.getPad(padId, null, message.authorId); @@ -499,7 +500,7 @@ const handleGetChatMessages = async (socket:any, {data: {start, end}}:any) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleSuggestUserName = (socket:any, message: typeof ChatMessage) => { +const handleSuggestUserName = (socket:any, message: ClientSuggestUserName) => { const {newName, unnamedId} = message.data.payload; if (newName == null) throw new Error('missing newName'); if (unnamedId == null) throw new Error('missing unnamedId'); @@ -519,7 +520,7 @@ const handleSuggestUserName = (socket:any, message: typeof ChatMessage) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleUserInfoUpdate = async (socket:any, {data: {userInfo: {name, colorId}}}: PadUserInfo) => { +const handleUserInfoUpdate = async (socket:any, {data: {userInfo: {name, colorId}}}: UserNewInfoMessage) => { if (colorId == null) throw new Error('missing colorId'); if (!name) name = null; const session = sessioninfos[socket.id]; @@ -567,7 +568,9 @@ const handleUserInfoUpdate = async (socket:any, {data: {userInfo: {name, colorId * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleUserChanges = async (socket:any, message: typeof ChatMessage) => { +const handleUserChanges = async (socket:any, message: { + data: ClientUserChangesMessage +}) => { // This one's no longer pending, as we're gonna process it now stats.counter('pendingEdits').dec(); @@ -738,7 +741,7 @@ exports.updatePadClients = async (pad: PadType) => { /** * Copied from the Etherpad Source Code. Don't know what this method does excatly... */ -const _correctMarkersInPad = (atext: AText, apool: APool) => { +const _correctMarkersInPad = (atext: AText, apool: AttributePool) => { const text = atext.text; // collect char positions of line markers (e.g. bullets) in new atext @@ -785,7 +788,7 @@ const _correctMarkersInPad = (atext: AText, apool: APool) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -const handleClientReady = async (socket:any, message: typeof ChatMessage) => { +const handleClientReady = async (socket:any, message: ClientReadyMessage) => { const sessionInfo = sessioninfos[socket.id]; if (sessionInfo == null) throw new Error('client disconnected'); assert(sessionInfo.author); @@ -793,8 +796,9 @@ const handleClientReady = async (socket:any, message: typeof ChatMessage) => { await hooks.aCallAll('clientReady', message); // Deprecated due to awkward context. let {colorId: authorColorId, name: authorName} = message.userInfo || {}; - if (authorColorId && !/^#(?:[0-9A-F]{3}){1,2}$/i.test(authorColorId)) { + if (authorColorId && !/^#(?:[0-9A-F]{3}){1,2}$/i.test(authorColorId as string)) { messageLogger.warn(`Ignoring invalid colorId in CLIENT_READY message: ${authorColorId}`); + // @ts-ignore authorColorId = null; } await Promise.all([ @@ -872,7 +876,7 @@ const handleClientReady = async (socket:any, message: typeof ChatMessage) => { const revisionsNeeded = []; const changesets:MapArrayType = {}; - let startNum = message.client_rev + 1; + let startNum = message.client_rev! + 1; let endNum = pad.getHeadRevisionNumber() + 1; const headNum = pad.getHeadRevisionNumber(); diff --git a/src/node/hooks/i18n.ts b/src/node/hooks/i18n.ts index 500f1f88784..c4cc58bdd0b 100644 --- a/src/node/hooks/i18n.ts +++ b/src/node/hooks/i18n.ts @@ -7,7 +7,7 @@ const languages = require('languages4translatewiki'); const fs = require('fs'); const path = require('path'); const _ = require('underscore'); -const pluginDefs = require('../../static/js/pluginfw/plugin_defs.js'); +const pluginDefs = require('../../static/js/pluginfw/plugin_defs'); const existsSync = require('../utils/path_exists'); const settings = require('../utils/Settings'); diff --git a/src/node/utils/ExportHelper.ts b/src/node/utils/ExportHelper.ts index f3a438e86e6..e12332b0672 100644 --- a/src/node/utils/ExportHelper.ts +++ b/src/node/utils/ExportHelper.ts @@ -19,7 +19,8 @@ * limitations under the License. */ -const AttributeMap = require('../../static/js/AttributeMap'); +import AttributeMap from '../../static/js/AttributeMap'; +import AttributePool from "../../static/js/AttributePool"; const Changeset = require('../../static/js/Changeset'); const { checkValidRev } = require('./checkValidRev'); @@ -51,7 +52,7 @@ type LineModel = { [id:string]:string|number|LineModel } -exports._analyzeLine = (text:string, aline: LineModel, apool: Function) => { +exports._analyzeLine = (text:string, aline: LineModel, apool: AttributePool) => { const line: LineModel = {}; // identify list diff --git a/src/node/utils/ExportHtml.ts b/src/node/utils/ExportHtml.ts index 3b84c4380ec..138f084ba6e 100644 --- a/src/node/utils/ExportHtml.ts +++ b/src/node/utils/ExportHtml.ts @@ -27,7 +27,7 @@ const hooks = require('../../static/js/pluginfw/hooks'); const eejs = require('../eejs'); const _analyzeLine = require('./ExportHelper')._analyzeLine; const _encodeWhitespace = require('./ExportHelper')._encodeWhitespace; -const padutils = require('../../static/js/pad_utils').padutils; +import padutils from "../../static/js/pad_utils"; const getPadHTML = async (pad: PadType, revNum: string) => { let atext = pad.atext; diff --git a/src/node/utils/ImportEtherpad.ts b/src/node/utils/ImportEtherpad.ts index e75c5fd4273..cf34107c73e 100644 --- a/src/node/utils/ImportEtherpad.ts +++ b/src/node/utils/ImportEtherpad.ts @@ -18,7 +18,7 @@ import {APool} from "../types/PadType"; * limitations under the License. */ -const AttributePool = require('../../static/js/AttributePool'); +import AttributePool from '../../static/js/AttributePool'; const {Pad} = require('../db/Pad'); const Stream = require('./Stream'); const authorManager = require('../db/AuthorManager'); @@ -61,7 +61,7 @@ exports.setPadRaw = async (padId: string, r: string, authorId = '') => { try { const processRecord = async (key:string, value: null|{ padIDs: string|Record, - pool: APool + pool: AttributePool }) => { if (!value) return; const keyParts = key.split(':'); diff --git a/src/node/utils/padDiff.ts b/src/node/utils/padDiff.ts index d731ebbe40a..6074f8bbc6c 100644 --- a/src/node/utils/padDiff.ts +++ b/src/node/utils/padDiff.ts @@ -3,7 +3,7 @@ import {PadAuthor, PadType} from "../types/PadType"; import {MapArrayType} from "../types/MapType"; -const AttributeMap = require('../../static/js/AttributeMap'); +import AttributeMap from '../../static/js/AttributeMap'; const Changeset = require('../../static/js/Changeset'); const attributes = require('../../static/js/attributes'); const exportHtml = require('./ExportHtml'); diff --git a/src/package.json b/src/package.json index ce80bd88e63..80031af7af9 100644 --- a/src/package.json +++ b/src/package.json @@ -86,6 +86,7 @@ "@types/formidable": "^3.4.5", "@types/http-errors": "^2.0.4", "@types/jquery": "^3.5.30", + "@types/js-cookie": "^3.0.6", "@types/jsdom": "^21.1.7", "@types/jsonwebtoken": "^9.0.6", "@types/mime-types": "^2.1.4", diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.ts similarity index 99% rename from src/static/js/AttributeManager.js rename to src/static/js/AttributeManager.ts index 63af431d908..6a0a545b469 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.ts @@ -1,6 +1,7 @@ +// @ts-nocheck 'use strict'; -const AttributeMap = require('./AttributeMap'); +import AttributeMap from './AttributeMap'; const Changeset = require('./Changeset'); const ChangesetUtils = require('./ChangesetUtils'); const attributes = require('./attributes'); diff --git a/src/static/js/AttributeMap.js b/src/static/js/AttributeMap.ts similarity index 78% rename from src/static/js/AttributeMap.js rename to src/static/js/AttributeMap.ts index 55640eb8bc6..07bc106a501 100644 --- a/src/static/js/AttributeMap.js +++ b/src/static/js/AttributeMap.ts @@ -1,6 +1,9 @@ 'use strict'; -const attributes = require('./attributes'); +import AttributePool from "./AttributePool"; +import {Attribute} from "./types/Attribute"; + +import attributes from './attributes'; /** * A `[key, value]` pair of strings describing a text attribute. @@ -21,6 +24,7 @@ const attributes = require('./attributes'); * Convenience class to convert an Op's attribute string to/from a Map of key, value pairs. */ class AttributeMap extends Map { + private readonly pool? : AttributePool|null /** * Converts an attribute string into an AttributeMap. * @@ -28,14 +32,14 @@ class AttributeMap extends Map { * @param {AttributePool} pool - Attribute pool. * @returns {AttributeMap} */ - static fromString(str, pool) { + public static fromString(str: string, pool?: AttributePool|null): AttributeMap { return new AttributeMap(pool).updateFromString(str); } /** * @param {AttributePool} pool - Attribute pool. */ - constructor(pool) { + constructor(pool?: AttributePool|null) { super(); /** @public */ this.pool = pool; @@ -46,15 +50,15 @@ class AttributeMap extends Map { * @param {string} v - Attribute value. * @returns {AttributeMap} `this` (for chaining). */ - set(k, v) { + set(k: string, v: string):this { k = k == null ? '' : String(k); v = v == null ? '' : String(v); - this.pool.putAttrib([k, v]); + this.pool!.putAttrib([k, v]); return super.set(k, v); } toString() { - return attributes.attribsToString(attributes.sort([...this]), this.pool); + return attributes.attribsToString(attributes.sort([...this]), this.pool!); } /** @@ -63,7 +67,7 @@ class AttributeMap extends Map { * key is removed from this map (if present). * @returns {AttributeMap} `this` (for chaining). */ - update(entries, emptyValueIsDelete = false) { + update(entries: Iterable, emptyValueIsDelete: boolean = false): AttributeMap { for (let [k, v] of entries) { k = k == null ? '' : String(k); v = v == null ? '' : String(v); @@ -83,9 +87,9 @@ class AttributeMap extends Map { * key is removed from this map (if present). * @returns {AttributeMap} `this` (for chaining). */ - updateFromString(str, emptyValueIsDelete = false) { - return this.update(attributes.attribsFromString(str, this.pool), emptyValueIsDelete); + updateFromString(str: string, emptyValueIsDelete: boolean = false): AttributeMap { + return this.update(attributes.attribsFromString(str, this.pool!), emptyValueIsDelete); } } -module.exports = AttributeMap; +export default AttributeMap diff --git a/src/static/js/AttributePool.js b/src/static/js/AttributePool.ts similarity index 91% rename from src/static/js/AttributePool.js rename to src/static/js/AttributePool.ts index ccdd2eb35ca..5bbe52122a4 100644 --- a/src/static/js/AttributePool.js +++ b/src/static/js/AttributePool.ts @@ -44,6 +44,8 @@ * @property {number} nextNum - The attribute ID to assign to the next new attribute. */ +import {Attribute} from "./types/Attribute"; + /** * Represents an attribute pool, which is a collection of attributes (pairs of key and value * strings) along with their identifiers (non-negative integers). @@ -55,6 +57,14 @@ * in the pad. */ class AttributePool { + numToAttrib: { + [key: number]: [string, string] + } + private attribToNum: { + [key: number]: [string, string] + } + private nextNum: number + constructor() { /** * Maps an attribute identifier to the attribute's `[key, value]` string pair. @@ -96,7 +106,10 @@ class AttributePool { */ clone() { const c = new AttributePool(); - for (const [n, a] of Object.entries(this.numToAttrib)) c.numToAttrib[n] = [a[0], a[1]]; + for (const [n, a] of Object.entries(this.numToAttrib)){ + // @ts-ignore + c.numToAttrib[n] = [a[0], a[1]]; + } Object.assign(c.attribToNum, this.attribToNum); c.nextNum = this.nextNum; return c; @@ -111,15 +124,17 @@ class AttributePool { * membership in the pool without mutating the pool. * @returns {number} The attribute's identifier, or -1 if the attribute is not in the pool. */ - putAttrib(attrib, dontAddIfAbsent = false) { + putAttrib(attrib: Attribute, dontAddIfAbsent = false) { const str = String(attrib); if (str in this.attribToNum) { + // @ts-ignore return this.attribToNum[str]; } if (dontAddIfAbsent) { return -1; } const num = this.nextNum++; + // @ts-ignore this.attribToNum[str] = num; this.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')]; return num; @@ -130,7 +145,7 @@ class AttributePool { * @returns {Attribute} The attribute with the given identifier, or nullish if there is no such * attribute. */ - getAttrib(num) { + getAttrib(num: number): Attribute { const pair = this.numToAttrib[num]; if (!pair) { return pair; @@ -143,7 +158,7 @@ class AttributePool { * @returns {string} Eqivalent to `getAttrib(num)[0]` if the attribute exists, otherwise the empty * string. */ - getAttribKey(num) { + getAttribKey(num: number): string { const pair = this.numToAttrib[num]; if (!pair) return ''; return pair[0]; @@ -154,7 +169,7 @@ class AttributePool { * @returns {string} Eqivalent to `getAttrib(num)[1]` if the attribute exists, otherwise the empty * string. */ - getAttribValue(num) { + getAttribValue(num: number) { const pair = this.numToAttrib[num]; if (!pair) return ''; return pair[1]; @@ -166,8 +181,8 @@ class AttributePool { * @param {Function} func - Callback to call with two arguments: key and value. Its return value * is ignored. */ - eachAttrib(func) { - for (const n of Object.keys(this.numToAttrib)) { + eachAttrib(func: (k: string, v: string)=>void) { + for (const n in this.numToAttrib) { const pair = this.numToAttrib[n]; func(pair[0], pair[1]); } @@ -196,11 +211,12 @@ class AttributePool { * `new AttributePool().fromJsonable(pool.toJsonable())` to copy because the resulting shared * state will lead to pool corruption. */ - fromJsonable(obj) { + fromJsonable(obj: this) { this.numToAttrib = obj.numToAttrib; this.nextNum = obj.nextNum; this.attribToNum = {}; for (const n of Object.keys(this.numToAttrib)) { + // @ts-ignore this.attribToNum[String(this.numToAttrib[n])] = Number(n); } return this; @@ -213,6 +229,7 @@ class AttributePool { if (!Number.isInteger(this.nextNum)) throw new Error('nextNum property is not an integer'); if (this.nextNum < 0) throw new Error('nextNum property is negative'); for (const prop of ['numToAttrib', 'attribToNum']) { + // @ts-ignore const obj = this[prop]; if (obj == null) throw new Error(`${prop} property is null`); if (typeof obj !== 'object') throw new TypeError(`${prop} property is not an object`); @@ -231,9 +248,10 @@ class AttributePool { if (v == null) throw new TypeError(`attrib ${i} value is null`); if (typeof v !== 'string') throw new TypeError(`attrib ${i} value is not a string`); const attrStr = String(attr); + // @ts-ignore if (this.attribToNum[attrStr] !== i) throw new Error(`attribToNum for ${attrStr} !== ${i}`); } } } -module.exports = AttributePool; +export default AttributePool diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.ts similarity index 99% rename from src/static/js/Changeset.js rename to src/static/js/Changeset.ts index 53b3f2c8f09..99987f8a8a2 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /* @@ -22,10 +23,10 @@ * https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js */ -const AttributeMap = require('./AttributeMap'); -const AttributePool = require('./AttributePool'); +import AttributeMap from './AttributeMap'; +import AttributePool from './AttributePool'; const attributes = require('./attributes'); -const {padutils} = require('./pad_utils'); +import padutils from './pad_utils'; /** * A `[key, value]` pair of strings describing a text attribute. diff --git a/src/static/js/ChangesetUtils.js b/src/static/js/ChangesetUtils.ts similarity index 99% rename from src/static/js/ChangesetUtils.js rename to src/static/js/ChangesetUtils.ts index ef2be2ebe07..b42a7e8f974 100644 --- a/src/static/js/ChangesetUtils.js +++ b/src/static/js/ChangesetUtils.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/ChatMessage.js b/src/static/js/ChatMessage.ts similarity index 66% rename from src/static/js/ChatMessage.js rename to src/static/js/ChatMessage.ts index a627f88f9f3..db2d3403a19 100644 --- a/src/static/js/ChatMessage.js +++ b/src/static/js/ChatMessage.ts @@ -1,6 +1,6 @@ 'use strict'; -const {padutils: {warnDeprecated}} = require('./pad_utils'); +import padUtils from './pad_utils' /** * Represents a chat message stored in the database and transmitted among users. Plugins can extend @@ -8,14 +8,25 @@ const {padutils: {warnDeprecated}} = require('./pad_utils'); * * Supports serialization to JSON. */ -class ChatMessage { - static fromObject(obj) { +export class ChatMessage { + customMetadata: any + text: string|null + public authorId: string|null + displayName: string|null + time: number|null + static fromObject(obj: ChatMessage) { // The userId property was renamed to authorId, and userName was renamed to displayName. Accept // the old names in case the db record was written by an older version of Etherpad. obj = Object.assign({}, obj); // Don't mutate the caller's object. - if ('userId' in obj && !('authorId' in obj)) obj.authorId = obj.userId; + if ('userId' in obj && !('authorId' in obj)) { // @ts-ignore + obj.authorId = obj.userId; + } + // @ts-ignore delete obj.userId; - if ('userName' in obj && !('displayName' in obj)) obj.displayName = obj.userName; + if ('userName' in obj && !('displayName' in obj)) { // @ts-ignore + obj.displayName = obj.userName; + } + // @ts-ignore delete obj.userName; return Object.assign(new ChatMessage(), obj); } @@ -25,7 +36,7 @@ class ChatMessage { * @param {?string} [authorId] - Initial value of the `authorId` property. * @param {?number} [time] - Initial value of the `time` property. */ - constructor(text = null, authorId = null, time = null) { + constructor(text: string | null = null, authorId: string | null = null, time: number | null = null) { /** * The raw text of the user's chat message (before any rendering or processing). * @@ -62,11 +73,11 @@ class ChatMessage { * @type {string} */ get userId() { - warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead'); + padUtils.warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead'); return this.authorId; } set userId(val) { - warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead'); + padUtils.warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead'); this.authorId = val; } @@ -77,11 +88,11 @@ class ChatMessage { * @type {string} */ get userName() { - warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead'); + padUtils.warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead'); return this.displayName; } set userName(val) { - warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead'); + padUtils.warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead'); this.displayName = val; } @@ -89,10 +100,12 @@ class ChatMessage { // doesn't support authorId and displayName. toJSON() { const {authorId, displayName, ...obj} = this; + // @ts-ignore obj.userId = authorId; + // @ts-ignore obj.userName = displayName; return obj; } } -module.exports = ChatMessage; +export default ChatMessage diff --git a/src/static/js/ace.js b/src/static/js/ace.ts similarity index 99% rename from src/static/js/ace.js rename to src/static/js/ace.ts index a1b5d99c8a5..4c062584cde 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** * This code is mostly from the old Etherpad. Please help us to comment this code. diff --git a/src/static/js/ace2_common.js b/src/static/js/ace2_common.ts similarity index 76% rename from src/static/js/ace2_common.js rename to src/static/js/ace2_common.ts index c1dab5cfd8b..0a5f308e6a2 100644 --- a/src/static/js/ace2_common.js +++ b/src/static/js/ace2_common.ts @@ -6,6 +6,8 @@ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED */ +import {MapArrayType} from "../../node/types/MapType"; + /** * Copyright 2009 Google Inc. * @@ -22,11 +24,13 @@ * limitations under the License. */ -const isNodeText = (node) => (node.nodeType === 3); +export const isNodeText = (node: { + nodeType: number +}) => (node.nodeType === 3); -const getAssoc = (obj, name) => obj[`_magicdom_${name}`]; +export const getAssoc = (obj: MapArrayType, name: string) => obj[`_magicdom_${name}`]; -const setAssoc = (obj, name, value) => { +export const setAssoc = (obj: MapArrayType, name: string, value: string) => { // note that in IE designMode, properties of a node can get // copied to new nodes that are spawned during editing; also, // properties representable in HTML text can survive copy-and-paste @@ -38,7 +42,7 @@ const setAssoc = (obj, name, value) => { // between false and true, a number between 0 and numItems inclusive. -const binarySearch = (numItems, func) => { +export const binarySearch = (numItems: number, func: (num: number)=>boolean) => { if (numItems < 1) return 0; if (func(0)) return 0; if (!func(numItems - 1)) return numItems; @@ -52,17 +56,10 @@ const binarySearch = (numItems, func) => { return high; }; -const binarySearchInfinite = (expectedLength, func) => { +export const binarySearchInfinite = (expectedLength: number, func: (num: number)=>boolean) => { let i = 0; while (!func(i)) i += expectedLength; return binarySearch(i, func); }; -const noop = () => {}; - -exports.isNodeText = isNodeText; -exports.getAssoc = getAssoc; -exports.setAssoc = setAssoc; -exports.binarySearch = binarySearch; -exports.binarySearchInfinite = binarySearchInfinite; -exports.noop = noop; +export const noop = () => {}; diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.ts similarity index 99% rename from src/static/js/ace2_inner.js rename to src/static/js/ace2_inner.ts index 641c5ecdbe6..af391f1152d 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -18,9 +19,9 @@ */ let documentAttributeManager; -const AttributeMap = require('./AttributeMap'); +import AttributeMap from './AttributeMap'; const browser = require('./vendors/browser'); -const padutils = require('./pad_utils').padutils; +import padutils from './pad_utils' const Ace2Common = require('./ace2_common'); const $ = require('./rjquery').$; @@ -29,19 +30,18 @@ const getAssoc = Ace2Common.getAssoc; const setAssoc = Ace2Common.setAssoc; const noop = Ace2Common.noop; const hooks = require('./pluginfw/hooks'); - +import SkipList from "./skiplist"; import Scroll from './scroll' +import AttribPool from './AttributePool' function Ace2Inner(editorInfo, cssManagers) { const makeChangesetTracker = require('./changesettracker').makeChangesetTracker; const colorutils = require('./colorutils').colorutils; const makeContentCollector = require('./contentcollector').makeContentCollector; const domline = require('./domline').domline; - const AttribPool = require('./AttributePool'); const Changeset = require('./Changeset'); const ChangesetUtils = require('./ChangesetUtils'); const linestylefilter = require('./linestylefilter').linestylefilter; - const SkipList = require('./skiplist'); const undoModule = require('./undomodule').undoModule; const AttributeManager = require('./AttributeManager'); const DEBUG = false; diff --git a/src/static/js/attributes.js b/src/static/js/attributes.ts similarity index 77% rename from src/static/js/attributes.js rename to src/static/js/attributes.ts index 4ab34701957..b164f8759c4 100644 --- a/src/static/js/attributes.js +++ b/src/static/js/attributes.ts @@ -17,6 +17,9 @@ * @typedef {string} AttributeString */ +import AttributePool from "./AttributePool"; +import {Attribute} from "./types/Attribute"; + /** * Converts an attribute string into a sequence of attribute identifier numbers. * @@ -28,7 +31,7 @@ * appear in `str`. * @returns {Generator} */ -exports.decodeAttribString = function* (str) { +export const decodeAttribString = function* (str: string): Generator { const re = /\*([0-9a-z]+)|./gy; let match; while ((match = re.exec(str)) != null) { @@ -38,7 +41,7 @@ exports.decodeAttribString = function* (str) { } }; -const checkAttribNum = (n) => { +const checkAttribNum = (n: number|object) => { if (typeof n !== 'number') throw new TypeError(`not a number: ${n}`); if (n < 0) throw new Error(`attribute number is negative: ${n}`); if (n !== Math.trunc(n)) throw new Error(`attribute number is not an integer: ${n}`); @@ -50,7 +53,7 @@ const checkAttribNum = (n) => { * @param {Iterable} attribNums - Sequence of attribute numbers. * @returns {AttributeString} */ -exports.encodeAttribString = (attribNums) => { +export const encodeAttribString = (attribNums: Iterable): string => { let str = ''; for (const n of attribNums) { checkAttribNum(n); @@ -67,7 +70,7 @@ exports.encodeAttribString = (attribNums) => { * @yields {Attribute} The identified attributes, in the same order as `attribNums`. * @returns {Generator} */ -exports.attribsFromNums = function* (attribNums, pool) { +export const attribsFromNums = function* (attribNums: Iterable, pool: AttributePool): Generator { for (const n of attribNums) { checkAttribNum(n); const attrib = pool.getAttrib(n); @@ -87,7 +90,7 @@ exports.attribsFromNums = function* (attribNums, pool) { * @yields {number} The attribute number of each attribute in `attribs`, in order. * @returns {Generator} */ -exports.attribsToNums = function* (attribs, pool) { +export const attribsToNums = function* (attribs: Iterable, pool: AttributePool) { for (const attrib of attribs) yield pool.putAttrib(attrib); }; @@ -102,8 +105,8 @@ exports.attribsToNums = function* (attribs, pool) { * @yields {Attribute} The attributes identified in `str`, in order. * @returns {Generator} */ -exports.attribsFromString = function* (str, pool) { - yield* exports.attribsFromNums(exports.decodeAttribString(str), pool); +export const attribsFromString = function* (str: string, pool: AttributePool): Generator { + yield* attribsFromNums(decodeAttribString(str), pool); }; /** @@ -116,8 +119,8 @@ exports.attribsFromString = function* (str, pool) { * @param {AttributePool} pool - Attribute pool. * @returns {AttributeString} */ -exports.attribsToString = - (attribs, pool) => exports.encodeAttribString(exports.attribsToNums(attribs, pool)); +export const attribsToString = + (attribs: Iterable, pool: AttributePool): string => encodeAttribString(attribsToNums(attribs, pool)); /** * Sorts the attributes in canonical order. The order of entries with the same attribute name is @@ -126,5 +129,14 @@ exports.attribsToString = * @param {Attribute[]} attribs - Attributes to sort in place. * @returns {Attribute[]} `attribs` (for chaining). */ -exports.sort = - (attribs) => attribs.sort(([keyA], [keyB]) => (keyA > keyB ? 1 : 0) - (keyA < keyB ? 1 : 0)); +export const sort = (attribs: Attribute[]): Attribute[] => attribs.sort(([keyA], [keyB]) => (keyA > keyB ? 1 : 0) - (keyA < keyB ? 1 : 0)); + +export default { + decodeAttribString, + encodeAttribString, + attribsFromNums, + attribsToNums, + attribsFromString, + attribsToString, + sort, +} diff --git a/src/static/js/basic_error_handler.js b/src/static/js/basic_error_handler.ts similarity index 99% rename from src/static/js/basic_error_handler.js rename to src/static/js/basic_error_handler.ts index ab400aa8a83..ce8c4735348 100644 --- a/src/static/js/basic_error_handler.js +++ b/src/static/js/basic_error_handler.ts @@ -1,3 +1,4 @@ +// @ts-nocheck // @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt Apache-2.0 /* Copyright 2021 Richard Hansen */ diff --git a/src/static/js/broadcast.js b/src/static/js/broadcast.ts similarity index 99% rename from src/static/js/broadcast.js rename to src/static/js/broadcast.ts index 2163fd78ee3..518107e5a9e 100644 --- a/src/static/js/broadcast.js +++ b/src/static/js/broadcast.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -24,7 +25,7 @@ const makeCSSManager = require('./cssmanager').makeCSSManager; const domline = require('./domline').domline; -const AttribPool = require('./AttributePool'); +import AttribPool from './AttributePool'; const Changeset = require('./Changeset'); const attributes = require('./attributes'); const linestylefilter = require('./linestylefilter').linestylefilter; diff --git a/src/static/js/broadcast_revisions.js b/src/static/js/broadcast_revisions.ts similarity index 99% rename from src/static/js/broadcast_revisions.js rename to src/static/js/broadcast_revisions.ts index 8a9eb2e6c16..37272d86078 100644 --- a/src/static/js/broadcast_revisions.js +++ b/src/static/js/broadcast_revisions.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.ts similarity index 99% rename from src/static/js/broadcast_slider.js rename to src/static/js/broadcast_slider.ts index 848ba06cf05..ccd896c4c0b 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** * This code is mostly from the old Etherpad. Please help us to comment this code. diff --git a/src/static/js/changesettracker.js b/src/static/js/changesettracker.ts similarity index 98% rename from src/static/js/changesettracker.js rename to src/static/js/changesettracker.ts index 30c70aa748f..86a63546bb0 100644 --- a/src/static/js/changesettracker.js +++ b/src/static/js/changesettracker.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -22,8 +23,8 @@ * limitations under the License. */ -const AttributeMap = require('./AttributeMap'); -const AttributePool = require('./AttributePool'); +import AttributeMap from './AttributeMap'; +import AttributePool from './AttributePool'; const Changeset = require('./Changeset'); const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => { diff --git a/src/static/js/chat.js b/src/static/js/chat.ts old mode 100755 new mode 100644 similarity index 99% rename from src/static/js/chat.js rename to src/static/js/chat.ts index d32a62c7ab0..6357663c4f6 --- a/src/static/js/chat.js +++ b/src/static/js/chat.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** * Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd) @@ -15,8 +16,8 @@ * limitations under the License. */ -const ChatMessage = require('./ChatMessage'); -const padutils = require('./pad_utils').padutils; +import ChatMessage from './ChatMessage'; +import padutils from './pad_utils' const padcookie = require('./pad_cookie').padcookie; const Tinycon = require('tinycon/tinycon'); const hooks = require('./pluginfw/hooks'); diff --git a/src/static/js/collab_client.js b/src/static/js/collab_client.ts similarity index 99% rename from src/static/js/collab_client.js rename to src/static/js/collab_client.ts index 69c8e41f302..7bc1c37df51 100644 --- a/src/static/js/collab_client.js +++ b/src/static/js/collab_client.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/colorutils.js b/src/static/js/colorutils.ts similarity index 99% rename from src/static/js/colorutils.js rename to src/static/js/colorutils.ts index 9688b8e59ce..b60b32aa97d 100644 --- a/src/static/js/colorutils.js +++ b/src/static/js/colorutils.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.ts similarity index 99% rename from src/static/js/contentcollector.js rename to src/static/js/contentcollector.ts index 4735374eeac..0c67687e4f2 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** * This code is mostly from the old Etherpad. Please help us to comment this code. @@ -26,7 +27,7 @@ const _MAX_LIST_LEVEL = 16; -const AttributeMap = require('./AttributeMap'); +import AttributeMap from './AttributeMap'; const UNorm = require('unorm'); const Changeset = require('./Changeset'); const hooks = require('./pluginfw/hooks'); diff --git a/src/static/js/cssmanager.js b/src/static/js/cssmanager.ts similarity index 99% rename from src/static/js/cssmanager.js rename to src/static/js/cssmanager.ts index 5bf2adb303b..89036df6723 100644 --- a/src/static/js/cssmanager.js +++ b/src/static/js/cssmanager.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/domline.js b/src/static/js/domline.ts similarity index 99% rename from src/static/js/domline.js rename to src/static/js/domline.ts index af786b2dc40..900f60176f4 100644 --- a/src/static/js/domline.js +++ b/src/static/js/domline.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline diff --git a/src/static/js/linestylefilter.js b/src/static/js/linestylefilter.ts similarity index 99% rename from src/static/js/linestylefilter.js rename to src/static/js/linestylefilter.ts index 632e6b3ccb5..3caab50f064 100644 --- a/src/static/js/linestylefilter.js +++ b/src/static/js/linestylefilter.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -35,7 +36,7 @@ const attributes = require('./attributes'); const hooks = require('./pluginfw/hooks'); const linestylefilter = {}; const AttributeManager = require('./AttributeManager'); -const padutils = require('./pad_utils').padutils; +import padutils from './pad_utils' linestylefilter.ATTRIB_CLASSES = { bold: 'tag:b', diff --git a/src/static/js/pad.js b/src/static/js/pad.ts similarity index 99% rename from src/static/js/pad.js rename to src/static/js/pad.ts index d6648f031db..e94611fcd3b 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -33,7 +34,8 @@ require('./vendors/gritter'); import html10n from './vendors/html10n' -const Cookies = require('./pad_utils').Cookies; +import {Cookies} from "./pad_utils"; + const chat = require('./chat').chat; const getCollabClient = require('./collab_client').getCollabClient; const padconnectionstatus = require('./pad_connectionstatus').padconnectionstatus; @@ -44,9 +46,9 @@ const padimpexp = require('./pad_impexp').padimpexp; const padmodals = require('./pad_modals').padmodals; const padsavedrevs = require('./pad_savedrevs'); const paduserlist = require('./pad_userlist').paduserlist; -const padutils = require('./pad_utils').padutils; +import padutils from './pad_utils' const colorutils = require('./colorutils').colorutils; -const randomString = require('./pad_utils').randomString; +import {randomString} from "./pad_utils"; const socketio = require('./socketio'); const hooks = require('./pluginfw/hooks'); diff --git a/src/static/js/pad_automatic_reconnect.js b/src/static/js/pad_automatic_reconnect.ts similarity index 99% rename from src/static/js/pad_automatic_reconnect.js rename to src/static/js/pad_automatic_reconnect.ts index 03fc9143258..8172d5be789 100644 --- a/src/static/js/pad_automatic_reconnect.js +++ b/src/static/js/pad_automatic_reconnect.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; import html10n from './vendors/html10n'; diff --git a/src/static/js/pad_connectionstatus.js b/src/static/js/pad_connectionstatus.ts similarity index 99% rename from src/static/js/pad_connectionstatus.js rename to src/static/js/pad_connectionstatus.ts index 7b0497d96d8..600defa8dd0 100644 --- a/src/static/js/pad_connectionstatus.js +++ b/src/static/js/pad_connectionstatus.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/pad_cookie.js b/src/static/js/pad_cookie.ts similarity index 97% rename from src/static/js/pad_cookie.js rename to src/static/js/pad_cookie.ts index 0e946ea5c89..bc624e96250 100644 --- a/src/static/js/pad_cookie.js +++ b/src/static/js/pad_cookie.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -16,7 +17,7 @@ * limitations under the License. */ -const Cookies = require('./pad_utils').Cookies; +import {Cookies} from "./pad_utils"; exports.padcookie = new class { constructor() { diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.ts similarity index 99% rename from src/static/js/pad_editbar.js rename to src/static/js/pad_editbar.ts index af8d59f1fc5..d98174fe568 100644 --- a/src/static/js/pad_editbar.js +++ b/src/static/js/pad_editbar.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -24,7 +25,7 @@ const browser = require('./vendors/browser'); const hooks = require('./pluginfw/hooks'); -const padutils = require('./pad_utils').padutils; +import padutils from "./pad_utils"; const padeditor = require('./pad_editor').padeditor; const padsavedrevs = require('./pad_savedrevs'); const _ = require('underscore'); diff --git a/src/static/js/pad_editor.js b/src/static/js/pad_editor.ts similarity index 98% rename from src/static/js/pad_editor.js rename to src/static/js/pad_editor.ts index 47a25073458..362412f2fdd 100644 --- a/src/static/js/pad_editor.js +++ b/src/static/js/pad_editor.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** * This code is mostly from the old Etherpad. Please help us to comment this code. @@ -21,9 +22,8 @@ * limitations under the License. */ -const Cookies = require('./pad_utils').Cookies; +import padutils,{Cookies} from "./pad_utils"; const padcookie = require('./pad_cookie').padcookie; -const padutils = require('./pad_utils').padutils; const Ace2Editor = require('./ace').Ace2Editor; import html10n from '../js/vendors/html10n' diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.ts similarity index 99% rename from src/static/js/pad_impexp.js rename to src/static/js/pad_impexp.ts index 3aca9fb7cdd..8b8575c819d 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/pad_modals.js b/src/static/js/pad_modals.ts similarity index 99% rename from src/static/js/pad_modals.js rename to src/static/js/pad_modals.ts index 54bd838772d..3e2c2459b9b 100644 --- a/src/static/js/pad_modals.js +++ b/src/static/js/pad_modals.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/pad_savedrevs.js b/src/static/js/pad_savedrevs.ts similarity index 98% rename from src/static/js/pad_savedrevs.js rename to src/static/js/pad_savedrevs.ts index 4082e03807a..6722a03a21d 100644 --- a/src/static/js/pad_savedrevs.js +++ b/src/static/js/pad_savedrevs.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/pad_userlist.js b/src/static/js/pad_userlist.ts similarity index 99% rename from src/static/js/pad_userlist.js rename to src/static/js/pad_userlist.ts index a0cbd4b44aa..227e6b0630a 100644 --- a/src/static/js/pad_userlist.js +++ b/src/static/js/pad_userlist.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -16,7 +17,7 @@ * limitations under the License. */ -const padutils = require('./pad_utils').padutils; +import padutils from './pad_utils' const hooks = require('./pluginfw/hooks'); import html10n from './vendors/html10n'; let myUserInfo = {}; diff --git a/src/static/js/pad_utils.js b/src/static/js/pad_utils.ts similarity index 58% rename from src/static/js/pad_utils.js rename to src/static/js/pad_utils.ts index 467a8adc90d..0a66abf0003 100644 --- a/src/static/js/pad_utils.js +++ b/src/static/js/pad_utils.ts @@ -6,6 +6,8 @@ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED */ +import {binarySearch} from "./ace2_common"; + /** * Copyright 2009 Google Inc. * @@ -22,13 +24,14 @@ * limitations under the License. */ -const Security = require('./security'); +const Security = require('security'); +import jsCookie, {CookiesStatic} from 'js-cookie' /** * Generates a random String with the given length. Is needed to generate the Author, Group, * readonly, session Ids */ -const randomString = (len) => { +export const randomString = (len?: number) => { const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; let randomstring = ''; len = len || 20; @@ -85,13 +88,41 @@ const urlRegex = (() => { 'tel', ].join('|')}):`; return new RegExp( - `(?:${withAuth}|${withoutAuth}|www\\.)${urlChar}*(?!${postUrlPunct})${urlChar}`, 'g'); + `(?:${withAuth}|${withoutAuth}|www\\.)${urlChar}*(?!${postUrlPunct})${urlChar}`, 'g'); })(); // https://stackoverflow.com/a/68957976 const base64url = /^(?=(?:.{4})*$)[A-Za-z0-9_-]*(?:[AQgw]==|[AEIMQUYcgkosw048]=)?$/; -const padutils = { +type PadEvent = { + which: number +} + +type JQueryNode = JQuery + +class PadUtils { + public urlRegex: RegExp + public wordCharRegex: RegExp + public warnDeprecatedFlags: { + disabledForTestingOnly: boolean, + _rl?: { + prevs: Map, + now: () => number, + period: number + } + logger?: any + } + public globalExceptionHandler: null | any = null; + + + constructor() { + this.warnDeprecatedFlags = { + disabledForTestingOnly: false + } + this.wordCharRegex = wordCharRegex + this.urlRegex = urlRegex + } + /** * Prints a warning message followed by a stack trace (to make it easier to figure out what code * is using the deprecated function). @@ -107,41 +138,41 @@ const padutils = { * @param {...*} args - Passed to `padutils.warnDeprecated.logger.warn` (or `console.warn` if no * logger is set), with a stack trace appended if available. */ - warnDeprecated: (...args) => { - if (padutils.warnDeprecated.disabledForTestingOnly) return; + warnDeprecated = (...args: any[]) => { + if (this.warnDeprecatedFlags.disabledForTestingOnly) return; const err = new Error(); - if (Error.captureStackTrace) Error.captureStackTrace(err, padutils.warnDeprecated); + if (Error.captureStackTrace) Error.captureStackTrace(err, this.warnDeprecated); err.name = ''; // Rate limit identical deprecation warnings (as determined by the stack) to avoid log spam. if (typeof err.stack === 'string') { - if (padutils.warnDeprecated._rl == null) { - padutils.warnDeprecated._rl = - {prevs: new Map(), now: () => Date.now(), period: 10 * 60 * 1000}; + if (this.warnDeprecatedFlags._rl == null) { + this.warnDeprecatedFlags._rl = + {prevs: new Map(), now: () => Date.now(), period: 10 * 60 * 1000}; } - const rl = padutils.warnDeprecated._rl; + const rl = this.warnDeprecatedFlags._rl; const now = rl.now(); const prev = rl.prevs.get(err.stack); if (prev != null && now - prev < rl.period) return; rl.prevs.set(err.stack, now); } if (err.stack) args.push(err.stack); - (padutils.warnDeprecated.logger || console).warn(...args); - }, - - escapeHtml: (x) => Security.escapeHTML(String(x)), - uniqueId: () => { + (this.warnDeprecatedFlags.logger || console).warn(...args); + } + escapeHtml = (x: string) => Security.escapeHTML(String(x)) + uniqueId = () => { const pad = require('./pad').pad; // Sidestep circular dependency // returns string that is exactly 'width' chars, padding with zeros and taking rightmost digits const encodeNum = - (n, width) => (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width); + (n: number, width: number) => (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width); return [ pad.getClientIp(), encodeNum(+new Date(), 7), encodeNum(Math.floor(Math.random() * 1e9), 4), ].join('.'); - }, + } + // e.g. "Thu Jun 18 2009 13:09" - simpleDateTime: (date) => { + simpleDateTime = (date: string) => { const d = new Date(+date); // accept either number or date const dayOfWeek = (['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'])[d.getDay()]; const month = ([ @@ -162,16 +193,14 @@ const padutils = { const year = d.getFullYear(); const hourmin = `${d.getHours()}:${(`0${d.getMinutes()}`).slice(-2)}`; return `${dayOfWeek} ${month} ${dayOfMonth} ${year} ${hourmin}`; - }, - wordCharRegex, - urlRegex, + } // returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...] - findURLs: (text) => { + findURLs = (text: string) => { // Copy padutils.urlRegex so that the use of .exec() below (which mutates the RegExp object) // does not break other concurrent uses of padutils.urlRegex. - const urlRegex = new RegExp(padutils.urlRegex, 'g'); + const urlRegex = new RegExp(this.urlRegex, 'g'); urlRegex.lastIndex = 0; - let urls = null; + let urls: [number, string][] | null = null; let execResult; // TODO: Switch to String.prototype.matchAll() after support for Node.js < 12.0.0 is dropped. while ((execResult = urlRegex.exec(text))) { @@ -181,18 +210,19 @@ const padutils = { urls.push([startIndex, url]); } return urls; - }, - escapeHtmlWithClickableLinks: (text, target) => { + } + escapeHtmlWithClickableLinks = (text: string, target: string) => { let idx = 0; const pieces = []; - const urls = padutils.findURLs(text); + const urls = this.findURLs(text); - const advanceTo = (i) => { - if (i > idx) { - pieces.push(Security.escapeHTML(text.substring(idx, i))); - idx = i; + const advanceTo = (i: number) => { + if (i > idx) { + pieces.push(Security.escapeHTML(text.substring(idx, i))); + idx = i; + } } - }; + ; if (urls) { for (let j = 0; j < urls.length; j++) { const startIndex = urls[j][0]; @@ -206,25 +236,25 @@ const padutils = { // https://mathiasbynens.github.io/rel-noopener/ // https://github.com/ether/etherpad-lite/pull/3636 pieces.push( - ''); + ''); advanceTo(startIndex + href.length); pieces.push(''); } } advanceTo(text.length); return pieces.join(''); - }, - bindEnterAndEscape: (node, onEnter, onEscape) => { + } + bindEnterAndEscape = (node: JQueryNode, onEnter: Function, onEscape: Function) => { // Use keypress instead of keyup in bindEnterAndEscape. Keyup event is fired on enter in IME // (Input Method Editor), But keypress is not. So, I changed to use keypress instead of keyup. // It is work on Windows (IE8, Chrome 6.0.472), CentOs (Firefox 3.0) and Mac OSX (Firefox // 3.6.10, Chrome 6.0.472, Safari 5.0). if (onEnter) { - node.on('keypress', (evt) => { + node.on('keypress', (evt: { which: number; }) => { if (evt.which === 13) { onEnter(evt); } @@ -238,13 +268,15 @@ const padutils = { } }); } - }, - timediff: (d) => { + } + + timediff = (d: number) => { const pad = require('./pad').pad; // Sidestep circular dependency - const format = (n, word) => { - n = Math.round(n); - return (`${n} ${word}${n !== 1 ? 's' : ''} ago`); - }; + const format = (n: number, word: string) => { + n = Math.round(n); + return (`${n} ${word}${n !== 1 ? 's' : ''} ago`); + } + ; d = Math.max(0, (+(new Date()) - (+d) - pad.clientTimeOffset) / 1000); if (d < 60) { return format(d, 'second'); @@ -259,78 +291,89 @@ const padutils = { } d /= 24; return format(d, 'day'); - }, - makeAnimationScheduler: (funcToAnimateOneStep, stepTime, stepsAtOnce) => { - if (stepsAtOnce === undefined) { - stepsAtOnce = 1; - } + } + makeAnimationScheduler = + (funcToAnimateOneStep: any, stepTime: number, stepsAtOnce?: number) => { + if (stepsAtOnce === undefined) { + stepsAtOnce = 1; + } - let animationTimer = null; + let animationTimer: any = null; - const scheduleAnimation = () => { - if (!animationTimer) { - animationTimer = window.setTimeout(() => { - animationTimer = null; - let n = stepsAtOnce; - let moreToDo = true; - while (moreToDo && n > 0) { - moreToDo = funcToAnimateOneStep(); - n--; - } - if (moreToDo) { - // more to do - scheduleAnimation(); - } - }, stepTime * stepsAtOnce); - } - }; - return {scheduleAnimation}; - }, - makeFieldLabeledWhenEmpty: (field, labelText) => { - field = $(field); + const scheduleAnimation = () => { + if (!animationTimer) { + animationTimer = window.setTimeout(() => { + animationTimer = null; + let n = stepsAtOnce; + let moreToDo = true; + while (moreToDo && n > 0) { + moreToDo = funcToAnimateOneStep(); + n--; + } + if (moreToDo) { + // more to do + scheduleAnimation(); + } + }, stepTime * stepsAtOnce); + } + }; + return {scheduleAnimation}; + } - const clear = () => { - field.addClass('editempty'); - field.val(labelText); - }; - field.focus(() => { - if (field.hasClass('editempty')) { - field.val(''); - } - field.removeClass('editempty'); - }); - field.on('blur', () => { - if (!field.val()) { - clear(); + makeFieldLabeledWhenEmpty + = + (field: JQueryNode, labelText: string) => { + field = $(field); + + const clear = () => { + field.addClass('editempty'); + field.val(labelText); + } + ; + field.focus(() => { + if (field.hasClass('editempty')) { + field.val(''); + } + field.removeClass('editempty'); + }); + field.on('blur', () => { + if (!field.val()) { + clear(); + } + }); + return { + clear, + }; + } + getCheckbox = (node: string) => $(node).is(':checked') + setCheckbox = + (node: JQueryNode, value: boolean) => { + if (value) { + $(node).attr('checked', 'checked'); + } else { + $(node).prop('checked', false); } - }); - return { - clear, - }; - }, - getCheckbox: (node) => $(node).is(':checked'), - setCheckbox: (node, value) => { - if (value) { - $(node).attr('checked', 'checked'); - } else { - $(node).prop('checked', false); } - }, - bindCheckboxChange: (node, func) => { - $(node).on('change', func); - }, - encodeUserId: (userId) => userId.replace(/[^a-y0-9]/g, (c) => { - if (c === '.') return '-'; - return `z${c.charCodeAt(0)}z`; - }), - decodeUserId: (encodedUserId) => encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, (cc) => { - if (cc === '-') { return '.'; } else if (cc.charAt(0) === 'z') { - return String.fromCharCode(Number(cc.slice(1, -1))); - } else { - return cc; + bindCheckboxChange = + (node: JQueryNode, func: Function) => { + // @ts-ignore + $(node).on("change", func); } - }), - + encodeUserId = + (userId: string) => userId.replace(/[^a-y0-9]/g, (c) => { + if (c === '.') return '-'; + return `z${c.charCodeAt(0)}z`; + }) + decodeUserId = + (encodedUserId: string) => encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, (cc) => { + if (cc === '-') { + return '.'; + } else if (cc.charAt(0) === 'z') { + return String.fromCharCode(Number(cc.slice(1, -1))); + } else { + return cc; + } + }) /** * Returns whether a string has the expected format to be used as a secret token identifying an * author. The format is defined as: 't.' followed by a non-empty base64url string (RFC 4648 @@ -340,109 +383,109 @@ const padutils = { * conditional transformation of a token to a database key in a way that does not allow a * malicious user to impersonate another user). */ - isValidAuthorToken: (t) => { + isValidAuthorToken = (t: string | object) => { if (typeof t !== 'string' || !t.startsWith('t.')) return false; const v = t.slice(2); return v.length > 0 && base64url.test(v); - }, + } + /** * Returns a string that can be used in the `token` cookie as a secret that authenticates a * particular author. */ - generateAuthorToken: () => `t.${randomString()}`, -}; - -let globalExceptionHandler = null; -padutils.setupGlobalExceptionHandler = () => { - if (globalExceptionHandler == null) { - globalExceptionHandler = (e) => { - let type; - let err; - let msg, url, linenumber; - if (e instanceof ErrorEvent) { - type = 'Uncaught exception'; - err = e.error || {}; - ({message: msg, filename: url, lineno: linenumber} = e); - } else if (e instanceof PromiseRejectionEvent) { - type = 'Unhandled Promise rejection'; - err = e.reason || {}; - ({message: msg = 'unknown', fileName: url = 'unknown', lineNumber: linenumber = -1} = err); - } else { - throw new Error(`unknown event: ${e.toString()}`); - } - if (err.name != null && msg !== err.name && !msg.startsWith(`${err.name}: `)) { - msg = `${err.name}: ${msg}`; - } - const errorId = randomString(20); - - let msgAlreadyVisible = false; - $('.gritter-item .error-msg').each(function () { - if ($(this).text() === msg) { - msgAlreadyVisible = true; + generateAuthorToken = () => `t.${randomString()}` + setupGlobalExceptionHandler = () => { + if (this.globalExceptionHandler == null) { + this.globalExceptionHandler = (e: any) => { + let type; + let err; + let msg, url, linenumber; + if (e instanceof ErrorEvent) { + type = 'Uncaught exception'; + err = e.error || {}; + ({message: msg, filename: url, lineno: linenumber} = e); + } else if (e instanceof PromiseRejectionEvent) { + type = 'Unhandled Promise rejection'; + err = e.reason || {}; + ({message: msg = 'unknown', fileName: url = 'unknown', lineNumber: linenumber = -1} = err); + } else { + throw new Error(`unknown event: ${e.toString()}`); } - }); + if (err.name != null && msg !== err.name && !msg.startsWith(`${err.name}: `)) { + msg = `${err.name}: ${msg}`; + } + const errorId = randomString(20); + + let msgAlreadyVisible = false; + $('.gritter-item .error-msg').each(function () { + if ($(this).text() === msg) { + msgAlreadyVisible = true; + } + }); - if (!msgAlreadyVisible) { - const txt = document.createTextNode.bind(document); // Convenience shorthand. - const errorMsg = [ - $('

') + if (!msgAlreadyVisible) { + const txt = document.createTextNode.bind(document); // Convenience shorthand. + const errorMsg = [ + $('

') .append($('').text('Please press and hold Ctrl and press F5 to reload this page')), - $('

') + $('

') .text('If the problem persists, please send this error message to your webmaster:'), - $('

').css('text-align', 'left').css('font-size', '.8em').css('margin-top', '1em') + $('
').css('text-align', 'left').css('font-size', '.8em').css('margin-top', '1em') .append($('').addClass('error-msg').text(msg)).append($('
')) .append(txt(`at ${url} at line ${linenumber}`)).append($('
')) .append(txt(`ErrorId: ${errorId}`)).append($('
')) .append(txt(type)).append($('
')) .append(txt(`URL: ${window.location.href}`)).append($('
')) .append(txt(`UserAgent: ${navigator.userAgent}`)).append($('
')), - ]; + ]; - $.gritter.add({ - title: 'An error occurred', - text: errorMsg, - class_name: 'error', - position: 'bottom', - sticky: true, - }); - } + // @ts-ignore + $.gritter.add({ + title: 'An error occurred', + text: errorMsg, + class_name: 'error', + position: 'bottom', + sticky: true, + }); + } - // send javascript errors to the server - $.post('../jserror', { - errorInfo: JSON.stringify({ - errorId, - type, - msg, - url: window.location.href, - source: url, - linenumber, - userAgent: navigator.userAgent, - stack: err.stack, - }), - }); - }; - window.onerror = null; // Clear any pre-existing global error handler. - window.addEventListener('error', globalExceptionHandler); - window.addEventListener('unhandledrejection', globalExceptionHandler); + // send javascript errors to the server + $.post('../jserror', { + errorInfo: JSON.stringify({ + errorId, + type, + msg, + url: window.location.href, + source: url, + linenumber, + userAgent: navigator.userAgent, + stack: err.stack, + }), + }); + }; + window.onerror = null; // Clear any pre-existing global error handler. + window.addEventListener('error', this.globalExceptionHandler); + window.addEventListener('unhandledrejection', this.globalExceptionHandler); + } } -}; - -padutils.binarySearch = require('./ace2_common').binarySearch; + binarySearch = binarySearch +} // https://stackoverflow.com/a/42660748 const inThirdPartyIframe = () => { try { - return (!window.top.location.hostname); + return (!window.top!.location.hostname); } catch (e) { return true; } }; +export let Cookies: CookiesStatic // This file is included from Node so that it can reuse randomString, but Node doesn't have a global // window object. if (typeof window !== 'undefined') { - exports.Cookies = require('js-cookie').withAttributes({ + Cookies = jsCookie.withAttributes({ // Use `SameSite=Lax`, unless Etherpad is embedded in an iframe from another site in which case // use `SameSite=None`. For iframes from another site, only `None` has a chance of working // because the cookies are third-party (not same-site). Many browsers/users block third-party @@ -455,5 +498,5 @@ if (typeof window !== 'undefined') { secure: window.location.protocol === 'https:', }); } -exports.randomString = randomString; -exports.padutils = padutils; + +export default new PadUtils() diff --git a/src/static/js/pluginfw/client_plugins.js b/src/static/js/pluginfw/client_plugins.ts similarity index 99% rename from src/static/js/pluginfw/client_plugins.js rename to src/static/js/pluginfw/client_plugins.ts index 3a068773335..0688d12ca7a 100644 --- a/src/static/js/pluginfw/client_plugins.js +++ b/src/static/js/pluginfw/client_plugins.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; const pluginUtils = require('./shared'); diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.ts similarity index 99% rename from src/static/js/pluginfw/hooks.js rename to src/static/js/pluginfw/hooks.ts index 731cbd19070..a480ecf46ee 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; const pluginDefs = require('./plugin_defs'); diff --git a/src/static/js/pluginfw/plugin_defs.js b/src/static/js/pluginfw/plugin_defs.ts similarity index 100% rename from src/static/js/pluginfw/plugin_defs.js rename to src/static/js/pluginfw/plugin_defs.ts diff --git a/src/static/js/pluginfw/plugins.js b/src/static/js/pluginfw/plugins.ts similarity index 99% rename from src/static/js/pluginfw/plugins.js rename to src/static/js/pluginfw/plugins.ts index f48d6cc6dd0..97c1694e217 100644 --- a/src/static/js/pluginfw/plugins.js +++ b/src/static/js/pluginfw/plugins.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; const fs = require('fs').promises; diff --git a/src/static/js/pluginfw/shared.js b/src/static/js/pluginfw/shared.ts similarity index 99% rename from src/static/js/pluginfw/shared.js rename to src/static/js/pluginfw/shared.ts index b2c2337f64e..a7c76178619 100644 --- a/src/static/js/pluginfw/shared.js +++ b/src/static/js/pluginfw/shared.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; const defs = require('./plugin_defs'); diff --git a/src/static/js/pluginfw/tsort.js b/src/static/js/pluginfw/tsort.ts similarity index 99% rename from src/static/js/pluginfw/tsort.js rename to src/static/js/pluginfw/tsort.ts index 6f0b4bf006b..a067d29de8d 100644 --- a/src/static/js/pluginfw/tsort.js +++ b/src/static/js/pluginfw/tsort.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/rjquery.js b/src/static/js/rjquery.ts similarity index 93% rename from src/static/js/rjquery.js rename to src/static/js/rjquery.ts index a80e1f8d30a..167e960907b 100644 --- a/src/static/js/rjquery.js +++ b/src/static/js/rjquery.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; // Provides a require'able version of jQuery without leaking $ and jQuery; window.$ = require('./vendors/jquery'); diff --git a/src/static/js/security.js b/src/static/js/security.ts similarity index 97% rename from src/static/js/security.js rename to src/static/js/security.ts index d92425cb789..d5f9b726622 100644 --- a/src/static/js/security.js +++ b/src/static/js/security.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/skin_variants.js b/src/static/js/skin_variants.ts similarity index 99% rename from src/static/js/skin_variants.js rename to src/static/js/skin_variants.ts index 9a0427ac908..c9783aaa3d8 100644 --- a/src/static/js/skin_variants.js +++ b/src/static/js/skin_variants.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; // Specific hash to display the skin variants builder popup diff --git a/src/static/js/skiplist.js b/src/static/js/skiplist.ts similarity index 61% rename from src/static/js/skiplist.js rename to src/static/js/skiplist.ts index f10a4e7a8ce..0e7724b2329 100644 --- a/src/static/js/skiplist.js +++ b/src/static/js/skiplist.ts @@ -22,10 +22,24 @@ * limitations under the License. */ -const _entryWidth = (e) => (e && e.width) || 0; +const _entryWidth = (e: Entry) => (e && e.width) || 0; + +type Entry = { + key: string, + value?: string + width?: number +} class Node { - constructor(entry, levels = 0, downSkips = 1, downSkipWidths = 0) { + public key: string|null + readonly entry: Entry|null + levels: number + upPtrs: Node[] + downPtrs: Node[] + downSkips: number[] + readonly downSkipWidths: number[] + + constructor(entry: Entry|null, levels = 0, downSkips: number|null = 1, downSkipWidths:number|null = 0) { this.key = entry != null ? entry.key : null; this.entry = entry; this.levels = levels; @@ -37,9 +51,9 @@ class Node { propagateWidthChange() { const oldWidth = this.downSkipWidths[0]; - const newWidth = _entryWidth(this.entry); + const newWidth = _entryWidth(this.entry!); const widthChange = newWidth - oldWidth; - let n = this; + let n: Node = this; let lvl = 0; while (lvl < n.levels) { n.downSkipWidths[lvl] += widthChange; @@ -57,17 +71,23 @@ class Node { // is still valid and points to the same index in the skiplist. Other operations with other points // invalidate this point. class Point { - constructor(skipList, loc) { - this._skipList = skipList; + private skipList: SkipList + private readonly loc: number + private readonly idxs: number[] + private readonly nodes: Node[] + private widthSkips: number[] + + constructor(skipList: SkipList, loc: number) { + this.skipList = skipList; this.loc = loc; - const numLevels = this._skipList._start.levels; + const numLevels = this.skipList.start.levels; let lvl = numLevels - 1; let i = -1; let ws = 0; - const nodes = new Array(numLevels); - const idxs = new Array(numLevels); - const widthSkips = new Array(numLevels); - nodes[lvl] = this._skipList._start; + const nodes: Node[] = new Array(numLevels); + const idxs: number[] = new Array(numLevels); + const widthSkips: number[] = new Array(numLevels); + nodes[lvl] = this.skipList.start; idxs[lvl] = -1; widthSkips[lvl] = 0; while (lvl >= 0) { @@ -94,9 +114,9 @@ class Point { return `Point(${this.loc})`; } - insert(entry) { + insert(entry: Entry) { if (entry.key == null) throw new Error('entry.key must not be null'); - if (this._skipList.containsKey(entry.key)) { + if (this.skipList.containsKey(entry.key)) { throw new Error(`an entry with key ${entry.key} already exists`); } @@ -115,14 +135,14 @@ class Point { if (lvl === pNodes.length) { // assume we have just passed the end of this.nodes, and reached one level greater // than the skiplist currently supports - pNodes[lvl] = this._skipList._start; + pNodes[lvl] = this.skipList.start; pIdxs[lvl] = -1; - this._skipList._start.levels++; - this._skipList._end.levels++; - this._skipList._start.downPtrs[lvl] = this._skipList._end; - this._skipList._end.upPtrs[lvl] = this._skipList._start; - this._skipList._start.downSkips[lvl] = this._skipList._keyToNodeMap.size + 1; - this._skipList._start.downSkipWidths[lvl] = this._skipList._totalWidth; + this.skipList.start.levels++; + this.skipList.end.levels++; + this.skipList.start.downPtrs[lvl] = this.skipList.end; + this.skipList.end.upPtrs[lvl] = this.skipList.start; + this.skipList.start.downSkips[lvl] = this.skipList.keyToNodeMap.size + 1; + this.skipList.start.downSkipWidths[lvl] = this.skipList._totalWidth; this.widthSkips[lvl] = 0; } const me = newNode; @@ -146,13 +166,13 @@ class Point { up.downSkips[lvl]++; up.downSkipWidths[lvl] += newWidth; } - this._skipList._keyToNodeMap.set(newNode.key, newNode); - this._skipList._totalWidth += newWidth; + this.skipList.keyToNodeMap.set(newNode.key as string, newNode); + this.skipList._totalWidth += newWidth; } delete() { const elem = this.nodes[0].downPtrs[0]; - const elemWidth = _entryWidth(elem.entry); + const elemWidth = _entryWidth(elem.entry!); for (let i = 0; i < this.nodes.length; i++) { if (i < elem.levels) { const up = elem.upPtrs[i]; @@ -169,8 +189,8 @@ class Point { up.downSkipWidths[i] -= elemWidth; } } - this._skipList._keyToNodeMap.delete(elem.key); - this._skipList._totalWidth -= elemWidth; + this.skipList.keyToNodeMap.delete(elem.key as string); + this.skipList._totalWidth -= elemWidth; } getNode() { @@ -183,20 +203,26 @@ class Point { * property that is a string. */ class SkipList { + start: Node + end: Node + _totalWidth: number + keyToNodeMap: Map + + constructor() { // if there are N elements in the skiplist, "start" is element -1 and "end" is element N - this._start = new Node(null, 1); - this._end = new Node(null, 1, null, null); + this.start = new Node(null, 1); + this.end = new Node(null, 1, null, null); this._totalWidth = 0; - this._keyToNodeMap = new Map(); - this._start.downPtrs[0] = this._end; - this._end.upPtrs[0] = this._start; + this.keyToNodeMap = new Map(); + this.start.downPtrs[0] = this.end; + this.end.upPtrs[0] = this.start; } - _getNodeAtOffset(targetOffset) { + _getNodeAtOffset(targetOffset: number) { let i = 0; - let n = this._start; - let lvl = this._start.levels - 1; + let n = this.start; + let lvl = this.start.levels - 1; while (lvl >= 0 && n.downPtrs[lvl]) { while (n.downPtrs[lvl] && (i + n.downSkipWidths[lvl] <= targetOffset)) { i += n.downSkipWidths[lvl]; @@ -204,17 +230,17 @@ class SkipList { } lvl--; } - if (n === this._start) return (this._start.downPtrs[0] || null); - if (n === this._end) { - return targetOffset === this._totalWidth ? (this._end.upPtrs[0] || null) : null; + if (n === this.start) return (this.start.downPtrs[0] || null); + if (n === this.end) { + return targetOffset === this._totalWidth ? (this.end.upPtrs[0] || null) : null; } return n; } - _getNodeIndex(node, byWidth) { + _getNodeIndex(node: Node, byWidth?: boolean) { let dist = (byWidth ? 0 : -1); let n = node; - while (n !== this._start) { + while (n !== this.start) { const lvl = n.levels - 1; n = n.upPtrs[lvl]; if (byWidth) dist += n.downSkipWidths[lvl]; @@ -223,17 +249,19 @@ class SkipList { return dist; } + totalWidth() { return this._totalWidth; } + // Returns index of first entry such that entryFunc(entry) is truthy, // or length() if no such entry. Assumes all falsy entries come before // all truthy entries. - search(entryFunc) { - let low = this._start; - let lvl = this._start.levels - 1; + search(entryFunc: Function) { + let low = this.start; + let lvl = this.start.levels - 1; let lowIndex = -1; - const f = (node) => { - if (node === this._start) return false; - else if (node === this._end) return true; + const f = (node: Node) => { + if (node === this.start) return false; + else if (node === this.end) return true; else return entryFunc(node.entry); }; @@ -249,20 +277,20 @@ class SkipList { return lowIndex + 1; } - length() { return this._keyToNodeMap.size; } + length() { return this.keyToNodeMap.size; } - atIndex(i) { + atIndex(i: number) { if (i < 0) console.warn(`atIndex(${i})`); - if (i >= this._keyToNodeMap.size) console.warn(`atIndex(${i}>=${this._keyToNodeMap.size})`); + if (i >= this.keyToNodeMap.size) console.warn(`atIndex(${i}>=${this.keyToNodeMap.size})`); return (new Point(this, i)).getNode().entry; } // differs from Array.splice() in that new elements are in an array, not varargs - splice(start, deleteCount, newEntryArray) { + splice(start: number, deleteCount: number, newEntryArray: Entry[]) { if (start < 0) console.warn(`splice(${start}, ...)`); - if (start + deleteCount > this._keyToNodeMap.size) { - console.warn(`splice(${start}, ${deleteCount}, ...), N=${this._keyToNodeMap.size}`); - console.warn('%s %s %s', typeof start, typeof deleteCount, typeof this._keyToNodeMap.size); + if (start + deleteCount > this.keyToNodeMap.size) { + console.warn(`splice(${start}, ${deleteCount}, ...), N=${this.keyToNodeMap.size}`); + console.warn('%s %s %s', typeof start, typeof deleteCount, typeof this.keyToNodeMap.size); console.trace(); } @@ -275,56 +303,55 @@ class SkipList { } } - next(entry) { return this._keyToNodeMap.get(entry.key).downPtrs[0].entry || null; } - prev(entry) { return this._keyToNodeMap.get(entry.key).upPtrs[0].entry || null; } - push(entry) { this.splice(this._keyToNodeMap.size, 0, [entry]); } + next(entry: Entry) { return this.keyToNodeMap.get(entry.key)!.downPtrs[0].entry || null; } + prev(entry: Entry) { return this.keyToNodeMap.get(entry.key)!.upPtrs[0].entry || null; } + push(entry: Entry) { this.splice(this.keyToNodeMap.size, 0, [entry]); } - slice(start, end) { + slice(start: number, end: number) { // act like Array.slice() if (start === undefined) start = 0; - else if (start < 0) start += this._keyToNodeMap.size; - if (end === undefined) end = this._keyToNodeMap.size; - else if (end < 0) end += this._keyToNodeMap.size; + else if (start < 0) start += this.keyToNodeMap.size; + if (end === undefined) end = this.keyToNodeMap.size; + else if (end < 0) end += this.keyToNodeMap.size; if (start < 0) start = 0; - if (start > this._keyToNodeMap.size) start = this._keyToNodeMap.size; + if (start > this.keyToNodeMap.size) start = this.keyToNodeMap.size; if (end < 0) end = 0; - if (end > this._keyToNodeMap.size) end = this._keyToNodeMap.size; + if (end > this.keyToNodeMap.size) end = this.keyToNodeMap.size; if (end <= start) return []; let n = this.atIndex(start); const array = [n]; for (let i = 1; i < (end - start); i++) { - n = this.next(n); + n = this.next(n!); array.push(n); } return array; } - atKey(key) { return this._keyToNodeMap.get(key).entry; } - indexOfKey(key) { return this._getNodeIndex(this._keyToNodeMap.get(key)); } - indexOfEntry(entry) { return this.indexOfKey(entry.key); } - containsKey(key) { return this._keyToNodeMap.has(key); } + atKey(key: string) { return this.keyToNodeMap.get(key)!.entry; } + indexOfKey(key: string) { return this._getNodeIndex(this.keyToNodeMap.get(key)!); } + indexOfEntry(entry: Entry) { return this.indexOfKey(entry.key); } + containsKey(key: string) { return this.keyToNodeMap.has(key); } // gets the last entry starting at or before the offset - atOffset(offset) { return this._getNodeAtOffset(offset).entry; } - keyAtOffset(offset) { return this.atOffset(offset).key; } - offsetOfKey(key) { return this._getNodeIndex(this._keyToNodeMap.get(key), true); } - offsetOfEntry(entry) { return this.offsetOfKey(entry.key); } - setEntryWidth(entry, width) { + atOffset(offset: number) { return this._getNodeAtOffset(offset)!.entry; } + keyAtOffset(offset: number) { return this.atOffset(offset)!.key; } + offsetOfKey(key: string) { return this._getNodeIndex(this.keyToNodeMap.get(key)!, true); } + offsetOfEntry(entry: Entry) { return this.offsetOfKey(entry.key); } + setEntryWidth(entry: Entry, width: number) { entry.width = width; - this._totalWidth += this._keyToNodeMap.get(entry.key).propagateWidthChange(); + this._totalWidth += this.keyToNodeMap.get(entry.key)!.propagateWidthChange(); } - totalWidth() { return this._totalWidth; } - offsetOfIndex(i) { + offsetOfIndex(i: number) { if (i < 0) return 0; - if (i >= this._keyToNodeMap.size) return this._totalWidth; - return this.offsetOfEntry(this.atIndex(i)); + if (i >= this.keyToNodeMap.size) return this._totalWidth; + return this.offsetOfEntry(this.atIndex(i)!); } - indexOfOffset(offset) { + indexOfOffset(offset: number) { if (offset <= 0) return 0; - if (offset >= this._totalWidth) return this._keyToNodeMap.size; - return this.indexOfEntry(this.atOffset(offset)); + if (offset >= this._totalWidth) return this.keyToNodeMap.size; + return this.indexOfEntry(this.atOffset(offset)!); } } -module.exports = SkipList; +export default SkipList diff --git a/src/static/js/socketio.js b/src/static/js/socketio.ts similarity index 99% rename from src/static/js/socketio.js rename to src/static/js/socketio.ts index cdc1c9a23e9..aedd68c20e8 100644 --- a/src/static/js/socketio.js +++ b/src/static/js/socketio.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import io from 'socket.io-client'; /** diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.ts similarity index 97% rename from src/static/js/timeslider.js rename to src/static/js/timeslider.ts index 8d8604b91ac..befc4f8b7b4 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** @@ -26,10 +27,9 @@ // assigns to the global `$` and augments it with plugins. require('./vendors/jquery'); -const Cookies = require('./pad_utils').Cookies; -const randomString = require('./pad_utils').randomString; +import {randomString, Cookies} from "./pad_utils"; const hooks = require('./pluginfw/hooks'); -const padutils = require('./pad_utils').padutils; +import padutils from './pad_utils' const socketio = require('./socketio'); import html10n from '../js/vendors/html10n' let token, padId, exportLinks, socket, changesetLoader, BroadcastSlider; diff --git a/src/static/js/types/AText.ts b/src/static/js/types/AText.ts new file mode 100644 index 00000000000..d58626de25f --- /dev/null +++ b/src/static/js/types/AText.ts @@ -0,0 +1,4 @@ +export type AText = { + text: string, + attribs: string, +} diff --git a/src/static/js/types/Attribute.ts b/src/static/js/types/Attribute.ts new file mode 100644 index 00000000000..f1c06b3cb48 --- /dev/null +++ b/src/static/js/types/Attribute.ts @@ -0,0 +1 @@ +export type Attribute = [string, string] diff --git a/src/static/js/types/PadRevision.ts b/src/static/js/types/PadRevision.ts new file mode 100644 index 00000000000..c54dc437932 --- /dev/null +++ b/src/static/js/types/PadRevision.ts @@ -0,0 +1,7 @@ +export type PadRevision = { + revNum: number; + savedById: string; + label: string; + timestamp: number; + id: string; +} diff --git a/src/static/js/types/SocketIOMessage.ts b/src/static/js/types/SocketIOMessage.ts new file mode 100644 index 00000000000..9c9ffea7fc9 --- /dev/null +++ b/src/static/js/types/SocketIOMessage.ts @@ -0,0 +1,317 @@ +import {MapArrayType} from "../../../node/types/MapType"; +import {AText} from "./AText"; +import AttributePool from "../AttributePool"; +import attributePool from "../AttributePool"; +import ChatMessage from "../ChatMessage"; +import {PadRevision} from "./PadRevision"; + +export type Part = { + name: string, + client_hooks: MapArrayType, + hooks: MapArrayType + pre?: string[] + post?: string[] + plugin?: string +} + + +export type MappedPlugin = Part& { + plugin: string + full_name: string +} + +export type SocketIOMessage = { + type: string + accessStatus: string +} + +export type HistoricalAuthorData = MapArrayType<{ + name: string; + colorId: number; + userId?: string +}> + +export type ServerVar = { + rev: number + clientIp: string + padId: string + historicalAuthorData?: HistoricalAuthorData, + initialAttributedText: { + attribs: string + text: string + }, + apool: AttributePoolWire + time: number +} + +export type AttributePoolWire = {numToAttrib: {[p: number]: [string, string]}, nextNum: number} + + +export type UserInfo = { + userId: string + colorId: string, + name: string|null +} + +export type ClientVarPayload = { + readOnlyId: string + automaticReconnectionTimeout: number + sessionRefreshInterval: number, + atext?: AText, + apool?: AttributePool, + userName?: string, + userColor: number, + hideChat?: boolean, + padOptions: PadOption, + padId: string, + clientIp: string, + colorPalette: string[], + accountPrivs: { + maxRevisions: number, + }, + collab_client_vars: ServerVar, + chatHead: number, + readonly: boolean, + serverTimestamp: number, + initialOptions: MapArrayType, + userId: string, + mode: string, + randomVersionString: string, + skinName: string + skinVariants: string, + exportAvailable: string + savedRevisions: PadRevision[], + initialRevisionList: number[], + padShortcutEnabled: MapArrayType, + initialTitle: string, + opts: {} + numConnectedUsers: number + abiwordAvailable: string + sofficeAvailable: string + plugins: { + plugins: MapArrayType + parts: MappedPlugin[] + } + indentationOnNewLine: boolean + scrollWhenFocusLineIsOutOfViewport : { + percentage: { + editionAboveViewport: number, + editionBelowViewport: number + } + duration: number + scrollWhenCaretIsInTheLastLineOfViewport: boolean + percentageToScrollWhenUserPressesArrowUp: number + } + initialChangesets: [] +} + +export type ClientVarData = { + type: "CLIENT_VARS" + data: ClientVarPayload +} + +export type ClientNewChanges = { + type : 'NEW_CHANGES' + apool: AttributePool, + author: string, + changeset: string, + newRev: number, + payload?: ClientNewChanges +} + +export type ClientAcceptCommitMessage = { + type: 'ACCEPT_COMMIT' + newRev: number +} + +export type ClientConnectMessage = { + type: 'CLIENT_RECONNECT', + noChanges: boolean, + headRev: number, + newRev: number, + changeset: string, + author: string + apool: AttributePool +} + + +export type UserNewInfoMessage = { + type: 'USER_NEWINFO', + data: { + userInfo: UserInfo + } +} + +export type UserLeaveMessage = { + type: 'USER_LEAVE' + userInfo: UserInfo +} + + + +export type ClientMessageMessage = { + type: 'CLIENT_MESSAGE', + payload: ClientSendMessages +} + +export type ChatMessageMessage = { + type: 'CHAT_MESSAGE' + data: { + message: ChatMessage + } +} + +export type ChatMessageMessages = { + type: 'CHAT_MESSAGES' + messages: string +} + +export type ClientUserChangesMessage = { + type: 'USER_CHANGES', + baseRev: number, + changeset: string, + apool: attributePool +} + + + +export type ClientSendMessages = ClientUserChangesMessage |ClientReadyMessage| ClientSendUserInfoUpdate|ChatMessageMessage| ClientMessageMessage | GetChatMessageMessage |ClientSuggestUserName | NewRevisionListMessage | RevisionLabel | PadOptionsMessage| ClientSaveRevisionMessage + +export type ClientReadyMessage = { + type: 'CLIENT_READY', + component: string, + padId: string, + sessionID: string, + token: string, + userInfo: UserInfo, + reconnect?: boolean + client_rev?: number +} + +export type ClientSaveRevisionMessage = { + type: 'SAVE_REVISION' +} + +export type GetChatMessageMessage = { + type: 'GET_CHAT_MESSAGES', + start: number, + end: number +} + +export type ClientSendUserInfoUpdate = { + type: 'USERINFO_UPDATE', + userInfo: UserInfo +} + +export type ClientSuggestUserName = { + type: 'suggestUserName', + data: { + payload: { + unnamedId: string, + newName: string + } + } +} + +export type NewRevisionListMessage = { + type: 'newRevisionList', + revisionList: number[] +} + +export type RevisionLabel = { + type: 'revisionLabel' + revisionList: number[] +} + +export type PadOptionsMessage = { + type: 'padoptions' + options: PadOption + changedBy: string +} + +export type PadOption = { + "noColors"?: boolean, + "showControls"?: boolean, + "showChat"?: boolean, + "showLineNumbers"?: boolean, + "useMonospaceFont"?: boolean, + "userName"?: null|string, + "userColor"?: null|string, + "rtl"?: boolean, + "alwaysShowChat"?: boolean, + "chatAndUsers"?: boolean, + "lang"?: null|string, + view? : MapArrayType +} + + +type SharedMessageType = { + payload:{ + timestamp: number + } +} + +export type x = { + disconnect: boolean +} + +export type ClientDisconnectedMessage = { + type: "disconnected" + disconnected: boolean +} + +export type UserChanges = { + data: ClientUserChangesMessage +} + +export type UserSuggestUserName = { + data: { + payload: ClientSuggestUserName + } +} + +export type ChangesetRequestMessage = { + type: 'CHANGESET_REQ' + data: { + granularity: number + start: number + requestID: string + } +} + + + +export type CollabroomMessage = { + type: 'COLLABROOM' + data: ClientSendUserInfoUpdate | ClientUserChangesMessage | ChatMessageMessage | GetChatMessageMessage | ClientSaveRevisionMessage | ClientMessageMessage +} + +export type ClientVarMessage = | ClientVarData | ClientDisconnectedMessage | ClientReadyMessage| ChangesetRequestMessage | CollabroomMessage | CustomMessage + + +export type CustomMessage = { + type: 'CUSTOM' + data: any +} + +export type ClientCustomMessage = { + type: 'CUSTOM', + action: string, + payload: any + +} + +export type SocketClientReadyMessage = { + type: string + component: string + padId: string + sessionID: string + token: string + userInfo: { + colorId: string|null + name: string|null + }, + reconnect?: boolean + client_rev?: number +} + diff --git a/src/static/js/underscore.js b/src/static/js/underscore.ts similarity index 78% rename from src/static/js/underscore.js rename to src/static/js/underscore.ts index d30543cabc4..79a3e8e7f10 100644 --- a/src/static/js/underscore.js +++ b/src/static/js/underscore.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; module.exports = require('underscore'); diff --git a/src/static/js/undomodule.js b/src/static/js/undomodule.ts similarity index 99% rename from src/static/js/undomodule.js rename to src/static/js/undomodule.ts index d0b83419dd9..32f651248ad 100644 --- a/src/static/js/undomodule.js +++ b/src/static/js/undomodule.ts @@ -1,3 +1,4 @@ +// @ts-nocheck 'use strict'; /** diff --git a/src/static/js/vendors/browser.js b/src/static/js/vendors/browser.ts similarity index 99% rename from src/static/js/vendors/browser.js rename to src/static/js/vendors/browser.ts index 2516a325b0b..a785d8a8ef9 100644 --- a/src/static/js/vendors/browser.js +++ b/src/static/js/vendors/browser.ts @@ -1,3 +1,4 @@ +// @ts-nocheck // WARNING: This file may have been modified from original. // TODO: Check requirement of this file, this afaik was to cover weird edge cases // that have probably been fixed in browsers. diff --git a/src/static/js/vendors/farbtastic.js b/src/static/js/vendors/farbtastic.ts similarity index 99% rename from src/static/js/vendors/farbtastic.js rename to src/static/js/vendors/farbtastic.ts index 5d0c4718cc8..d9a4d6311f7 100644 --- a/src/static/js/vendors/farbtastic.js +++ b/src/static/js/vendors/farbtastic.ts @@ -1,3 +1,4 @@ +// @ts-nocheck // WARNING: This file has been modified from original. // TODO: Replace with https://github.com/Simonwep/pickr diff --git a/src/static/js/vendors/gritter.js b/src/static/js/vendors/gritter.ts similarity index 99% rename from src/static/js/vendors/gritter.js rename to src/static/js/vendors/gritter.ts index 1b8b9a759cd..993514c74e7 100644 --- a/src/static/js/vendors/gritter.js +++ b/src/static/js/vendors/gritter.ts @@ -1,3 +1,4 @@ +// @ts-nocheck // WARNING: This file has been modified from the Original /* diff --git a/src/static/js/vendors/jquery.js b/src/static/js/vendors/jquery.ts similarity index 99% rename from src/static/js/vendors/jquery.js rename to src/static/js/vendors/jquery.ts index bca73371f7d..1b9923a6204 100644 --- a/src/static/js/vendors/jquery.js +++ b/src/static/js/vendors/jquery.ts @@ -1,3 +1,4 @@ +// @ts-nocheck /*! * jQuery JavaScript Library v3.7.1 * https://jquery.com/ diff --git a/src/static/js/vendors/nice-select.js b/src/static/js/vendors/nice-select.ts similarity index 99% rename from src/static/js/vendors/nice-select.js rename to src/static/js/vendors/nice-select.ts index 447ff64137d..ec76b3cef15 100644 --- a/src/static/js/vendors/nice-select.js +++ b/src/static/js/vendors/nice-select.ts @@ -1,3 +1,4 @@ +// @ts-nocheck // WARNING: This file has been modified from the Original // TODO: Nice Select seems relatively abandoned, we should consider other options. diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index e2178e54ee0..4ef6928ffbf 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -29,7 +29,6 @@ for the JavaScript code in this page.| */ - diff --git a/src/tests/backend-new/specs/AttributeMap.ts b/src/tests/backend-new/specs/AttributeMap.ts index 3e43abf7c97..ce5e61f74af 100644 --- a/src/tests/backend-new/specs/AttributeMap.ts +++ b/src/tests/backend-new/specs/AttributeMap.ts @@ -1,17 +1,18 @@ 'use strict'; -const AttributeMap = require('../../../static/js/AttributeMap.js'); -const AttributePool = require('../../../static/js/AttributePool'); -const attributes = require('../../../static/js/attributes'); +import AttributeMap from '../../../static/js/AttributeMap'; +import AttributePool from '../../../static/js/AttributePool'; +import attributes from '../../../static/js/attributes'; import {expect, describe, it, beforeEach} from 'vitest' +import {Attribute} from "../../../static/js/types/Attribute"; describe('AttributeMap', function () { - const attribs = [ + const attribs: Attribute[] = [ ['foo', 'bar'], ['baz', 'bif'], ['emptyValue', ''], ]; - let pool: { eachAttrib: (arg0: () => number) => void; putAttrib: (arg0: string[]) => any; getAttrib: (arg0: number) => any; }; + let pool: AttributePool; const getPoolSize = () => { let n = 0; @@ -70,12 +71,14 @@ describe('AttributeMap', function () { describe(desc as string, function () { it('key is coerced to string', async function () { const m = new AttributeMap(pool); + // @ts-ignore m.set(input, 'value'); expect(m.get(want)).to.equal('value'); }); it('value is coerced to string', async function () { const m = new AttributeMap(pool); + // @ts-ignore m.set('key', input); expect(m.get('key')).to.equal(want); }); @@ -122,6 +125,7 @@ describe('AttributeMap', function () { // @ts-ignore args[0] = attributes.attribsToString(attributes.sort([...args[0]]), pool); } + // @ts-ignore return AttributeMap.prototype[funcName].call(m, ...args); }; diff --git a/src/tests/backend-new/specs/attributes.ts b/src/tests/backend-new/specs/attributes.ts index 5f33f52b3fb..64a4464bd64 100644 --- a/src/tests/backend-new/specs/attributes.ts +++ b/src/tests/backend-new/specs/attributes.ts @@ -2,14 +2,15 @@ import {APool} from "../../../node/types/PadType"; -const AttributePool = require('../../../static/js/AttributePool'); -const attributes = require('../../../static/js/attributes'); +import AttributePool from '../../../static/js/AttributePool'; +import attributes from '../../../static/js/attributes'; import {expect, describe, it, beforeEach} from 'vitest'; +import {Attribute} from "../../../static/js/types/Attribute"; describe('attributes', function () { - const attribs = [['foo', 'bar'], ['baz', 'bif']]; - let pool: APool; + const attribs: Attribute[] = [['foo', 'bar'], ['baz', 'bif']]; + let pool: AttributePool; beforeEach(async function () { pool = new AttributePool(); @@ -45,6 +46,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const got = [...attributes.decodeAttribString(input)]; expect(JSON.stringify(got)).to.equal(JSON.stringify(want)); }); @@ -61,6 +63,7 @@ describe('attributes', function () { ]; for (const [desc, input] of testCases) { it(desc as string, async function () { + // @ts-ignore expect(attributes.encodeAttribString(input)).to.equal('*0*1'); }); } @@ -78,6 +81,7 @@ describe('attributes', function () { ]; for (const [input, wantErr] of testCases) { it(JSON.stringify(input), async function () { + // @ts-ignore expect(() => attributes.encodeAttribString(input)).toThrowError(wantErr as RegExp); }); } @@ -97,6 +101,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore expect(attributes.encodeAttribString(input)).to.equal(want); }); } @@ -117,6 +122,7 @@ describe('attributes', function () { for (const [desc, input] of testCases) { it(desc as string, async function () { + // @ts-ignore const gotAttribs = [...attributes.attribsFromNums(input, pool)]; expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(attribs)); }); @@ -136,6 +142,7 @@ describe('attributes', function () { ]; for (const [input, wantErr] of testCases) { it(JSON.stringify(input), async function () { + // @ts-ignore expect(() => [...attributes.attribsFromNums(input, pool)]).toThrowError(wantErr as RegExp); }); } @@ -151,6 +158,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const gotAttribs = [...attributes.attribsFromNums(input, pool)]; expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(want)); }); @@ -172,6 +180,7 @@ describe('attributes', function () { for (const [desc, input] of testCases) { it(desc as string, async function () { + // @ts-ignore const gotNums = [...attributes.attribsToNums(input, pool)]; expect(JSON.stringify(gotNums)).to.equal(JSON.stringify([0, 1])); }); @@ -182,6 +191,7 @@ describe('attributes', function () { const testCases = [null, [null]]; for (const input of testCases) { it(JSON.stringify(input), async function () { + // @ts-ignore expect(() => [...attributes.attribsToNums(input, pool)]).toThrowError(); }); } @@ -197,6 +207,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const got = [...attributes.attribsToNums(input, pool)]; expect(JSON.stringify(got)).to.equal(JSON.stringify(want)); }); @@ -211,6 +222,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const got = [...attributes.attribsToNums(input, pool)]; expect(JSON.stringify(got)).to.equal(JSON.stringify(want)); expect(JSON.stringify(pool.getAttrib(attribs.length))) @@ -234,6 +246,7 @@ describe('attributes', function () { ['value is coerced to string', [['key', inputVal]], [['key', wantVal]]], ]) { it(desc as string, async function () { + // @ts-ignore const gotNums = [...attributes.attribsToNums(inputAttribs, pool)]; // Each attrib in inputAttribs is expected to be new to the pool. const wantNums = [...Array(attribs.length + 1).keys()].slice(attribs.length); @@ -265,6 +278,7 @@ describe('attributes', function () { ]; for (const [input, wantErr] of testCases) { it(JSON.stringify(input), async function () { + // @ts-ignore expect(() => [...attributes.attribsFromString(input, pool)]).toThrowError(wantErr); }); } @@ -280,6 +294,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const gotAttribs = [...attributes.attribsFromString(input, pool)]; expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(want)); }); @@ -297,6 +312,7 @@ describe('attributes', function () { for (const [desc, input] of testCases) { it(desc as string, async function () { + // @ts-ignore const got = attributes.attribsToString(input, pool); expect(got).to.equal('*0*1'); }); @@ -307,6 +323,7 @@ describe('attributes', function () { const testCases = [null, [null]]; for (const input of testCases) { it(JSON.stringify(input), async function () { + // @ts-ignore expect(() => attributes.attribsToString(input, pool)).toThrowError(); }); } @@ -322,6 +339,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const got = attributes.attribsToString(input, pool); expect(got).to.equal(want); }); @@ -336,6 +354,7 @@ describe('attributes', function () { ]; for (const [input, want] of testCases) { it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () { + // @ts-ignore const got = attributes.attribsToString(input, pool); expect(got).to.equal(want); expect(JSON.stringify(pool.getAttrib(attribs.length))) diff --git a/src/tests/backend-new/specs/contentcollector.ts b/src/tests/backend-new/specs/contentcollector.ts deleted file mode 100644 index 81f0bc2ea7b..00000000000 --- a/src/tests/backend-new/specs/contentcollector.ts +++ /dev/null @@ -1,398 +0,0 @@ -'use strict'; - -/* - * While importexport tests target the `setHTML` API endpoint, which is nearly identical to what - * happens when a user manually imports a document via the UI, the contentcollector tests here don't - * use rehype to process the document. Rehype removes spaces and newĺines were applicable, so the - * expected results here can differ from importexport.js. - * - * If you add tests here, please also add them to importexport.js - */ - -import {APool} from "../../../node/types/PadType"; - -const AttributePool = require('../../../static/js/AttributePool'); -const Changeset = require('../../../static/js/Changeset'); -const assert = require('assert').strict; -const attributes = require('../../../static/js/attributes'); -const contentcollector = require('../../../static/js/contentcollector'); -const jsdom = require('jsdom'); - -import {describe, it, beforeAll, test} from 'vitest'; - -// All test case `wantAlines` values must only refer to attributes in this list so that the -// attribute numbers do not change due to changes in pool insertion order. -const knownAttribs = [ - ['insertorder', 'first'], - ['italic', 'true'], - ['list', 'bullet1'], - ['list', 'bullet2'], - ['list', 'number1'], - ['list', 'number2'], - ['lmkr', '1'], - ['start', '1'], - ['start', '2'], -]; - -const testCases = [ - { - description: 'Simple', - html: '

foo

', - wantAlines: ['+3'], - wantText: ['foo'], - }, - { - description: 'Line starts with asterisk', - html: '

*foo

', - wantAlines: ['+4'], - wantText: ['*foo'], - }, - { - description: 'Complex nested Li', - html: '
  1. one
    1. 1.1
  2. two
', - wantAlines: [ - '*0*4*6*7+1+3', - '*0*5*6*8+1+3', - '*0*4*6*8+1+3', - ], - wantText: [ - '*one', '*1.1', '*two', - ], - }, - { - description: 'Complex list of different types', - html: '
  • one
  • two
  • 0
  • 1
  • 2
    • 3
    • 4
  1. item
    1. item1
    2. item2
', - wantAlines: [ - '*0*2*6+1+3', - '*0*2*6+1+3', - '*0*2*6+1+1', - '*0*2*6+1+1', - '*0*2*6+1+1', - '*0*3*6+1+1', - '*0*3*6+1+1', - '*0*4*6*7+1+4', - '*0*5*6*8+1+5', - '*0*5*6*8+1+5', - ], - wantText: [ - '*one', - '*two', - '*0', - '*1', - '*2', - '*3', - '*4', - '*item', - '*item1', - '*item2', - ], - }, - { - description: 'Tests if uls properly get attributes', - html: '
  • a
  • b
div

foo

', - wantAlines: [ - '*0*2*6+1+1', - '*0*2*6+1+1', - '+3', - '+3', - ], - wantText: ['*a', '*b', 'div', 'foo'], - }, - { - description: 'Tests if indented uls properly get attributes', - html: '
  • a
    • b
  • a

foo

', - wantAlines: [ - '*0*2*6+1+1', - '*0*3*6+1+1', - '*0*2*6+1+1', - '+3', - ], - wantText: ['*a', '*b', '*a', 'foo'], - }, - { - description: 'Tests if ols properly get line numbers when in a normal OL', - html: '
  1. a
  2. b
  3. c

test

', - wantAlines: [ - '*0*4*6*7+1+1', - '*0*4*6*7+1+1', - '*0*4*6*7+1+1', - '+4', - ], - wantText: ['*a', '*b', '*c', 'test'], - noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?', - }, - { - description: 'A single completely empty line break within an ol should reset count if OL is closed off..', - html: '
  1. should be 1

hello

  1. should be 1
  2. should be 2

', - wantAlines: [ - '*0*4*6*7+1+b', - '+5', - '*0*4*6*8+1+b', - '*0*4*6*8+1+b', - '', - ], - wantText: ['*should be 1', 'hello', '*should be 1', '*should be 2', ''], - noteToSelf: "Shouldn't include attribute marker in the

line", - }, - { - description: 'A single

should create a new line', - html: '

', - wantAlines: ['', ''], - wantText: ['', ''], - noteToSelf: '

should create a line break but not break numbering', - }, - { - description: 'Tests if ols properly get line numbers when in a normal OL #2', - html: 'a
  1. b
    1. c
notlist

foo

', - wantAlines: [ - '+1', - '*0*4*6*7+1+1', - '*0*5*6*8+1+1', - '+7', - '+3', - ], - wantText: ['a', '*b', '*c', 'notlist', 'foo'], - noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?', - }, - { - description: 'First item being an UL then subsequent being OL will fail', - html: '
  • a
    1. b
    2. c
', - wantAlines: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1'], - wantText: ['a', '*b', '*c'], - noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?', - disabled: true, - }, - { - description: 'A single completely empty line break within an ol should NOT reset count', - html: '
  1. should be 1
  2. should be 2
  3. should be 3

', - wantAlines: [], - wantText: ['*should be 1', '*should be 2', '*should be 3'], - noteToSelf: "

should create a line break but not break numbering -- This is what I can't get working!", - disabled: true, - }, - { - description: 'Content outside body should be ignored', - html: 'titleempty
', - wantAlines: ['+5'], - wantText: ['empty'], - }, - { - description: 'Multiple spaces should be preserved', - html: 'Text with more than one space.
', - wantAlines: ['+10'], - wantText: ['Text with more than one space.'], - }, - { - description: 'non-breaking and normal space should be preserved', - html: 'Text with  more   than  one space.
', - wantAlines: ['+10'], - wantText: ['Text with more than one space.'], - }, - { - description: 'Multiple nbsp should be preserved', - html: '  
', - wantAlines: ['+2'], - wantText: [' '], - }, - { - description: 'Multiple nbsp between words ', - html: '  word1  word2   word3
', - wantAlines: ['+m'], - wantText: [' word1 word2 word3'], - }, - { - description: 'A non-breaking space preceded by a normal space', - html: '  word1  word2  word3
', - wantAlines: ['+l'], - wantText: [' word1 word2 word3'], - }, - { - description: 'A non-breaking space followed by a normal space', - html: '  word1  word2  word3
', - wantAlines: ['+l'], - wantText: [' word1 word2 word3'], - }, - { - description: 'Don\'t collapse spaces that follow a newline', - html: 'something
something
', - wantAlines: ['+9', '+m'], - wantText: ['something', ' something'], - }, - { - description: 'Don\'t collapse spaces that follow a empty paragraph', - html: 'something

something
', - wantAlines: ['+9', '', '+m'], - wantText: ['something', '', ' something'], - }, - { - description: 'Don\'t collapse spaces that preceed/follow a newline', - html: 'something
something
', - wantAlines: ['+l', '+m'], - wantText: ['something ', ' something'], - }, - { - description: 'Don\'t collapse spaces that preceed/follow a empty paragraph', - html: 'something

something
', - wantAlines: ['+l', '', '+m'], - wantText: ['something ', '', ' something'], - }, - { - description: 'Don\'t collapse non-breaking spaces that follow a newline', - html: 'something
   something
', - wantAlines: ['+9', '+c'], - wantText: ['something', ' something'], - }, - { - description: 'Don\'t collapse non-breaking spaces that follow a paragraph', - html: 'something

   something
', - wantAlines: ['+9', '', '+c'], - wantText: ['something', '', ' something'], - }, - { - description: 'Preserve all spaces when multiple are present', - html: 'Need more space s !
', - wantAlines: ['+h*1+4+2'], - wantText: ['Need more space s !'], - }, - { - description: 'Newlines and multiple spaces across newlines should be preserved', - html: ` - Need - more - space - s - !
`, - wantAlines: ['+19*1+4+b'], - wantText: ['Need more space s !'], - }, - { - description: 'Multiple new lines at the beginning should be preserved', - html: '

first line

second line
', - wantAlines: ['', '', '', '', '+a', '', '+b'], - wantText: ['', '', '', '', 'first line', '', 'second line'], - }, - { - description: 'A paragraph with multiple lines should not loose spaces when lines are combined', - html: `

-а б в г ґ д е є ж з и і ї й к л м н о -п р с т у ф х ц ч ш щ ю я ь

-`, - wantAlines: ['+1t'], - wantText: ['а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь'], - }, - { - description: 'lines in preformatted text should be kept intact', - html: `

-а б в г ґ д е є ж з и і ї й к л м н о

multiple
-lines
-in
-pre
-

п р с т у ф х ц ч ш щ ю я -ь

-`, - wantAlines: ['+11', '+8', '+5', '+2', '+3', '+r'], - wantText: [ - 'а б в г ґ д е є ж з и і ї й к л м н о', - 'multiple', - 'lines', - 'in', - 'pre', - 'п р с т у ф х ц ч ш щ ю я ь', - ], - }, - { - description: 'pre should be on a new line not preceded by a space', - html: `

- 1 -

preline
-
`, - wantAlines: ['+6', '+7'], - wantText: [' 1 ', 'preline'], - }, - { - description: 'Preserve spaces on the beginning and end of a element', - html: 'Need more space s !
', - wantAlines: ['+f*1+3+1'], - wantText: ['Need more space s !'], - }, - { - description: 'Preserve spaces outside elements', - html: 'Need more space s !
', - wantAlines: ['+g*1+1+2'], - wantText: ['Need more space s !'], - }, - { - description: 'Preserve spaces at the end of an element', - html: 'Need more space s !
', - wantAlines: ['+g*1+2+1'], - wantText: ['Need more space s !'], - }, - { - description: 'Preserve spaces at the start of an element', - html: 'Need more space s !
', - wantAlines: ['+f*1+2+2'], - wantText: ['Need more space s !'], - }, -]; - -describe(__filename, function () { - for (const tc of testCases) { - describe(tc.description, function () { - let apool: APool; - let result: { - lines: string[], - lineAttribs: string[], - }; - if (tc.disabled) { - test.skip('If disabled we do not run the test'); - return; - } - - beforeAll(async function () { - - const {window: {document}} = new jsdom.JSDOM(tc.html); - apool = new AttributePool(); - // To reduce test fragility, the attribute pool is seeded with `knownAttribs`, and all - // attributes in `tc.wantAlines` must be in `knownAttribs`. (This guarantees that attribute - // numbers do not change if the attribute processing code changes.) - for (const attrib of knownAttribs) apool.putAttrib(attrib); - for (const aline of tc.wantAlines) { - for (const op of Changeset.deserializeOps(aline)) { - for (const n of attributes.decodeAttribString(op.attribs)) { - assert(n < knownAttribs.length); - } - } - } - const cc = contentcollector.makeContentCollector(true, null, apool); - cc.collectContent(document.body); - result = cc.finish(); - console.log(result); - }); - - it('text matches', async function () { - assert.deepEqual(result.lines, tc.wantText); - }); - - it('alines match', async function () { - assert.deepEqual(result.lineAttribs, tc.wantAlines); - }); - - it('attributes are sorted in canonical order', async function () { - const gotAttribs:string[][][] = []; - const wantAttribs = []; - for (const aline of result.lineAttribs) { - const gotAlineAttribs:string[][] = []; - gotAttribs.push(gotAlineAttribs); - const wantAlineAttribs:string[] = []; - wantAttribs.push(wantAlineAttribs); - for (const op of Changeset.deserializeOps(aline)) { - const gotOpAttribs:string[] = [...attributes.attribsFromString(op.attribs, apool)]; - gotAlineAttribs.push(gotOpAttribs); - wantAlineAttribs.push(attributes.sort([...gotOpAttribs])); - } - } - assert.deepEqual(gotAttribs, wantAttribs); - }); - }); - } -}); diff --git a/src/tests/backend-new/specs/pad_utils.ts b/src/tests/backend-new/specs/pad_utils.ts index bcccdf4496d..569f49d04c9 100644 --- a/src/tests/backend-new/specs/pad_utils.ts +++ b/src/tests/backend-new/specs/pad_utils.ts @@ -1,19 +1,19 @@ import {MapArrayType} from "../../../node/types/MapType"; -const {padutils} = require('../../../static/js/pad_utils'); +import padutils from '../../../static/js/pad_utils'; import {describe, it, expect, afterEach, beforeAll} from "vitest"; describe(__filename, function () { describe('warnDeprecated', function () { - const {warnDeprecated} = padutils; + const {warnDeprecatedFlags, warnDeprecated} = padutils; const backups:MapArrayType = {}; beforeAll(async function () { - backups.logger = warnDeprecated.logger; + backups.logger = warnDeprecatedFlags.logger; }); afterEach(async function () { - warnDeprecated.logger = backups.logger; - delete warnDeprecated._rl; // Reset internal rate limiter state. + warnDeprecatedFlags.logger = backups.logger; + delete warnDeprecatedFlags._rl; // Reset internal rate limiter state. }); /*it('includes the stack', async function () { @@ -25,13 +25,13 @@ describe(__filename, function () { it('rate limited', async function () { let got = 0; - warnDeprecated.logger = {warn: () => ++got}; + warnDeprecatedFlags.logger = {warn: () => ++got}; warnDeprecated(); // Initialize internal rate limiter state. - const {period} = warnDeprecated._rl; + const {period} = warnDeprecatedFlags._rl!; got = 0; const testCases = [[0, 1], [0, 1], [period - 1, 1], [period, 2]]; for (const [now, want] of testCases) { // In a loop so that the stack trace is the same. - warnDeprecated._rl.now = () => now; + warnDeprecatedFlags._rl!.now = () => now; warnDeprecated(); expect(got).toEqual(want); } diff --git a/src/tests/backend-new/specs/skiplist.ts b/src/tests/backend-new/specs/skiplist.ts index c1b408e3a7c..23ad46ae650 100644 --- a/src/tests/backend-new/specs/skiplist.ts +++ b/src/tests/backend-new/specs/skiplist.ts @@ -1,12 +1,13 @@ 'use strict'; -const SkipList = require('ep_etherpad-lite/static/js/skiplist'); +import SkipList from 'ep_etherpad-lite/static/js/skiplist'; import {expect, describe, it} from 'vitest'; describe('skiplist.js', function () { it('rejects null keys', async function () { const skiplist = new SkipList(); for (const key of [undefined, null]) { + // @ts-ignore expect(() => skiplist.push({key})).toThrowError(); } }); diff --git a/src/tests/backend/common.ts b/src/tests/backend/common.ts index 21fb01e2f48..4f39375462c 100644 --- a/src/tests/backend/common.ts +++ b/src/tests/backend/common.ts @@ -2,12 +2,11 @@ import {MapArrayType} from "../../node/types/MapType"; -const AttributePool = require('../../static/js/AttributePool'); -const apiHandler = require('../../node/handler/APIHandler'); +import AttributePool from '../../static/js/AttributePool'; const assert = require('assert').strict; const io = require('socket.io-client'); const log4js = require('log4js'); -const {padutils} = require('../../static/js/pad_utils'); +import padutils from '../../static/js/pad_utils'; const process = require('process'); const server = require('../../node/server'); const setCookieParser = require('set-cookie-parser'); diff --git a/src/tests/backend/specs/ImportEtherpad.ts b/src/tests/backend/specs/ImportEtherpad.ts index b0a208b4ac6..b9ac9baaf96 100644 --- a/src/tests/backend/specs/ImportEtherpad.ts +++ b/src/tests/backend/specs/ImportEtherpad.ts @@ -8,7 +8,7 @@ const db = require('../../../node/db/DB'); const importEtherpad = require('../../../node/utils/ImportEtherpad'); const padManager = require('../../../node/db/PadManager'); const plugins = require('../../../static/js/pluginfw/plugin_defs'); -const {randomString} = require('../../../static/js/pad_utils'); +import {randomString} from '../../../static/js/pad_utils'; describe(__filename, function () { let padId: string; diff --git a/src/tests/backend/specs/chat.ts b/src/tests/backend/specs/chat.ts index 5070a30a1c1..62ac9701252 100644 --- a/src/tests/backend/specs/chat.ts +++ b/src/tests/backend/specs/chat.ts @@ -3,7 +3,7 @@ import {MapArrayType} from "../../../node/types/MapType"; import {PluginDef} from "../../../node/types/PartType"; -const ChatMessage = require('../../../static/js/ChatMessage'); +import ChatMessage from '../../../static/js/ChatMessage'; const {Pad} = require('../../../node/db/Pad'); const assert = require('assert').strict; const common = require('../common'); @@ -103,10 +103,14 @@ describe(__filename, function () { checkHook('chatNewMessage', ({message}) => { assert(message != null); assert(message instanceof ChatMessage); - assert.equal(message.authorId, authorId); - assert.equal(message.text, this.test!.title); - assert(message.time >= start); - assert(message.time <= Date.now()); + // @ts-ignore + assert.equal(message!.authorId, authorId); + // @ts-ignore + assert.equal(message!.text, this.test!.title); + // @ts-ignore + assert(message!.time >= start); + // @ts-ignore + assert(message!.time <= Date.now()); }), sendChat(socket, {text: this.test!.title}), ]); @@ -153,7 +157,9 @@ describe(__filename, function () { const customMetadata = {foo: this.test!.title}; await Promise.all([ checkHook('chatNewMessage', ({message}) => { + // @ts-ignore message.text = modifiedText; + // @ts-ignore message.customMetadata = customMetadata; }), (async () => { diff --git a/src/tests/backend/specs/contentcollector.ts b/src/tests/backend/specs/contentcollector.ts index 51ae0002f2b..50178fbae06 100644 --- a/src/tests/backend/specs/contentcollector.ts +++ b/src/tests/backend/specs/contentcollector.ts @@ -11,16 +11,17 @@ import {APool} from "../../../node/types/PadType"; -const AttributePool = require('../../../static/js/AttributePool'); +import AttributePool from '../../../static/js/AttributePool'; const Changeset = require('../../../static/js/Changeset'); const assert = require('assert').strict; -const attributes = require('../../../static/js/attributes'); +import attributes from '../../../static/js/attributes'; const contentcollector = require('../../../static/js/contentcollector'); -const jsdom = require('jsdom'); +import jsdom from 'jsdom'; +import {Attribute} from "../../../static/js/types/Attribute"; // All test case `wantAlines` values must only refer to attributes in this list so that the // attribute numbers do not change due to changes in pool insertion order. -const knownAttribs = [ +const knownAttribs: Attribute[] = [ ['insertorder', 'first'], ['italic', 'true'], ['list', 'bullet1'], @@ -336,7 +337,7 @@ pre describe(__filename, function () { for (const tc of testCases) { describe(tc.description, function () { - let apool: APool; + let apool: AttributePool; let result: { lines: string[], lineAttribs: string[], @@ -376,11 +377,12 @@ describe(__filename, function () { for (const aline of result.lineAttribs) { const gotAlineAttribs:string[][] = []; gotAttribs.push(gotAlineAttribs); - const wantAlineAttribs:string[] = []; + const wantAlineAttribs:Attribute[] = []; wantAttribs.push(wantAlineAttribs); for (const op of Changeset.deserializeOps(aline)) { - const gotOpAttribs:string[] = [...attributes.attribsFromString(op.attribs, apool)]; + const gotOpAttribs = [...attributes.attribsFromString(op.attribs, apool)] as unknown as Attribute; gotAlineAttribs.push(gotOpAttribs); + // @ts-ignore wantAlineAttribs.push(attributes.sort([...gotOpAttribs])); } } From 3dae23a1e59f49939cdd73e3ffc9026f86045723 Mon Sep 17 00:00:00 2001 From: SamTV12345 <40429738+samtv12345@users.noreply.github.com> Date: Sat, 17 Aug 2024 21:13:51 +0200 Subject: [PATCH 17/49] Fixed etherpad not being available from subdirectory --- src/node/hooks/express/specialpages.ts | 4 ++-- src/templates/pad.html | 2 +- src/templates/timeslider.html | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node/hooks/express/specialpages.ts b/src/node/hooks/express/specialpages.ts index f0d1a7fa0fb..9e4642ca8a8 100644 --- a/src/node/hooks/express/specialpages.ts +++ b/src/node/hooks/express/specialpages.ts @@ -342,7 +342,7 @@ exports.expressCreateServer = async (hookName: string, args: any, cb: Function) req, toolbar, isReadOnly, - entrypoint: "/"+fileNamePad + entrypoint: "../../"+fileNamePad }) res.send(content); }); @@ -356,7 +356,7 @@ exports.expressCreateServer = async (hookName: string, args: any, cb: Function) res.send(eejs.require('ep_etherpad-lite/templates/timeslider.html', { req, toolbar, - entrypoint: "/"+fileNameTimeSlider + entrypoint: "../../../"+fileNameTimeSlider })); }); } else { diff --git a/src/templates/pad.html b/src/templates/pad.html index 08437b628ce..c3e253791f4 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -10,7 +10,7 @@ <% e.begin_block("htmlHead"); %> <% e.end_block(); %> <%=settings.title%> - + - + From 28e04bdf71ae3e25d77f39900e2ff540c1d9c4d1 Mon Sep 17 00:00:00 2001 From: SamTV12345 <40429738+SamTV12345@users.noreply.github.com> Date: Sun, 18 Aug 2024 12:14:24 +0200 Subject: [PATCH 18/49] Feat/changeset ts (#6594) * Migrated changeset * Added more tests. * Fixed test scopes --- src/node/db/API.ts | 12 +- src/node/db/Pad.ts | 33 +- src/node/handler/PadMessageHandler.ts | 49 +- src/node/types/PadType.ts | 5 +- src/node/utils/ExportHelper.ts | 10 +- src/node/utils/ExportHtml.ts | 15 +- src/node/utils/ExportTxt.ts | 13 +- src/node/utils/ImportHtml.ts | 7 +- src/node/utils/padDiff.ts | 61 +- src/static/js/AttributeManager.ts | 43 +- src/static/js/Builder.ts | 108 ++ src/static/js/Changeset.ts | 1399 ++++------------- src/static/js/ChangesetUtils.ts | 29 +- src/static/js/MergingOpAssembler.ts | 73 + src/static/js/Op.ts | 78 + src/static/js/OpAssembler.ts | 21 + src/static/js/OpIter.ts | 47 + src/static/js/SmartOpAssembler.ts | 115 ++ src/static/js/StringAssembler.ts | 18 + src/static/js/StringIterator.ts | 54 + src/static/js/TextLinesMutator.ts | 348 ++++ src/static/js/ace2_inner.ts | 83 +- src/static/js/broadcast.ts | 28 +- src/static/js/changesettracker.ts | 53 +- src/static/js/contentcollector.ts | 16 +- src/static/js/linestylefilter.ts | 9 +- src/static/js/types/ChangeSet.ts | 6 + src/static/js/types/ChangeSetBuilder.ts | 7 + src/static/js/undomodule.ts | 22 +- src/tests/backend-new/easysync-helper.ts | 220 +++ .../backend-new/specs/StringIteratorTest.ts | 47 + .../backend-new/specs/easysync-assembler.ts | 224 +++ .../backend-new/specs/easysync-compose.ts | 54 + .../backend-new/specs/easysync-mutations.ts | 320 ++++ .../backend-new/specs/easysync-other.test.ts | 166 ++ .../specs/easysync-subAttribution.ts | 55 + src/vitest.config.ts | 2 +- 37 files changed, 2540 insertions(+), 1310 deletions(-) create mode 100644 src/static/js/Builder.ts create mode 100644 src/static/js/MergingOpAssembler.ts create mode 100644 src/static/js/Op.ts create mode 100644 src/static/js/OpAssembler.ts create mode 100644 src/static/js/OpIter.ts create mode 100644 src/static/js/SmartOpAssembler.ts create mode 100644 src/static/js/StringAssembler.ts create mode 100644 src/static/js/StringIterator.ts create mode 100644 src/static/js/TextLinesMutator.ts create mode 100644 src/static/js/types/ChangeSet.ts create mode 100644 src/static/js/types/ChangeSetBuilder.ts create mode 100644 src/tests/backend-new/easysync-helper.ts create mode 100644 src/tests/backend-new/specs/StringIteratorTest.ts create mode 100644 src/tests/backend-new/specs/easysync-assembler.ts create mode 100644 src/tests/backend-new/specs/easysync-compose.ts create mode 100644 src/tests/backend-new/specs/easysync-mutations.ts create mode 100644 src/tests/backend-new/specs/easysync-other.test.ts create mode 100644 src/tests/backend-new/specs/easysync-subAttribution.ts diff --git a/src/node/db/API.ts b/src/node/db/API.ts index e9f445560ba..88910ca68e7 100644 --- a/src/node/db/API.ts +++ b/src/node/db/API.ts @@ -19,8 +19,10 @@ * limitations under the License. */ -const Changeset = require('../../static/js/Changeset'); +import {deserializeOps} from '../../static/js/Changeset'; import ChatMessage from '../../static/js/ChatMessage'; +import {Builder} from "../../static/js/Builder"; +import {Attribute} from "../../static/js/types/Attribute"; const CustomError = require('../utils/customError'); const padManager = require('./PadManager'); const padMessageHandler = require('../handler/PadMessageHandler'); @@ -563,11 +565,11 @@ exports.restoreRevision = async (padID: string, rev: number, authorId = '') => { const oldText = pad.text(); atext.text += '\n'; - const eachAttribRun = (attribs: string[], func:Function) => { + const eachAttribRun = (attribs: string, func:Function) => { let textIndex = 0; const newTextStart = 0; const newTextEnd = atext.text.length; - for (const op of Changeset.deserializeOps(attribs)) { + for (const op of deserializeOps(attribs)) { const nextIndex = textIndex + op.chars; if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) { func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); @@ -577,10 +579,10 @@ exports.restoreRevision = async (padID: string, rev: number, authorId = '') => { }; // create a new changeset with a helper builder object - const builder = Changeset.builder(oldText.length); + const builder = new Builder(oldText.length); // assemble each line into the builder - eachAttribRun(atext.attribs, (start: number, end: number, attribs:string[]) => { + eachAttribRun(atext.attribs, (start: number, end: number, attribs:Attribute[]) => { builder.insert(atext.text.substring(start, end), attribs); }); diff --git a/src/node/db/Pad.ts b/src/node/db/Pad.ts index e117bb3434d..c3e11aa15a6 100644 --- a/src/node/db/Pad.ts +++ b/src/node/db/Pad.ts @@ -8,7 +8,7 @@ import {MapArrayType} from "../types/MapType"; */ import AttributeMap from '../../static/js/AttributeMap'; -const Changeset = require('../../static/js/Changeset'); +import {applyToAText, checkRep, copyAText, deserializeOps, makeAText, makeSplice, opsFromAText, pack, unpack} from '../../static/js/Changeset'; import ChatMessage from '../../static/js/ChatMessage'; import AttributePool from '../../static/js/AttributePool'; const Stream = require('../utils/Stream'); @@ -24,6 +24,7 @@ const readOnlyManager = require('./ReadOnlyManager'); const randomString = require('../utils/randomstring'); const hooks = require('../../static/js/pluginfw/hooks'); import pad_utils from "../../static/js/pad_utils"; +import {SmartOpAssembler} from "../../static/js/SmartOpAssembler"; const promises = require('../utils/promises'); /** @@ -56,7 +57,7 @@ class Pad { */ constructor(id:string, database = db) { this.db = database; - this.atext = Changeset.makeAText('\n'); + this.atext = makeAText('\n'); this.pool = new AttributePool(); this.head = -1; this.chatHead = -1; @@ -93,13 +94,13 @@ class Pad { * @param {String} authorId The id of the author * @return {Promise} */ - async appendRevision(aChangeset:AChangeSet, authorId = '') { - const newAText = Changeset.applyToAText(aChangeset, this.atext, this.pool); + async appendRevision(aChangeset:string, authorId = '') { + const newAText = applyToAText(aChangeset, this.atext, this.pool); if (newAText.text === this.atext.text && newAText.attribs === this.atext.attribs && this.head !== -1) { return this.head; } - Changeset.copyAText(newAText, this.atext); + copyAText(newAText, this.atext); const newRev = ++this.head; @@ -215,7 +216,7 @@ class Pad { ]); const apool = this.apool(); let atext = keyAText; - for (const cs of changesets) atext = Changeset.applyToAText(cs, atext, apool); + for (const cs of changesets) atext = applyToAText(cs, atext, apool); return atext; } @@ -293,7 +294,7 @@ class Pad { (!ins && start > 0 && orig[start - 1] === '\n'); if (!willEndWithNewline) ins += '\n'; if (ndel === 0 && ins.length === 0) return; - const changeset = Changeset.makeSplice(orig, start, ndel, ins); + const changeset = makeSplice(orig, start, ndel, ins); await this.appendRevision(changeset, authorId); } @@ -393,7 +394,7 @@ class Pad { if (context.type !== 'text') throw new Error(`unsupported content type: ${context.type}`); text = exports.cleanText(context.content); } - const firstChangeset = Changeset.makeSplice('\n', 0, 0, text); + const firstChangeset = makeSplice('\n', 0, 0, text); await this.appendRevision(firstChangeset, authorId); } await hooks.aCallAll('padLoad', {pad: this}); @@ -520,8 +521,8 @@ class Pad { const oldAText = this.atext; // based on Changeset.makeSplice - const assem = Changeset.smartOpAssembler(); - for (const op of Changeset.opsFromAText(oldAText)) assem.append(op); + const assem = new SmartOpAssembler(); + for (const op of opsFromAText(oldAText)) assem.append(op); assem.endDocument(); // although we have instantiated the dstPad with '\n', an additional '\n' is @@ -533,7 +534,7 @@ class Pad { // create a changeset that removes the previous text and add the newText with // all atributes present on the source pad - const changeset = Changeset.pack(oldLength, newLength, assem.toString(), newText); + const changeset = pack(oldLength, newLength, assem.toString(), newText); dstPad.appendRevision(changeset, authorId); await hooks.aCallAll('padCopy', { @@ -706,7 +707,7 @@ class Pad { } }) .batch(100).buffer(99); - let atext = Changeset.makeAText('\n'); + let atext = makeAText('\n'); for await (const [r, changeset, authorId, timestamp, isKeyRev, keyAText] of revs) { try { assert(authorId != null); @@ -717,10 +718,10 @@ class Pad { assert(timestamp > 0); assert(changeset != null); assert.equal(typeof changeset, 'string'); - Changeset.checkRep(changeset); - const unpacked = Changeset.unpack(changeset); + checkRep(changeset); + const unpacked = unpack(changeset); let text = atext.text; - for (const op of Changeset.deserializeOps(unpacked.ops)) { + for (const op of deserializeOps(unpacked.ops)) { if (['=', '-'].includes(op.opcode)) { assert(text.length >= op.chars); const consumed = text.slice(0, op.chars); @@ -731,7 +732,7 @@ class Pad { } assert.equal(op.attribs, AttributeMap.fromString(op.attribs, pool).toString()); } - atext = Changeset.applyToAText(changeset, atext, pool); + atext = applyToAText(changeset, atext, pool); if (isKeyRev) assert.deepEqual(keyAText, atext); } catch (err:any) { err.message = `(pad ${this.id} revision ${r}) ${err.message}`; diff --git a/src/node/handler/PadMessageHandler.ts b/src/node/handler/PadMessageHandler.ts index 2976381bb1c..6d2ee0d5785 100644 --- a/src/node/handler/PadMessageHandler.ts +++ b/src/node/handler/PadMessageHandler.ts @@ -23,7 +23,7 @@ import {MapArrayType} from "../types/MapType"; import AttributeMap from '../../static/js/AttributeMap'; const padManager = require('../db/PadManager'); -const Changeset = require('../../static/js/Changeset'); +import {checkRep, cloneAText, compose, deserializeOps, follow, identity, inverse, makeAText, makeSplice, moveOpsToNewPool, mutateAttributionLines, mutateTextLines, oldLen, prepareForWire, splitAttributionLines, splitTextLines, unpack} from '../../static/js/Changeset'; import ChatMessage from '../../static/js/ChatMessage'; import AttributePool from '../../static/js/AttributePool'; const AttributeManager = require('../../static/js/AttributeManager'); @@ -44,6 +44,7 @@ import {ChangesetRequest, PadUserInfo, SocketClientRequest} from "../types/Socke import {APool, AText, PadAuthor, PadType} from "../types/PadType"; import {ChangeSet} from "../types/ChangeSet"; import {ChatMessageMessage, ClientReadyMessage, ClientSaveRevisionMessage, ClientSuggestUserName, ClientUserChangesMessage, ClientVarMessage, CustomMessage, UserNewInfoMessage} from "../../static/js/types/SocketIOMessage"; +import {Builder} from "../../static/js/Builder"; const webaccess = require('../hooks/express/webaccess'); const { checkValidRev } = require('../utils/checkValidRev'); @@ -594,10 +595,10 @@ const handleUserChanges = async (socket:any, message: { const pad = await padManager.getPad(thisSession.padId, null, thisSession.author); // Verify that the changeset has valid syntax and is in canonical form - Changeset.checkRep(changeset); + checkRep(changeset); // Validate all added 'author' attribs to be the same value as the current user - for (const op of Changeset.deserializeOps(Changeset.unpack(changeset).ops)) { + for (const op of deserializeOps(unpack(changeset).ops)) { // + can add text with attribs // = can change or add attribs // - can have attribs, but they are discarded and don't show up in the attribs - @@ -616,7 +617,7 @@ const handleUserChanges = async (socket:any, message: { // ex. adoptChangesetAttribs // Afaik, it copies the new attributes from the changeset, to the global Attribute Pool - let rebasedChangeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool); + let rebasedChangeset = moveOpsToNewPool(changeset, wireApool, pad.pool); // ex. applyUserChanges let r = baseRev; @@ -629,21 +630,21 @@ const handleUserChanges = async (socket:any, message: { const {changeset: c, meta: {author: authorId}} = await pad.getRevision(r); if (changeset === c && thisSession.author === authorId) { // Assume this is a retransmission of an already applied changeset. - rebasedChangeset = Changeset.identity(Changeset.unpack(changeset).oldLen); + rebasedChangeset = identity(unpack(changeset).oldLen); } // At this point, both "c" (from the pad) and "changeset" (from the // client) are relative to revision r - 1. The follow function // rebases "changeset" so that it is relative to revision r // and can be applied after "c". - rebasedChangeset = Changeset.follow(c, rebasedChangeset, false, pad.pool); + rebasedChangeset = follow(c, rebasedChangeset, false, pad.pool); } const prevText = pad.text(); - if (Changeset.oldLen(rebasedChangeset) !== prevText.length) { + if (oldLen(rebasedChangeset) !== prevText.length) { throw new Error( `Can't apply changeset ${rebasedChangeset} with oldLen ` + - `${Changeset.oldLen(rebasedChangeset)} to document of length ${prevText.length}`); + `${oldLen(rebasedChangeset)} to document of length ${prevText.length}`); } const newRev = await pad.appendRevision(rebasedChangeset, thisSession.author); @@ -658,7 +659,7 @@ const handleUserChanges = async (socket:any, message: { // Make sure the pad always ends with an empty line. if (pad.text().lastIndexOf('\n') !== pad.text().length - 1) { - const nlChangeset = Changeset.makeSplice(pad.text(), pad.text().length - 1, 0, '\n'); + const nlChangeset = makeSplice(pad.text(), pad.text().length - 1, 0, '\n'); await pad.appendRevision(nlChangeset, thisSession.author); } @@ -713,7 +714,7 @@ exports.updatePadClients = async (pad: PadType) => { const revChangeset = revision.changeset; const currentTime = revision.meta.timestamp; - const forWire = Changeset.prepareForWire(revChangeset, pad.pool); + const forWire = prepareForWire(revChangeset, pad.pool); const msg = { type: 'COLLABROOM', data: { @@ -748,7 +749,7 @@ const _correctMarkersInPad = (atext: AText, apool: AttributePool) => { // that aren't at the start of a line const badMarkers = []; let offset = 0; - for (const op of Changeset.deserializeOps(atext.attribs)) { + for (const op of deserializeOps(atext.attribs)) { const attribs = AttributeMap.fromString(op.attribs, apool); const hasMarker = AttributeManager.lineAttributes.some((a: string) => attribs.has(a)); if (hasMarker) { @@ -770,7 +771,7 @@ const _correctMarkersInPad = (atext: AText, apool: AttributePool) => { // create changeset that removes these bad markers offset = 0; - const builder = Changeset.builder(text.length); + const builder = new Builder(text.length); badMarkers.forEach((pos) => { builder.keepText(text.substring(offset, pos)); @@ -905,7 +906,7 @@ const handleClientReady = async (socket:any, message: ClientReadyMessage) => { // return pending changesets for (const r of revisionsNeeded) { - const forWire = Changeset.prepareForWire(changesets[r].changeset, pad.pool); + const forWire = prepareForWire(changesets[r].changeset, pad.pool); const wireMsg = {type: 'COLLABROOM', data: {type: 'CLIENT_RECONNECT', headRev: pad.getHeadRevisionNumber(), @@ -930,8 +931,8 @@ const handleClientReady = async (socket:any, message: ClientReadyMessage) => { let apool; // prepare all values for the wire, there's a chance that this throws, if the pad is corrupted try { - atext = Changeset.cloneAText(pad.atext); - const attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool); + atext = cloneAText(pad.atext); + const attribsForWire = prepareForWire(atext.attribs, pad.pool); apool = attribsForWire.pool.toJsonable(); atext.attribs = attribsForWire.translated; } catch (e:any) { @@ -1167,13 +1168,13 @@ const getChangesetInfo = async (pad: PadType, startNum: number, endNum:number, g if (compositeEnd > endNum || compositeEnd > headRevision + 1) break; const forwards = composedChangesets[`${compositeStart}/${compositeEnd}`]; - const backwards = Changeset.inverse(forwards, lines.textlines, lines.alines, pad.apool()); + const backwards = inverse(forwards, lines.textlines, lines.alines, pad.apool()); - Changeset.mutateAttributionLines(forwards, lines.alines, pad.apool()); - Changeset.mutateTextLines(forwards, lines.textlines); + mutateAttributionLines(forwards, lines.alines, pad.apool()); + mutateTextLines(forwards, lines.textlines); - const forwards2 = Changeset.moveOpsToNewPool(forwards, pad.apool(), apool); - const backwards2 = Changeset.moveOpsToNewPool(backwards, pad.apool(), apool); + const forwards2 = moveOpsToNewPool(forwards, pad.apool(), apool); + const backwards2 = moveOpsToNewPool(backwards, pad.apool(), apool); const t1 = (compositeStart === 0) ? revisionDate[0] : revisionDate[compositeStart - 1]; const t2 = revisionDate[compositeEnd - 1]; @@ -1199,12 +1200,12 @@ const getPadLines = async (pad: PadType, revNum: number) => { if (revNum >= 0) { atext = await pad.getInternalRevisionAText(revNum); } else { - atext = Changeset.makeAText('\n'); + atext = makeAText('\n'); } return { - textlines: Changeset.splitTextLines(atext.text), - alines: Changeset.splitAttributionLines(atext.attribs, atext.text), + textlines: splitTextLines(atext.text), + alines: splitAttributionLines(atext.attribs, atext.text), }; }; @@ -1239,7 +1240,7 @@ const composePadChangesets = async (pad: PadType, startNum: number, endNum: numb for (r = startNum + 1; r < endNum; r++) { const cs = changesets[r]; - changeset = Changeset.compose(changeset, cs, pool); + changeset = compose(changeset as string, cs as string, pool); } return changeset; } catch (e) { diff --git a/src/node/types/PadType.ts b/src/node/types/PadType.ts index 5b96ac5f615..f35b51361c9 100644 --- a/src/node/types/PadType.ts +++ b/src/node/types/PadType.ts @@ -1,10 +1,11 @@ import {MapArrayType} from "./MapType"; +import AttributePool from "../../static/js/AttributePool"; export type PadType = { id: string, - apool: ()=>APool, + apool: ()=>AttributePool, atext: AText, - pool: APool, + pool: AttributePool, getInternalRevisionAText: (text:number|string)=>Promise, getValidRevisionRange: (fromRev: string, toRev: string)=>PadRange, getRevisionAuthor: (rev: number)=>Promise, diff --git a/src/node/utils/ExportHelper.ts b/src/node/utils/ExportHelper.ts index e12332b0672..4c29534f447 100644 --- a/src/node/utils/ExportHelper.ts +++ b/src/node/utils/ExportHelper.ts @@ -21,7 +21,7 @@ import AttributeMap from '../../static/js/AttributeMap'; import AttributePool from "../../static/js/AttributePool"; -const Changeset = require('../../static/js/Changeset'); +import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset'; const { checkValidRev } = require('./checkValidRev'); /* @@ -31,7 +31,7 @@ exports.getPadPlainText = (pad: { getInternalRevisionAText: (arg0: any) => any; const _analyzeLine = exports._analyzeLine; const atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(checkValidRev(revNum)) : pad.atext); const textLines = atext.text.slice(0, -1).split('\n'); - const attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); + const attribLines = splitAttributionLines(atext.attribs, atext.text); const apool = pad.pool; const pieces = []; @@ -52,14 +52,14 @@ type LineModel = { [id:string]:string|number|LineModel } -exports._analyzeLine = (text:string, aline: LineModel, apool: AttributePool) => { +exports._analyzeLine = (text:string, aline: string, apool: AttributePool) => { const line: LineModel = {}; // identify list let lineMarker = 0; line.listLevel = 0; if (aline) { - const [op] = Changeset.deserializeOps(aline); + const [op] = deserializeOps(aline); if (op != null) { const attribs = AttributeMap.fromString(op.attribs, apool); let listType = attribs.get('list'); @@ -79,7 +79,7 @@ exports._analyzeLine = (text:string, aline: LineModel, apool: AttributePool) => } if (lineMarker) { line.text = text.substring(1); - line.aline = Changeset.subattribution(aline, 1); + line.aline = subattribution(aline, 1); } else { line.text = text; line.aline = aline; diff --git a/src/node/utils/ExportHtml.ts b/src/node/utils/ExportHtml.ts index 138f084ba6e..29b171c8fc7 100644 --- a/src/node/utils/ExportHtml.ts +++ b/src/node/utils/ExportHtml.ts @@ -18,7 +18,7 @@ import {MapArrayType} from "../types/MapType"; * limitations under the License. */ -const Changeset = require('../../static/js/Changeset'); +import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset'; const attributes = require('../../static/js/attributes'); const padManager = require('../db/PadManager'); const _ = require('underscore'); @@ -28,6 +28,8 @@ const eejs = require('../eejs'); const _analyzeLine = require('./ExportHelper')._analyzeLine; const _encodeWhitespace = require('./ExportHelper')._encodeWhitespace; import padutils from "../../static/js/pad_utils"; +import {StringIterator} from "../../static/js/StringIterator"; +import {StringAssembler} from "../../static/js/StringAssembler"; const getPadHTML = async (pad: PadType, revNum: string) => { let atext = pad.atext; @@ -44,7 +46,7 @@ const getPadHTML = async (pad: PadType, revNum: string) => { const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string[]) => { const apool = pad.apool(); const textLines = atext.text.slice(0, -1).split('\n'); - const attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); + const attribLines = splitAttributionLines(atext.attribs, atext.text); const tags = ['h1', 'h2', 'strong', 'em', 'u', 's']; const props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough']; @@ -80,6 +82,7 @@ const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string css += '
{ setSearchParams({ ...searchParams, @@ -136,7 +136,7 @@ export const PadPage = ()=>{