diff --git a/.config/vite-utils/polyfills-bun.ts b/.config/vite-utils/polyfills-bun.ts index 526ae82e..c94942f6 100644 --- a/.config/vite-utils/polyfills-bun.ts +++ b/.config/vite-utils/polyfills-bun.ts @@ -1,7 +1,6 @@ import * as vitestExpect from '@vitest/expect' import * as vitestSpy from '@vitest/spy' import { afterAll, afterEach, beforeAll, beforeEach, vi as bunVi, it, jest } from 'bun:test' -// https://github.com/oven-sh/bun/issues/6044 import * as chai from 'chai' import { setupChai, stubGlobal, unstubAllGlobals, waitFor } from './polyfills' diff --git a/.config/vite.bun.ts b/.config/vite.bun.ts index 338687e8..f8dbbc15 100644 --- a/.config/vite.bun.ts +++ b/.config/vite.bun.ts @@ -55,7 +55,7 @@ export default defineConfig({ name: 'polyfills', transform(code) { if (!code.includes('vitest')) return code - code = code.replace(/^import \{(.+?)\} from ['"]vitest['"]/gms, (_, names) => { + code = code.replace(/^import \{([^}]+)\} from ['"]vitest['"];?$/gm, (_, names) => { const namesParsed = names.split(',').map(name => name.trim()) const namesFromFixup: string[] = [] diff --git a/.config/vite.deno.ts b/.config/vite.deno.ts index 30560e2a..289dc693 100644 --- a/.config/vite.deno.ts +++ b/.config/vite.deno.ts @@ -54,7 +54,7 @@ export default defineConfig({ name: 'polyfills', transform(code) { if (!code.includes('vitest')) return code - code = code.replace(/^import \{(.+?)\} from ['"]vitest['"]/gms, (_, names) => { + code = code.replace(/^import \{([^}]+)\} from ['"]vitest['"];?$/gm, (_, names) => { const namesParsed = names.split(',').map(name => name.trim()) return `import {${namesParsed.join(', ')}} from '${POLYFILLS}'` diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7442fcf3..63524fba 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -69,11 +69,11 @@ jobs: - uses: ./.github/actions/init - uses: denoland/setup-deno@v1 with: - deno-version: '1.46.3' + deno-version: '2.0' - name: 'Build tests' run: pnpm exec vite build -c .config/vite.deno.ts - name: 'Run tests' - run: cd dist/tests && deno test -A --unstable-ffi + run: cd dist/tests && deno test -A --unstable-ffi --node-modules-dir=false test-web: runs-on: ubuntu-latest @@ -102,12 +102,15 @@ jobs: actions: write steps: - uses: actions/checkout@v4 + - uses: ./.github/actions/init - name: Run end-to-end tests env: API_ID: ${{ secrets.TELEGRAM_API_ID }} API_HASH: ${{ secrets.TELEGRAM_API_HASH }} + SESSION_DC1: ${{ secrets.SESSION_DC1 }} + SESSION_DC2: ${{ secrets.SESSION_DC2 }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: cd e2e/node && ./cli.sh ci + run: 'cd e2e && pnpm run test:all' e2e-deno: runs-on: ubuntu-latest needs: [lint, test-node, test-web, test-bun, test-deno] @@ -116,17 +119,18 @@ jobs: actions: write steps: - uses: actions/checkout@v4 + - uses: ./.github/actions/init + - uses: denoland/setup-deno@v1 + with: + deno-version: '2.0' - name: Run end-to-end tests under Deno env: API_ID: ${{ secrets.TELEGRAM_API_ID }} API_HASH: ${{ secrets.TELEGRAM_API_HASH }} + SESSION_DC1: ${{ secrets.SESSION_DC1 }} + SESSION_DC2: ${{ secrets.SESSION_DC2 }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: nick-fields/retry@v2 - # thanks docker networking very cool - with: - max_attempts: 3 - timeout_minutes: 30 - command: cd e2e/deno && ./cli.sh ci + run: 'cd e2e && pnpm run deno:test:all' cr: needs: diff --git a/e2e/.env.example b/e2e/.env.example new file mode 100644 index 00000000..3e091a02 --- /dev/null +++ b/e2e/.env.example @@ -0,0 +1,9 @@ +# obtain these values from my.telegram.org +API_ID= +API_HASH= + +# mtcute session strings for test dc1 and test dc2 +SESSION_DC1= +SESSION_DC2= + +GITHUB_TOKEN= \ No newline at end of file diff --git a/e2e/.gitignore b/e2e/.gitignore new file mode 100644 index 00000000..2eea525d --- /dev/null +++ b/e2e/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/e2e/_runtime/.gitignore b/e2e/_runtime/.gitignore new file mode 100644 index 00000000..c96a04f0 --- /dev/null +++ b/e2e/_runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/e2e/deno.lock b/e2e/deno.lock new file mode 100644 index 00000000..fc50df05 --- /dev/null +++ b/e2e/deno.lock @@ -0,0 +1,50 @@ +{ + "version": "4", + "specifiers": { + "jsr:@fuman/utils@0.0.1": "0.0.1", + "jsr:@std/assert@^1.0.8": "1.0.8", + "jsr:@std/internal@^1.0.5": "1.0.5", + "jsr:@std/testing@1.0.5": "1.0.5" + }, + "jsr": { + "@fuman/utils@0.0.1": { + "integrity": "7cf43898814272c0918e813b34a1ae848ded5710383018dacd27a8fbb1dd6437" + }, + "@std/assert@1.0.8": { + "integrity": "ebe0bd7eb488ee39686f77003992f389a06c3da1bbd8022184804852b2fa641b" + }, + "@std/internal@1.0.5": { + "integrity": "54a546004f769c1ac9e025abd15a76b6671ddc9687e2313b67376125650dc7ba" + }, + "@std/testing@1.0.5": { + "integrity": "6e693cbec94c81a1ad3df668685c7ba8e20742bb10305bc7137faa5cf16d2ec4", + "dependencies": [ + "jsr:@std/assert", + "jsr:@std/internal" + ] + } + }, + "redirects": { + "https://esm.sh/v135/@types/chai@~5/index.d.ts": "https://esm.sh/v135/@types/chai@5.0.1/index.d.ts" + }, + "remote": { + "https://esm.sh/chai@5.1.2": "52c79876382aaf6855c55ea66e2ff88675457f4727bfde37363bb199cdda8488", + "https://esm.sh/v135/chai@5.1.2/denonext/chai.mjs": "05cc6071c804cf39d4325a2c93807727623019abedca6ed1cb6534137bd4f65e" + }, + "workspace": { + "packageJson": { + "dependencies": [ + "npm:@fuman/utils@0.0.1", + "npm:@types/chai@^4.3.8", + "npm:@types/mocha@^10.0.2", + "npm:@types/node@^20.8.10", + "npm:better-sqlite3@11.6.0", + "npm:chai@^4.3.10", + "npm:dotenv-cli@7.4.4", + "npm:esbuild@0.24", + "npm:globstar@1.0.0", + "npm:tsx@^4.19.2" + ] + } + } +} diff --git a/e2e/deno/.dockerignore b/e2e/deno/.dockerignore deleted file mode 100644 index a1de89c1..00000000 --- a/e2e/deno/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -/.jsr-data -/dist -/deno.lock \ No newline at end of file diff --git a/e2e/deno/.env.example b/e2e/deno/.env.example deleted file mode 100644 index 20a2ee7d..00000000 --- a/e2e/deno/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -# obtain these values from my.telegram.org -API_ID= -API_HASH= - -GITHUB_TOKEN= \ No newline at end of file diff --git a/e2e/deno/.gitignore b/e2e/deno/.gitignore deleted file mode 100644 index 6771fc36..00000000 --- a/e2e/deno/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/.jsr-data -.env -/deno.lock -/.sessions \ No newline at end of file diff --git a/e2e/deno/Dockerfile.build b/e2e/deno/Dockerfile.build deleted file mode 100644 index 5d1df581..00000000 --- a/e2e/deno/Dockerfile.build +++ /dev/null @@ -1,24 +0,0 @@ -FROM denoland/deno:bin-1.45.5 as deno-bin - -FROM node:20 -WORKDIR /app - -COPY --from=deno-bin /deno /bin/deno - -RUN corepack enable && \ - corepack prepare pnpm@9.0.6 --activate - -COPY ../.. /app/ - -RUN pnpm install --frozen-lockfile && \ - pnpm -C packages/tl run gen-code - -RUN apt update && apt install -y socat - -ENV REGISTRY="http://jsr/" -ENV E2E="1" -ENV JSR="1" -ENV JSR_TOKEN="token" - -ENTRYPOINT [ "node", "/app/scripts/publish.js" ] -CMD [ "all" ] \ No newline at end of file diff --git a/e2e/deno/Dockerfile.jsr b/e2e/deno/Dockerfile.jsr deleted file mode 100644 index 170237fb..00000000 --- a/e2e/deno/Dockerfile.jsr +++ /dev/null @@ -1,3 +0,0 @@ -FROM ghcr.io/teidesu/jsr-api:latest - -RUN apt update && apt install -y curl \ No newline at end of file diff --git a/e2e/deno/Dockerfile.test b/e2e/deno/Dockerfile.test deleted file mode 100644 index ad9b1efb..00000000 --- a/e2e/deno/Dockerfile.test +++ /dev/null @@ -1,10 +0,0 @@ -FROM denoland/deno:1.45.5 -WORKDIR /app - -RUN apt update && apt install -y socat - -COPY ./ /app/ - -ENV DOCKER="1" - -ENTRYPOINT [ "./cli.sh", "run" ] \ No newline at end of file diff --git a/e2e/deno/README.md b/e2e/deno/README.md deleted file mode 100644 index 034bfa89..00000000 --- a/e2e/deno/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# mtcute e2e tests (Deno edition) - -This directory contains end-to-end tests for mtcute under Deno. - -They are made for 2 purposes: - - Ensure published packages work as expected and can properly be imported - - Ensure that the library works with the actual Telegram API - -To achieve the first goal, we use a local JSR instance container where we publish the package, -and then install it from there in another container - -## Setting up - -Before running the tests, you need to copy `.env.example` to `.env` and fill in the values - -## Running tests - -```bash -# first start a local jsr instance -./cli.sh start - -# push all packages to the local registry -./cli.sh update -# pushing a particular package is not supported due to jsr limitations - -# run the tests -./cli.sh run -# or in docker -./cli.sh run-docker -``` diff --git a/e2e/deno/cli.sh b/e2e/deno/cli.sh deleted file mode 100755 index 79c7c8ff..00000000 --- a/e2e/deno/cli.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash - -set -eau - -method=$1 -shift - -case "$method" in - "start") - docker compose up -d --wait jsr - node ./init-server.js - ;; - "update") - # unpublish all packages - if [ -d .jsr-data/gcs/modules/@mtcute ]; then - rm -rf .jsr-data/gcs/modules/@mtcute - docker compose exec jsr-db psql registry -U user -c "delete from publishing_tasks;" - docker compose exec jsr-db psql registry -U user -c "delete from package_files;" - docker compose exec jsr-db psql registry -U user -c "delete from npm_tarballs;" - docker compose exec jsr-db psql registry -U user -c "delete from package_version_dependencies;" - docker compose exec jsr-db psql registry -U user -c "delete from package_versions;" - docker compose exec jsr-db psql registry -U user -c "delete from packages;" - fi - - # publish all packages - docker compose run --rm --build build all - - # clear cache - if command -v deno &> /dev/null; then - rm -rf $(deno info --json | jq .denoDir -r)/deps - fi - if [ -f deno.lock ]; then - rm deno.lock - fi - ;; - "clean") - docker compose down - rm -rf .jsr-data - ;; - "stop") - docker compose down - ;; - "run") - if [ -f .env ]; then - source .env - fi - - export JSR_URL=http://localhost:4873 - - if [ ! -z ${DOCKER+x} ]; then - # running behind a socat proxy seems to fix some of the docker networking issues (thx kamillaova) - socat TCP-LISTEN:4873,fork,reuseaddr TCP4:jsr:80 & - socat_pid=$! - - # run `deno cache` with a few retries to make sure everything is cached - for i in {1..5}; do - if deno cache tests/*.ts; then - break - fi - done - - trap "kill $socat_pid" EXIT - fi - - if [ $# -eq 0 ]; then - deno test -A --unstable-ffi tests/**/*.ts - else - deno test -A --unstable-ffi $@ - fi - ;; - "run-docker") - source .env - docker compose run --rm --build test $@ - ;; - "ci") - set -eaux - if [ -d .jsr-data ]; then - # clean up data from previous runs - docker compose down - sudo rm -rf .jsr-data - fi - mkdir .jsr-data - ./cli.sh start - ./cli.sh update - docker compose run --rm --build test - ;; - *) - echo "Unknown command" - ;; -esac \ No newline at end of file diff --git a/e2e/deno/deno.json b/e2e/deno/deno.json deleted file mode 100644 index 120e67e5..00000000 --- a/e2e/deno/deno.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "imports": { - "@mtcute/web": "jsr:@mtcute/web@*", - "@mtcute/wasm": "jsr:@mtcute/wasm@*", - "@mtcute/tl": "jsr:@mtcute/tl@*", - "@mtcute/tl-runtime": "jsr:@mtcute/tl-runtime@*", - "@mtcute/core": "jsr:@mtcute/core@*", - "@mtcute/deno": "jsr:@mtcute/deno@*" - } -} diff --git a/e2e/deno/docker-compose.yaml b/e2e/deno/docker-compose.yaml deleted file mode 100644 index 80cc9395..00000000 --- a/e2e/deno/docker-compose.yaml +++ /dev/null @@ -1,78 +0,0 @@ -version: "3" -services: - # jsr (based on https://github.com/teidesu/docker-images/blob/main/jsr/docker-compose.yaml) - jsr-db: - image: postgres:15 - command: postgres -c 'max_connections=1000' - restart: always - environment: - POSTGRES_USER: user - POSTGRES_PASSWORD: password - POSTGRES_DB: registry - healthcheck: - test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB" - interval: 5s - retries: 20 - start_period: 5s - volumes: - - ./.jsr-data/db:/var/lib/postgresql/data - jsr-gcs: - image: fsouza/fake-gcs-server:latest - command: -scheme http -filesystem-root=/gcs-data -port 4080 - volumes: - - ./.jsr-data/gcs:/gcs-data - jsr-api: - depends_on: - jsr-db: - condition: service_healthy - jsr-gcs: - condition: service_started - healthcheck: - test: "curl --fail http://localhost:8001/sitemap.xml || exit 1" - interval: 5s - retries: 20 - start_period: 5s - build: - context: . - dockerfile: Dockerfile.jsr - environment: - - "DATABASE_URL=postgres://user:password@jsr-db/registry" - - "GITHUB_CLIENT_ID=fake" - - "GITHUB_CLIENT_SECRET=fake" - - "GCS_ENDPOINT=http://jsr-gcs:4080" - - "MODULES_BUCKET=modules" - - "PUBLISHING_BUCKET=publishing" - - "DOCS_BUCKET=docs" - - "NPM_BUCKET=npm" - - "REGISTRY_URL=http://localhost:4873" - - "NPM_URL=http://example.com/unused" - jsr: - depends_on: - jsr-api: - condition: service_healthy - image: nginx:1.21 - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - ports: - - "4873:80" - - # our stuff - build: - build: - context: ../.. - dockerfile: e2e/deno/Dockerfile.build - environment: - - GITHUB_TOKEN=${GITHUB_TOKEN} - depends_on: - - jsr - test: - build: - context: . - dockerfile: Dockerfile.test - environment: - - API_ID=${API_ID} - - API_HASH=${API_HASH} - depends_on: - - jsr -networks: - mtcute-e2e: {} \ No newline at end of file diff --git a/e2e/deno/init-server.js b/e2e/deno/init-server.js deleted file mode 100644 index dc1152fd..00000000 --- a/e2e/deno/init-server.js +++ /dev/null @@ -1,65 +0,0 @@ -/* eslint-disable no-console */ -import { execSync } from 'node:child_process' - -function getDockerContainerIp(name) { - const containerId = execSync(`docker compose ps -q ${name}`).toString().trim() - const ip = execSync(`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ${containerId}`) - .toString() - .trim() - - return ip -} - -for (const stmt of [ - "delete from tokens where user_id = '00000000-0000-0000-0000-000000000000';", - "insert into tokens (hash, user_id, type, expires_at) values ('3c469e9d6c5875d37a43f353d4f88e61fcf812c66eee3457465a40b0da4153e0', '00000000-0000-0000-0000-000000000000', 'web', current_date + interval '100' year);", - "update users set is_staff = true, scope_limit = 99999 where id = '00000000-0000-0000-0000-000000000000';", -]) { - execSync(`docker compose exec jsr-db psql registry -U user -c "${stmt}"`) -} - -console.log('[i] Initialized database') - -const GCS_URL = `http://${getDockerContainerIp('jsr-gcs')}:4080/` -const API_URL = `http://${getDockerContainerIp('jsr-api')}:8001/` - -async function createBucket(name) { - try { - const resp = await fetch(`${GCS_URL}storage/v1/b`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name }), - }) - - await resp.text() - - return resp.ok || resp.status === 409 - } catch (e) { - console.log(e) - - return false - } -} - -for (const bucket of ['modules', 'docs', 'publishing', 'npm']) { - const ok = await createBucket(bucket) - console.log(`[i] Created bucket ${bucket}: ${ok}`) -} - -// create @mtcute scope if it doesn't exist -const resp = await fetch(`${API_URL}api/scopes`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Cookie': 'token=token', - }, - body: JSON.stringify({ scope: 'mtcute' }), -}) - -if (resp.status !== 200 && resp.status !== 409) { - throw new Error(`Failed to create scope: ${resp.statusText} ${await resp.text()}`) -} - -if (resp.status === 200) { - console.log('[i] Created scope mtcute') -} diff --git a/e2e/deno/nginx.conf b/e2e/deno/nginx.conf deleted file mode 100644 index 846ef740..00000000 --- a/e2e/deno/nginx.conf +++ /dev/null @@ -1,29 +0,0 @@ -events {} - -http { - upstream gcs { - server jsr-gcs:4080; - } - - upstream api { - server jsr-api:8001; - } - - error_log /error.log debug; - - server { - listen 80; - - location ~ ^/(@.*)$ { - proxy_pass http://gcs/storage/v1/b/modules/o/$1?alt=media; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - location / { - proxy_pass http://api; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - } -} \ No newline at end of file diff --git a/e2e/deno/tests/01.auth.ts b/e2e/deno/tests/01.auth.ts deleted file mode 100644 index 796444aa..00000000 --- a/e2e/deno/tests/01.auth.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { tl, User } from '@mtcute/core' -import { BaseTelegramClient, TelegramClient } from '@mtcute/core/client.js' -import { assertEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts' - -import { getApiParams } from '../utils.ts' - -function getAccountId() { - return Math.floor(Math.random() * 10000) - .toString() - .padStart(4, '0') -} - -async function authorizeInDc(dc: number, base: BaseTelegramClient) { - const tg = new TelegramClient({ client: base }) - - while (true) { - await base.mt.storage.load() - await base.storage.clear(true) - - const phone = `99966${dc}${getAccountId()}` - - let user - - try { - const sentCode = await tg.sendCode({ phone }) - - let auth = await tg.call({ - _: 'auth.signIn', - phoneNumber: phone, - phoneCode: `${dc}${dc}${dc}${dc}${dc}`, - phoneCodeHash: sentCode.phoneCodeHash, - }) - - if (auth._ === 'auth.authorizationSignUpRequired') { - auth = await tg.call({ - _: 'auth.signUp', - phoneNumber: phone, - phoneCodeHash: sentCode.phoneCodeHash, - firstName: 'mtcute e2e', - lastName: '', - }) - - if (auth._ !== 'auth.authorization') { - throw new Error('Unexpected response') - } - } - - await tg.notifyLoggedIn(auth) - - user = new User(auth.user) - } catch (e) { - if (tl.RpcError.is(e, 'SESSION_PASSWORD_NEEDED') || tl.RpcError.is(e, 'PHONE_NUMBER_FLOOD')) { - // retry with another number - await tg.close() - continue - } - - throw e - } - - await tg.close() - - assertEquals(user.isSelf, true) - assertEquals(user.phoneNumber, phone) - break - } -} - -Deno.test('1. authorization', { sanitizeResources: false }, async (t) => { - await t.step('should authorize in default dc', async () => { - const base = new BaseTelegramClient(getApiParams('dc2.session')) - - await authorizeInDc(2, base) - }) - - await t.step('should authorize in dc 1', async () => { - const base = new BaseTelegramClient(getApiParams('dc1.session')) - - await authorizeInDc(1, base) - }) -}) diff --git a/e2e/deno/tests/02.methods.ts b/e2e/deno/tests/02.methods.ts deleted file mode 100644 index 07b8e3ff..00000000 --- a/e2e/deno/tests/02.methods.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { MtPeerNotFoundError } from '@mtcute/core' -import { TelegramClient } from '@mtcute/core/client.js' -import { assertEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts' - -import { getApiParams } from '../utils.ts' - -Deno.test('2. calling methods', { sanitizeResources: false }, async (t) => { - const tg = new TelegramClient(getApiParams('dc2.session')) - - await tg.connect() - - await t.step('getUsers(@BotFather)', async () => { - const [user] = await tg.getUsers('botfather') - - assertEquals(user?.isBot, true) - assertEquals(user?.displayName, 'BotFather') - }) - - await t.step('getUsers(@BotFather) - cached', async () => { - const [user] = await tg.getUsers('botfather') - - assertEquals(user?.isBot, true) - assertEquals(user?.displayName, 'BotFather') - }) - - await t.step('getHistory(777000)', async () => { - try { - await tg.findDialogs(777000) // ensure it's cached - } catch (e) { - if (e instanceof MtPeerNotFoundError) { - // this happens sometimes :D gracefully skip - return - } - - throw e - } - - const history = await tg.getHistory(777000, { limit: 5 }) - - assertEquals(history[0].chat.chatType, 'private') - assertEquals(history[0].chat.id, 777000) - assertEquals(history[0].chat.firstName, 'Telegram') - }) - - await tg.close() -}) diff --git a/e2e/deno/tests/03.files.ts b/e2e/deno/tests/03.files.ts deleted file mode 100644 index 83c38162..00000000 --- a/e2e/deno/tests/03.files.ts +++ /dev/null @@ -1,171 +0,0 @@ -import type { FileDownloadLocation } from '@mtcute/core' - -import { createHash } from 'node:crypto' -import { Thumbnail } from '@mtcute/core' -import { TelegramClient } from '@mtcute/core/client.js' -import { sleep } from '@mtcute/core/utils.js' -import { assertEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts' - -import { getApiParams } from '../utils.ts' - -const CINNAMOROLL_PFP_CHAT = 'test_file_dc2' -const CINNAMOROLL_PFP_THUMB_SHA256 = '3e6f220235a12547c16129f50c19ed3224d39b827414d1d500f79569a3431eae' -const CINNAMOROLL_PFP_SHA256 = '4d9836a71ac039f5656cde55b83525871549bfbff9cfb658c3f8381c5ba89ce8' - -const UWU_MSG = 'https://t.me/test_file_dc2/8' -const UWU_SHA256 = '357b78c9f9d20e813f729a19dd90c6727f30ebd4c8c83557022285f283a705b9' - -const SHREK_MSG = 'https://t.me/test_file_dc2/11' -const SHREK_SHA256 = 'd3e6434e027f3d31dc3e05c6ea2eaf84fdd1fb00774a215f89d9ed8b56f86258' - -const LARGE_MSG = 'https://t.me/test_file_dc2/12' - -async function downloadAsSha256(client: TelegramClient, location: FileDownloadLocation): Promise { - const sha = createHash('sha256') - - for await (const chunk of client.downloadAsIterable(location)) { - sha.update(chunk) - } - - return sha.digest('hex') -} - -Deno.test('3. working with files', { sanitizeResources: false }, async (t) => { - // sometimes test dcs are overloaded and we get FILE_REFERENCE_EXPIRED - // because we got multiple -500:No workers running errors in a row - // we currently don't have file references database, so we can just retry the test for now - // - // ...except we can't under deno because it's not implemented - // https://github.com/denoland/deno/issues/19882 - // this.retries(2) - - await t.step('same-dc', async (t) => { - const tg = new TelegramClient(getApiParams('dc2.session')) - - await tg.connect() - - await t.step('should download pfp thumbs', async () => { - const chat = await tg.getChat(CINNAMOROLL_PFP_CHAT) - if (!chat.photo) throw new Error('Chat has no photo') - - assertEquals(await downloadAsSha256(tg, chat.photo.big), CINNAMOROLL_PFP_THUMB_SHA256) - }) - - await t.step('should download animated pfps', async () => { - const chat = await tg.getFullChat(CINNAMOROLL_PFP_CHAT) - const thumb = chat.fullPhoto?.getThumbnail(Thumbnail.THUMB_VIDEO_PROFILE) - if (!thumb) throw new Error('Chat has no animated pfp') - - assertEquals(await downloadAsSha256(tg, thumb), CINNAMOROLL_PFP_SHA256) - }) - - await t.step('should download photos', async () => { - const msg = await tg.getMessageByLink(UWU_MSG) - - if (msg?.media?.type !== 'photo') { - throw new Error('Message not found or not a photo') - } - - assertEquals(await downloadAsSha256(tg, msg.media), UWU_SHA256) - }) - - await t.step('should download documents', async () => { - const msg = await tg.getMessageByLink(SHREK_MSG) - - if (msg?.media?.type !== 'document') { - throw new Error('Message not found or not a document') - } - - assertEquals(await downloadAsSha256(tg, msg.media), SHREK_SHA256) - }) - - await t.step('should cancel downloads', async () => { - const msg = await tg.getMessageByLink(LARGE_MSG) - - if (msg?.media?.type !== 'document') { - throw new Error('Message not found or not a document') - } - - const media = msg.media - - const abort = new AbortController() - - let downloaded = 0 - - async function download() { - const dl = tg.downloadAsIterable(media, { abortSignal: abort.signal }) - - try { - for await (const chunk of dl) { - downloaded += chunk.length - } - } catch (e) { - if (!(e instanceof DOMException && e.name === 'AbortError')) throw e - } - } - - const promise = download() - - // let it download for 10 seconds - await sleep(10000) - abort.abort() - // abort and snap the downloaded amount - const downloadedBefore = downloaded - - const avgSpeed = downloaded / 10 - // eslint-disable-next-line no-console - console.log('Average speed: %d KiB/s', avgSpeed / 1024) - - // wait a bit more to make sure it's aborted - await sleep(2000) - await promise - - assertEquals(downloaded, downloadedBefore, 'nothing should be downloaded after abort') - }) - - await tg.close() - }) - - await t.step('cross-dc', async (t) => { - const tg = new TelegramClient(getApiParams('dc1.session')) - - await tg.connect() - - await t.step('should download pfp thumbs', async () => { - const chat = await tg.getChat(CINNAMOROLL_PFP_CHAT) - if (!chat.photo) throw new Error('Chat has no photo') - - assertEquals(await downloadAsSha256(tg, chat.photo.big), CINNAMOROLL_PFP_THUMB_SHA256) - }) - - await t.step('should download animated pfps', async () => { - const chat = await tg.getFullChat(CINNAMOROLL_PFP_CHAT) - const thumb = chat.fullPhoto?.getThumbnail(Thumbnail.THUMB_VIDEO_PROFILE) - if (!thumb) throw new Error('Chat has no animated pfp') - - assertEquals(await downloadAsSha256(tg, thumb), CINNAMOROLL_PFP_SHA256) - }) - - await t.step('should download photos', async () => { - const msg = await tg.getMessageByLink(UWU_MSG) - - if (msg?.media?.type !== 'photo') { - throw new Error('Message not found or not a photo') - } - - assertEquals(await downloadAsSha256(tg, msg.media), UWU_SHA256) - }) - - await t.step('should download documents', async () => { - const msg = await tg.getMessageByLink(SHREK_MSG) - - if (msg?.media?.type !== 'document') { - throw new Error('Message not found or not a document') - } - - assertEquals(await downloadAsSha256(tg, msg.media), SHREK_SHA256) - }) - - await tg.close() - }) -}) diff --git a/e2e/deno/tests/04.updates.ts b/e2e/deno/tests/04.updates.ts deleted file mode 100644 index 31c7fe07..00000000 --- a/e2e/deno/tests/04.updates.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { Message } from '@mtcute/core' -import { TelegramClient } from '@mtcute/core/client.js' -import { assertEquals, assertNotEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts' - -import { getApiParams, waitFor } from '../utils.ts' - -Deno.test('4. handling updates', { sanitizeResources: false }, async (t) => { - const tg1 = new TelegramClient(getApiParams('dc1.session')) - tg1.log.prefix = '[tg1] ' - const tg2 = new TelegramClient(getApiParams('dc2.session')) - tg2.log.prefix = '[tg2] ' - - await tg1.connect() - await tg1.startUpdatesLoop() - await tg2.connect() - - await t.step('should send and receive messages', async () => { - const tg1Messages: Message[] = [] - - tg1.on('new_message', msg => tg1Messages.push(msg)) - - const [tg1User] = await tg1.getUsers('self') - let username = tg1User!.username - - if (!username) { - username = `mtcute_e2e_${Math.random().toString(36).slice(2)}` - await tg1.setMyUsername(username) - } - - const messageText = `mtcute test message ${Math.random().toString(36).slice(2)}` - const sentMsg = await tg2.sendText(username, messageText) - - assertEquals(sentMsg.text, messageText) - assertEquals(sentMsg.chat.id, tg1User!.id) - - await waitFor(() => { - assertNotEquals( - tg1Messages.find(msg => msg.text === messageText), - undefined, - ) - }) - }) - - await tg1.close() - await tg2.close() -}) diff --git a/e2e/deno/tests/05.worker.ts b/e2e/deno/tests/05.worker.ts deleted file mode 100644 index a74a2dc2..00000000 --- a/e2e/deno/tests/05.worker.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { Message } from '@mtcute/deno' -import type { CustomMethods } from './_worker.ts' -import { TelegramClient } from '@mtcute/core/client.js' -import { Long, TelegramWorkerPort, tl } from '@mtcute/deno' - -import { assertEquals, assertGreater, assertInstanceOf } from 'https://deno.land/std@0.223.0/assert/mod.ts' - -import { getApiParams, waitFor } from '../utils.ts' - -Deno.test('5. worker', { sanitizeResources: false }, async (t) => { - const worker = new Worker(new URL('_worker.ts', import.meta.url), { - type: 'module', - }) - - const port = new TelegramWorkerPort({ - worker, - }) - const portClient = new TelegramClient({ client: port }) - - await t.step('should make api calls', async () => { - const res = await port.call({ _: 'help.getConfig' }) - assertEquals(res._, 'config') - - const premiumPromo = await port.call({ _: 'help.getPremiumPromo' }) - // ensure Long-s are correctly serialized - assertEquals(Long.isLong((premiumPromo.users[0] as tl.RawUser).accessHash), true) - }) - - await t.step('should call custom methods', async () => { - const hello = await port.invokeCustom('hello') - assertEquals(hello, 'world') - - const sum = await port.invokeCustom('sum', 2, 3) - assertEquals(sum, 5) - }) - - await t.step('should throw errors', async () => { - try { - await port.call({ _: 'test.useConfigSimple' }) - throw new Error('should have thrown') - } catch (e) { - assertInstanceOf(e, tl.RpcError) - } - }) - - await t.step('should receive updates', async () => { - const client2 = new TelegramClient(getApiParams('dc2.session')) - - try { - await client2.connect() - await port.startUpdatesLoop() - - const me = await portClient.getMe() - // ensure Long-s are correctly serialized - assertEquals(Long.isLong(me.raw.accessHash), true) - let username = me.username - - if (!username) { - username = `mtcute_e2e_${Math.random().toString(36).slice(2, 8)}` - await portClient.setMyUsername(username) - } - - const msgs: Message[] = [] - portClient.on('new_message', (msg) => { - msgs.push(msg) - }) - - const testText = `test ${Math.random()}` - await client2.sendText(username, testText) - - await waitFor(() => { - assertGreater(msgs.length, 0) - assertEquals(msgs[0].text, testText) - }) - } catch (e) { - await client2.close() - throw e - } - - await client2.close() - }) - - await port.close() - worker.terminate() -}) diff --git a/e2e/deno/tests/_worker.ts b/e2e/deno/tests/_worker.ts deleted file mode 100644 index 967cbd9a..00000000 --- a/e2e/deno/tests/_worker.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { WorkerCustomMethods } from '@mtcute/core/worker.js' -import { BaseTelegramClient, TelegramWorker } from '@mtcute/deno' - -import { getApiParams } from '../utils.ts' - -const customMethods = { - hello: async () => 'world', - sum: async (a: number, b: number) => a + b, -} as const satisfies WorkerCustomMethods -export type CustomMethods = typeof customMethods - -const client = new BaseTelegramClient(getApiParams('dc1.session')) - -// eslint-disable-next-line no-new -new TelegramWorker({ - client, - customMethods, -}) diff --git a/e2e/deno/tests/packaging/base-client.ts b/e2e/deno/tests/packaging/base-client.ts deleted file mode 100644 index 4ef85b31..00000000 --- a/e2e/deno/tests/packaging/base-client.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { BaseTelegramClient } from '@mtcute/core/client.js' -import { assertEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts' - -import { getApiParams } from '../../utils.ts' - -Deno.test('@mtcute/core', async (t) => { - await t.step('connects to test DC and makes help.getNearestDc', async () => { - const tg = new BaseTelegramClient({ - ...getApiParams(), - }) - - await tg.connect() - const config = await tg.call({ _: 'help.getNearestDc' }) - await tg.close() - - assertEquals(typeof config, 'object') - assertEquals(config._, 'nearestDc') - assertEquals(config.thisDc, 2) - }) -}) diff --git a/e2e/deno/tests/packaging/tl-runtime.ts b/e2e/deno/tests/packaging/tl-runtime.ts deleted file mode 100644 index 7fceabf1..00000000 --- a/e2e/deno/tests/packaging/tl-runtime.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Long } from '@mtcute/core' -import { setPlatform } from '@mtcute/core/platform.js' -import { TlBinaryReader, TlBinaryWriter, TlSerializationCounter } from '@mtcute/tl-runtime' -import { WebPlatform } from '@mtcute/web' -import { assertEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts' - -// here we primarily want to check that everything imports properly, -// and that the code is actually executable. The actual correctness -// of the implementation is covered tested by unit tests - -const p = new WebPlatform() -setPlatform(p) - -Deno.test('encodings', () => { - assertEquals(p.hexEncode(new Uint8Array([1, 2, 3, 4, 5])), '0102030405') -}) - -Deno.test('TlBinaryReader', () => { - const map = { - 85337187(r: any) { - const ret: any = {} - ret._ = 'mt_resPQ' - ret.nonce = r.int128() - ret.serverNonce = r.int128() - ret.pq = r.bytes() - ret.serverPublicKeyFingerprints = r.vector(r.long) - - return ret - }, - } - const data - = '000000000000000001c8831ec97ae55140000000632416053e0549828cca27e966b301a48fece2fca5cf4d33f4a11ea877ba4aa5739073300817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3' - const buf = p.hexDecode(data) - - const r = new TlBinaryReader(map, buf, 8) - - assertEquals(r.long().toString(16), '51e57ac91e83c801') - assertEquals(r.uint(), 64) - - const obj: any = r.object() - assertEquals(obj._, 'mt_resPQ') -}) - -Deno.test('TlBinaryWriter', () => { - const map = { - mt_resPQ(w: any, obj: any) { - w.uint(85337187) - w.bytes(obj.pq) - w.vector(w.long, obj.serverPublicKeyFingerprints) - }, - _staticSize: {} as any, - } - - const obj = { - _: 'mt_resPQ', - pq: p.hexDecode('17ED48941A08F981'), - serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', 16)], - } - - assertEquals(TlSerializationCounter.countNeededBytes(map, obj), 32) - - const w = TlBinaryWriter.alloc(map, 48) - w.long(Long.ZERO) - w.long(Long.fromString('51E57AC91E83C801', true, 16)) // messageId - w.object(obj) - - assertEquals( - p.hexEncode(w.result()), - '000000000000000001c8831ec97ae551632416050817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3', - ) -}) diff --git a/e2e/deno/tests/packaging/tl-schema.ts b/e2e/deno/tests/packaging/tl-schema.ts deleted file mode 100644 index 65aed652..00000000 --- a/e2e/deno/tests/packaging/tl-schema.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Long } from '@mtcute/core' -import { setPlatform } from '@mtcute/core/platform.js' -import { tl } from '@mtcute/tl' -import { TlBinaryReader, TlBinaryWriter } from '@mtcute/tl-runtime' -import { __tlReaderMap } from '@mtcute/tl/binary/reader.js' -import { __tlWriterMap } from '@mtcute/tl/binary/writer.js' -import { WebPlatform } from '@mtcute/web' -import { assertEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts' - -// here we primarily want to check that @mtcute/tl correctly works with @mtcute/tl-runtime - -const p = new WebPlatform() -setPlatform(p) - -Deno.test('@mtcute/tl', async (t) => { - await t.step('writers map works with TlBinaryWriter', () => { - const obj = { - _: 'inputPeerUser', - userId: 123, - accessHash: Long.fromNumber(456), - } - - assertEquals( - p.hexEncode(TlBinaryWriter.serializeObject(__tlWriterMap, obj)), - '4ca5e8dd7b00000000000000c801000000000000', - ) - }) - - await t.step('readers map works with TlBinaryReader', () => { - const buf = p.hexDecode('4ca5e8dd7b00000000000000c801000000000000') - - const obj = TlBinaryReader.deserializeObject(__tlReaderMap, buf) - - assertEquals(obj._, 'inputPeerUser') - assertEquals(obj.userId, 123) - assertEquals(obj.accessHash.toString(), '456') - }) - - await t.step('correctly checks for combinator types', () => { - assertEquals(tl.isAnyInputUser({ _: 'inputUserEmpty' }), true) - }) -}) diff --git a/e2e/deno/tests/packaging/wasm.ts b/e2e/deno/tests/packaging/wasm.ts deleted file mode 100644 index 47a7043d..00000000 --- a/e2e/deno/tests/packaging/wasm.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ige256Decrypt, ige256Encrypt } from '@mtcute/wasm' -import { WebCryptoProvider, WebPlatform } from '@mtcute/web' -import { assertEquals } from 'https://deno.land/std@0.223.0/assert/mod.ts' - -await new WebCryptoProvider().initialize() -const platform = new WebPlatform() - -Deno.test('@mtcute/wasm', async (t) => { - const key = platform.hexDecode('5468697320697320616E20696D706C655468697320697320616E20696D706C65') - const iv = platform.hexDecode('6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353') - - const data = platform.hexDecode('99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b') - const dataEnc = platform.hexDecode('792ea8ae577b1a66cb3bd92679b8030ca54ee631976bd3a04547fdcb4639fa69') - - await t.step('should work with Buffers', () => { - assertEquals(ige256Encrypt(data, key, iv), dataEnc) - assertEquals(ige256Decrypt(dataEnc, key, iv), data) - }) - - await t.step('should work with Uint8Arrays', () => { - assertEquals(ige256Encrypt(data, key, iv), dataEnc) - assertEquals(ige256Decrypt(dataEnc, key, iv), data) - }) -}) diff --git a/e2e/deno/utils.ts b/e2e/deno/utils.ts deleted file mode 100644 index 86f0f183..00000000 --- a/e2e/deno/utils.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { MaybePromise } from '@mtcute/core' -import { MemoryStorage } from '@mtcute/core' -import { setPlatform } from '@mtcute/core/platform.js' -import { LogManager, sleep } from '@mtcute/core/utils.js' -import { DenoCryptoProvider, DenoPlatform, SqliteStorage, TcpTransport } from '@mtcute/deno' - -export function getApiParams(storage?: string) { - if (!Deno.env.has('API_ID') || !Deno.env.has('API_HASH')) { - throw new Error('API_ID and API_HASH env variables must be set') - } - - Deno.mkdirSync('.sessions', { recursive: true }) - - setPlatform(new DenoPlatform()) - - return { - apiId: Number.parseInt(Deno.env.get('API_ID')!), - apiHash: Deno.env.get('API_HASH')!, - testMode: true, - storage: storage ? new SqliteStorage(`.sessions/${storage}`) : new MemoryStorage(), - logLevel: LogManager.VERBOSE, - transport: () => new TcpTransport(), - crypto: new DenoCryptoProvider(), - } -} - -export async function waitFor(condition: () => MaybePromise, timeout = 5000): Promise { - const start = Date.now() - let lastError - - while (Date.now() - start < timeout) { - try { - await condition() - - return - } catch (e) { - lastError = e - await sleep(100) - } - } - - throw lastError -} diff --git a/e2e/import-map.json b/e2e/import-map.json new file mode 100644 index 00000000..e254cef5 --- /dev/null +++ b/e2e/import-map.json @@ -0,0 +1,23 @@ +{ + "imports": { + "@fuman/utils": "jsr:@fuman/utils@0.0.1", + "chai": "https://esm.sh/chai@5.1.2", + "node:test": "./tests/deno-shims/node-test.js", + "mtcute": "../packages/deno/src/index.ts", + "mtcute/utils.js": "../packages/deno/src/utils.ts", + "@mtcute/core": "../packages/core/src/index.ts", + "@mtcute/core/client.js": "../packages/core/src/highlevel/client.ts", + "@mtcute/core/methods.js": "../packages/core/src/highlevel/methods.ts", + "@mtcute/core/utils.js": "../packages/core/src/utils/index.ts", + "@mtcute/core/worker.js": "../packages/core/src/highlevel/worker/index.ts", + "@mtcute/markdown-parser": "../packages/markdown-parser/src/index.ts", + "@mtcute/html-parser": "../packages/html-parser/src/index.ts", + "@mtcute/file-id": "../packages/file-id/src/index.ts", + "@mtcute/tl-runtime": "../packages/tl-runtime/src/index.ts", + "@mtcute/wasm": "../packages/wasm/src/index.ts", + "@mtcute/tl": "./tests/deno-shims/tl.js", + "@mtcute/tl/binary/rsa-keys.js": "./tests/deno-shims/tl-rsa.js", + "@mtcute/tl/binary/reader.js": "./tests/deno-shims/tl-reader.js", + "@mtcute/tl/binary/writer.js": "./tests/deno-shims/tl-writer.js" + } +} \ No newline at end of file diff --git a/e2e/node/.dockerignore b/e2e/node/.dockerignore deleted file mode 100644 index ee6ac304..00000000 --- a/e2e/node/.dockerignore +++ /dev/null @@ -1,7 +0,0 @@ -.verdaccio -node_modules -private -dist -pnpm-lock.yaml -.npmrc -.env* \ No newline at end of file diff --git a/e2e/node/.env.example b/e2e/node/.env.example deleted file mode 100644 index 20a2ee7d..00000000 --- a/e2e/node/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -# obtain these values from my.telegram.org -API_ID= -API_HASH= - -GITHUB_TOKEN= \ No newline at end of file diff --git a/e2e/node/.gitignore b/e2e/node/.gitignore deleted file mode 100644 index 3f599169..00000000 --- a/e2e/node/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.verdaccio/storage -.npmrc -pnpm-lock.yaml -.env \ No newline at end of file diff --git a/e2e/node/.mocharc.json b/e2e/node/.mocharc.json deleted file mode 100644 index 04373577..00000000 --- a/e2e/node/.mocharc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "node-option": [] -} diff --git a/e2e/node/.verdaccio/config.yaml b/e2e/node/.verdaccio/config.yaml deleted file mode 100644 index 91b1c240..00000000 --- a/e2e/node/.verdaccio/config.yaml +++ /dev/null @@ -1,13 +0,0 @@ -storage: ./storage - -auth: - htpasswd: - file: ./htpasswd - max_users: -1 - -packages: - '**': - access: $all - publish: $all - -logs: { type: stdout, format: pretty, level: trace } \ No newline at end of file diff --git a/e2e/node/.verdaccio/htpasswd b/e2e/node/.verdaccio/htpasswd deleted file mode 100644 index a3bd7a7a..00000000 --- a/e2e/node/.verdaccio/htpasswd +++ /dev/null @@ -1 +0,0 @@ -mtcute-bot:$apr1$7rbqxva0$zyfFgknsbAxni.cq158Sf. \ No newline at end of file diff --git a/e2e/node/Dockerfile.build b/e2e/node/Dockerfile.build deleted file mode 100644 index 23302ed9..00000000 --- a/e2e/node/Dockerfile.build +++ /dev/null @@ -1,20 +0,0 @@ -FROM node:20-alpine -WORKDIR /app - -RUN apk add python3 py3-pip make g++ && \ - python3 -m pip install --break-system-packages setuptools && \ - corepack enable && \ - corepack prepare pnpm@9.0.6 --activate - -COPY ../.. /app/ - -RUN pnpm install --frozen-lockfile && \ - pnpm -C packages/tl run gen-code && \ - # verdaccio is configured to allow anonymous publish, but npm requires a token 🥴 - npm config set //verdaccio:4873/:_authToken fake-token - -ENV REGISTRY="http://verdaccio:4873/" -ENV E2E="1" - -ENTRYPOINT [ "node", "/app/scripts/publish.js" ] -CMD [ "all" ] \ No newline at end of file diff --git a/e2e/node/Dockerfile.test b/e2e/node/Dockerfile.test deleted file mode 100644 index 7be02a0d..00000000 --- a/e2e/node/Dockerfile.test +++ /dev/null @@ -1,14 +0,0 @@ -FROM node:20-alpine -WORKDIR /app - -RUN apk add python3 py3-pip make g++ && \ - python3 -m pip install --break-system-packages setuptools && \ - corepack enable && \ - corepack prepare pnpm@9.0.6 --activate - -COPY ./ /app/ -RUN npm config set -L project @mtcute:registry http://verdaccio:4873/ && \ - chmod +x ./docker-entrypoint.sh - -ENTRYPOINT [ "./docker-entrypoint.sh" ] -CMD [ "all" ] \ No newline at end of file diff --git a/e2e/node/README.md b/e2e/node/README.md deleted file mode 100644 index 3733d8e4..00000000 --- a/e2e/node/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# mtcute e2e tests - -This directory contains end-to-end tests for mtcute. - -They are made for 2 purposes: - - Ensure published packages work as expected and can properly be imported - - Ensure that the library works with the actual Telegram API (WIP) - -To achieve the first goal, we use a Verdaccio container to publish the package to, -and then install it from there in another container - -## Setting up - -Before running the tests, you need to copy `.env.example` to `.env` and fill in the values - -## Running tests - -To run tests, you need to have Docker installed. - -```bash -# first start Verdaccio: -./cli.sh start - -# build and publish the package -./cli.sh update -# or a particular package -./cli.sh update tl-runtime - -# run the tests -./cli.sh run -# or in docker -./cli.sh run-docker -``` - -## Developing - -Once you have Verdaccio running, you can run the following commands to setup -the environment for development: - -```bash -npm config set -L project @mtcute:registry http://verdaccio.e2e.orb.local/ -./cli.sh install -``` - -> Replace the URL above with the one generated with your Docker GUI of choice -> (e2e > verdaccio > RMB > Open in browser). Example above assumes OrbStack - -Then use `./cli.sh run` to run the tests diff --git a/e2e/node/cjs/package.json b/e2e/node/cjs/package.json deleted file mode 100644 index 0967ef42..00000000 --- a/e2e/node/cjs/package.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/e2e/node/cjs/tests/base-client.js b/e2e/node/cjs/tests/base-client.js deleted file mode 100644 index a046e841..00000000 --- a/e2e/node/cjs/tests/base-client.js +++ /dev/null @@ -1,23 +0,0 @@ -const { BaseTelegramClient } = require('@mtcute/core/client.js') -const { expect } = require('chai') -const { describe, it } = require('mocha') - -const { getApiParams } = require('../utils') - -describe('@mtcute/core', function () { - this.timeout(300_000) - - it('connects to test DC and makes help.getNearestDc', async () => { - const tg = new BaseTelegramClient({ - ...getApiParams(), - }) - - await tg.connect() - const config = await tg.call({ _: 'help.getNearestDc' }) - await tg.close() - - expect(config).to.be.an('object') - expect(config._).to.equal('nearestDc') - expect(config.thisDc).to.equal(2) - }) -}) diff --git a/e2e/node/cjs/tests/tl-runtime.js b/e2e/node/cjs/tests/tl-runtime.js deleted file mode 100644 index 3eca6a46..00000000 --- a/e2e/node/cjs/tests/tl-runtime.js +++ /dev/null @@ -1,112 +0,0 @@ -const { NodePlatform } = require('@mtcute/node') -const { TlBinaryReader, TlBinaryWriter, TlSerializationCounter } = require('@mtcute/tl-runtime') -const { expect } = require('chai') -const Long = require('long') -const { describe, it } = require('mocha') - -// here we primarily want to check that everything imports properly, -// and that the code is actually executable. The actual correctness -// of the implementation is covered tested by unit tests - -const p = new NodePlatform() - -describe('@mtcute/tl-runtime', () => { - describe('encodings', () => { - it('works with Buffers', () => { - expect(p.hexEncode(Buffer.from('hello'))).to.equal('68656c6c6f') - expect(p.hexDecode('0102030405')).eql(Buffer.from([1, 2, 3, 4, 5])) - }) - - it('works with Uint8Arrays', () => { - expect(p.hexEncode(new Uint8Array([1, 2, 3, 4, 5]))).to.equal('0102030405') - }) - }) - - describe('TlBinaryReader', () => { - const map = { - 85337187(r) { - const ret = {} - ret._ = 'mt_resPQ' - ret.nonce = r.int128() - ret.serverNonce = r.int128() - ret.pq = r.bytes() - ret.serverPublicKeyFingerprints = r.vector(r.long) - - return ret - }, - } - const data - = '000000000000000001c8831ec97ae55140000000632416053e0549828cca27e966b301a48fece2fca5cf4d33f4a11ea877ba4aa5739073300817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3' - - it('should work with Buffers', () => { - const buf = Buffer.from(data, 'hex') - const r = new TlBinaryReader(map, buf, 8) - - expect(r.long().toString(16)).to.equal('51e57ac91e83c801') - expect(r.uint()).to.equal(64) - - const obj = r.object() - expect(obj._).equal('mt_resPQ') - }) - - it('should work with Uint8Arrays', () => { - const buf = p.hexDecode(data) - - const r = new TlBinaryReader(map, buf, 8) - - expect(r.long().toString(16)).to.equal('51e57ac91e83c801') - expect(r.uint()).to.equal(64) - - const obj = r.object() - expect(obj._).equal('mt_resPQ') - }) - }) - - describe('TlBinaryWriter', () => { - const map = { - mt_resPQ(w, obj) { - w.uint(85337187) - w.bytes(obj.pq) - w.vector(w.long, obj.serverPublicKeyFingerprints) - }, - } - - it('should work with Buffers', () => { - const obj = { - _: 'mt_resPQ', - pq: Buffer.from('17ED48941A08F981', 'hex'), - serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', 16)], - } - - expect(TlSerializationCounter.countNeededBytes(map, obj)).to.equal(32) - - const w = TlBinaryWriter.alloc(map, 48) - w.long(Long.ZERO) - w.long(Long.fromString('51E57AC91E83C801', true, 16)) // messageId - w.object(obj) - - expect(p.hexEncode(w.result())).eq( - '000000000000000001c8831ec97ae551632416050817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3', - ) - }) - - it('should work with Uint8Arrays', () => { - const obj = { - _: 'mt_resPQ', - pq: p.hexDecode('17ED48941A08F981'), - serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', 16)], - } - - expect(TlSerializationCounter.countNeededBytes(map, obj)).to.equal(32) - - const w = TlBinaryWriter.alloc(map, 48) - w.long(Long.ZERO) - w.long(Long.fromString('51E57AC91E83C801', true, 16)) // messageId - w.object(obj) - - expect(p.hexEncode(w.result())).eq( - '000000000000000001c8831ec97ae551632416050817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3', - ) - }) - }) -}) diff --git a/e2e/node/cjs/tests/tl-schema.js b/e2e/node/cjs/tests/tl-schema.js deleted file mode 100644 index ac6e717f..00000000 --- a/e2e/node/cjs/tests/tl-schema.js +++ /dev/null @@ -1,39 +0,0 @@ -const { NodePlatform } = require('@mtcute/node') -const { tl } = require('@mtcute/tl') -const { TlBinaryReader, TlBinaryWriter } = require('@mtcute/tl-runtime') -const { __tlReaderMap } = require('@mtcute/tl/binary/reader') -const { __tlWriterMap } = require('@mtcute/tl/binary/writer') -const { expect } = require('chai') -const Long = require('long') -const { describe, it } = require('mocha') - -// here we primarily want to check that @mtcute/tl correctly works with @mtcute/tl-runtime - -const p = new NodePlatform() - -describe('@mtcute/tl', () => { - it('writers map works with TlBinaryWriter', () => { - const obj = { - _: 'inputPeerUser', - userId: 123, - accessHash: Long.fromNumber(456), - } - - expect(p.hexEncode(TlBinaryWriter.serializeObject(__tlWriterMap, obj))).to.equal( - '4ca5e8dd7b00000000000000c801000000000000', - ) - }) - - it('readers map works with TlBinaryReader', () => { - const buf = Buffer.from('4ca5e8dd7b00000000000000c801000000000000', 'hex') - const obj = TlBinaryReader.deserializeObject(__tlReaderMap, buf) - - expect(obj._).equal('inputPeerUser') - expect(obj.userId).equal(123) - expect(obj.accessHash.toString()).equal('456') - }) - - it('correctly checks for combinator types', () => { - expect(tl.isAnyInputUser({ _: 'inputUserEmpty' })).to.eq(true) - }) -}) diff --git a/e2e/node/cjs/tests/wasm.js b/e2e/node/cjs/tests/wasm.js deleted file mode 100644 index 6f31481b..00000000 --- a/e2e/node/cjs/tests/wasm.js +++ /dev/null @@ -1,30 +0,0 @@ -const { NodeCryptoProvider } = require('@mtcute/node/utils.js') -const wasm = require('@mtcute/wasm') -const { expect } = require('chai') -const { describe, it, before } = require('mocha') - -before(async () => { - await new NodeCryptoProvider().initialize() -}) - -describe('@mtcute/wasm', () => { - const key = Buffer.from('5468697320697320616E20696D706C655468697320697320616E20696D706C65', 'hex') - const iv = Buffer.from('6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353', 'hex') - - const data = Buffer.from('99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b', 'hex') - const dataEnc = Buffer.from('792ea8ae577b1a66cb3bd92679b8030ca54ee631976bd3a04547fdcb4639fa69', 'hex') - - it('should work with Buffers', () => { - expect(wasm.ige256Encrypt(data, key, iv)).to.deep.equal(new Uint8Array(dataEnc)) - expect(wasm.ige256Decrypt(dataEnc, key, iv)).to.deep.equal(new Uint8Array(data)) - }) - - it('should work with Uint8Arrays', () => { - expect(wasm.ige256Encrypt(new Uint8Array(data), new Uint8Array(key), new Uint8Array(iv))).to.deep.equal( - new Uint8Array(dataEnc), - ) - expect(wasm.ige256Decrypt(new Uint8Array(dataEnc), new Uint8Array(key), new Uint8Array(iv))).to.deep.equal( - new Uint8Array(data), - ) - }) -}) diff --git a/e2e/node/cjs/utils.js b/e2e/node/cjs/utils.js deleted file mode 100644 index be23b064..00000000 --- a/e2e/node/cjs/utils.js +++ /dev/null @@ -1,23 +0,0 @@ -const { MemoryStorage } = require('@mtcute/core') -const { setPlatform } = require('@mtcute/core/platform.js') -const { LogManager } = require('@mtcute/core/utils.js') -const { NodePlatform, TcpTransport } = require('@mtcute/node') -const { NodeCryptoProvider } = require('@mtcute/node/utils.js') - -exports.getApiParams = () => { - if (!process.env.API_ID || !process.env.API_HASH) { - throw new Error('API_ID and API_HASH env variables must be set') - } - - setPlatform(new NodePlatform()) - - return { - apiId: Number.parseInt(process.env.API_ID), - apiHash: process.env.API_HASH, - testMode: true, - storage: new MemoryStorage(), - logLevel: LogManager.DEBUG, - transport: () => new TcpTransport(), - crypto: new NodeCryptoProvider(), - } -} diff --git a/e2e/node/cli.sh b/e2e/node/cli.sh deleted file mode 100755 index d60d0372..00000000 --- a/e2e/node/cli.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -set -eau - -method=$1 -shift - -case "$method" in - "run") - node runner.js $@ - ;; - "run-docker") - source .env - docker compose run --rm --build test $@ - ;; - "update") - docker compose run --build build $@ - ./cli.sh install - ;; - "start") - docker compose up -d verdaccio - ;; - "stop") - docker compose down - ;; - "install") - rm -rf pnpm-lock.yaml node_modules - pnpm install - ;; - "ci") - set -eaux - chmod -R 777 .verdaccio - docker compose up -d verdaccio - docker compose run --rm --build build - docker compose run --build test - ;; - "ci-publish") - export CURRENT_COMMIT=$(git rev-parse HEAD) - docker compose up -d verdaccio - node publish-canary.js - ;; - *) - echo "Unknown command" - ;; -esac \ No newline at end of file diff --git a/e2e/node/config.js b/e2e/node/config.js deleted file mode 100644 index 1f7159fc..00000000 --- a/e2e/node/config.js +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = { - cjs: { - getFiles: () => 'tests/**/*.js', - runFile: file => `mocha ${file}`, - }, - esm: { - getFiles: () => 'tests/**/*.js', - runFile: file => `mocha ${file}`, - }, - ts: { - getFiles: () => 'tests/**/*.ts', - beforeAll: () => ['tsc', 'node build-esm.cjs'], - runFile: (file) => { - if (require('node:path').basename(file)[0] === '_') return null - - if (file.startsWith('tests/packaging/')) { - // packaging tests - we need to make sure everything imports and works - return [ - `mocha -r ts-node/register ${file}`, - `mocha dist/${file.replace(/\.ts$/, '.js')}`, - `node run-esm.cjs ${file}`, - `mocha dist/esm/${file.replace(/\.ts$/, '.js')}`, - ] - } - - // normal e2e tests - testing features etc - return `mocha dist/${file.replace(/\.ts$/, '.js')}` - }, - }, -} diff --git a/e2e/node/docker-compose.yaml b/e2e/node/docker-compose.yaml deleted file mode 100644 index 07eaea85..00000000 --- a/e2e/node/docker-compose.yaml +++ /dev/null @@ -1,35 +0,0 @@ -version: "3" -services: - verdaccio: - restart: unless-stopped - image: verdaccio/verdaccio:5.27 - container_name: "verdaccio" - volumes: - - "./.verdaccio:/verdaccio/conf" - ports: - - "4873:4873" - networks: - - mtcute-e2e - build: - build: - context: ../.. - dockerfile: e2e/node/Dockerfile.build - environment: - - GITHUB_TOKEN=${GITHUB_TOKEN} - networks: - - mtcute-e2e - depends_on: - - verdaccio - test: - build: - context: . - dockerfile: Dockerfile.test - environment: - - API_ID=${API_ID} - - API_HASH=${API_HASH} - networks: - - mtcute-e2e - depends_on: - - verdaccio -networks: - mtcute-e2e: {} \ No newline at end of file diff --git a/e2e/node/docker-entrypoint.sh b/e2e/node/docker-entrypoint.sh deleted file mode 100644 index 3675a1e0..00000000 --- a/e2e/node/docker-entrypoint.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -set -e - -# we can't do this during build because we don't have network access -pnpm install - -node runner.js $@ \ No newline at end of file diff --git a/e2e/node/esm/package.json b/e2e/node/esm/package.json deleted file mode 100644 index 5ffd9800..00000000 --- a/e2e/node/esm/package.json +++ /dev/null @@ -1 +0,0 @@ -{ "type": "module" } diff --git a/e2e/node/esm/tests/base-client.js b/e2e/node/esm/tests/base-client.js deleted file mode 100644 index fa109e4b..00000000 --- a/e2e/node/esm/tests/base-client.js +++ /dev/null @@ -1,23 +0,0 @@ -import { BaseTelegramClient } from '@mtcute/core/client.js' -import { expect } from 'chai' -import { describe, it } from 'mocha' - -import { getApiParams } from '../utils.js' - -describe('@mtcute/core', function () { - this.timeout(300_000) - - it('connects to test DC and makes help.getNearestDc', async () => { - const tg = new BaseTelegramClient({ - ...getApiParams(), - }) - - await tg.connect() - const config = await tg.call({ _: 'help.getNearestDc' }) - await tg.close() - - expect(config).to.be.an('object') - expect(config._).to.equal('nearestDc') - expect(config.thisDc).to.equal(2) - }) -}) diff --git a/e2e/node/esm/tests/tl-runtime.js b/e2e/node/esm/tests/tl-runtime.js deleted file mode 100644 index 071e7621..00000000 --- a/e2e/node/esm/tests/tl-runtime.js +++ /dev/null @@ -1,110 +0,0 @@ -import { NodePlatform } from '@mtcute/node' -import { TlBinaryReader, TlBinaryWriter, TlSerializationCounter } from '@mtcute/tl-runtime' -import { expect } from 'chai' -import Long from 'long' -import { describe, it } from 'mocha' - -// here we primarily want to check that everything imports properly, -// and that the code is actually executable. The actual correctness -// of the implementation is covered tested by unit tests - -const p = new NodePlatform() - -describe('encodings', () => { - it('works with Buffers', () => { - expect(p.hexEncode(Buffer.from('hello'))).to.equal('68656c6c6f') - expect(p.hexDecode('0102030405')).eql(Buffer.from([1, 2, 3, 4, 5])) - }) - - it('works with Uint8Arrays', () => { - expect(p.hexEncode(new Uint8Array([1, 2, 3, 4, 5]))).to.equal('0102030405') - }) -}) - -describe('TlBinaryReader', () => { - const map = { - 85337187(r) { - const ret = {} - ret._ = 'mt_resPQ' - ret.nonce = r.int128() - ret.serverNonce = r.int128() - ret.pq = r.bytes() - ret.serverPublicKeyFingerprints = r.vector(r.long) - - return ret - }, - } - const data - = '000000000000000001c8831ec97ae55140000000632416053e0549828cca27e966b301a48fece2fca5cf4d33f4a11ea877ba4aa5739073300817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3' - - it('should work with Buffers', () => { - const buf = Buffer.from(data, 'hex') - const r = new TlBinaryReader(map, buf, 8) - - expect(r.long().toString(16)).to.equal('51e57ac91e83c801') - expect(r.uint()).to.equal(64) - - const obj = r.object() - expect(obj._).equal('mt_resPQ') - }) - - it('should work with Uint8Arrays', () => { - const buf = p.hexDecode(data) - - const r = new TlBinaryReader(map, buf, 8) - - expect(r.long().toString(16)).to.equal('51e57ac91e83c801') - expect(r.uint()).to.equal(64) - - const obj = r.object() - expect(obj._).equal('mt_resPQ') - }) -}) - -describe('TlBinaryWriter', () => { - const map = { - mt_resPQ(w, obj) { - w.uint(85337187) - w.bytes(obj.pq) - w.vector(w.long, obj.serverPublicKeyFingerprints) - }, - } - - it('should work with Buffers', () => { - const obj = { - _: 'mt_resPQ', - pq: Buffer.from('17ED48941A08F981', 'hex'), - serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', 16)], - } - - expect(TlSerializationCounter.countNeededBytes(map, obj)).to.equal(32) - - const w = TlBinaryWriter.alloc(map, 48) - w.long(Long.ZERO) - w.long(Long.fromString('51E57AC91E83C801', true, 16)) // messageId - w.object(obj) - - expect(p.hexEncode(w.result())).eq( - '000000000000000001c8831ec97ae551632416050817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3', - ) - }) - - it('should work with Uint8Arrays', () => { - const obj = { - _: 'mt_resPQ', - pq: p.hexDecode('17ED48941A08F981'), - serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', 16)], - } - - expect(TlSerializationCounter.countNeededBytes(map, obj)).to.equal(32) - - const w = TlBinaryWriter.alloc(map, 48) - w.long(Long.ZERO) - w.long(Long.fromString('51E57AC91E83C801', true, 16)) // messageId - w.object(obj) - - expect(p.hexEncode(w.result())).eq( - '000000000000000001c8831ec97ae551632416050817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3', - ) - }) -}) diff --git a/e2e/node/esm/tests/tl-schema.js b/e2e/node/esm/tests/tl-schema.js deleted file mode 100644 index 1b0f4f9d..00000000 --- a/e2e/node/esm/tests/tl-schema.js +++ /dev/null @@ -1,39 +0,0 @@ -import { NodePlatform } from '@mtcute/node' -import { tl } from '@mtcute/tl' -import { TlBinaryReader, TlBinaryWriter } from '@mtcute/tl-runtime' -import { __tlReaderMap } from '@mtcute/tl/binary/reader.js' -import { __tlWriterMap } from '@mtcute/tl/binary/writer.js' -import { expect } from 'chai' -import Long from 'long' -import { describe, it } from 'mocha' - -// here we primarily want to check that @mtcute/tl correctly works with @mtcute/tl-runtime - -const p = new NodePlatform() - -describe('@mtcute/tl', () => { - it('writers map works with TlBinaryWriter', () => { - const obj = { - _: 'inputPeerUser', - userId: 123, - accessHash: Long.fromNumber(456), - } - - expect(p.hexEncode(TlBinaryWriter.serializeObject(__tlWriterMap, obj))).to.equal( - '4ca5e8dd7b00000000000000c801000000000000', - ) - }) - - it('readers map works with TlBinaryReader', () => { - const buf = p.hexDecode('4ca5e8dd7b00000000000000c801000000000000') - const obj = TlBinaryReader.deserializeObject(__tlReaderMap, buf) - - expect(obj._).equal('inputPeerUser') - expect(obj.userId).equal(123) - expect(obj.accessHash.toString()).equal('456') - }) - - it('correctly checks for combinator types', () => { - expect(tl.isAnyInputUser({ _: 'inputUserEmpty' })).to.eq(true) - }) -}) diff --git a/e2e/node/esm/tests/wasm.js b/e2e/node/esm/tests/wasm.js deleted file mode 100644 index 1cdde98b..00000000 --- a/e2e/node/esm/tests/wasm.js +++ /dev/null @@ -1,30 +0,0 @@ -import { NodeCryptoProvider } from '@mtcute/node/utils.js' -import { ige256Decrypt, ige256Encrypt } from '@mtcute/wasm' -import { expect } from 'chai' -import { before, describe, it } from 'mocha' - -before(async () => { - await new NodeCryptoProvider().initialize() -}) - -describe('@mtcute/wasm', () => { - const key = Buffer.from('5468697320697320616E20696D706C655468697320697320616E20696D706C65', 'hex') - const iv = Buffer.from('6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353', 'hex') - - const data = Buffer.from('99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b', 'hex') - const dataEnc = Buffer.from('792ea8ae577b1a66cb3bd92679b8030ca54ee631976bd3a04547fdcb4639fa69', 'hex') - - it('should work with Buffers', () => { - expect(ige256Encrypt(data, key, iv)).to.deep.equal(new Uint8Array(dataEnc)) - expect(ige256Decrypt(dataEnc, key, iv)).to.deep.equal(new Uint8Array(data)) - }) - - it('should work with Uint8Arrays', () => { - expect(ige256Encrypt(new Uint8Array(data), new Uint8Array(key), new Uint8Array(iv))).to.deep.equal( - new Uint8Array(dataEnc), - ) - expect(ige256Decrypt(new Uint8Array(dataEnc), new Uint8Array(key), new Uint8Array(iv))).to.deep.equal( - new Uint8Array(data), - ) - }) -}) diff --git a/e2e/node/esm/utils.js b/e2e/node/esm/utils.js deleted file mode 100644 index 3b2b9aeb..00000000 --- a/e2e/node/esm/utils.js +++ /dev/null @@ -1,23 +0,0 @@ -import { MemoryStorage } from '@mtcute/core' -import { setPlatform } from '@mtcute/core/platform.js' -import { LogManager } from '@mtcute/core/utils.js' -import { NodePlatform, TcpTransport } from '@mtcute/node' -import { NodeCryptoProvider } from '@mtcute/node/utils.js' - -export function getApiParams() { - if (!process.env.API_ID || !process.env.API_HASH) { - throw new Error('API_ID and API_HASH env variables must be set') - } - - setPlatform(new NodePlatform()) - - return { - apiId: Number.parseInt(process.env.API_ID), - apiHash: process.env.API_HASH, - testMode: true, - storage: new MemoryStorage(), - logLevel: LogManager.DEBUG, - transport: () => new TcpTransport(), - crypto: new NodeCryptoProvider(), - } -} diff --git a/e2e/node/package.json b/e2e/node/package.json deleted file mode 100644 index dd273173..00000000 --- a/e2e/node/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "mtcute-e2e", - "private": true, - "dependencies": { - "@mtcute/bun": "*", - "@mtcute/core": "*", - "@mtcute/crypto-node": "*", - "@mtcute/dispatcher": "*", - "@mtcute/file-id": "*", - "@mtcute/html-parser": "*", - "@mtcute/http-proxy": "*", - "@mtcute/i18n": "*", - "@mtcute/markdown-parser": "*", - "@mtcute/mtproxy": "*", - "@mtcute/node": "*", - "@mtcute/socks-proxy": "*", - "@mtcute/tl": "*", - "@mtcute/tl-runtime": "*", - "@mtcute/tl-utils": "*", - "@mtcute/wasm": "*", - "@mtcute/web": "*", - "@types/chai": "^4.3.8", - "@types/mocha": "^10.0.2", - "chai": "^4.3.10", - "dotenv": "16.3.1", - "glob": "10.3.10", - "long": "^5.2.3", - "mocha": "^10.2.0", - "ts-node": "^10.9.1", - "typescript": "^5.2.2" - }, - "devDependencies": { - "@types/node": "^20.8.10" - } -} diff --git a/e2e/node/pnpm-workspace.yaml b/e2e/node/pnpm-workspace.yaml deleted file mode 100644 index 1a3cf7ae..00000000 --- a/e2e/node/pnpm-workspace.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - - . diff --git a/e2e/node/publish-canary.js b/e2e/node/publish-canary.js deleted file mode 100644 index c0781f8a..00000000 --- a/e2e/node/publish-canary.js +++ /dev/null @@ -1,80 +0,0 @@ -// this scripts publishes our e2e-tested builds to canary npm -// at this point, we should have all our packages installed in node_modules -// so it should be safe to just cd into them and run `npm publish` on them - -const { execSync } = require('node:child_process') -const fs = require('node:fs') -const path = require('node:path') - -// setup tokenw -const { NPM_TOKEN, REGISTRY, CURRENT_COMMIT } = process.env - -if (!NPM_TOKEN || !REGISTRY || !CURRENT_COMMIT) { - console.error('Missing NPM_TOKEN, REGISTRY or CURRENT_COMMIT env variables!') - process.exit(1) -} - -execSync(`npm config set //${REGISTRY.replace(/^https?:\/\//, '')}/:_authToken ${NPM_TOKEN}`, { stdio: 'inherit' }) -const commit = CURRENT_COMMIT.slice(0, 7) - -const myPkgJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')) -const packages = Object.keys(myPkgJson.dependencies) - .filter(x => x.startsWith('@mtcute/')) - .map(x => x.slice('@mtcute/'.length)) - -const workDir = path.join(__dirname, 'temp') -fs.mkdirSync(workDir, { recursive: true }) - -async function main() { - const versions = {} - - function fixDependencies(pkgJson, key) { - if (!pkgJson[key]) return - - const deps = pkgJson[key] - - for (const dep of Object.keys(deps)) { - if (!dep.startsWith('@mtcute/')) continue - deps[dep] = versions[dep.slice('@mtcute/'.length)] - } - } - - // prepare working directory - for (const pkg of packages) { - const data = await fetch(`http://localhost:4873/@mtcute/${pkg}`).then(x => x.json()) - const version = data['dist-tags'].latest - const tarball = data.versions[version].dist.tarball - - execSync(`wget -O ${pkg}.tgz ${tarball}`, { cwd: workDir, stdio: 'inherit' }) - execSync(`tar -xzf ${pkg}.tgz`, { cwd: workDir, stdio: 'inherit' }) - execSync(`rm ${pkg}.tgz`, { cwd: workDir, stdio: 'inherit' }) - execSync(`mv package ${pkg}`, { cwd: workDir, stdio: 'inherit' }) - - versions[pkg] = `${version}-git.${commit}` - } - - for (const pkg of packages) { - const pkgDir = path.join(workDir, pkg) - - const pkgJsonPath = path.join(pkgDir, 'package.json') - const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')) - - fixDependencies(pkgJson, 'dependencies') - fixDependencies(pkgJson, 'peerDependencies') - fixDependencies(pkgJson, 'devDependencies') - fixDependencies(pkgJson, 'optionalDependencies') - pkgJson.version = versions[pkg] - - fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4)) - - execSync(`npm publish --registry ${REGISTRY} -q --tag canary`, { - cwd: pkgDir, - stdio: 'inherit', - }) - } -} - -main().catch((err) => { - console.error(err) - process.exit(1) -}) diff --git a/e2e/node/runner.js b/e2e/node/runner.js deleted file mode 100644 index 95c9ad27..00000000 --- a/e2e/node/runner.js +++ /dev/null @@ -1,136 +0,0 @@ -/* eslint-disable no-console */ -const cp = require('node:child_process') -const path = require('node:path') - -const glob = require('glob') - -const env = {} -require('dotenv').config({ processEnv: env }) - -const config = require('./config') - -const DIRS = Object.keys(config) - -function runForFile(dir, file, single = true) { - const { runFile, beforeAll } = config[dir] - - if (!runFile) { - console.log('No runFile for %s', dir) - - return - } - - let cmds = runFile(file) - - if (!cmds) { - return - } - - const options = { - env: { - ...env, - ...process.env, - }, - cwd: path.join(__dirname, dir), - stdio: 'inherit', - } - - if (!Array.isArray(cmds)) { - cmds = [cmds] - } - - if (beforeAll && single) { - cmds.unshift(...beforeAll()) - } - - for (const c of cmds) { - console.log('%s $ %s', dir, c) - cp.execSync(`pnpm exec ${c}`, options) - } -} - -function runForDir(dir) { - const { getFiles, beforeAll } = config[dir] - - if (!getFiles) { - console.log('No getFiles for %s', dir) - - return - } - - const options = { - env: { - ...env, - ...process.env, - }, - cwd: path.join(__dirname, dir), - stdio: 'inherit', - } - - if (beforeAll) { - for (const c of beforeAll()) { - console.log('%s $ %s', dir, c) - cp.execSync(`pnpm exec ${c}`, options) - } - } - - const files = glob.sync(getFiles(), { cwd: path.join(__dirname, dir) }) - files.sort() - - for (const file of files) { - runForFile(dir, file, false) - } -} - -async function main() { - if (!process.argv[2]) { - console.log('Usage: node runner.js ') - console.log(' where is one of:') - console.log(' publish-canary - publish everything to canary npm') - console.log(' all - run all tests') - console.log(' - (one of %s) - run tests for that directory', DIRS.join(', ')) - console.log(' - run tests for that file') - process.exit(1) - } - - const [dir, file] = process.argv.slice(2) - - if (dir === 'publish-canary') { - cp.execSync('node publish-canary.js', { stdio: 'inherit' }) - - return - } - - if (dir === 'all') { - for (const d of DIRS) { - console.log('Entering %s', d) - runForDir(d) - } - - return - } - - if (!DIRS.includes(dir)) { - console.log('Unknown directory %s', dir) - process.exit(1) - } - - if (file) { - const files = glob.sync(config[dir].getFiles(), { cwd: path.join(__dirname, dir) }) - const matchingFile = files.find(f => f.endsWith(file)) - - if (!matchingFile) { - console.log("Can't find file %s", file) - process.exit(1) - } - - runForFile(dir, matchingFile) - } else { - runForDir(dir) - } -} - -main().catch((e) => { - console.error(e) - process.exit(1) -}) diff --git a/e2e/node/ts/build-esm.cjs b/e2e/node/ts/build-esm.cjs deleted file mode 100644 index 4281353e..00000000 --- a/e2e/node/ts/build-esm.cjs +++ /dev/null @@ -1,52 +0,0 @@ -const cp = require('node:child_process') -const fs = require('node:fs') -const path = require('node:path') - -const glob = require('glob') - -function fixForEsm() { - const modified = {} - - fs.writeFileSync(path.join(__dirname, 'package.json'), JSON.stringify({ type: 'module' })) - - for (const file of glob.sync('tests/**/*.ts')) { - let content = fs.readFileSync(file, 'utf8') - - if (content.includes('@fix-import')) { - modified[file] = content - content = content.replace(/(?<=@fix-import\nimport.*?')(.*?)(?='$)/gms, '$1.js') - fs.writeFileSync(file, content) - } - } - - return () => { - fs.writeFileSync(path.join(__dirname, 'package.json'), JSON.stringify({ type: 'commonjs' })) - - for (const file of Object.keys(modified)) { - fs.writeFileSync(file, modified[file]) - } - } -} -exports.fixForEsm = fixForEsm - -function main() { - const restore = fixForEsm() - let error = null - - try { - cp.execSync('pnpm exec tsc --outDir dist/esm', { stdio: 'inherit' }) - fs.writeFileSync(path.join(__dirname, 'dist/esm/package.json'), JSON.stringify({ type: 'module' })) - } catch (e) { - error = e - } - - restore() - - if (error) { - throw error - } -} - -if (require.main === module) { - main() -} diff --git a/e2e/node/ts/mocha.esm.json b/e2e/node/ts/mocha.esm.json deleted file mode 100644 index 72a03424..00000000 --- a/e2e/node/ts/mocha.esm.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "node-option": ["experimental-specifier-resolution=node", "loader=ts-node/esm"] -} diff --git a/e2e/node/ts/package.json b/e2e/node/ts/package.json deleted file mode 100644 index a3c15a7a..00000000 --- a/e2e/node/ts/package.json +++ /dev/null @@ -1 +0,0 @@ -{ "type": "commonjs" } diff --git a/e2e/node/ts/run-esm.cjs b/e2e/node/ts/run-esm.cjs deleted file mode 100644 index 1ef05b1c..00000000 --- a/e2e/node/ts/run-esm.cjs +++ /dev/null @@ -1,25 +0,0 @@ -const cp = require('node:child_process') - -const { fixForEsm } = require('./build-esm.cjs') - -const file = process.argv[2] - -if (!file) { - console.error('Usage: run-esm.cjs ') - process.exit(1) -} - -let error = null -const restore = fixForEsm() - -try { - cp.execSync(`pnpm exec mocha --config=mocha.esm.json ${file}`, { stdio: 'inherit' }) -} catch (e) { - error = e -} - -restore() - -if (error) { - throw error -} diff --git a/e2e/node/ts/tests/01.auth.ts b/e2e/node/ts/tests/01.auth.ts deleted file mode 100644 index 1d4a3b6a..00000000 --- a/e2e/node/ts/tests/01.auth.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { tl, User } from '@mtcute/core' -import { BaseTelegramClient, TelegramClient } from '@mtcute/core/client.js' -import { expect } from 'chai' -import { describe, it } from 'mocha' - -import { getApiParams } from '../utils.js' - -function getAccountId() { - return Math.floor(Math.random() * 10000) - .toString() - .padStart(4, '0') -} - -async function authorizeInDc(dc: number, base: BaseTelegramClient) { - const tg = new TelegramClient({ client: base }) - - while (true) { - await base.mt.storage.load() - await base.storage.clear(true) - - const phone = `99966${dc}${getAccountId()}` - - let user - - try { - const sentCode = await tg.sendCode({ phone }) - - let auth = await tg.call({ - _: 'auth.signIn', - phoneNumber: phone, - phoneCode: `${dc}${dc}${dc}${dc}${dc}`, - phoneCodeHash: sentCode.phoneCodeHash, - }) - - if (auth._ === 'auth.authorizationSignUpRequired') { - auth = await tg.call({ - _: 'auth.signUp', - phoneNumber: phone, - phoneCodeHash: sentCode.phoneCodeHash, - firstName: 'mtcute e2e', - lastName: '', - }) - - if (auth._ !== 'auth.authorization') { - throw new Error('Unexpected response') - } - } - - await tg.notifyLoggedIn(auth) - - user = new User(auth.user) - } catch (e) { - if (tl.RpcError.is(e, 'SESSION_PASSWORD_NEEDED') || tl.RpcError.is(e, 'PHONE_NUMBER_FLOOD')) { - // retry with another number - await tg.close() - continue - } - - throw e - } - - await tg.close() - - expect(user.isSelf).to.eq(true) - expect(user.phoneNumber).to.equal(phone) - break - } -} - -describe('1. authorization', function () { - this.timeout(300_000) - - it('should authorize in default dc', async () => { - const base = new BaseTelegramClient(getApiParams('dc2.session')) - - await authorizeInDc(2, base) - }) - - it('should authorize in dc 1', async () => { - const base = new BaseTelegramClient(getApiParams('dc1.session')) - - await authorizeInDc(1, base) - }) -}) diff --git a/e2e/node/ts/tests/05.worker.ts b/e2e/node/ts/tests/05.worker.ts deleted file mode 100644 index a85ba3d2..00000000 --- a/e2e/node/ts/tests/05.worker.ts +++ /dev/null @@ -1,92 +0,0 @@ -import type { Message } from '@mtcute/node' -import type { CustomMethods } from './_worker.js' - -import path from 'node:path' -import { Worker } from 'node:worker_threads' -import { TelegramClient } from '@mtcute/core/client.js' -import { Long, TelegramWorkerPort, tl } from '@mtcute/node' -import { expect } from 'chai' - -import { describe, it } from 'mocha' - -import { getApiParams, waitFor } from '../utils.js' - -describe('5. worker', function () { - this.timeout(300_000) - - const worker = new Worker(path.resolve(__dirname, '_worker.js')) - - const port = new TelegramWorkerPort({ - worker, - }) - const portClient = new TelegramClient({ client: port }) - - it('should make api calls', async () => { - const res = await port.call({ _: 'help.getConfig' }) - expect(res._).to.equal('config') - - const premiumPromo = await port.call({ _: 'help.getPremiumPromo' }) - // ensure Long-s are correctly serialized - expect(Long.isLong((premiumPromo.users[0] as tl.RawUser).accessHash)).to.equal(true) - }) - - it('should call custom methods', async () => { - const hello = await port.invokeCustom('hello') - expect(hello).to.equal('world') - - const sum = await port.invokeCustom('sum', 2, 3) - expect(sum).to.equal(5) - }) - - it('should throw errors', async () => { - try { - await port.call({ _: 'test.useConfigSimple' }) - throw new Error('should have thrown') - } catch (e) { - expect(e).to.be.an.instanceOf(tl.RpcError) - } - }) - - it('should receive updates', async () => { - const client2 = new TelegramClient(getApiParams('dc2.session')) - - try { - await client2.connect() - await port.startUpdatesLoop() - - const me = await portClient.getMe() - // ensure Long-s are correctly serialized - expect(Long.isLong(me.raw.accessHash)).equals(true) - - let username = me.username - - if (!username) { - username = `mtcute_e2e_${Math.random().toString(36).slice(2, 8)}` - await portClient.setMyUsername(username) - } - - const msgs: Message[] = [] - portClient.on('new_message', (msg) => { - msgs.push(msg) - }) - - const testText = `test ${Math.random()}` - await client2.sendText(username, testText) - - await waitFor(() => { - expect(msgs.length).to.be.greaterThan(0) - expect(msgs[0].text).to.equal(testText) - }) - } catch (e) { - await client2.close() - throw e - } - - await client2.close() - }) - - this.afterAll(async () => { - await port.close() - void worker.terminate() - }) -}) diff --git a/e2e/node/ts/tests/packaging/base-client.ts b/e2e/node/ts/tests/packaging/base-client.ts deleted file mode 100644 index dcb85573..00000000 --- a/e2e/node/ts/tests/packaging/base-client.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { BaseTelegramClient } from '@mtcute/core/client.js' -import { expect } from 'chai' -import { describe, it } from 'mocha' - -// @fix-import -import { getApiParams } from '../../utils' - -describe('@mtcute/core', function () { - this.timeout(300_000) - - it('connects to test DC and makes help.getNearestDc', async () => { - const tg = new BaseTelegramClient({ - ...getApiParams(), - }) - - await tg.connect() - const config = await tg.call({ _: 'help.getNearestDc' }) - await tg.close() - - expect(config).to.be.an('object') - expect(config._).to.equal('nearestDc') - expect(config.thisDc).to.equal(2) - }) -}) diff --git a/e2e/node/ts/tests/packaging/tl-runtime.ts b/e2e/node/ts/tests/packaging/tl-runtime.ts deleted file mode 100644 index 16390583..00000000 --- a/e2e/node/ts/tests/packaging/tl-runtime.ts +++ /dev/null @@ -1,115 +0,0 @@ -/* eslint-disable */ -import { expect } from 'chai' -import Long from 'long' -import { describe, it } from 'mocha' - -import { TlBinaryReader, TlBinaryWriter, TlSerializationCounter } from '@mtcute/tl-runtime' -import { NodePlatform } from '@mtcute/node' -import { setPlatform } from '@mtcute/core/platform.js' - -// here we primarily want to check that everything imports properly, -// and that the code is actually executable. The actual correctness -// of the implementation is covered tested by unit tests - -const p = new NodePlatform() -setPlatform(p) - -describe('encodings', () => { - it('works with Buffers', () => { - expect(p.hexEncode(Buffer.from('hello'))).to.equal('68656c6c6f') - expect(p.hexDecode('0102030405')).eql(Buffer.from([1, 2, 3, 4, 5])) - }) - - it('works with Uint8Arrays', () => { - expect(p.hexEncode(new Uint8Array([1, 2, 3, 4, 5]))).to.equal('0102030405') - }) -}) - -describe('TlBinaryReader', () => { - const map = { - '85337187': function (r: any) { - const ret: any = {} - ret._ = 'mt_resPQ' - ret.nonce = r.int128() - ret.serverNonce = r.int128() - ret.pq = r.bytes() - ret.serverPublicKeyFingerprints = r.vector(r.long) - - return ret - }, - } - const data = - '000000000000000001c8831ec97ae55140000000632416053e0549828cca27e966b301a48fece2fca5cf4d33f4a11ea877ba4aa5739073300817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3' - - it('should work with Buffers', () => { - const buf = Buffer.from(data, 'hex') - const r = new TlBinaryReader(map, buf, 8) - - expect(r.long().toString(16)).to.equal('51e57ac91e83c801') - expect(r.uint()).to.equal(64) - - const obj: any = r.object() - expect(obj._).equal('mt_resPQ') - }) - - it('should work with Uint8Arrays', () => { - const buf = p.hexDecode(data) - - const r = new TlBinaryReader(map, buf, 8) - - expect(r.long().toString(16)).to.equal('51e57ac91e83c801') - expect(r.uint()).to.equal(64) - - const obj: any = r.object() - expect(obj._).equal('mt_resPQ') - }) -}) - -describe('TlBinaryWriter', () => { - const map = { - mt_resPQ: function (w: any, obj: any) { - w.uint(85337187) - w.bytes(obj.pq) - w.vector(w.long, obj.serverPublicKeyFingerprints) - }, - _staticSize: {} as any, - } - - it('should work with Buffers', () => { - const obj = { - _: 'mt_resPQ', - pq: Buffer.from('17ED48941A08F981', 'hex'), - serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', 16)], - } - - expect(TlSerializationCounter.countNeededBytes(map, obj)).to.equal(32) - - const w = TlBinaryWriter.alloc(map, 48) - w.long(Long.ZERO) - w.long(Long.fromString('51E57AC91E83C801', true, 16)) // messageId - w.object(obj) - - expect(p.hexEncode(w.result())).eq( - '000000000000000001c8831ec97ae551632416050817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3', - ) - }) - - it('should work with Uint8Arrays', () => { - const obj = { - _: 'mt_resPQ', - pq: p.hexDecode('17ED48941A08F981'), - serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', 16)], - } - - expect(TlSerializationCounter.countNeededBytes(map, obj)).to.equal(32) - - const w = TlBinaryWriter.alloc(map, 48) - w.long(Long.ZERO) - w.long(Long.fromString('51E57AC91E83C801', true, 16)) // messageId - w.object(obj) - - expect(p.hexEncode(w.result())).eq( - '000000000000000001c8831ec97ae551632416050817ed48941a08f98100000015c4b51c01000000216be86c022bb4c3', - ) - }) -}) diff --git a/e2e/node/ts/tests/packaging/tl-schema.ts b/e2e/node/ts/tests/packaging/tl-schema.ts deleted file mode 100644 index 3988ca0e..00000000 --- a/e2e/node/ts/tests/packaging/tl-schema.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { setPlatform } from '@mtcute/core/platform.js' -import { NodePlatform } from '@mtcute/node' -import { tl } from '@mtcute/tl' -import { TlBinaryReader, TlBinaryWriter } from '@mtcute/tl-runtime' -import { __tlReaderMap } from '@mtcute/tl/binary/reader.js' -import { __tlWriterMap } from '@mtcute/tl/binary/writer.js' -import { expect } from 'chai' -import Long from 'long' -import { describe, it } from 'mocha' - -// here we primarily want to check that @mtcute/tl correctly works with @mtcute/tl-runtime - -const p = new NodePlatform() -setPlatform(p) - -describe('@mtcute/tl', () => { - it('writers map works with TlBinaryWriter', () => { - const obj = { - _: 'inputPeerUser', - userId: 123, - accessHash: Long.fromNumber(456), - } - - expect(p.hexEncode(TlBinaryWriter.serializeObject(__tlWriterMap, obj))).to.equal( - '4ca5e8dd7b00000000000000c801000000000000', - ) - }) - - it('readers map works with TlBinaryReader', () => { - const buf = p.hexDecode('4ca5e8dd7b00000000000000c801000000000000') - const obj = TlBinaryReader.deserializeObject(__tlReaderMap, buf) - - expect(obj._).equal('inputPeerUser') - expect(obj.userId).equal(123) - expect(obj.accessHash.toString()).equal('456') - }) - - it('correctly checks for combinator types', () => { - expect(tl.isAnyInputUser({ _: 'inputUserEmpty' })).to.eq(true) - }) -}) diff --git a/e2e/node/ts/tests/packaging/wasm.ts b/e2e/node/ts/tests/packaging/wasm.ts deleted file mode 100644 index 1cdde98b..00000000 --- a/e2e/node/ts/tests/packaging/wasm.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { NodeCryptoProvider } from '@mtcute/node/utils.js' -import { ige256Decrypt, ige256Encrypt } from '@mtcute/wasm' -import { expect } from 'chai' -import { before, describe, it } from 'mocha' - -before(async () => { - await new NodeCryptoProvider().initialize() -}) - -describe('@mtcute/wasm', () => { - const key = Buffer.from('5468697320697320616E20696D706C655468697320697320616E20696D706C65', 'hex') - const iv = Buffer.from('6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353', 'hex') - - const data = Buffer.from('99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b', 'hex') - const dataEnc = Buffer.from('792ea8ae577b1a66cb3bd92679b8030ca54ee631976bd3a04547fdcb4639fa69', 'hex') - - it('should work with Buffers', () => { - expect(ige256Encrypt(data, key, iv)).to.deep.equal(new Uint8Array(dataEnc)) - expect(ige256Decrypt(dataEnc, key, iv)).to.deep.equal(new Uint8Array(data)) - }) - - it('should work with Uint8Arrays', () => { - expect(ige256Encrypt(new Uint8Array(data), new Uint8Array(key), new Uint8Array(iv))).to.deep.equal( - new Uint8Array(dataEnc), - ) - expect(ige256Decrypt(new Uint8Array(dataEnc), new Uint8Array(key), new Uint8Array(iv))).to.deep.equal( - new Uint8Array(data), - ) - }) -}) diff --git a/e2e/node/ts/tsconfig.json b/e2e/node/ts/tsconfig.json deleted file mode 100644 index 2cbc7b39..00000000 --- a/e2e/node/ts/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "incremental": true, - "target": "es2020", - "rootDir": ".", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "allowJs": true, - "strict": true, - "noImplicitAny": true, - "noImplicitThis": true, - "declaration": true, - "inlineSources": true, - "outDir": "./dist", - "sourceMap": true, - "stripInternal": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "skipLibCheck": true - }, - "include": [ - "./tests", - "./utils.ts" - ], - "exclude": [ - "**/node_modules" - ] -} diff --git a/e2e/package.json b/e2e/package.json new file mode 100644 index 00000000..5f566f33 --- /dev/null +++ b/e2e/package.json @@ -0,0 +1,30 @@ +{ + "name": "@mtcute/e2e-tests", + "type": "module", + "version": "0.0.0", + "private": true, + "fuman": { + "private": true + }, + "scripts": { + "test": "dotenv tsx --trace-warnings --test --test-timeout=300000", + "test:all": "find tests -type f -name '*.e2e.ts' -print0 | sort -z | xargs -0 -n1 pnpm run test", + "deno:test": "deno test -A --unstable-ffi --trace-leaks --import-map=import-map.json --unstable-sloppy-imports --no-check --env-file=.env", + "deno:test:all": "pnpm run deno:test tests/*.e2e.ts" + }, + "dependencies": { + "@fuman/utils": "0.0.1", + "chai": "^4.3.10", + "dotenv-cli": "7.4.4", + "mtcute": "file:../packages/node", + "tsx": "^4.19.2", + "esbuild": "^0.24.0", + "better-sqlite3": "11.6.0" + }, + "devDependencies": { + "@types/chai": "^4.3.8", + "@types/mocha": "^10.0.2", + "@types/node": "^20.8.10", + "globstar": "1.0.0" + } +} diff --git a/e2e/tests/01.auth.e2e.ts b/e2e/tests/01.auth.e2e.ts new file mode 100644 index 00000000..13692dd5 --- /dev/null +++ b/e2e/tests/01.auth.e2e.ts @@ -0,0 +1,27 @@ +import { describe, it } from 'node:test' +import { expect } from 'chai' +import { TelegramClient, User } from 'mtcute' + +import { getApiParams } from './_utils.js' + +describe('1. authorization', () => { + it('should authorize in default dc', async () => { + const tg = new TelegramClient(getApiParams('dc2.session')) + + await tg.importSession(process.env.SESSION_DC2!) + + expect(await tg.getMe()).to.be.instanceOf(User) + + await tg.close() + }) + + it('should authorize in dc 1', async () => { + const tg = new TelegramClient(getApiParams('dc1.session')) + + await tg.importSession(process.env.SESSION_DC1!) + + expect(await tg.getMe()).to.be.instanceOf(User) + + await tg.close() + }) +}) diff --git a/e2e/node/ts/tests/02.methods.ts b/e2e/tests/02.methods.e2e.ts similarity index 56% rename from e2e/node/ts/tests/02.methods.ts rename to e2e/tests/02.methods.e2e.ts index ccfcc4cb..a3cf0680 100644 --- a/e2e/node/ts/tests/02.methods.ts +++ b/e2e/tests/02.methods.e2e.ts @@ -1,16 +1,14 @@ -import { MtPeerNotFoundError } from '@mtcute/core' -import { TelegramClient } from '@mtcute/core/client.js' +import { after, before, describe, it } from 'node:test' import { expect } from 'chai' -import { describe, it } from 'mocha' +import { TelegramClient } from 'mtcute' -import { getApiParams } from '../utils.js' +import { getApiParams } from './_utils.js' -describe('2. calling methods', function () { - this.timeout(300_000) +describe('2. calling methods', () => { const tg = new TelegramClient(getApiParams('dc2.session')) - this.beforeAll(() => tg.connect()) - this.afterAll(() => tg.close()) + before(() => tg.connect()) + after(() => tg.close()) it('getUsers(@BotFather)', async () => { const [user] = await tg.getUsers('botfather') @@ -27,16 +25,7 @@ describe('2. calling methods', function () { }) it('getHistory(777000)', async () => { - try { - await tg.findDialogs(777000) // ensure it's cached - } catch (e) { - if (e instanceof MtPeerNotFoundError) { - // this happens sometimes :D gracefully skip - return - } - - throw e - } + await tg.findDialogs(777000) // ensure it's cached const history = await tg.getHistory(777000, { limit: 5 }) diff --git a/e2e/node/ts/tests/03.files.ts b/e2e/tests/03.files.e2e.ts similarity index 87% rename from e2e/node/ts/tests/03.files.ts rename to e2e/tests/03.files.e2e.ts index aba2dc4f..a4c0d4e4 100644 --- a/e2e/node/ts/tests/03.files.ts +++ b/e2e/tests/03.files.e2e.ts @@ -1,13 +1,12 @@ -import type { FileDownloadLocation } from '@mtcute/core' +import type { FileDownloadLocation } from 'mtcute' import { createHash } from 'node:crypto' -import { Thumbnail } from '@mtcute/core' -import { TelegramClient } from '@mtcute/core/client.js' -import { sleep } from '@mtcute/core/utils.js' +import { after, before, describe, it } from 'node:test' +import { sleep } from '@fuman/utils' import { expect } from 'chai' -import { describe, it } from 'mocha' +import { TelegramClient, Thumbnail } from 'mtcute' -import { getApiParams } from '../utils.js' +import { getApiParams } from './_utils.js' const CINNAMOROLL_PFP_CHAT = 'test_file_dc2' const CINNAMOROLL_PFP_THUMB_SHA256 = '3e6f220235a12547c16129f50c19ed3224d39b827414d1d500f79569a3431eae' @@ -31,18 +30,15 @@ async function downloadAsSha256(client: TelegramClient, location: FileDownloadLo return sha.digest('hex') } -describe('3. working with files', function () { - this.timeout(300_000) - // sometimes test dcs are overloaded and we get FILE_REFERENCE_EXPIRED - // because we got multiple -500:No workers running errors in a row - // we currently don't have file references database, so we can just retry the test for now - this.retries(2) - +// sometimes test dcs are overloaded and we get FILE_REFERENCE_EXPIRED +// because we got multiple -500:No workers running errors in a row +// we currently don't have file references database, so we can just retry the test for now +describe('3. working with files', () => { describe('same-dc', () => { const tg = new TelegramClient(getApiParams('dc2.session')) - this.beforeAll(() => tg.connect()) - this.afterAll(() => tg.close()) + before(() => tg.connect()) + after(() => tg.close()) it('should download pfp thumbs', async () => { const chat = await tg.getChat(CINNAMOROLL_PFP_CHAT) @@ -107,6 +103,7 @@ describe('3. working with files', function () { const promise = download() // let it download for 10 seconds + // file is 1gb so it is safe to assume it will take more than that to download await sleep(10000) abort.abort() // abort and snap the downloaded amount @@ -127,8 +124,8 @@ describe('3. working with files', function () { describe('cross-dc', () => { const tg = new TelegramClient(getApiParams('dc1.session')) - this.beforeAll(() => tg.connect()) - this.afterAll(() => tg.close()) + before(() => tg.connect()) + after(() => tg.close()) it('should download pfp thumbs', async () => { const chat = await tg.getChat(CINNAMOROLL_PFP_CHAT) diff --git a/e2e/node/ts/tests/04.updates.ts b/e2e/tests/04.updates.e2e.ts similarity index 62% rename from e2e/node/ts/tests/04.updates.ts rename to e2e/tests/04.updates.e2e.ts index a7837abf..b403cd9c 100644 --- a/e2e/node/ts/tests/04.updates.ts +++ b/e2e/tests/04.updates.e2e.ts @@ -1,24 +1,23 @@ -import type { Message } from '@mtcute/core' -import { TelegramClient } from '@mtcute/core/client.js' +import type { Message } from 'mtcute' +import { after, before, describe, it } from 'node:test' +import { asNonNull } from '@fuman/utils' import { expect } from 'chai' -import { describe, it } from 'mocha' -import { getApiParams, waitFor } from '../utils.js' - -describe('4. handling updates', function () { - this.timeout(300_000) +import { TelegramClient } from 'mtcute' +import { getApiParams, waitFor } from './_utils.js' +describe('4. handling updates', () => { const tg1 = new TelegramClient(getApiParams('dc1.session')) tg1.log.prefix = '[tg1] ' const tg2 = new TelegramClient(getApiParams('dc2.session')) tg2.log.prefix = '[tg2] ' - this.beforeAll(async () => { + before(async () => { await tg1.connect() await tg1.startUpdatesLoop() await tg2.connect() }) - this.afterAll(async () => { + after(async () => { await tg1.close() await tg2.close() }) @@ -26,15 +25,10 @@ describe('4. handling updates', function () { it('should send and receive messages', async () => { const tg1Messages: Message[] = [] - tg1.on('new_message', msg => tg1Messages.push(msg)) + tg1.onNewMessage.add(msg => tg1Messages.push(msg)) const [tg1User] = await tg1.getUsers('self') - let username = tg1User!.username - - if (!username) { - username = `mtcute_e2e_${Math.random().toString(36).slice(2)}` - await tg1.setMyUsername(username) - } + const username = asNonNull(tg1User!.username) const messageText = `mtcute test message ${Math.random().toString(36).slice(2)}` const sentMsg = await tg2.sendText(username, messageText) diff --git a/e2e/tests/05.worker.e2e.ts b/e2e/tests/05.worker.e2e.ts new file mode 100644 index 00000000..ef68b9d2 --- /dev/null +++ b/e2e/tests/05.worker.e2e.ts @@ -0,0 +1,127 @@ +import type { Message } from 'mtcute' + +import type { Worker as NodeWorker } from 'node:worker_threads' +import type { CustomMethods } from './_worker.js' +import { writeFile } from 'node:fs/promises' +import { join } from 'node:path' + +import { after, describe, it } from 'node:test' +import { fileURLToPath } from 'node:url' +import { asNonNull } from '@fuman/utils' +import { expect } from 'chai' +import { build } from 'esbuild' +import { Long, TelegramClient, TelegramWorkerPort, tl } from 'mtcute' +import { getApiParams, IS_BUN, IS_DENO, IS_NODE, RUNTIME_DIR, waitFor } from './_utils.js' + +const workerFile = new URL('./_worker.ts', import.meta.url) +let worker: Worker | NodeWorker +if (IS_NODE) { + // because of https://github.com/privatenumber/tsx/issues/354 we can't use tsx directly lol + const compiled = (await build({ + entryPoints: [fileURLToPath(workerFile)], + bundle: true, + format: 'esm', + write: false, + target: 'esnext', + platform: 'node', + // NB: we have better-sqlite3 in deps to this package because otherwise node fails to resolve it + external: ['node:*', 'better-sqlite3'], + })).outputFiles[0].text + const compiledWorkerFile = join(RUNTIME_DIR, '_worker.js') + await writeFile(compiledWorkerFile, compiled) + + const { Worker } = await import('node:worker_threads') + worker = new Worker(compiledWorkerFile) + worker.on('exit', (code) => { + console.error('worker exited with code', code) + }) + + worker.on('error', (err) => { + console.error('worker error', err) + process.exit(1) + }) +} else if (IS_BUN) { + const { Worker } = await import('node:worker_threads') + worker = new Worker(workerFile) + worker.on('exit', (code) => { + console.error('worker exited with code', code) + }) + + worker.on('error', (err) => { + console.error('worker error', err) + process.exit(1) + }) +} else if (IS_DENO) { + worker = new Worker(workerFile, { type: 'module' }) +} + +describe('5. worker', () => { + const port = new TelegramWorkerPort({ + worker, + }) + const portClient = new TelegramClient({ client: port }) + + it('should make api calls', async () => { + const res = await port.call({ _: 'help.getConfig' }) + expect(res._).to.equal('config') + + const premiumPromo = await port.call({ _: 'help.getPremiumPromo' }) + // ensure Long-s are correctly serialized + expect(Long.isLong((premiumPromo.users[0] as tl.RawUser).accessHash)).to.equal(true) + }) + + it('should call custom methods', async () => { + const hello = await port.invokeCustom('hello') + expect(hello).to.equal('world') + + const sum = await port.invokeCustom('sum', 2, 3) + expect(sum).to.equal(5) + }) + + it('should throw errors', async () => { + try { + await port.call({ _: 'test.useConfigSimple' }) + throw new Error('should have thrown') + } catch (e) { + expect(e).to.be.an.instanceOf(tl.RpcError) + } + }) + + it('should receive updates', async () => { + const client2 = new TelegramClient(getApiParams('dc2.session')) + + try { + await client2.connect() + await port.startUpdatesLoop() + + const me = await portClient.getMe() + // ensure Long-s are correctly serialized + expect(Long.isLong(me.raw.accessHash)).equals(true) + + const username = asNonNull(me.username) + + const msgs: Message[] = [] + portClient.onNewMessage.add((msg) => { + msgs.push(msg) + }) + + const testText = `mtcute worker test ${Math.random()}` + await client2.sendText(username, testText) + + await waitFor(() => { + expect(msgs.length).to.be.greaterThan(0) + expect(msgs[0].text).to.equal(testText) + }) + } catch (e) { + await client2.close() + throw e + } + + await client2.close() + }) + + after(async () => { + await port.close() + void worker.terminate() + }) +}) diff --git a/e2e/node/ts/utils.ts b/e2e/tests/_utils.ts similarity index 55% rename from e2e/node/ts/utils.ts rename to e2e/tests/_utils.ts index 4dcc9b2d..3998f7e6 100644 --- a/e2e/node/ts/utils.ts +++ b/e2e/tests/_utils.ts @@ -1,28 +1,27 @@ -import type { MaybePromise } from '@mtcute/core' +import type { BaseTelegramClientOptions, MaybePromise } from 'mtcute' -import type { BaseTelegramClientOptions } from '@mtcute/core/client.js' import { join } from 'node:path' -import { MemoryStorage } from '@mtcute/core' -import { setPlatform } from '@mtcute/core/platform.js' -import { LogManager, sleep } from '@mtcute/core/utils.js' -import { NodePlatform, SqliteStorage, TcpTransport } from '@mtcute/node' -import { NodeCryptoProvider } from '@mtcute/node/utils.js' +import { fileURLToPath } from 'node:url' +import { sleep } from '@fuman/utils' +import { MemoryStorage, SqliteStorage } from 'mtcute' +import { LogManager } from 'mtcute/utils.js' + +export const RUNTIME_DIR: string = fileURLToPath(new URL('../_runtime', import.meta.url)) +export const IS_DENO = 'Deno' in globalThis +export const IS_BUN = 'Bun' in globalThis +export const IS_NODE = !IS_DENO && !IS_BUN export function getApiParams(storage?: string): BaseTelegramClientOptions { if (!process.env.API_ID || !process.env.API_HASH) { throw new Error('API_ID and API_HASH env variables must be set') } - setPlatform(new NodePlatform()) - return { apiId: Number.parseInt(process.env.API_ID), apiHash: process.env.API_HASH, testMode: true, - storage: storage ? new SqliteStorage(join(__dirname, storage)) : new MemoryStorage(), + storage: storage ? new SqliteStorage(join(RUNTIME_DIR, storage)) : new MemoryStorage(), logLevel: LogManager.VERBOSE, - transport: () => new TcpTransport(), - crypto: new NodeCryptoProvider(), } } diff --git a/e2e/node/ts/tests/_worker.ts b/e2e/tests/_worker.ts similarity index 66% rename from e2e/node/ts/tests/_worker.ts rename to e2e/tests/_worker.ts index 03ea259d..98b788ba 100644 --- a/e2e/node/ts/tests/_worker.ts +++ b/e2e/tests/_worker.ts @@ -1,7 +1,6 @@ -import type { WorkerCustomMethods } from '@mtcute/core/worker.js' -import { BaseTelegramClient, TelegramWorker } from '@mtcute/node' - -import { getApiParams } from '../utils.js' +import type { WorkerCustomMethods } from 'mtcute' +import { BaseTelegramClient, TelegramWorker } from 'mtcute' +import { getApiParams } from './_utils.ts' const customMethods = { hello: async () => 'world', diff --git a/e2e/tests/deno-shims/node-test.js b/e2e/tests/deno-shims/node-test.js new file mode 100644 index 00000000..fef4c07f --- /dev/null +++ b/e2e/tests/deno-shims/node-test.js @@ -0,0 +1,17 @@ +import * as bdd from 'jsr:@std/testing@1.0.5/bdd' + +export function describe(name, opts, fn) { + if (typeof opts === 'function') { + fn = opts + opts = {} + } + + bdd.describe(name, { + ...opts, + // we don't close @db/sqlite + sanitizeResources: false, + }, fn) +} +export const it = bdd.it +export const before = bdd.beforeAll +export const after = bdd.afterAll diff --git a/e2e/tests/deno-shims/tl-reader.js b/e2e/tests/deno-shims/tl-reader.js new file mode 100644 index 00000000..3f4bd0a7 --- /dev/null +++ b/e2e/tests/deno-shims/tl-reader.js @@ -0,0 +1,5 @@ +import { createRequire } from 'node:module' + +const require = createRequire(import.meta.url) + +export const { __tlReaderMap } = require('../../../packages/tl/binary/reader.js') diff --git a/e2e/tests/deno-shims/tl-rsa.js b/e2e/tests/deno-shims/tl-rsa.js new file mode 100644 index 00000000..7be0398d --- /dev/null +++ b/e2e/tests/deno-shims/tl-rsa.js @@ -0,0 +1,5 @@ +import { createRequire } from 'node:module' + +const require = createRequire(import.meta.url) + +export const { __publicKeyIndex } = require('../../../packages/tl/binary/rsa-keys.js') diff --git a/e2e/tests/deno-shims/tl-writer.js b/e2e/tests/deno-shims/tl-writer.js new file mode 100644 index 00000000..b2882e45 --- /dev/null +++ b/e2e/tests/deno-shims/tl-writer.js @@ -0,0 +1,5 @@ +import { createRequire } from 'node:module' + +const require = createRequire(import.meta.url) + +export const { __tlWriterMap } = require('../../../packages/tl/binary/writer.js') diff --git a/e2e/tests/deno-shims/tl.js b/e2e/tests/deno-shims/tl.js new file mode 100644 index 00000000..7d0486d4 --- /dev/null +++ b/e2e/tests/deno-shims/tl.js @@ -0,0 +1,5 @@ +import { createRequire } from 'node:module' + +const require = createRequire(import.meta.url) + +export const { tl, mtp } = require('../../../packages/tl/index.js') diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json new file mode 100644 index 00000000..bc28d591 --- /dev/null +++ b/e2e/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "allowImportingTsExtensions": true, + "isolatedDeclarations": false + }, + "include": [ + "./tests" + ] +} diff --git a/eslint.config.js b/eslint.config.js index e21d2cd5..e95e9869 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -2,6 +2,7 @@ import antfu from '@antfu/eslint-config' export default antfu({ type: 'lib', + ignores: ['e2e/runtime/*'], typescript: process.env.CI ? { tsconfigPath: 'tsconfig.json', diff --git a/packages/core/src/highlevel/methods/bots/open-webview.ts b/packages/core/src/highlevel/methods/bots/open-webview.ts index 1640bb6c..5e347cf1 100644 --- a/packages/core/src/highlevel/methods/bots/open-webview.ts +++ b/packages/core/src/highlevel/methods/bots/open-webview.ts @@ -81,12 +81,12 @@ export async function openWebview( * - `unigram` - Unigram */ platform: - | 'android' - | 'ios' - | 'tdesktop' - | 'macos' - | 'unigram' - | (string & {}) + | 'android' + | 'ios' + | 'tdesktop' + | 'macos' + | 'unigram' + | (string & {}) }, ): Promise { const { diff --git a/packages/core/src/highlevel/methods/messages/send-copy-group.ts b/packages/core/src/highlevel/methods/messages/send-copy-group.ts index dc9184f7..2712d08b 100644 --- a/packages/core/src/highlevel/methods/messages/send-copy-group.ts +++ b/packages/core/src/highlevel/methods/messages/send-copy-group.ts @@ -25,14 +25,14 @@ export interface SendCopyGroupParams extends CommonSendParams { export async function sendCopyGroup( client: ITelegramClient, params: SendCopyGroupParams & - ( - | { - /** Source chat ID */ - fromChatId: InputPeerLike - /** Message IDs to forward */ - messages: number[] - } - | { messages: Message[] } + ( + | { + /** Source chat ID */ + fromChatId: InputPeerLike + /** Message IDs to forward */ + messages: number[] + } + | { messages: Message[] } ), ): Promise { const { toChatId, ...rest } = params diff --git a/packages/core/src/highlevel/methods/messages/send-copy.ts b/packages/core/src/highlevel/methods/messages/send-copy.ts index 6487caa7..cb9824df 100644 --- a/packages/core/src/highlevel/methods/messages/send-copy.ts +++ b/packages/core/src/highlevel/methods/messages/send-copy.ts @@ -38,14 +38,14 @@ export interface SendCopyParams extends CommonSendParams { export async function sendCopy( client: ITelegramClient, params: SendCopyParams & - ( - | { - /** Source chat ID */ - fromChatId: InputPeerLike - /** Message ID to forward */ - message: number - } - | { message: Message } + ( + | { + /** Source chat ID */ + fromChatId: InputPeerLike + /** Message ID to forward */ + message: number + } + | { message: Message } ), ): Promise { const { toChatId, ...rest } = params diff --git a/packages/core/src/highlevel/types/bots/webview.ts b/packages/core/src/highlevel/types/bots/webview.ts index 0c4ab03a..6d90d433 100644 --- a/packages/core/src/highlevel/types/bots/webview.ts +++ b/packages/core/src/highlevel/types/bots/webview.ts @@ -16,106 +16,106 @@ import { makeInspectable } from '../../utils/inspectable.js' * - `from_link` - webview opened via [direct links](https://corefork.telegram.org/api/bots/webapps#direct-link-mini-apps) */ export type InputWebview = - | { - type: 'main' + | { + type: 'main' - /** - * If set, requests to open the mini app in compact mode (as opposed to fullview mode). - * Must be set if the `mode` parameter of the Main Mini App link is equal to `compact`. - */ - compact?: boolean + /** + * If set, requests to open the mini app in compact mode (as opposed to fullview mode). + * Must be set if the `mode` parameter of the Main Mini App link is equal to `compact`. + */ + compact?: boolean - /** Start parameter from the deep link for the mini app */ - startParam?: string - } - | { - type: 'from_reply_keyboard' | 'from_switch_inline' + /** Start parameter from the deep link for the mini app */ + startParam?: string + } + | { + type: 'from_reply_keyboard' | 'from_switch_inline' - /** URL of the mini app found in the button */ - url: string - } - | { type: 'from_side_menu' } - | { - type: 'from_inline_keyboard' | 'from_bot_menu' + /** URL of the mini app found in the button */ + url: string + } + | { type: 'from_side_menu' } + | { + type: 'from_inline_keyboard' | 'from_bot_menu' - /** URL of the mini app found in the button */ - url: string + /** URL of the mini app found in the button */ + url: string - /** - * If set, requests to open the mini app in compact mode (as opposed to fullview mode). - * Must be set if the `mode` parameter of the Main Mini App link is equal to `compact`. - */ - compact?: boolean + /** + * If set, requests to open the mini app in compact mode (as opposed to fullview mode). + * Must be set if the `mode` parameter of the Main Mini App link is equal to `compact`. + */ + compact?: boolean - /** ID of the message to which we should reply if the mini app asks to do so */ - replyTo?: number | tl.TypeInputReplyTo - /** - * Peer to use when sending the message. - */ - sendAs?: InputPeerLike + /** ID of the message to which we should reply if the mini app asks to do so */ + replyTo?: number | tl.TypeInputReplyTo + /** + * Peer to use when sending the message. + */ + sendAs?: InputPeerLike - /** If the mini app asks to send a message, whether to send it silently */ - silent?: boolean + /** If the mini app asks to send a message, whether to send it silently */ + silent?: boolean - /** - * Telegram asks us to keep sending `messages.prolongWebView` requests every minute until the webview is closed. - * If this parameter is set to `true`, the timer will not be started, and the webview will never be prolonged. - */ - fireAndForget?: boolean - } - | { - type: 'from_attach_menu' + /** + * Telegram asks us to keep sending `messages.prolongWebView` requests every minute until the webview is closed. + * If this parameter is set to `true`, the timer will not be started, and the webview will never be prolonged. + */ + fireAndForget?: boolean + } + | { + type: 'from_attach_menu' - /** - * If set, requests to open the mini app in compact mode (as opposed to fullview mode). - * Must be set if the `mode` parameter of the Main Mini App link is equal to `compact`. - */ - compact?: boolean + /** + * If set, requests to open the mini app in compact mode (as opposed to fullview mode). + * Must be set if the `mode` parameter of the Main Mini App link is equal to `compact`. + */ + compact?: boolean - /** ID of the message to which we should reply if the mini app asks to do so */ - replyTo?: number | tl.TypeInputReplyTo + /** ID of the message to which we should reply if the mini app asks to do so */ + replyTo?: number | tl.TypeInputReplyTo - /** - * Peer to use when sending the message. - */ - sendAs?: InputPeerLike + /** + * Peer to use when sending the message. + */ + sendAs?: InputPeerLike - /** If the mini app asks to send a message, whether to send it silently */ - silent?: boolean + /** If the mini app asks to send a message, whether to send it silently */ + silent?: boolean - /** - * Telegram asks us to keep sending `messages.prolongWebView` requests every minute until the webview is closed. - * If this parameter is set to `true`, the timer will not be started, and the webview will never be prolonged. - */ - fireAndForget?: boolean - } - | { - type: 'from_link' + /** + * Telegram asks us to keep sending `messages.prolongWebView` requests every minute until the webview is closed. + * If this parameter is set to `true`, the timer will not be started, and the webview will never be prolonged. + */ + fireAndForget?: boolean + } + | { + type: 'from_link' - /** Short name of the app (from the link) */ - shortName: string + /** Short name of the app (from the link) */ + shortName: string - /** - * If the bot is asking permission to send messages to the user, - * whether to allow it to do so - */ - allowWrite?: boolean + /** + * If the bot is asking permission to send messages to the user, + * whether to allow it to do so + */ + allowWrite?: boolean - /** - * If set, requests to open the mini app in compact mode (as opposed to fullview mode). - * Must be set if the `mode` parameter of the Main Mini App link is equal to `compact`. - */ - compact?: boolean + /** + * If set, requests to open the mini app in compact mode (as opposed to fullview mode). + * Must be set if the `mode` parameter of the Main Mini App link is equal to `compact`. + */ + compact?: boolean - /** Start parameter from the deep link for the mini app */ - startParam?: string + /** Start parameter from the deep link for the mini app */ + startParam?: string - /** - * Telegram asks us to keep sending `messages.prolongWebView` requests every minute until the webview is closed. - * If this parameter is set to `true`, the timer will not be started, and the webview will never be prolonged. - */ - fireAndForget?: boolean - } + /** + * Telegram asks us to keep sending `messages.prolongWebView` requests every minute until the webview is closed. + * If this parameter is set to `true`, the timer will not be started, and the webview will never be prolonged. + */ + fireAndForget?: boolean + } /** * Result of {@link openWebview} method call diff --git a/packages/core/src/highlevel/types/premium/business-work-hours.ts b/packages/core/src/highlevel/types/premium/business-work-hours.ts index 219184fa..6fd2863a 100644 --- a/packages/core/src/highlevel/types/premium/business-work-hours.ts +++ b/packages/core/src/highlevel/types/premium/business-work-hours.ts @@ -177,7 +177,7 @@ export class BusinessWorkHours { interval.startHour === 0 && interval.startMinute === 0 && ((interval.endHour === 24 && interval.endMinute === 0) - || (interval.endHour === 23 && interval.endMinute === 59)) + || (interval.endHour === 23 && interval.endMinute === 59)) ) { (day as tl.Mutable).is24h = true } diff --git a/packages/core/src/highlevel/updates/manager.ts b/packages/core/src/highlevel/updates/manager.ts index 17c7c8c8..dd120457 100644 --- a/packages/core/src/highlevel/updates/manager.ts +++ b/packages/core/src/highlevel/updates/manager.ts @@ -673,7 +673,7 @@ export class UpdatesManager { msg.fwdFrom && (!( await fetchPeer(msg.fwdFrom.fromId)) - || !(await fetchPeer(msg.fwdFrom.savedFromPeer, true)) + || !(await fetchPeer(msg.fwdFrom.savedFromPeer, true)) ) ) { return missing diff --git a/packages/dispatcher/src/dispatcher.ts b/packages/dispatcher/src/dispatcher.ts index 2ff8ce33..79252fed 100644 --- a/packages/dispatcher/src/dispatcher.ts +++ b/packages/dispatcher/src/dispatcher.ts @@ -436,12 +436,12 @@ export class Dispatcher { this._storage && this._scenes && (update.name === 'new_message' - || update.name === 'edit_message' - || update.name === 'callback_query' - || update.name === 'message_group' - || update.name === 'new_business_message' - || update.name === 'edit_business_message' - || update.name === 'business_message_group') + || update.name === 'edit_message' + || update.name === 'callback_query' + || update.name === 'message_group' + || update.name === 'new_business_message' + || update.name === 'edit_business_message' + || update.name === 'business_message_group') ) { // no need to fetch scene if there are no registered scenes @@ -478,12 +478,12 @@ export class Dispatcher { if ( this._storage && (update.name === 'new_message' - || update.name === 'edit_message' - || update.name === 'callback_query' - || update.name === 'message_group' - || update.name === 'new_business_message' - || update.name === 'edit_business_message' - || update.name === 'business_message_group') + || update.name === 'edit_message' + || update.name === 'callback_query' + || update.name === 'message_group' + || update.name === 'new_business_message' + || update.name === 'edit_business_message' + || update.name === 'business_message_group') ) { if (!parsedContext) parsedContext = _parsedUpdateToContext(this._client, update) const key = await this._stateKeyDelegate!(parsedContext as any) diff --git a/packages/dispatcher/src/filters/text.ts b/packages/dispatcher/src/filters/text.ts index 83bb0c29..3352592d 100644 --- a/packages/dispatcher/src/filters/text.ts +++ b/packages/dispatcher/src/filters/text.ts @@ -15,15 +15,15 @@ import { } from '@mtcute/core' type UpdatesWithText = - | Message - | BusinessMessage - | UpdateContextDistributed< - | InlineQuery - | ChosenInlineResult - | CallbackQuery - | InlineCallbackQuery - | BusinessCallbackQuery - > + | Message + | BusinessMessage + | UpdateContextDistributed< + | InlineQuery + | ChosenInlineResult + | CallbackQuery + | InlineCallbackQuery + | BusinessCallbackQuery + > function extractText(obj: UpdatesWithText): string | null { if (obj instanceof Message || obj instanceof BusinessMessage) { diff --git a/packages/file-id/src/parse.ts b/packages/file-id/src/parse.ts index dac8d700..25230b1b 100644 --- a/packages/file-id/src/parse.ts +++ b/packages/file-id/src/parse.ts @@ -251,8 +251,8 @@ function fromPersistentIdV23(binary: Uint8Array, version: number): td.RawFullRem if ( location.source.fileType !== fileType || (fileType !== td.FileType.Photo - && fileType !== td.FileType.Thumbnail - && fileType !== td.FileType.EncryptedThumbnail) + && fileType !== td.FileType.Thumbnail + && fileType !== td.FileType.EncryptedThumbnail) ) { throw new td.InvalidFileIdError('Invalid FileType in PhotoRemoteFileLocation Thumbnail') } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9aea638..51603f45 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,6 +102,43 @@ importers: specifier: 2.0.5 version: 2.0.5(@types/node@20.10.0)(@vitest/browser@2.0.5)(@vitest/ui@2.0.5) + e2e: + dependencies: + '@fuman/utils': + specifier: 0.0.1 + version: 0.0.1 + better-sqlite3: + specifier: 11.6.0 + version: 11.6.0 + chai: + specifier: ^4.3.10 + version: 4.5.0 + dotenv-cli: + specifier: 7.4.4 + version: 7.4.4 + esbuild: + specifier: ^0.24.0 + version: 0.24.0 + mtcute: + specifier: file:../packages/node + version: '@mtcute/node@file:packages/node' + tsx: + specifier: ^4.19.2 + version: 4.19.2 + devDependencies: + '@types/chai': + specifier: ^4.3.8 + version: 4.3.20 + '@types/mocha': + specifier: ^10.0.2 + version: 10.0.10 + '@types/node': + specifier: ^20.8.10 + version: 20.10.0 + globstar: + specifier: 1.0.0 + version: 1.0.0 + packages/bun: dependencies: '@fuman/bun': @@ -609,6 +646,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.24.0': + resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} @@ -621,6 +664,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.24.0': + resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.21.5': resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} @@ -633,6 +682,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.24.0': + resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} @@ -645,6 +700,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.24.0': + resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} @@ -657,6 +718,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.24.0': + resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.21.5': resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} @@ -669,6 +736,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.24.0': + resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} @@ -681,6 +754,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.24.0': + resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} @@ -693,6 +772,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.24.0': + resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} @@ -705,6 +790,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.24.0': + resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.21.5': resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} @@ -717,6 +808,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.24.0': + resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.21.5': resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} @@ -729,6 +826,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.24.0': + resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} @@ -741,6 +844,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.24.0': + resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} @@ -753,6 +862,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.24.0': + resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.21.5': resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} @@ -765,6 +880,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.24.0': + resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.21.5': resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} @@ -777,6 +898,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.24.0': + resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.21.5': resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} @@ -789,6 +916,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.24.0': + resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.21.5': resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} @@ -801,6 +934,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.24.0': + resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} @@ -813,12 +952,24 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.24.0': + resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.23.0': resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.24.0': + resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} @@ -831,6 +982,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.24.0': + resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} @@ -843,6 +1000,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.24.0': + resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} @@ -855,6 +1018,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.24.0': + resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.21.5': resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} @@ -867,6 +1036,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.24.0': + resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.21.5': resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} @@ -879,6 +1054,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.24.0': + resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-plugin-eslint-comments@4.4.1': resolution: {integrity: sha512-lb/Z/MzbTf7CaVYM9WCFNQZ4L1yi3ev2fsFPF99h31ljhSEyUoyEsKsNWiU+qD1glbYTDJdqgyaLKtyTkkqtuQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1096,6 +1277,9 @@ packages: resolution: {integrity: sha512-lDiHQMCBV9qz8c7+zxaNFQtWWaSogTYkqJ3Pg+FGYYC76nsfSxkMQ0df8fojyz16E+w4vp57NLjN2muNG7LugQ==} engines: {node: '>=18'} + '@mtcute/node@file:packages/node': + resolution: {directory: packages/node, type: directory} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1304,6 +1488,9 @@ packages: '@types/bun@1.1.14': resolution: {integrity: sha512-opVYiFGtO2af0dnWBdZWlioLBoxSdDO5qokaazLhq8XQtGZbY4pY3/JxY8Zdf/hEwGubbp7ErZXoN1+h2yesxA==} + '@types/chai@4.3.20': + resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==} + '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} @@ -1331,6 +1518,9 @@ packages: '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mocha@10.0.10': + resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} + '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} @@ -1589,6 +1779,10 @@ packages: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} + ansi-regex@2.1.1: + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} + engines: {node: '>=0.10.0'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1609,10 +1803,17 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} + ansi@0.3.1: + resolution: {integrity: sha512-iFY7JCgHbepc0b82yLaw4IMortylNb6wG4kL+4R0C3iv6i+RHGHux/yUX5BTiRvSX/shMnngjR1YyNMnXEFh5A==} + are-docs-informative@0.0.2: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} + are-we-there-yet@1.0.6: + resolution: {integrity: sha512-Zfw6bteqM9gQXZ1BIWOgM8xEwMrUGoyL8nW13+O+OOgNX3YhuDN1GDgg1NzdTlmm3j+9sHy7uBZ12r+z9lXnZQ==} + deprecated: This package is no longer supported. + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -1628,6 +1829,9 @@ packages: assert@2.1.0: resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -1645,6 +1849,9 @@ packages: better-sqlite3@11.3.0: resolution: {integrity: sha512-iHt9j8NPYF3oKCNOO5ZI4JwThjt3Z6J6XrcwG85VNMVzv1ByqrHWv5VILEbCMFWDsoHhXvQ7oC8vgRXFAKgl9w==} + better-sqlite3@11.6.0: + resolution: {integrity: sha512-2J6k/eVxcFYY2SsTxsXrj6XylzHWPxveCn4fKPKZFv/Vqn/Cd7lOuX4d7rGQXT5zL+97MkNL3nSbCrIoe3LkgA==} + bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} @@ -1729,12 +1936,20 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camelcase@2.1.1: + resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==} + engines: {node: '>=0.10.0'} + caniuse-lite@1.0.30001680: resolution: {integrity: sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} + engines: {node: '>=4'} + chai@5.1.0: resolution: {integrity: sha512-kDZ7MZyM6Q1DhR9jy7dalKohXQ2yrlXkk59CR52aRKxJrobmlBNqnFQxX9xOX8w+4mz8SYlKJa/7D7ddltFXCw==} engines: {node: '>=12'} @@ -1763,6 +1978,9 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -1803,6 +2021,9 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} + cliui@3.2.0: + resolution: {integrity: sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1811,6 +2032,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + code-point-at@1.1.0: + resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} + engines: {node: '>=0.10.0'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1920,6 +2145,10 @@ packages: supports-color: optional: true + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} @@ -1927,6 +2156,10 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -1949,6 +2182,9 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -2004,12 +2240,20 @@ packages: domutils@3.1.0: resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + dotenv-cli@7.4.4: + resolution: {integrity: sha512-XkBYCG0tPIes+YZr4SpfFv76SQrV/LeCE8CI7JSEMi3VR9MvTihCGTOtbIexD6i2mXF+6px7trb1imVCXSNMDw==} + hasBin: true + + dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + dotenv-flow@4.1.0: resolution: {integrity: sha512-0cwP9jpQBQfyHwvE0cRhraZMkdV45TQedA8AAUZMsFzvmLcQyc1HPv+oX0OOYwLFjIlvgVepQ+WuQHbqDaHJZg==} engines: {node: '>= 12.0.0'} - dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} engines: {node: '>=12'} dpdm@3.14.0: @@ -2072,6 +2316,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.24.0: + resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -2393,10 +2642,17 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gauge@1.2.7: + resolution: {integrity: sha512-fVbU2wRE91yDvKUnrIaQlHKAWKY5e08PmztCrwuH5YVQ+Z/p3d0ny2T48o6uvAAXHIUnfaQdHkmxYbQft1eHVA==} + deprecated: This package is no longer supported. + get-caller-file@2.0.5: 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'} @@ -2428,6 +2684,10 @@ packages: engines: {node: 20 || >=22} hasBin: true + glob@5.0.15: + resolution: {integrity: sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==} + deprecated: Glob versions prior to v9 are no longer supported + globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} @@ -2440,6 +2700,10 @@ packages: resolution: {integrity: sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==} engines: {node: '>=18'} + globstar@1.0.0: + resolution: {integrity: sha512-UNXhfJYrwD6DNxMU4C9GJI1NhCMNvdsFnAGPLJHAeGW1io9l3N2FN7UUH76gQXhAUGNY+1rsVSkQnU59VRvxuQ==} + hasBin: true + gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} @@ -2477,6 +2741,9 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + hash-base@3.0.4: resolution: {integrity: sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==} engines: {node: '>=4'} @@ -2553,6 +2820,10 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2563,6 +2834,10 @@ packages: resolution: {integrity: sha512-B2LafrnnhbRzCWfAdOXisUzL89Kg8cVJlYmhqoi3flSiV/TveO+nsXwgKr9h9PIo+J1hz7nBSk6gegRIMBBf7g==} engines: {node: '>=14.18.0'} + invert-kv@1.0.0: + resolution: {integrity: sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==} + engines: {node: '>=0.10.0'} + is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -2586,6 +2861,10 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@1.0.0: + resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==} + engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -2720,6 +2999,10 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + lcid@1.0.0: + resolution: {integrity: sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==} + engines: {node: '>=0.10.0'} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -2749,6 +3032,15 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.pad@4.5.1: + resolution: {integrity: sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==} + + lodash.padend@4.6.1: + resolution: {integrity: sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==} + + lodash.padstart@4.6.1: + resolution: {integrity: sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -2762,6 +3054,9 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + loupe@3.1.2: resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} @@ -3058,9 +3353,21 @@ packages: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npmlog@1.2.1: + resolution: {integrity: sha512-1J5KqSRvESP6XbjPaXt2H6qDzgizLTM7x0y1cXIjP2PpvdCqyNC7TO3cPRKsuYlElbi/DwkzRRdG2zpmE0IktQ==} + deprecated: This package is no longer supported. + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + number-is-nan@1.0.1: + resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} + engines: {node: '>=0.10.0'} + + object-assign@2.1.1: + resolution: {integrity: sha512-CdsOUYIh5wIiozhJ3rLQgmUTgcyzFwZZrqhkKhODMoGtPKM+wt0h0CNIoauJWMsS9822EdzPsF/6mb4nLvPN5g==} + engines: {node: '>=0.10.0'} + object-inspect@1.13.3: resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} engines: {node: '>= 0.4'} @@ -3080,6 +3387,10 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@1.1.0: + resolution: {integrity: sha512-GZ+g4jayMqzCRMgB2sol7GiCLjKfS1PINkjmx8spcKce1LiVqcbQreXwqs2YAFXC6R03VIG28ZS31t8M866v6A==} + engines: {node: '>=0.10.0'} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -3105,6 +3416,10 @@ packages: os-browserify@0.3.0: resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + os-locale@1.4.0: + resolution: {integrity: sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==} + engines: {node: '>=0.10.0'} + os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -3174,6 +3489,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -3199,6 +3518,9 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} @@ -3567,6 +3889,10 @@ packages: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} + string-width@1.0.2: + resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==} + engines: {node: '>=0.10.0'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3584,6 +3910,10 @@ packages: stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3709,6 +4039,11 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tsx@4.19.2: + resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} + engines: {node: '>=18.0.0'} + hasBin: true + tty-browserify@0.0.1: resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} @@ -3719,6 +4054,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} @@ -3934,6 +4273,11 @@ packages: engines: {node: '>=8'} hasBin: true + window-size@0.1.4: + resolution: {integrity: sha512-2thx4pB0cV3h+Bw7QmMXcEbdmOzv9t0HFplJH/Lz6yu60hXYy5RT8rUu+wlIreVxWsGN20mo+MHeCSfUpQBwPw==} + engines: {node: '>= 0.10.0'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -3941,6 +4285,10 @@ packages: wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + wrap-ansi@2.1.0: + resolution: {integrity: sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==} + engines: {node: '>=0.10.0'} + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -3988,6 +4336,9 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + y18n@3.2.2: + resolution: {integrity: sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -4012,6 +4363,9 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yargs@3.32.0: + resolution: {integrity: sha512-ONJZiimStfZzhKamYvR/xvmgW3uEkAUFSP91y2caTEPhzF6uP2JfPiVZcq66b/YR0C3uitxSV7+T1x8p5bkmMg==} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -4155,141 +4509,213 @@ snapshots: '@esbuild/aix-ppc64@0.23.0': optional: true + '@esbuild/aix-ppc64@0.24.0': + optional: true + '@esbuild/android-arm64@0.21.5': optional: true '@esbuild/android-arm64@0.23.0': optional: true + '@esbuild/android-arm64@0.24.0': + optional: true + '@esbuild/android-arm@0.21.5': optional: true '@esbuild/android-arm@0.23.0': optional: true + '@esbuild/android-arm@0.24.0': + optional: true + '@esbuild/android-x64@0.21.5': optional: true '@esbuild/android-x64@0.23.0': optional: true + '@esbuild/android-x64@0.24.0': + optional: true + '@esbuild/darwin-arm64@0.21.5': optional: true '@esbuild/darwin-arm64@0.23.0': optional: true + '@esbuild/darwin-arm64@0.24.0': + optional: true + '@esbuild/darwin-x64@0.21.5': optional: true '@esbuild/darwin-x64@0.23.0': optional: true + '@esbuild/darwin-x64@0.24.0': + optional: true + '@esbuild/freebsd-arm64@0.21.5': optional: true '@esbuild/freebsd-arm64@0.23.0': optional: true + '@esbuild/freebsd-arm64@0.24.0': + optional: true + '@esbuild/freebsd-x64@0.21.5': optional: true '@esbuild/freebsd-x64@0.23.0': optional: true + '@esbuild/freebsd-x64@0.24.0': + optional: true + '@esbuild/linux-arm64@0.21.5': optional: true '@esbuild/linux-arm64@0.23.0': optional: true + '@esbuild/linux-arm64@0.24.0': + optional: true + '@esbuild/linux-arm@0.21.5': optional: true '@esbuild/linux-arm@0.23.0': optional: true + '@esbuild/linux-arm@0.24.0': + optional: true + '@esbuild/linux-ia32@0.21.5': optional: true '@esbuild/linux-ia32@0.23.0': optional: true + '@esbuild/linux-ia32@0.24.0': + optional: true + '@esbuild/linux-loong64@0.21.5': optional: true '@esbuild/linux-loong64@0.23.0': optional: true + '@esbuild/linux-loong64@0.24.0': + optional: true + '@esbuild/linux-mips64el@0.21.5': optional: true '@esbuild/linux-mips64el@0.23.0': optional: true + '@esbuild/linux-mips64el@0.24.0': + optional: true + '@esbuild/linux-ppc64@0.21.5': optional: true '@esbuild/linux-ppc64@0.23.0': optional: true + '@esbuild/linux-ppc64@0.24.0': + optional: true + '@esbuild/linux-riscv64@0.21.5': optional: true '@esbuild/linux-riscv64@0.23.0': optional: true + '@esbuild/linux-riscv64@0.24.0': + optional: true + '@esbuild/linux-s390x@0.21.5': optional: true '@esbuild/linux-s390x@0.23.0': optional: true + '@esbuild/linux-s390x@0.24.0': + optional: true + '@esbuild/linux-x64@0.21.5': optional: true '@esbuild/linux-x64@0.23.0': optional: true + '@esbuild/linux-x64@0.24.0': + optional: true + '@esbuild/netbsd-x64@0.21.5': optional: true '@esbuild/netbsd-x64@0.23.0': optional: true + '@esbuild/netbsd-x64@0.24.0': + optional: true + '@esbuild/openbsd-arm64@0.23.0': optional: true + '@esbuild/openbsd-arm64@0.24.0': + optional: true + '@esbuild/openbsd-x64@0.21.5': optional: true '@esbuild/openbsd-x64@0.23.0': optional: true + '@esbuild/openbsd-x64@0.24.0': + optional: true + '@esbuild/sunos-x64@0.21.5': optional: true '@esbuild/sunos-x64@0.23.0': optional: true + '@esbuild/sunos-x64@0.24.0': + optional: true + '@esbuild/win32-arm64@0.21.5': optional: true '@esbuild/win32-arm64@0.23.0': optional: true + '@esbuild/win32-arm64@0.24.0': + optional: true + '@esbuild/win32-ia32@0.21.5': optional: true '@esbuild/win32-ia32@0.23.0': optional: true + '@esbuild/win32-ia32@0.24.0': + optional: true + '@esbuild/win32-x64@0.21.5': optional: true '@esbuild/win32-x64@0.23.0': optional: true + '@esbuild/win32-x64@0.24.0': + optional: true + '@eslint-community/eslint-plugin-eslint-comments@4.4.1(eslint@9.9.0)': dependencies: escape-string-regexp: 4.0.0 @@ -4570,6 +4996,17 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 + '@mtcute/node@file:packages/node': + dependencies: + '@fuman/net': 0.0.1 + '@fuman/node': 0.0.1 + '@fuman/utils': 0.0.1 + '@mtcute/core': link:packages/core + '@mtcute/html-parser': link:packages/html-parser + '@mtcute/markdown-parser': link:packages/markdown-parser + '@mtcute/wasm': link:packages/wasm + better-sqlite3: 11.3.0 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4770,6 +5207,8 @@ snapshots: dependencies: bun-types: 1.1.37 + '@types/chai@4.3.20': {} + '@types/cookie@0.6.0': {} '@types/cross-spawn@6.0.6': @@ -4799,6 +5238,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/mocha@10.0.10': {} + '@types/ms@0.7.34': {} '@types/node@20.10.0': @@ -5148,6 +5589,8 @@ snapshots: dependencies: type-fest: 0.21.3 + ansi-regex@2.1.1: {} + ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} @@ -5160,8 +5603,15 @@ snapshots: ansi-styles@6.2.1: {} + ansi@0.3.1: {} + are-docs-informative@0.0.2: {} + are-we-there-yet@1.0.6: + dependencies: + delegates: 1.0.0 + readable-stream: 2.3.8 + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -5186,6 +5636,8 @@ snapshots: object.assign: 4.1.5 util: 0.12.5 + assertion-error@1.1.0: {} + assertion-error@2.0.1: {} available-typed-arrays@1.0.7: @@ -5201,6 +5653,11 @@ snapshots: bindings: 1.5.0 prebuild-install: 7.1.2 + better-sqlite3@11.6.0: + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.2 + bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 @@ -5316,10 +5773,22 @@ snapshots: callsites@3.1.0: {} + camelcase@2.1.1: {} + caniuse-lite@1.0.30001680: {} ccount@2.0.1: {} + chai@4.5.0: + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.4 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.1.0 + chai@5.1.0: dependencies: assertion-error: 2.0.1 @@ -5351,6 +5820,10 @@ snapshots: chardet@0.7.0: {} + check-error@1.0.3: + dependencies: + get-func-name: 2.0.2 + check-error@2.1.1: {} cheerio-select@2.1.0: @@ -5395,6 +5868,12 @@ snapshots: cli-width@4.1.0: {} + cliui@3.2.0: + dependencies: + string-width: 1.0.2 + strip-ansi: 3.0.1 + wrap-ansi: 2.1.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -5403,6 +5882,8 @@ snapshots: clone@1.0.4: {} + code-point-at@1.1.0: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -5515,6 +5996,8 @@ snapshots: dependencies: ms: 2.1.3 + decamelize@1.2.0: {} + decode-named-character-reference@1.0.2: dependencies: character-entities: 2.0.2 @@ -5523,6 +6006,10 @@ snapshots: dependencies: mimic-response: 3.1.0 + deep-eql@4.1.4: + dependencies: + type-detect: 4.1.0 + deep-eql@5.0.2: {} deep-extend@0.6.0: {} @@ -5545,6 +6032,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + delegates@1.0.0: {} + dequal@2.0.3: {} des.js@1.1.0: @@ -5608,11 +6097,20 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + dotenv-cli@7.4.4: + dependencies: + cross-spawn: 7.0.6 + dotenv: 16.3.1 + dotenv-expand: 10.0.0 + minimist: 1.2.8 + + dotenv-expand@10.0.0: {} + dotenv-flow@4.1.0: dependencies: - dotenv: 16.4.5 + dotenv: 16.3.1 - dotenv@16.4.5: {} + dotenv@16.3.1: {} dpdm@3.14.0: dependencies: @@ -5722,6 +6220,33 @@ snapshots: '@esbuild/win32-ia32': 0.23.0 '@esbuild/win32-x64': 0.23.0 + esbuild@0.24.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.0 + '@esbuild/android-arm': 0.24.0 + '@esbuild/android-arm64': 0.24.0 + '@esbuild/android-x64': 0.24.0 + '@esbuild/darwin-arm64': 0.24.0 + '@esbuild/darwin-x64': 0.24.0 + '@esbuild/freebsd-arm64': 0.24.0 + '@esbuild/freebsd-x64': 0.24.0 + '@esbuild/linux-arm': 0.24.0 + '@esbuild/linux-arm64': 0.24.0 + '@esbuild/linux-ia32': 0.24.0 + '@esbuild/linux-loong64': 0.24.0 + '@esbuild/linux-mips64el': 0.24.0 + '@esbuild/linux-ppc64': 0.24.0 + '@esbuild/linux-riscv64': 0.24.0 + '@esbuild/linux-s390x': 0.24.0 + '@esbuild/linux-x64': 0.24.0 + '@esbuild/netbsd-x64': 0.24.0 + '@esbuild/openbsd-arm64': 0.24.0 + '@esbuild/openbsd-x64': 0.24.0 + '@esbuild/sunos-x64': 0.24.0 + '@esbuild/win32-arm64': 0.24.0 + '@esbuild/win32-ia32': 0.24.0 + '@esbuild/win32-x64': 0.24.0 + escalade@3.2.0: {} escape-string-regexp@1.0.5: {} @@ -6135,8 +6660,18 @@ snapshots: function-bind@1.1.2: {} + gauge@1.2.7: + dependencies: + ansi: 0.3.1 + has-unicode: 2.0.1 + lodash.pad: 4.5.1 + lodash.padend: 4.6.1 + lodash.padstart: 4.6.1 + get-caller-file@2.0.5: {} + get-func-name@2.0.2: {} + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 @@ -6179,6 +6714,14 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 2.0.0 + glob@5.0.15: + dependencies: + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + globals@13.24.0: dependencies: type-fest: 0.20.2 @@ -6187,6 +6730,14 @@ snapshots: globals@15.12.0: {} + globstar@1.0.0: + dependencies: + glob: 5.0.15 + npmlog: 1.2.1 + object-assign: 2.1.1 + onetime: 1.1.0 + yargs: 3.32.0 + gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 @@ -6220,6 +6771,8 @@ snapshots: dependencies: has-symbols: 1.0.3 + has-unicode@2.0.1: {} + hash-base@3.0.4: dependencies: inherits: 2.0.4 @@ -6305,6 +6858,11 @@ snapshots: indent-string@4.0.0: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + inherits@2.0.4: {} ini@1.3.8: {} @@ -6327,6 +6885,8 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 6.2.0 + invert-kv@1.0.0: {} + is-arguments@1.1.1: dependencies: call-bind: 1.0.7 @@ -6346,6 +6906,10 @@ snapshots: is-extglob@2.1.1: {} + is-fullwidth-code-point@1.0.0: + dependencies: + number-is-nan: 1.0.1 + is-fullwidth-code-point@3.0.0: {} is-generator-function@1.0.10: @@ -6463,6 +7027,10 @@ snapshots: kolorist@1.8.0: {} + lcid@1.0.0: + dependencies: + invert-kv: 1.0.0 + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -6494,6 +7062,12 @@ snapshots: lodash.merge@4.6.2: {} + lodash.pad@4.5.1: {} + + lodash.padend@4.6.1: {} + + lodash.padstart@4.6.1: {} + lodash@4.17.21: {} log-symbols@4.1.0: @@ -6505,6 +7079,10 @@ snapshots: longest-streak@3.1.0: {} + loupe@2.3.7: + dependencies: + get-func-name: 2.0.2 + loupe@3.1.2: {} lru-cache@10.4.3: {} @@ -7006,10 +7584,20 @@ snapshots: dependencies: path-key: 4.0.0 + npmlog@1.2.1: + dependencies: + ansi: 0.3.1 + are-we-there-yet: 1.0.6 + gauge: 1.2.7 + nth-check@2.1.1: dependencies: boolbase: 1.0.0 + number-is-nan@1.0.1: {} + + object-assign@2.1.1: {} + object-inspect@1.13.3: {} object-is@1.1.6: @@ -7030,6 +7618,8 @@ snapshots: dependencies: wrappy: 1.0.2 + onetime@1.1.0: {} + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -7069,6 +7659,10 @@ snapshots: os-browserify@0.3.0: {} + os-locale@1.4.0: + dependencies: + lcid: 1.0.0 + os-tmpdir@1.0.2: {} outvariant@1.4.3: {} @@ -7137,6 +7731,8 @@ snapshots: path-exists@4.0.0: {} + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} path-key@4.0.0: {} @@ -7157,6 +7753,8 @@ snapshots: pathe@1.1.2: {} + pathval@1.1.1: {} + pathval@2.0.0: {} pbkdf2@3.1.2: @@ -7551,6 +8149,12 @@ snapshots: string-argv@0.3.2: {} + string-width@1.0.2: + dependencies: + code-point-at: 1.1.0 + is-fullwidth-code-point: 1.0.0 + strip-ansi: 3.0.1 + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -7576,6 +8180,10 @@ snapshots: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -7693,6 +8301,13 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tsx@4.19.2: + dependencies: + esbuild: 0.23.0 + get-tsconfig: 4.8.1 + optionalDependencies: + fsevents: 2.3.3 + tty-browserify@0.0.1: {} tunnel-agent@0.6.0: @@ -7703,6 +8318,8 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-detect@4.1.0: {} + type-fest@0.20.2: {} type-fest@0.21.3: {} @@ -7942,10 +8559,17 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + window-size@0.1.4: {} + word-wrap@1.2.5: {} wordwrap@1.0.0: {} + wrap-ansi@2.1.0: + dependencies: + string-width: 1.0.2 + strip-ansi: 3.0.1 + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -7974,6 +8598,8 @@ snapshots: xtend@4.0.2: {} + y18n@3.2.2: {} + y18n@5.0.8: {} yallist@4.0.0: {} @@ -7998,6 +8624,16 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yargs@3.32.0: + dependencies: + camelcase: 2.1.1 + cliui: 3.2.0 + decamelize: 1.2.0 + os-locale: 1.4.0 + string-width: 1.0.2 + window-size: 0.1.4 + y18n: 3.2.2 + yocto-queue@0.1.0: {} yoctocolors-cjs@2.1.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e6912143..b1ba0f9f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,3 @@ packages: - packages/* - - '!e2e/*' + - e2e \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index d46a47c7..577e262f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -32,7 +32,6 @@ "skipLibCheck": true }, "exclude": [ - "e2e/", "**/node_modules", "**/private", "**/__snapshots__"