From 9081d6f56f2ab6bf40f00573b7ce0b4e9d192812 Mon Sep 17 00:00:00 2001 From: Steven Vancoillie Date: Fri, 22 Sep 2023 15:20:14 +0200 Subject: [PATCH] feat: add tools with generated changelog cli --- .eslint-import-groups.cjs | 17 +- .eslintrc.cjs | 11 +- .github/workflows/publish.yml | 2 +- justfile | 43 +- overlay/esbuild.mjs | 4 +- package-lock.json | 1563 ++- package.json | 11 +- player/esbuild.mjs | 4 +- player/package.json | 2 +- scripts/changelog.mjs | 236 - scripts/ci-video-test.sh | 10 - scripts/top-level-dir.mjs | 18 - scripts/workspaces.mjs | 10 - streams/esbuild.mjs | 4 +- streams/package.json | 2 - streams/src/components/replayer/index.ts | 4 +- streams/src/components/tcp/index.ts | 2 +- tools/CHANGELOG.md | 5 + tools/package.json | 12 + tools/src/__generated__/changelog.mjs | 201 + tools/src/__generated__/tcp-ws-proxy.mjs | 10661 +++++++++++++++++++++ tools/src/changelog/changeset.ts | 133 + tools/src/changelog/cli.ts | 74 + tools/src/changelog/git.ts | 32 + tools/src/tcp-ws-proxy.ts | 41 + tools/tsconfig.eslint.json | 10 + tools/tsconfig.json | 7 + 27 files changed, 12608 insertions(+), 511 deletions(-) delete mode 100755 scripts/changelog.mjs delete mode 100755 scripts/top-level-dir.mjs delete mode 100755 scripts/workspaces.mjs create mode 100644 tools/CHANGELOG.md create mode 100644 tools/package.json create mode 100755 tools/src/__generated__/changelog.mjs create mode 100755 tools/src/__generated__/tcp-ws-proxy.mjs create mode 100644 tools/src/changelog/changeset.ts create mode 100644 tools/src/changelog/cli.ts create mode 100644 tools/src/changelog/git.ts create mode 100755 tools/src/tcp-ws-proxy.ts create mode 100644 tools/tsconfig.eslint.json create mode 100644 tools/tsconfig.json diff --git a/.eslint-import-groups.cjs b/.eslint-import-groups.cjs index 1d67ad5a6..a09f4600f 100644 --- a/.eslint-import-groups.cjs +++ b/.eslint-import-groups.cjs @@ -16,29 +16,16 @@ const externalDeps = [ ), ].sort() -const nodeDeps = [ - 'child_process', - 'crypto', - 'dgram', - 'electron', - 'fs', - 'http', - 'https', - 'net', - 'os', - 'path', - 'tls', - 'url', -] const reactDeps = ['react', 'react-dom'] const baseUrlPatterns = ['^[a-z_]'] const parentPatterns = ['^\\.\\.'] const siblingPatterns = ['^\\.'] const nakedImports = ['^\\u0000'] +const explicitNodeImports = ['^node:'] module.exports = [ nakedImports, - nodeDeps.map((dep) => `^${dep}$`), + explicitNodeImports, reactDeps.map((dep) => `^${dep}(\\/|$)`), externalDeps.map((dep) => `^${dep}(\\/|$)`), baseUrlPatterns, diff --git a/.eslintrc.cjs b/.eslintrc.cjs index afdb15fa0..efb54c9b0 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -9,11 +9,12 @@ module.exports = { ecmaFeatures: { jsx: true }, ecmaVersion: 2022, project: [ - './tsconfig.eslint.json', + './example-overlay-react/tsconfig.json', './overlay/tsconfig.json', './player/tsconfig.json', './streams/tsconfig.json', - './example-overlay-react/tsconfig.json', + './tools/tsconfig.json', + './tsconfig.eslint.json', ], sourceType: 'module', tsconfigRootDir: __dirname, @@ -34,5 +35,9 @@ module.exports = { settings: { react: { version: '18' }, }, - ignorePatterns: ['**/dist/'], + ignorePatterns: [ + '**/*.min.js*', + '**/__generated__/', + '**/dist/', + ], } diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ba51daba0..7d5a3823c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -44,7 +44,7 @@ jobs: with: tag_name: ${{ steps.vars.outputs.version }} release_name: Release ${{ steps.vars.outputs.version }} - body_path: changeset.md + body_path: CHANGELOG.md draft: false prerelease: false diff --git a/justfile b/justfile index 45673c80c..fef8b4430 100644 --- a/justfile +++ b/justfile @@ -1,6 +1,6 @@ set shell := ["bash", "-uc"] -export PATH := "./node_modules/.bin:" + env_var('PATH') +export PATH := "./node_modules/.bin" + ":" + join(justfile_directory(), "node_modules/.bin") + ":" + env_var('PATH') # just setup -- default when running just with no command default: @@ -13,9 +13,19 @@ build: _build-streams _build-player _build-overlay @changed: git diff --diff-filter=d --name-only $(git merge-base --fork-point origin/main) -# create a changelog or changeset +# create a changelog changelog: - scripts/changelog.mjs --write=CHANGELOG.md > changeset.md + #!/usr/bin/env bash + set -euo pipefail + new_version="$(jq -r .version package.json)" + old_version="$(git show HEAD:package.json | jq -r .version)" + url="$(jq -r .repository.url package.json)" + range=$(just sha v$old_version)..$(just sha HEAD) + changelog $new_version $range --url $url --outfile=CHANGELOG.md + +# check if there are uncommitted changes in the workspace +@check-dirty: + git diff --quiet || (echo "workspace dirty!"; git diff; exit 1) # format or check files with dprint (default formats all matching files) dprint +args="fmt": @@ -62,25 +72,44 @@ rtsp-ws: set -euo pipefail trap "kill 0" EXIT SIGINT scripts/rtsp-server.sh & - scripts/tcp-ws-proxy.cjs >& tcp-ws-proxy.log & + tcp-ws-proxy >& tcp-ws-proxy.log & wait # statically serve a directory serve path *args='--bind 0.0.0.0': http-server {{ path }} {{ args }} +# get the complete SHA ID for a commit +@sha $commitish='HEAD': + git rev-parse $commitish + +# generate tools +tools: + cd tools && esbuild --platform=node --outfile=src/__generated__/changelog.mjs --format=esm --out-extension:.js=.mjs --bundle --external:cmd-ts src/changelog/cli.ts + cd tools && esbuild --platform=node --outfile=src/__generated__/tcp-ws-proxy.mjs --format=esm --out-extension:.js=.mjs --bundle --external:cmd-ts src/tcp-ws-proxy.ts + just dprint fmt 'tools/src/__generated__/*' + +# update a specific dependency to latest update package: just ncu -u {{ package }} npm install npm update --include-workspace-root --workspaces {{ package }} +# CI verification +verify: + just build + just lint . + just test + just tools + just check-dirty + # update the package version of all workspaces version $level='prerelease': #!/usr/bin/env bash current=$(jq -r '.version' package.json) next=$(semver -i $level --preid alpha $current) echo "update: $current => $next" - npm version $next --workspace=streams --workspace=player --workspace=overlay + npm version $next --git-tag-version=false --workspace=streams --workspace=player --workspace=overlay --include-workspace-root # run vite development server, WORKSPACE=(player) vite WORKSPACE *ARGS: @@ -106,10 +135,6 @@ tsc workspace: uvu path pattern='.*\.test\.tsx?': c8 -r none --clean=false --src={{ path }} -- tsx --tsconfig {{ path }}/tsconfig.json node_modules/uvu/bin.js {{ path }}/tests/ {{ pattern }} -# get workspace from pathname -@workspace pathname=invocation_directory(): - node scripts/top-level-dir.mjs {{ justfile_directory() }} {{ pathname }} - # # hidden commands (these can be run but they are not shown with just --list) # diff --git a/overlay/esbuild.mjs b/overlay/esbuild.mjs index 2790975e7..b36f2937f 100755 --- a/overlay/esbuild.mjs +++ b/overlay/esbuild.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { existsSync, mkdirSync } from 'fs' -import { join } from 'path' +import { existsSync, mkdirSync } from 'node:fs' +import { join } from 'node:path' import { buildSync } from 'esbuild' diff --git a/package-lock.json b/package-lock.json index c92cd1ceb..e35616ffe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,10 +1,12 @@ { "name": "media-js", + "version": "12.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "media-js", + "version": "12.2.0", "workspaces": [ "example-overlay-react", "example-player-react", @@ -13,7 +15,8 @@ "example-streams-web", "overlay", "player", - "streams" + "streams", + "tools" ], "devDependencies": { "@typescript-eslint/eslint-plugin": "5.62.0", @@ -23,6 +26,7 @@ "cypress": "12.17.4", "dpdm": "3.14.0", "dprint": "0.41.0", + "esbuild": "0.17.19", "eslint": "8.49.0", "eslint-plugin-deprecation": "1.5.0", "eslint-plugin-node": "11.1.0", @@ -1293,9 +1297,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.3.tgz", - "integrity": "sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", "cpu": [ "arm" ], @@ -1309,9 +1313,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.3.tgz", - "integrity": "sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", "cpu": [ "arm64" ], @@ -1325,9 +1329,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.3.tgz", - "integrity": "sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", "cpu": [ "x64" ], @@ -1341,9 +1345,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.3.tgz", - "integrity": "sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", "cpu": [ "arm64" ], @@ -1357,9 +1361,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.3.tgz", - "integrity": "sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", "cpu": [ "x64" ], @@ -1373,9 +1377,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.3.tgz", - "integrity": "sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", "cpu": [ "arm64" ], @@ -1389,9 +1393,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.3.tgz", - "integrity": "sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", "cpu": [ "x64" ], @@ -1405,9 +1409,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.3.tgz", - "integrity": "sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", "cpu": [ "arm" ], @@ -1421,9 +1425,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.3.tgz", - "integrity": "sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", "cpu": [ "arm64" ], @@ -1437,9 +1441,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.3.tgz", - "integrity": "sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", "cpu": [ "ia32" ], @@ -1453,9 +1457,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.3.tgz", - "integrity": "sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", "cpu": [ "loong64" ], @@ -1469,9 +1473,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.3.tgz", - "integrity": "sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", "cpu": [ "mips64el" ], @@ -1485,9 +1489,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.3.tgz", - "integrity": "sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", "cpu": [ "ppc64" ], @@ -1501,9 +1505,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.3.tgz", - "integrity": "sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", "cpu": [ "riscv64" ], @@ -1517,9 +1521,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.3.tgz", - "integrity": "sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", "cpu": [ "s390x" ], @@ -1533,9 +1537,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.3.tgz", - "integrity": "sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", "cpu": [ "x64" ], @@ -1549,9 +1553,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.3.tgz", - "integrity": "sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", "cpu": [ "x64" ], @@ -1565,9 +1569,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.3.tgz", - "integrity": "sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", "cpu": [ "x64" ], @@ -1581,9 +1585,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.3.tgz", - "integrity": "sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", "cpu": [ "x64" ], @@ -1597,9 +1601,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.3.tgz", - "integrity": "sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", "cpu": [ "arm64" ], @@ -1613,9 +1617,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.3.tgz", - "integrity": "sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", "cpu": [ "ia32" ], @@ -1629,9 +1633,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.3.tgz", - "integrity": "sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", "cpu": [ "x64" ], @@ -2744,7 +2748,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2753,7 +2756,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3584,7 +3586,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3749,7 +3750,6 @@ "version": "0.13.0", "resolved": "https://registry.npmjs.org/cmd-ts/-/cmd-ts-0.13.0.tgz", "integrity": "sha512-nsnxf6wNIM/JAS7T/x/1JmbEsjH0a8tezXqqpaL0O6+eV0/aDEnRxwjxpu0VzDdRcaC1ixGSbRlUuf/IU59I4g==", - "dev": true, "dependencies": { "chalk": "^4.0.0", "debug": "^4.3.4", @@ -3761,7 +3761,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3772,8 +3771,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-support": { "version": "1.1.3", @@ -4359,8 +4357,7 @@ "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, "node_modules/diff": { "version": "5.1.0", @@ -4684,9 +4681,9 @@ } }, "node_modules/esbuild": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.3.tgz", - "integrity": "sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", "dev": true, "hasInstallScript": true, "bin": { @@ -4696,28 +4693,28 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.19.3", - "@esbuild/android-arm64": "0.19.3", - "@esbuild/android-x64": "0.19.3", - "@esbuild/darwin-arm64": "0.19.3", - "@esbuild/darwin-x64": "0.19.3", - "@esbuild/freebsd-arm64": "0.19.3", - "@esbuild/freebsd-x64": "0.19.3", - "@esbuild/linux-arm": "0.19.3", - "@esbuild/linux-arm64": "0.19.3", - "@esbuild/linux-ia32": "0.19.3", - "@esbuild/linux-loong64": "0.19.3", - "@esbuild/linux-mips64el": "0.19.3", - "@esbuild/linux-ppc64": "0.19.3", - "@esbuild/linux-riscv64": "0.19.3", - "@esbuild/linux-s390x": "0.19.3", - "@esbuild/linux-x64": "0.19.3", - "@esbuild/netbsd-x64": "0.19.3", - "@esbuild/openbsd-x64": "0.19.3", - "@esbuild/sunos-x64": "0.19.3", - "@esbuild/win32-arm64": "0.19.3", - "@esbuild/win32-ia32": "0.19.3", - "@esbuild/win32-x64": "0.19.3" + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" } }, "node_modules/escalade": { @@ -6022,7 +6019,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -10695,7 +10691,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -10886,7 +10881,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -11139,6 +11133,10 @@ "node": ">=8.0" } }, + "node_modules/tools": { + "resolved": "tools", + "link": true + }, "node_modules/tough-cookie": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", @@ -12604,107 +12602,1282 @@ "react-dom": "^17.0.1 || ^18.0.0" } }, - "player": { - "name": "media-stream-player", - "version": "12.2.0", - "license": "MIT", - "dependencies": { - "@juggle/resize-observer": "3.4.0", - "debug": "4.3.4", - "react-hooks-shareable": "1.53.0" - }, - "devDependencies": { - "@types/debug": "4.1.8", - "@types/luxon": "3.3.2", - "@types/react": "18.2.22", - "@types/react-dom": "18.2.7", - "@types/styled-components": "5.1.27", - "@vitejs/plugin-react": "4.0.4", - "chalk": "5.3.0", - "esbuild": "0.19.3", - "luxon": "3.4.3", - "media-stream-library": "12.2.0", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-is": "18.2.0", - "semver": "7.5.4", - "styled-components": "5.3.11", - "typescript": "5.2.2", - "vite": "4.4.9" - }, - "peerDependencies": { - "luxon": "^3.0.0", - "media-stream-library": "^12.2.0", - "react": "^17.0.2 || ^18.1.0", - "react-dom": "^17.0.2 || ^18.1.0", - "styled-components": "^5.3.5" + "overlay/node_modules/@esbuild/android-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.3.tgz", + "integrity": "sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "player/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "overlay/node_modules/@esbuild/android-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.3.tgz", + "integrity": "sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=12" } }, - "streams": { - "name": "media-stream-library", - "version": "12.2.0", - "license": "MIT", - "dependencies": { - "buffer": "6.0.3", - "debug": "4.3.4", - "events": "3.3.0", - "process": "0.11.10", - "stream-browserify": "3.0.0", - "ts-md5": "1.3.1", - "ws": "8.14.2" - }, - "devDependencies": { - "@types/debug": "4.1.8", - "@types/node": "18.17.17", - "@types/ws": "8.5.5", - "esbuild": "0.19.3", - "global-jsdom": "8.8.0", - "jsdom": "21.1.2", - "mock-socket": "9.3.1", - "semver": "7.5.4", - "typescript": "5.2.2", - "uvu": "0.5.6" + "overlay/node_modules/@esbuild/android-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.3.tgz", + "integrity": "sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "streams/node_modules/@types/node": { - "version": "18.17.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.17.tgz", - "integrity": "sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==", - "dev": true + "overlay/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.3.tgz", + "integrity": "sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } }, - "streams/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } + "overlay/node_modules/@esbuild/darwin-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.3.tgz", + "integrity": "sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==", + "cpu": [ + "x64" ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.3.tgz", + "integrity": "sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.3.tgz", + "integrity": "sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/linux-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.3.tgz", + "integrity": "sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/linux-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.3.tgz", + "integrity": "sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/linux-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.3.tgz", + "integrity": "sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/linux-loong64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.3.tgz", + "integrity": "sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.3.tgz", + "integrity": "sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.3.tgz", + "integrity": "sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.3.tgz", + "integrity": "sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/linux-s390x": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.3.tgz", + "integrity": "sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/linux-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.3.tgz", + "integrity": "sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.3.tgz", + "integrity": "sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.3.tgz", + "integrity": "sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/sunos-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.3.tgz", + "integrity": "sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/win32-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.3.tgz", + "integrity": "sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/win32-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.3.tgz", + "integrity": "sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/@esbuild/win32-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.3.tgz", + "integrity": "sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "overlay/node_modules/esbuild": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.3.tgz", + "integrity": "sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.3", + "@esbuild/android-arm64": "0.19.3", + "@esbuild/android-x64": "0.19.3", + "@esbuild/darwin-arm64": "0.19.3", + "@esbuild/darwin-x64": "0.19.3", + "@esbuild/freebsd-arm64": "0.19.3", + "@esbuild/freebsd-x64": "0.19.3", + "@esbuild/linux-arm": "0.19.3", + "@esbuild/linux-arm64": "0.19.3", + "@esbuild/linux-ia32": "0.19.3", + "@esbuild/linux-loong64": "0.19.3", + "@esbuild/linux-mips64el": "0.19.3", + "@esbuild/linux-ppc64": "0.19.3", + "@esbuild/linux-riscv64": "0.19.3", + "@esbuild/linux-s390x": "0.19.3", + "@esbuild/linux-x64": "0.19.3", + "@esbuild/netbsd-x64": "0.19.3", + "@esbuild/openbsd-x64": "0.19.3", + "@esbuild/sunos-x64": "0.19.3", + "@esbuild/win32-arm64": "0.19.3", + "@esbuild/win32-ia32": "0.19.3", + "@esbuild/win32-x64": "0.19.3" + } + }, + "player": { + "name": "media-stream-player", + "version": "12.2.0", + "license": "MIT", + "dependencies": { + "@juggle/resize-observer": "3.4.0", + "debug": "4.3.4", + "react-hooks-shareable": "1.53.0" + }, + "devDependencies": { + "@types/debug": "4.1.8", + "@types/luxon": "3.3.2", + "@types/react": "18.2.22", + "@types/react-dom": "18.2.7", + "@types/styled-components": "5.1.27", + "@vitejs/plugin-react": "4.0.4", + "chalk": "5.3.0", + "esbuild": "0.19.3", + "luxon": "3.4.3", + "media-stream-library": "file:../streams", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-is": "18.2.0", + "semver": "7.5.4", + "styled-components": "5.3.11", + "typescript": "5.2.2", + "vite": "4.4.9" + }, + "peerDependencies": { + "luxon": "^3.0.0", + "media-stream-library": "^12.2.0", + "react": "^17.0.2 || ^18.1.0", + "react-dom": "^17.0.2 || ^18.1.0", + "styled-components": "^5.3.5" + } + }, + "player/node_modules/@esbuild/android-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.3.tgz", + "integrity": "sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/android-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.3.tgz", + "integrity": "sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/android-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.3.tgz", + "integrity": "sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.3.tgz", + "integrity": "sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/darwin-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.3.tgz", + "integrity": "sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.3.tgz", + "integrity": "sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.3.tgz", + "integrity": "sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/linux-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.3.tgz", + "integrity": "sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/linux-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.3.tgz", + "integrity": "sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/linux-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.3.tgz", + "integrity": "sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/linux-loong64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.3.tgz", + "integrity": "sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.3.tgz", + "integrity": "sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.3.tgz", + "integrity": "sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.3.tgz", + "integrity": "sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/linux-s390x": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.3.tgz", + "integrity": "sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/linux-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.3.tgz", + "integrity": "sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.3.tgz", + "integrity": "sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.3.tgz", + "integrity": "sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/sunos-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.3.tgz", + "integrity": "sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/win32-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.3.tgz", + "integrity": "sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/win32-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.3.tgz", + "integrity": "sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/@esbuild/win32-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.3.tgz", + "integrity": "sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "player/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "player/node_modules/esbuild": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.3.tgz", + "integrity": "sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.3", + "@esbuild/android-arm64": "0.19.3", + "@esbuild/android-x64": "0.19.3", + "@esbuild/darwin-arm64": "0.19.3", + "@esbuild/darwin-x64": "0.19.3", + "@esbuild/freebsd-arm64": "0.19.3", + "@esbuild/freebsd-x64": "0.19.3", + "@esbuild/linux-arm": "0.19.3", + "@esbuild/linux-arm64": "0.19.3", + "@esbuild/linux-ia32": "0.19.3", + "@esbuild/linux-loong64": "0.19.3", + "@esbuild/linux-mips64el": "0.19.3", + "@esbuild/linux-ppc64": "0.19.3", + "@esbuild/linux-riscv64": "0.19.3", + "@esbuild/linux-s390x": "0.19.3", + "@esbuild/linux-x64": "0.19.3", + "@esbuild/netbsd-x64": "0.19.3", + "@esbuild/openbsd-x64": "0.19.3", + "@esbuild/sunos-x64": "0.19.3", + "@esbuild/win32-arm64": "0.19.3", + "@esbuild/win32-ia32": "0.19.3", + "@esbuild/win32-x64": "0.19.3" + } + }, + "streams": { + "name": "media-stream-library", + "version": "12.2.0", + "license": "MIT", + "dependencies": { + "buffer": "6.0.3", + "debug": "4.3.4", + "events": "3.3.0", + "process": "0.11.10", + "stream-browserify": "3.0.0", + "ts-md5": "1.3.1", + "ws": "8.14.2" + }, + "devDependencies": { + "@types/debug": "4.1.8", + "@types/node": "18.17.17", + "@types/ws": "8.5.5", + "esbuild": "0.19.3", + "global-jsdom": "8.8.0", + "jsdom": "21.1.2", + "mock-socket": "9.3.1", + "semver": "7.5.4", + "typescript": "5.2.2", + "uvu": "0.5.6" + } + }, + "streams/node_modules/@esbuild/android-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.3.tgz", + "integrity": "sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/android-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.3.tgz", + "integrity": "sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/android-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.3.tgz", + "integrity": "sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.3.tgz", + "integrity": "sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/darwin-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.3.tgz", + "integrity": "sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.3.tgz", + "integrity": "sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.3.tgz", + "integrity": "sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/linux-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.3.tgz", + "integrity": "sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/linux-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.3.tgz", + "integrity": "sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/linux-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.3.tgz", + "integrity": "sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/linux-loong64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.3.tgz", + "integrity": "sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.3.tgz", + "integrity": "sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.3.tgz", + "integrity": "sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.3.tgz", + "integrity": "sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/linux-s390x": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.3.tgz", + "integrity": "sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/linux-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.3.tgz", + "integrity": "sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.3.tgz", + "integrity": "sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.3.tgz", + "integrity": "sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/sunos-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.3.tgz", + "integrity": "sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/win32-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.3.tgz", + "integrity": "sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/win32-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.3.tgz", + "integrity": "sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@esbuild/win32-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.3.tgz", + "integrity": "sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "streams/node_modules/@types/node": { + "version": "18.17.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.17.tgz", + "integrity": "sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==", + "dev": true + }, + "streams/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "streams/node_modules/esbuild": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.3.tgz", + "integrity": "sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.3", + "@esbuild/android-arm64": "0.19.3", + "@esbuild/android-x64": "0.19.3", + "@esbuild/darwin-arm64": "0.19.3", + "@esbuild/darwin-x64": "0.19.3", + "@esbuild/freebsd-arm64": "0.19.3", + "@esbuild/freebsd-x64": "0.19.3", + "@esbuild/linux-arm": "0.19.3", + "@esbuild/linux-arm64": "0.19.3", + "@esbuild/linux-ia32": "0.19.3", + "@esbuild/linux-loong64": "0.19.3", + "@esbuild/linux-mips64el": "0.19.3", + "@esbuild/linux-ppc64": "0.19.3", + "@esbuild/linux-riscv64": "0.19.3", + "@esbuild/linux-s390x": "0.19.3", + "@esbuild/linux-x64": "0.19.3", + "@esbuild/netbsd-x64": "0.19.3", + "@esbuild/openbsd-x64": "0.19.3", + "@esbuild/sunos-x64": "0.19.3", + "@esbuild/win32-arm64": "0.19.3", + "@esbuild/win32-ia32": "0.19.3", + "@esbuild/win32-x64": "0.19.3" + } + }, + "tools": { + "dependencies": { + "cmd-ts": "0.13.0" + }, + "bin": { + "changelog": "src/__generated__/changelog.mjs" } } } diff --git a/package.json b/package.json index c5a5d3fe8..d1edad965 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,12 @@ { "name": "media-js", - "private": true, "description": "Media libraries for Node and the Web.", + "private": true, + "version": "12.2.0", + "repository": { + "type": "git", + "url": "https://github.com/AxisCommunications/media-stream-library-js" + }, "workspaces": [ "example-overlay-react", "example-player-react", @@ -10,7 +15,8 @@ "example-streams-web", "overlay", "player", - "streams" + "streams", + "tools" ], "devDependencies": { "@typescript-eslint/eslint-plugin": "5.62.0", @@ -20,6 +26,7 @@ "cypress": "12.17.4", "dpdm": "3.14.0", "dprint": "0.41.0", + "esbuild": "0.17.19", "eslint": "8.49.0", "eslint-plugin-deprecation": "1.5.0", "eslint-plugin-node": "11.1.0", diff --git a/player/esbuild.mjs b/player/esbuild.mjs index d8f2f4c68..2d0fc22b4 100755 --- a/player/esbuild.mjs +++ b/player/esbuild.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { existsSync, mkdirSync } from 'fs' -import { join } from 'path' +import { existsSync, mkdirSync } from 'node:fs' +import { join } from 'node:path' import { buildSync } from 'esbuild' diff --git a/player/package.json b/player/package.json index d5397ca8e..b6a2d2a28 100644 --- a/player/package.json +++ b/player/package.json @@ -53,7 +53,7 @@ "chalk": "5.3.0", "esbuild": "0.19.3", "luxon": "3.4.3", - "media-stream-library": "12.2.0", + "media-stream-library": "file:../streams", "react": "18.2.0", "react-dom": "18.2.0", "react-is": "18.2.0", diff --git a/scripts/changelog.mjs b/scripts/changelog.mjs deleted file mode 100755 index f478ec0fb..000000000 --- a/scripts/changelog.mjs +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env node -import { execSync } from 'child_process' -import fs from 'fs' -import path from 'path' - -import { - binary, - boolean, - command, - flag, - option, - optional, - run, - string, -} from 'cmd-ts' -const ChangeLogHeader = `# Changelog - -All notable changes to this project will be documented in this file. -` -const GitHubCompareUrl = - 'https://github.com/AxisCommunications/media-stream-library-js/compare' -const GitHubCommitUrl = - 'https://github.com/AxisCommunications/media-stream-library-js/commit' -const GroupTitles = { - 'build': '\u{1F477} Build', - 'chore': '\u{1F6A7} Maintenance', - 'ci': '\u{1F6A6} Continous integration', - 'docs': '\u{1F4DD} Documentation', - 'feat': '\u2728 Features', - 'fix': '\u{1F41B} Bug fixes', - 'perf': '\u{1F3CE}\uFE0F Performance', - 'refactor': '\u267B\uFE0F Refactoring', - 'revert': '\u23EA\uFE0F Reverts', - 'style': '\u{1F484} Styling', - 'test': '\u{1F9EA} Test', -} -const GroupKeys = new Set(Object.keys(GroupTitles)) -const PackageJson = 'package.json' -const changelog = command({ - name: 'changelog', - description: `Generate a full or partial changelog. Outputs to stdout, -with an option to additionally write to a changelog file.`, - args: { - workspace: option({ - description: ` -Filter scope by workspace. In this case, the scope itself is not included in the output. -You can use this to only generate changelogs for a specific workspace, if you have -separate releases within the same monorepo for different workspaces. -`, - long: 'workspace', - type: optional(string), - }), - full: flag({ - defaultValue: () => false, - description: 'generate the entire changelog', - long: 'full', - short: 'f', - type: boolean, - }), - write: option({ - description: 'write (or update) a changelog file', - long: 'write', - short: 'w', - type: optional(string), - }), - }, - handler(args) { - const contents = args.full - ? generateChangelog(args.workspace) - : generateChangeset(args.workspace) - if (args.full) { - args.write !== void 0 - ? fs.writeFileSync(args.write, contents) - : process.stdout.write(contents) - } else { - args.write !== void 0 && updateChangelog(args.write, contents) - process.stdout.write(contents) - } - }, -}) -await run(binary(changelog), process.argv) -function generateChangeset(workspace) { - const packageJson = path.join(workspace ?? '.', PackageJson) - const toVersion = worktreeVersion(packageJson) - const fromVersion = committedVersion(packageJson) - return changeSetFromRange(fromVersion, toVersion, workspace, 'HEAD') -} -function generateChangelog(workspace) { - const packageJson = path.join(workspace ?? '.', PackageJson) - const outputChunks = [ChangeLogHeader] - let toRev = 'HEAD' - while (existingCommit(toRev)) { - const version = committedVersion(packageJson, toRev) - const parentRev = `${tagFromVersion(version, workspace)}~1` - if (!existingCommit(parentRev)) { - break - } - const previousVersion = committedVersion(packageJson, parentRev) - outputChunks.push( - changeSetFromRange(previousVersion, version, workspace) - ) - toRev = tagFromVersion(previousVersion, workspace) - } - return outputChunks.join('\n') -} -function updateChangelog(changelogPath, changeset) { - const oldChangelogChunks = fs.readFileSync(changelogPath).toString().split( - '\n' - ) - const startOfVersions = oldChangelogChunks.findIndex((chunk) => - chunk.startsWith('##') - ) - const newChangelogChunks = [ - ...oldChangelogChunks.slice(0, startOfVersions), - changeset, - ...oldChangelogChunks.slice(startOfVersions), - ] - const newChangelog = newChangelogChunks.join('\n') - fs.writeFileSync(changelogPath, newChangelog) -} -function changeSetFromRange(previousVersion, version, scope, rev) { - const outputChunks = [] - const prevTag = tagFromVersion(previousVersion, scope) - const tag = tagFromVersion(version, scope) - const date = rev !== void 0 - ? (/* @__PURE__ */ new Date()).toISOString() - : commitDate(tag) - outputChunks.push( - `## [${version}](${GitHubCompareUrl}/${prevTag}...${tag}) (${date}) -` - ) - const groups = {} - for (const [sha, msg] of gitLogFromRange(prevTag, rev ?? `${tag}~1`)) { - const cc = parseConventionalCommitMessage(msg) - if (scope !== void 0 && cc.scope !== scope) { - continue - } - if (groups[cc.group] === void 0) { - groups[cc.group] = [] - } - groups[cc.group].push([sha, cc]) - } - for (const group of GroupKeys) { - if (groups[group] === void 0) { - continue - } - outputChunks.push(` -### ${GroupTitles[group]} - -`) - for (const [sha, cc] of groups[group]) { - const scopePrefix = scope === void 0 && cc.scope !== void 0 - ? ` **${cc.scope}**:` - : '' - const breakingPrefix = cc.breaking ? ` **BREAKING**` : '' - const link = `([${shortSha(sha)}](${GitHubCommitUrl}/${sha}))` - outputChunks.push(` -${scopePrefix}${breakingPrefix} ${cc.title} ${link} -`) - } - } - return outputChunks.join('') -} -function parseConventionalCommitMessage(msg) { - try { - const match = msg.match(/^([^:!(]+)(?:\(([^)]+)\))?(!)?: (.*)$/) - if (match === null) { - throw new Error('no matches found') - } - const [_, group, scope, breaking, title] = match - return { - group, - scope, - breaking: breaking !== void 0, - title, - } - } catch { - process.stderr.write(`invalid conventional commit message: ${msg} -`) - } - return { - group: 'chore', - breaking: false, - title: msg, - } -} -function existingCommit(commit) { - try { - execSync(`git rev-parse -q --verify ${commit}`) - } catch { - return false - } - return true -} -function worktreeVersion(pkgPath) { - const { version } = JSON.parse(fs.readFileSync(pkgPath).toString()) - if (typeof version !== 'string') { - throw new Error(`no version in ${pkgPath}`) - } - return version -} -function committedVersion(pkgPath, commit = 'HEAD') { - const { version } = JSON.parse( - execSync(`git show ${commit}:${pkgPath}`).toString() - ) - if (typeof version !== 'string') { - throw new Error(`no version in ${commit}:${pkgPath}`) - } - return version -} -function tagFromVersion(version, scope) { - if (scope === void 0) { - return `v${version}` - } - return `${scope}-v${version}` -} -function gitLogFromRange(prev, curr = 'HEAD') { - try { - const logOut = execSync( - `git log --no-merges --date-order --format="%H%x09%s" ${prev}..${curr}` - ) - const lines = logOut.toString().trim().split('\n') - const shaMessagePairs = lines.map((line) => line.split(' ')) - return shaMessagePairs - } catch (err) { - console.warn( - `git log failed on range ${prev}..${curr}, one of those tags probably does not exist` - ) - return [] - } -} -function shortSha(sha) { - return execSync(`git log -1 --format=%h ${sha}`).toString().trim() -} -function commitDate(commit) { - return execSync(`git log -1 --format=%ci ${commit}`).toString().trim() -} diff --git a/scripts/ci-video-test.sh b/scripts/ci-video-test.sh index 63a9b059c..e0414448d 100755 --- a/scripts/ci-video-test.sh +++ b/scripts/ci-video-test.sh @@ -3,16 +3,6 @@ set -e CYPRESS_DOCKER_IMAGE=cypress/included:12.8.1 -#trap "kill 0" EXIT - -# echo -n "Starting RTSP WebSocket proxy..." -# node scripts/tcp-ws-proxy.cjs --rtspHost localhost:8554 >& tcp-ws-proxy.log & -# echo "done." - -# echo -n "Starting HTTP server with examples..." -# just serve example-streams-web >& http-server.log & -# echo "done." - echo -n "Starting RTSP/WebSocket proxy and example server..." just _run-example-streams-web >& example-streams-web.log & echo "done." diff --git a/scripts/top-level-dir.mjs b/scripts/top-level-dir.mjs deleted file mode 100755 index 44d9c4208..000000000 --- a/scripts/top-level-dir.mjs +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env node - -// Determine the top-level directory relative to a project root, -// used to determine the workspace from an invocation directory. -// Example usage: -// node top-level-dir.mjs /home/user/project '.' => '' -// node top-level-dir.mjs /home/user/project /home/user/project/hello/there => hello -// node top-level-dir.mjs /home/user/project ./hello/there => hello -import { relative, sep } from 'path' - -function topLevelDir(root, pathname) { - const relativeInvocationDir = relative(root, pathname) - const topDir = relativeInvocationDir.split(sep)[0] - return topDir -} - -const dir = topLevelDir(process.argv[2], process.argv[3]) -console.log(dir) diff --git a/scripts/workspaces.mjs b/scripts/workspaces.mjs deleted file mode 100755 index 3878efb44..000000000 --- a/scripts/workspaces.mjs +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env node -import { readFileSync } from 'fs' - -function workspacesFromPackage(packageJsonPath) { - const contents = JSON.parse(readFileSync(packageJsonPath)) - return contents.workspaces.packages -} - -const workspaces = workspacesFromPackage(process.argv[2]) -console.log(workspaces.join(' ')) diff --git a/streams/esbuild.mjs b/streams/esbuild.mjs index a929c990b..4235dcc61 100755 --- a/streams/esbuild.mjs +++ b/streams/esbuild.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { existsSync, mkdirSync } from 'fs' -import { join } from 'path' +import { existsSync, mkdirSync } from 'node:fs' +import { join } from 'node:path' import { buildSync } from 'esbuild' diff --git a/streams/package.json b/streams/package.json index 25b584aab..c229cdd44 100644 --- a/streams/package.json +++ b/streams/package.json @@ -16,11 +16,9 @@ "publishConfig": { "registry": "https://registry.npmjs.org/" }, - "types": "./dist/src/index.browser.d.ts", "main": "./dist/node.cjs", "module": "./dist/node.mjs", "exports": { - "types": "./dist/src/index.browser.d.ts", "browser": { "types": "./dist/src/index.browser.d.ts", "require": "./dist/browser.cjs", diff --git a/streams/src/components/replayer/index.ts b/streams/src/components/replayer/index.ts index bb5eb9122..2764fcc8f 100644 --- a/streams/src/components/replayer/index.ts +++ b/streams/src/components/replayer/index.ts @@ -1,5 +1,5 @@ -import { readFileSync } from 'fs' -import { join } from 'path' +import { readFileSync } from 'node:fs' +import { join } from 'node:path' import { Readable, Writable } from 'stream' diff --git a/streams/src/components/tcp/index.ts b/streams/src/components/tcp/index.ts index c0bebd440..50ee3e1a9 100644 --- a/streams/src/components/tcp/index.ts +++ b/streams/src/components/tcp/index.ts @@ -1,4 +1,4 @@ -import { connect, Socket } from 'net' +import { connect, Socket } from 'node:net' import { Readable, Writable } from 'stream' diff --git a/tools/CHANGELOG.md b/tools/CHANGELOG.md new file mode 100644 index 000000000..ef6a6dc94 --- /dev/null +++ b/tools/CHANGELOG.md @@ -0,0 +1,5 @@ +## [1.1.2](https://github.com/axteams-software/zeno-web/compare/8441e498d6933194cd16e038ca4007e050e053a0..41509cf37d3555a8bedc327765ff785a7bb82fac) (2023-07-10T08:52:40.748Z) + +### ๐Ÿ› Bug fixes + +- use correct registry (#1950) ([41509cf37](https://github.com/axteams-software/zeno-web/commit/41509cf37d3555a8bedc327765ff785a7bb82fac)) diff --git a/tools/package.json b/tools/package.json new file mode 100644 index 000000000..96fb5dda3 --- /dev/null +++ b/tools/package.json @@ -0,0 +1,12 @@ +{ + "name": "tools", + "private": true, + "type": "module", + "bin": { + "changelog": "src/__generated__/changelog.mjs", + "tcp-ws-proxy": "src/__generated__/tcp-ws-proxy.mjs" + }, + "dependencies": { + "cmd-ts": "0.13.0" + } +} diff --git a/tools/src/__generated__/changelog.mjs b/tools/src/__generated__/changelog.mjs new file mode 100755 index 000000000..65967b4da --- /dev/null +++ b/tools/src/__generated__/changelog.mjs @@ -0,0 +1,201 @@ +#!/usr/bin/env node + +// src/changelog/cli.ts +import fs from 'node:fs' +import { + binary, + command, + option, + optional, + positional, + run, + string, +} from 'cmd-ts' + +// src/changelog/git.ts +import { execSync } from 'node:child_process' +function sanitizeGitHubUrl(url) { + if (new URL(url).hostname !== 'github.com') { + throw new Error('not implemented: only GitHub repositories are supported') + } + return url.replace(/\.git$/, '') +} +var compareUrl = (url, range) => `${sanitizeGitHubUrl(url)}/compare/${range}` +var commitUrl = (url, commit) => `${sanitizeGitHubUrl(url)}/commit/${commit}` +function gitLogFromRange(range) { + try { + const logOut = execSync( + `git log --no-merges --date-order --format="%H%x09%s" ${range}` + ) + const lines = logOut.toString().trim().split('\n') + const shaMessagePairs = lines.map((line) => line.split(' ')) + return shaMessagePairs + } catch (err) { + console.warn( + `git log failed on range ${range}, one of those tags probably does not exist` + ) + return [] + } +} +function shortSha(sha) { + return execSync(`git log -1 --format=%h ${sha}`).toString().trim() +} + +// src/changelog/changeset.ts +var GroupTitles = { + 'build': '\u{1F477} Build', + 'chore': '\u{1F6A7} Maintenance', + 'ci': '\u{1F6A6} Continous integration', + 'docs': '\u{1F4DD} Documentation', + 'feat': '\u2728 Features', + 'fix': '\u{1F41B} Bug fixes', + 'perf': '\u{1F3CE}\uFE0F Performance', + 'refactor': '\u267B\uFE0F Refactoring', + 'revert': '\u23EA\uFE0F Reverts', + 'style': '\u{1F484} Styling', + 'test': '\u{1F9EA} Test', +} +var GroupKeys = new Set(Object.keys(GroupTitles)) +function changeset({ + date, + name, + range, + scope, + url, +}) { + return [ + changesetHeader({ date, name, range, url }), + changesetBody({ range, scope, url }), + ].join('') +} +function changesetHeader({ date, name, range, url }) { + if (url !== void 0) { + return `## [${name}](${compareUrl(url, range)}) (${date}) +` + } + return `## ${name} (${date}) +` +} +function changesetBody({ + range, + scope, + url, +}) { + const outputChunks = [] + const groups = {} + for (const [sha, msg] of gitLogFromRange(range)) { + const cc = parseConventionalCommitMessage(msg) + if (scope !== void 0 && cc.scope !== scope) { + continue + } + if (groups[cc.group] === void 0) { + groups[cc.group] = [] + } + groups[cc.group].push([sha, cc]) + } + for (const group of GroupKeys) { + if (groups[group] === void 0) { + continue + } + outputChunks.push(` +### ${GroupTitles[group]} + +`) + for (const [sha, cc] of groups[group]) { + const scopePrefix = scope === void 0 && cc.scope !== void 0 + ? ` **${cc.scope}**:` + : '' + const breakingPrefix = cc.breaking ? ` **BREAKING**` : '' + const link = url !== void 0 + ? `([${shortSha(sha)}](${commitUrl(url, sha)}))` + : `(${shortSha(sha)})` + outputChunks.push( + ` -${scopePrefix}${breakingPrefix} ${cc.title} ${link} +` + ) + } + } + return outputChunks.join('') +} +function parseConventionalCommitMessage(msg) { + try { + const match = msg.match(/^([^:!(]+)(?:\(([^)]+)\))?(!)?: (.*)$/) + if (match === null) { + throw new Error('no matches found') + } + const [_, group, scope, breaking, title] = match + return { + group, + scope, + breaking: breaking !== void 0, + title, + } + } catch { + process.stderr.write(`invalid conventional commit message: ${msg} +`) + } + return { + group: 'chore', + breaking: false, + title: msg, + } +} + +// src/changelog/cli.ts +var changesetCli = command({ + name: 'changelog', + description: `Generate a changelog for a range of commits.`, + args: { + name: positional({ + type: string, + description: 'name for the changes (used in header)', + displayName: 'name', + }), + range: positional({ + type: string, + description: 'range of the changes (git revision range)', + displayName: 'range', + }), + date: option({ + description: 'date of the changeset', + long: 'date', + short: 'd', + defaultValue: () => (/* @__PURE__ */ new Date()).toISOString(), + type: string, + }), + outfile: option({ + description: 'output file to write to', + long: 'outfile', + short: 'o', + type: optional(string), + }), + scope: option({ + description: + 'Only include conventional commits that match "...(): ..."', + long: 'scope', + short: 's', + type: optional(string), + }), + url: option({ + description: 'GitHub URL of the repository', + long: 'url', + short: 'u', + type: optional(string), + }), + }, + handler(args) { + const contents = changeset({ + date: args.date, + name: args.name, + range: args.range, + scope: args.scope, + url: args.url, + }) + if (args.outfile !== void 0) { + fs.writeFileSync(args.outfile, contents) + } else { + process.stdout.write(contents) + } + }, +}) +await run(binary(changesetCli), process.argv) diff --git a/tools/src/__generated__/tcp-ws-proxy.mjs b/tools/src/__generated__/tcp-ws-proxy.mjs new file mode 100755 index 000000000..01b11ab47 --- /dev/null +++ b/tools/src/__generated__/tcp-ws-proxy.mjs @@ -0,0 +1,10661 @@ +#!/usr/bin/env node +var __create = Object.create +var __defProp = Object.defineProperty +var __getOwnPropDesc = Object.getOwnPropertyDescriptor +var __getOwnPropNames = Object.getOwnPropertyNames +var __getProtoOf = Object.getPrototypeOf +var __hasOwnProp = Object.prototype.hasOwnProperty +var __require = /* @__PURE__ */ ((x) => + typeof require !== 'undefined' + ? require + : typeof Proxy !== 'undefined' + ? new Proxy(x, { + get: (a, b) => (typeof require !== 'undefined' ? require : a)[b], + }) + : x)(function(x) { + if (typeof require !== 'undefined') { + return require.apply(this, arguments) + } + throw new Error('Dynamic require of "' + x + '" is not supported') + }) +var __commonJS = (cb, mod) => + function __require3() { + return mod + || (0, cb[__getOwnPropNames(cb)[0]])( + (mod = { exports: {} }).exports, + mod + ), + mod.exports + } +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === 'object' || typeof from === 'function') { + for (let key of __getOwnPropNames(from)) { + if (!__hasOwnProp.call(to, key) && key !== except) { + __defProp(to, key, { + get: () => from[key], + enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable, + }) + } + } + } + return to +} +var __toESM = ( + mod, + isNodeMode, + target +) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, + __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule + ? __defProp(target, 'default', { value: mod, enumerable: true }) + : target, + mod + )) + +// ../node_modules/ws/lib/stream.js +var require_stream = __commonJS({ + '../node_modules/ws/lib/stream.js'(exports, module) { + 'use strict' + var { Duplex } = __require('stream') + function emitClose(stream) { + stream.emit('close') + } + function duplexOnEnd() { + if (!this.destroyed && this._writableState.finished) { + this.destroy() + } + } + function duplexOnError(err) { + this.removeListener('error', duplexOnError) + this.destroy() + if (this.listenerCount('error') === 0) { + this.emit('error', err) + } + } + function createWebSocketStream2(ws, options) { + let terminateOnDestroy = true + const duplex = new Duplex({ + ...options, + autoDestroy: false, + emitClose: false, + objectMode: false, + writableObjectMode: false, + }) + ws.on('message', function message(msg, isBinary) { + const data = !isBinary && duplex._readableState.objectMode + ? msg.toString() + : msg + if (!duplex.push(data)) { + ws.pause() + } + }) + ws.once('error', function error(err) { + if (duplex.destroyed) { + return + } + terminateOnDestroy = false + duplex.destroy(err) + }) + ws.once('close', function close() { + if (duplex.destroyed) { + return + } + duplex.push(null) + }) + duplex._destroy = function(err, callback) { + if (ws.readyState === ws.CLOSED) { + callback(err) + process.nextTick(emitClose, duplex) + return + } + let called = false + ws.once('error', function error(err2) { + called = true + callback(err2) + }) + ws.once('close', function close() { + if (!called) { + callback(err) + } + process.nextTick(emitClose, duplex) + }) + if (terminateOnDestroy) { + ws.terminate() + } + } + duplex._final = function(callback) { + if (ws.readyState === ws.CONNECTING) { + ws.once('open', function open() { + duplex._final(callback) + }) + return + } + if (ws._socket === null) { + return + } + if (ws._socket._writableState.finished) { + callback() + if (duplex._readableState.endEmitted) { + duplex.destroy() + } + } else { + ws._socket.once('finish', function finish() { + callback() + }) + ws.close() + } + } + duplex._read = function() { + if (ws.isPaused) { + ws.resume() + } + } + duplex._write = function(chunk, encoding, callback) { + if (ws.readyState === ws.CONNECTING) { + ws.once('open', function open() { + duplex._write(chunk, encoding, callback) + }) + return + } + ws.send(chunk, callback) + } + duplex.on('end', duplexOnEnd) + duplex.on('error', duplexOnError) + return duplex + } + module.exports = createWebSocketStream2 + }, +}) + +// ../node_modules/ws/lib/constants.js +var require_constants = __commonJS({ + '../node_modules/ws/lib/constants.js'(exports, module) { + 'use strict' + module.exports = { + BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'], + EMPTY_BUFFER: Buffer.alloc(0), + GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + kForOnEventAttribute: Symbol('kIsForOnEventAttribute'), + kListener: Symbol('kListener'), + kStatusCode: Symbol('status-code'), + kWebSocket: Symbol('websocket'), + NOOP: () => { + }, + } + }, +}) + +// ../node_modules/ws/lib/buffer-util.js +var require_buffer_util = __commonJS({ + '../node_modules/ws/lib/buffer-util.js'(exports, module) { + 'use strict' + var { EMPTY_BUFFER } = require_constants() + var FastBuffer = Buffer[Symbol.species] + function concat(list, totalLength) { + if (list.length === 0) { + return EMPTY_BUFFER + } + if (list.length === 1) { + return list[0] + } + const target = Buffer.allocUnsafe(totalLength) + let offset = 0 + for (let i = 0; i < list.length; i++) { + const buf = list[i] + target.set(buf, offset) + offset += buf.length + } + if (offset < totalLength) { + return new FastBuffer(target.buffer, target.byteOffset, offset) + } + return target + } + function _mask(source, mask, output, offset, length) { + for (let i = 0; i < length; i++) { + output[offset + i] = source[i] ^ mask[i & 3] + } + } + function _unmask(buffer, mask) { + for (let i = 0; i < buffer.length; i++) { + buffer[i] ^= mask[i & 3] + } + } + function toArrayBuffer(buf) { + if (buf.length === buf.buffer.byteLength) { + return buf.buffer + } + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length) + } + function toBuffer(data) { + toBuffer.readOnly = true + if (Buffer.isBuffer(data)) { + return data + } + let buf + if (data instanceof ArrayBuffer) { + buf = new FastBuffer(data) + } else if (ArrayBuffer.isView(data)) { + buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength) + } else { + buf = Buffer.from(data) + toBuffer.readOnly = false + } + return buf + } + module.exports = { + concat, + mask: _mask, + toArrayBuffer, + toBuffer, + unmask: _unmask, + } + if (!process.env.WS_NO_BUFFER_UTIL) { + try { + const bufferUtil = __require('bufferutil') + module.exports.mask = function(source, mask, output, offset, length) { + if (length < 48) { + _mask(source, mask, output, offset, length) + } else { + bufferUtil.mask(source, mask, output, offset, length) + } + } + module.exports.unmask = function(buffer, mask) { + if (buffer.length < 32) { + _unmask(buffer, mask) + } else { + bufferUtil.unmask(buffer, mask) + } + } + } catch (e) { + } + } + }, +}) + +// ../node_modules/ws/lib/limiter.js +var require_limiter = __commonJS({ + '../node_modules/ws/lib/limiter.js'(exports, module) { + 'use strict' + var kDone = Symbol('kDone') + var kRun = Symbol('kRun') + var Limiter = class { + /** + * Creates a new `Limiter`. + * + * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed + * to run concurrently + */ + constructor(concurrency) { + this[kDone] = () => { + this.pending-- + this[kRun]() + } + this.concurrency = concurrency || Infinity + this.jobs = [] + this.pending = 0 + } + /** + * Adds a job to the queue. + * + * @param {Function} job The job to run + * @public + */ + add(job) { + this.jobs.push(job) + this[kRun]() + } + /** + * Removes a job from the queue and runs it if possible. + * + * @private + */ + [kRun]() { + if (this.pending === this.concurrency) { + return + } + if (this.jobs.length) { + const job = this.jobs.shift() + this.pending++ + job(this[kDone]) + } + } + } + module.exports = Limiter + }, +}) + +// ../node_modules/ws/lib/permessage-deflate.js +var require_permessage_deflate = __commonJS({ + '../node_modules/ws/lib/permessage-deflate.js'(exports, module) { + 'use strict' + var zlib = __require('zlib') + var bufferUtil = require_buffer_util() + var Limiter = require_limiter() + var { kStatusCode } = require_constants() + var FastBuffer = Buffer[Symbol.species] + var TRAILER = Buffer.from([0, 0, 255, 255]) + var kPerMessageDeflate = Symbol('permessage-deflate') + var kTotalLength = Symbol('total-length') + var kCallback = Symbol('callback') + var kBuffers = Symbol('buffers') + var kError = Symbol('error') + var zlibLimiter + var PerMessageDeflate = class { + /** + * Creates a PerMessageDeflate instance. + * + * @param {Object} [options] Configuration options + * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support + * for, or request, a custom client window size + * @param {Boolean} [options.clientNoContextTakeover=false] Advertise/ + * acknowledge disabling of client context takeover + * @param {Number} [options.concurrencyLimit=10] The number of concurrent + * calls to zlib + * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the + * use of a custom server window size + * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept + * disabling of server context takeover + * @param {Number} [options.threshold=1024] Size (in bytes) below which + * messages should not be compressed if context takeover is disabled + * @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on + * deflate + * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on + * inflate + * @param {Boolean} [isServer=false] Create the instance in either server or + * client mode + * @param {Number} [maxPayload=0] The maximum allowed message length + */ + constructor(options, isServer, maxPayload) { + this._maxPayload = maxPayload | 0 + this._options = options || {} + this._threshold = this._options.threshold !== void 0 + ? this._options.threshold + : 1024 + this._isServer = !!isServer + this._deflate = null + this._inflate = null + this.params = null + if (!zlibLimiter) { + const concurrency = this._options.concurrencyLimit !== void 0 + ? this._options.concurrencyLimit + : 10 + zlibLimiter = new Limiter(concurrency) + } + } + /** + * @type {String} + */ + static get extensionName() { + return 'permessage-deflate' + } + /** + * Create an extension negotiation offer. + * + * @return {Object} Extension parameters + * @public + */ + offer() { + const params = {} + if (this._options.serverNoContextTakeover) { + params.server_no_context_takeover = true + } + if (this._options.clientNoContextTakeover) { + params.client_no_context_takeover = true + } + if (this._options.serverMaxWindowBits) { + params.server_max_window_bits = this._options.serverMaxWindowBits + } + if (this._options.clientMaxWindowBits) { + params.client_max_window_bits = this._options.clientMaxWindowBits + } else if (this._options.clientMaxWindowBits == null) { + params.client_max_window_bits = true + } + return params + } + /** + * Accept an extension negotiation offer/response. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Object} Accepted configuration + * @public + */ + accept(configurations) { + configurations = this.normalizeParams(configurations) + this.params = this._isServer + ? this.acceptAsServer(configurations) + : this.acceptAsClient(configurations) + return this.params + } + /** + * Releases all resources used by the extension. + * + * @public + */ + cleanup() { + if (this._inflate) { + this._inflate.close() + this._inflate = null + } + if (this._deflate) { + const callback = this._deflate[kCallback] + this._deflate.close() + this._deflate = null + if (callback) { + callback( + new Error( + 'The deflate stream was closed while data was being processed' + ) + ) + } + } + } + /** + * Accept an extension negotiation offer. + * + * @param {Array} offers The extension negotiation offers + * @return {Object} Accepted configuration + * @private + */ + acceptAsServer(offers) { + const opts = this._options + const accepted = offers.find((params) => { + if ( + opts.serverNoContextTakeover === false + && params.server_no_context_takeover + || params.server_max_window_bits + && (opts.serverMaxWindowBits === false + || typeof opts.serverMaxWindowBits === 'number' + && opts.serverMaxWindowBits > params.server_max_window_bits) + || typeof opts.clientMaxWindowBits === 'number' + && !params.client_max_window_bits + ) { + return false + } + return true + }) + if (!accepted) { + throw new Error('None of the extension offers can be accepted') + } + if (opts.serverNoContextTakeover) { + accepted.server_no_context_takeover = true + } + if (opts.clientNoContextTakeover) { + accepted.client_no_context_takeover = true + } + if (typeof opts.serverMaxWindowBits === 'number') { + accepted.server_max_window_bits = opts.serverMaxWindowBits + } + if (typeof opts.clientMaxWindowBits === 'number') { + accepted.client_max_window_bits = opts.clientMaxWindowBits + } else if ( + accepted.client_max_window_bits === true + || opts.clientMaxWindowBits === false + ) { + delete accepted.client_max_window_bits + } + return accepted + } + /** + * Accept the extension negotiation response. + * + * @param {Array} response The extension negotiation response + * @return {Object} Accepted configuration + * @private + */ + acceptAsClient(response) { + const params = response[0] + if ( + this._options.clientNoContextTakeover === false + && params.client_no_context_takeover + ) { + throw new Error('Unexpected parameter "client_no_context_takeover"') + } + if (!params.client_max_window_bits) { + if (typeof this._options.clientMaxWindowBits === 'number') { + params.client_max_window_bits = this._options.clientMaxWindowBits + } + } else if ( + this._options.clientMaxWindowBits === false + || typeof this._options.clientMaxWindowBits === 'number' + && params.client_max_window_bits > this._options.clientMaxWindowBits + ) { + throw new Error( + 'Unexpected or invalid parameter "client_max_window_bits"' + ) + } + return params + } + /** + * Normalize parameters. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Array} The offers/response with normalized parameters + * @private + */ + normalizeParams(configurations) { + configurations.forEach((params) => { + Object.keys(params).forEach((key) => { + let value = params[key] + if (value.length > 1) { + throw new Error( + `Parameter "${key}" must have only a single value` + ) + } + value = value[0] + if (key === 'client_max_window_bits') { + if (value !== true) { + const num = +value + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ) + } + value = num + } else if (!this._isServer) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ) + } + } else if (key === 'server_max_window_bits') { + const num = +value + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ) + } + value = num + } else if ( + key === 'client_no_context_takeover' + || key === 'server_no_context_takeover' + ) { + if (value !== true) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ) + } + } else { + throw new Error(`Unknown parameter "${key}"`) + } + params[key] = value + }) + }) + return configurations + } + /** + * Decompress data. Concurrency limited. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + decompress(data, fin, callback) { + zlibLimiter.add((done) => { + this._decompress(data, fin, (err, result) => { + done() + callback(err, result) + }) + }) + } + /** + * Compress data. Concurrency limited. + * + * @param {(Buffer|String)} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + compress(data, fin, callback) { + zlibLimiter.add((done) => { + this._compress(data, fin, (err, result) => { + done() + callback(err, result) + }) + }) + } + /** + * Decompress data. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _decompress(data, fin, callback) { + const endpoint = this._isServer ? 'client' : 'server' + if (!this._inflate) { + const key = `${endpoint}_max_window_bits` + const windowBits = typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key] + this._inflate = zlib.createInflateRaw({ + ...this._options.zlibInflateOptions, + windowBits, + }) + this._inflate[kPerMessageDeflate] = this + this._inflate[kTotalLength] = 0 + this._inflate[kBuffers] = [] + this._inflate.on('error', inflateOnError) + this._inflate.on('data', inflateOnData) + } + this._inflate[kCallback] = callback + this._inflate.write(data) + if (fin) { + this._inflate.write(TRAILER) + } + this._inflate.flush(() => { + const err = this._inflate[kError] + if (err) { + this._inflate.close() + this._inflate = null + callback(err) + return + } + const data2 = bufferUtil.concat( + this._inflate[kBuffers], + this._inflate[kTotalLength] + ) + if (this._inflate._readableState.endEmitted) { + this._inflate.close() + this._inflate = null + } else { + this._inflate[kTotalLength] = 0 + this._inflate[kBuffers] = [] + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._inflate.reset() + } + } + callback(null, data2) + }) + } + /** + * Compress data. + * + * @param {(Buffer|String)} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _compress(data, fin, callback) { + const endpoint = this._isServer ? 'server' : 'client' + if (!this._deflate) { + const key = `${endpoint}_max_window_bits` + const windowBits = typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key] + this._deflate = zlib.createDeflateRaw({ + ...this._options.zlibDeflateOptions, + windowBits, + }) + this._deflate[kTotalLength] = 0 + this._deflate[kBuffers] = [] + this._deflate.on('data', deflateOnData) + } + this._deflate[kCallback] = callback + this._deflate.write(data) + this._deflate.flush(zlib.Z_SYNC_FLUSH, () => { + if (!this._deflate) { + return + } + let data2 = bufferUtil.concat( + this._deflate[kBuffers], + this._deflate[kTotalLength] + ) + if (fin) { + data2 = new FastBuffer( + data2.buffer, + data2.byteOffset, + data2.length - 4 + ) + } + this._deflate[kCallback] = null + this._deflate[kTotalLength] = 0 + this._deflate[kBuffers] = [] + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._deflate.reset() + } + callback(null, data2) + }) + } + } + module.exports = PerMessageDeflate + function deflateOnData(chunk) { + this[kBuffers].push(chunk) + this[kTotalLength] += chunk.length + } + function inflateOnData(chunk) { + this[kTotalLength] += chunk.length + if ( + this[kPerMessageDeflate]._maxPayload < 1 + || this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload + ) { + this[kBuffers].push(chunk) + return + } + this[kError] = new RangeError('Max payload size exceeded') + this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' + this[kError][kStatusCode] = 1009 + this.removeListener('data', inflateOnData) + this.reset() + } + function inflateOnError(err) { + this[kPerMessageDeflate]._inflate = null + err[kStatusCode] = 1007 + this[kCallback](err) + } + }, +}) + +// ../node_modules/ws/lib/validation.js +var require_validation = __commonJS({ + '../node_modules/ws/lib/validation.js'(exports, module) { + 'use strict' + var { isUtf8 } = __require('buffer') + var tokenChars = [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + // 0 - 15 + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + // 16 - 31 + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + // 32 - 47 + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + // 48 - 63 + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + // 64 - 79 + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + // 80 - 95 + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + // 96 - 111 + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + // 112 - 127 + ] + function isValidStatusCode(code) { + return code >= 1e3 && code <= 1014 && code !== 1004 && code !== 1005 + && code !== 1006 || code >= 3e3 && code <= 4999 + } + function _isValidUTF8(buf) { + const len = buf.length + let i = 0 + while (i < len) { + if ((buf[i] & 128) === 0) { + i++ + } else if ((buf[i] & 224) === 192) { + if ( + i + 1 === len || (buf[i + 1] & 192) !== 128 + || (buf[i] & 254) === 192 + ) { + return false + } + i += 2 + } else if ((buf[i] & 240) === 224) { + if ( + i + 2 >= len || (buf[i + 1] & 192) !== 128 + || (buf[i + 2] & 192) !== 128 + || buf[i] === 224 && (buf[i + 1] & 224) === 128 // Overlong + || buf[i] === 237 && (buf[i + 1] & 224) === 160 + ) { + return false + } + i += 3 + } else if ((buf[i] & 248) === 240) { + if ( + i + 3 >= len || (buf[i + 1] & 192) !== 128 + || (buf[i + 2] & 192) !== 128 || (buf[i + 3] & 192) !== 128 + || buf[i] === 240 && (buf[i + 1] & 240) === 128 // Overlong + || buf[i] === 244 && buf[i + 1] > 143 || buf[i] > 244 + ) { + return false + } + i += 4 + } else { + return false + } + } + return true + } + module.exports = { + isValidStatusCode, + isValidUTF8: _isValidUTF8, + tokenChars, + } + if (isUtf8) { + module.exports.isValidUTF8 = function(buf) { + return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf) + } + } else if (!process.env.WS_NO_UTF_8_VALIDATE) { + try { + const isValidUTF8 = __require('utf-8-validate') + module.exports.isValidUTF8 = function(buf) { + return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf) + } + } catch (e) { + } + } + }, +}) + +// ../node_modules/ws/lib/receiver.js +var require_receiver = __commonJS({ + '../node_modules/ws/lib/receiver.js'(exports, module) { + 'use strict' + var { Writable: Writable6 } = __require('stream') + var PerMessageDeflate = require_permessage_deflate() + var { + BINARY_TYPES, + EMPTY_BUFFER, + kStatusCode, + kWebSocket, + } = require_constants() + var { concat, toArrayBuffer, unmask } = require_buffer_util() + var { isValidStatusCode, isValidUTF8 } = require_validation() + var FastBuffer = Buffer[Symbol.species] + var promise = Promise.resolve() + var queueTask = typeof queueMicrotask === 'function' + ? queueMicrotask + : queueMicrotaskShim + var GET_INFO = 0 + var GET_PAYLOAD_LENGTH_16 = 1 + var GET_PAYLOAD_LENGTH_64 = 2 + var GET_MASK = 3 + var GET_DATA = 4 + var INFLATING = 5 + var WAIT_MICROTASK = 6 + var Receiver2 = class extends Writable6 { + /** + * Creates a Receiver instance. + * + * @param {Object} [options] Options object + * @param {String} [options.binaryType=nodebuffer] The type for binary data + * @param {Object} [options.extensions] An object containing the negotiated + * extensions + * @param {Boolean} [options.isServer=false] Specifies whether to operate in + * client or server mode + * @param {Number} [options.maxPayload=0] The maximum allowed message length + * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or + * not to skip UTF-8 validation for text and close messages + */ + constructor(options = {}) { + super() + this._binaryType = options.binaryType || BINARY_TYPES[0] + this._extensions = options.extensions || {} + this._isServer = !!options.isServer + this._maxPayload = options.maxPayload | 0 + this._skipUTF8Validation = !!options.skipUTF8Validation + this[kWebSocket] = void 0 + this._bufferedBytes = 0 + this._buffers = [] + this._compressed = false + this._payloadLength = 0 + this._mask = void 0 + this._fragmented = 0 + this._masked = false + this._fin = false + this._opcode = 0 + this._totalPayloadLength = 0 + this._messageLength = 0 + this._fragments = [] + this._state = GET_INFO + this._loop = false + } + /** + * Implements `Writable.prototype._write()`. + * + * @param {Buffer} chunk The chunk of data to write + * @param {String} encoding The character encoding of `chunk` + * @param {Function} cb Callback + * @private + */ + _write(chunk, encoding, cb) { + if (this._opcode === 8 && this._state == GET_INFO) { + return cb() + } + this._bufferedBytes += chunk.length + this._buffers.push(chunk) + this.startLoop(cb) + } + /** + * Consumes `n` bytes from the buffered data. + * + * @param {Number} n The number of bytes to consume + * @return {Buffer} The consumed bytes + * @private + */ + consume(n) { + this._bufferedBytes -= n + if (n === this._buffers[0].length) { + return this._buffers.shift() + } + if (n < this._buffers[0].length) { + const buf = this._buffers[0] + this._buffers[0] = new FastBuffer( + buf.buffer, + buf.byteOffset + n, + buf.length - n + ) + return new FastBuffer(buf.buffer, buf.byteOffset, n) + } + const dst = Buffer.allocUnsafe(n) + do { + const buf = this._buffers[0] + const offset = dst.length - n + if (n >= buf.length) { + dst.set(this._buffers.shift(), offset) + } else { + dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset) + this._buffers[0] = new FastBuffer( + buf.buffer, + buf.byteOffset + n, + buf.length - n + ) + } + n -= buf.length + } while (n > 0) + return dst + } + /** + * Starts the parsing loop. + * + * @param {Function} cb Callback + * @private + */ + startLoop(cb) { + let err + this._loop = true + do { + switch (this._state) { + case GET_INFO: + err = this.getInfo() + break + case GET_PAYLOAD_LENGTH_16: + err = this.getPayloadLength16() + break + case GET_PAYLOAD_LENGTH_64: + err = this.getPayloadLength64() + break + case GET_MASK: + this.getMask() + break + case GET_DATA: + err = this.getData(cb) + break + case INFLATING: + this._loop = false + return + default: + this._loop = false + queueTask(() => { + this._state = GET_INFO + this.startLoop(cb) + }) + return + } + } while (this._loop) + cb(err) + } + /** + * Reads the first two bytes of a frame. + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getInfo() { + if (this._bufferedBytes < 2) { + this._loop = false + return + } + const buf = this.consume(2) + if ((buf[0] & 48) !== 0) { + this._loop = false + return error( + RangeError, + 'RSV2 and RSV3 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_2_3' + ) + } + const compressed = (buf[0] & 64) === 64 + if (compressed && !this._extensions[PerMessageDeflate.extensionName]) { + this._loop = false + return error( + RangeError, + 'RSV1 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_1' + ) + } + this._fin = (buf[0] & 128) === 128 + this._opcode = buf[0] & 15 + this._payloadLength = buf[1] & 127 + if (this._opcode === 0) { + if (compressed) { + this._loop = false + return error( + RangeError, + 'RSV1 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_1' + ) + } + if (!this._fragmented) { + this._loop = false + return error( + RangeError, + 'invalid opcode 0', + true, + 1002, + 'WS_ERR_INVALID_OPCODE' + ) + } + this._opcode = this._fragmented + } else if (this._opcode === 1 || this._opcode === 2) { + if (this._fragmented) { + this._loop = false + return error( + RangeError, + `invalid opcode ${this._opcode}`, + true, + 1002, + 'WS_ERR_INVALID_OPCODE' + ) + } + this._compressed = compressed + } else if (this._opcode > 7 && this._opcode < 11) { + if (!this._fin) { + this._loop = false + return error( + RangeError, + 'FIN must be set', + true, + 1002, + 'WS_ERR_EXPECTED_FIN' + ) + } + if (compressed) { + this._loop = false + return error( + RangeError, + 'RSV1 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_1' + ) + } + if ( + this._payloadLength > 125 + || this._opcode === 8 && this._payloadLength === 1 + ) { + this._loop = false + return error( + RangeError, + `invalid payload length ${this._payloadLength}`, + true, + 1002, + 'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH' + ) + } + } else { + this._loop = false + return error( + RangeError, + `invalid opcode ${this._opcode}`, + true, + 1002, + 'WS_ERR_INVALID_OPCODE' + ) + } + if (!this._fin && !this._fragmented) { + this._fragmented = this._opcode + } + this._masked = (buf[1] & 128) === 128 + if (this._isServer) { + if (!this._masked) { + this._loop = false + return error( + RangeError, + 'MASK must be set', + true, + 1002, + 'WS_ERR_EXPECTED_MASK' + ) + } + } else if (this._masked) { + this._loop = false + return error( + RangeError, + 'MASK must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_MASK' + ) + } + if (this._payloadLength === 126) { + this._state = GET_PAYLOAD_LENGTH_16 + } else if (this._payloadLength === 127) { + this._state = GET_PAYLOAD_LENGTH_64 + } else { + return this.haveLength() + } + } + /** + * Gets extended payload length (7+16). + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getPayloadLength16() { + if (this._bufferedBytes < 2) { + this._loop = false + return + } + this._payloadLength = this.consume(2).readUInt16BE(0) + return this.haveLength() + } + /** + * Gets extended payload length (7+64). + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getPayloadLength64() { + if (this._bufferedBytes < 8) { + this._loop = false + return + } + const buf = this.consume(8) + const num = buf.readUInt32BE(0) + if (num > Math.pow(2, 53 - 32) - 1) { + this._loop = false + return error( + RangeError, + 'Unsupported WebSocket frame: payload length > 2^53 - 1', + false, + 1009, + 'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH' + ) + } + this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4) + return this.haveLength() + } + /** + * Payload length has been read. + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + haveLength() { + if (this._payloadLength && this._opcode < 8) { + this._totalPayloadLength += this._payloadLength + if ( + this._totalPayloadLength > this._maxPayload && this._maxPayload > 0 + ) { + this._loop = false + return error( + RangeError, + 'Max payload size exceeded', + false, + 1009, + 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' + ) + } + } + if (this._masked) { + this._state = GET_MASK + } else { + this._state = GET_DATA + } + } + /** + * Reads mask bytes. + * + * @private + */ + getMask() { + if (this._bufferedBytes < 4) { + this._loop = false + return + } + this._mask = this.consume(4) + this._state = GET_DATA + } + /** + * Reads data bytes. + * + * @param {Function} cb Callback + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + getData(cb) { + let data = EMPTY_BUFFER + if (this._payloadLength) { + if (this._bufferedBytes < this._payloadLength) { + this._loop = false + return + } + data = this.consume(this._payloadLength) + if ( + this._masked + && (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) + !== 0 + ) { + unmask(data, this._mask) + } + } + if (this._opcode > 7) { + return this.controlMessage(data) + } + if (this._compressed) { + this._state = INFLATING + this.decompress(data, cb) + return + } + if (data.length) { + this._messageLength = this._totalPayloadLength + this._fragments.push(data) + } + return this.dataMessage() + } + /** + * Decompresses data. + * + * @param {Buffer} data Compressed data + * @param {Function} cb Callback + * @private + */ + decompress(data, cb) { + const perMessageDeflate = + this._extensions[PerMessageDeflate.extensionName] + perMessageDeflate.decompress(data, this._fin, (err, buf) => { + if (err) { + return cb(err) + } + if (buf.length) { + this._messageLength += buf.length + if ( + this._messageLength > this._maxPayload && this._maxPayload > 0 + ) { + return cb( + error( + RangeError, + 'Max payload size exceeded', + false, + 1009, + 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' + ) + ) + } + this._fragments.push(buf) + } + const er = this.dataMessage() + if (er) { + return cb(er) + } + this.startLoop(cb) + }) + } + /** + * Handles a data message. + * + * @return {(Error|undefined)} A possible error + * @private + */ + dataMessage() { + if (this._fin) { + const messageLength = this._messageLength + const fragments = this._fragments + this._totalPayloadLength = 0 + this._messageLength = 0 + this._fragmented = 0 + this._fragments = [] + if (this._opcode === 2) { + let data + if (this._binaryType === 'nodebuffer') { + data = concat(fragments, messageLength) + } else if (this._binaryType === 'arraybuffer') { + data = toArrayBuffer(concat(fragments, messageLength)) + } else { + data = fragments + } + this.emit('message', data, true) + } else { + const buf = concat(fragments, messageLength) + if (!this._skipUTF8Validation && !isValidUTF8(buf)) { + this._loop = false + return error( + Error, + 'invalid UTF-8 sequence', + true, + 1007, + 'WS_ERR_INVALID_UTF8' + ) + } + this.emit('message', buf, false) + } + } + this._state = WAIT_MICROTASK + } + /** + * Handles a control message. + * + * @param {Buffer} data Data to handle + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + controlMessage(data) { + if (this._opcode === 8) { + this._loop = false + if (data.length === 0) { + this.emit('conclude', 1005, EMPTY_BUFFER) + this.end() + this._state = GET_INFO + } else { + const code = data.readUInt16BE(0) + if (!isValidStatusCode(code)) { + return error( + RangeError, + `invalid status code ${code}`, + true, + 1002, + 'WS_ERR_INVALID_CLOSE_CODE' + ) + } + const buf = new FastBuffer( + data.buffer, + data.byteOffset + 2, + data.length - 2 + ) + if (!this._skipUTF8Validation && !isValidUTF8(buf)) { + return error( + Error, + 'invalid UTF-8 sequence', + true, + 1007, + 'WS_ERR_INVALID_UTF8' + ) + } + this.emit('conclude', code, buf) + this.end() + this._state = GET_INFO + } + } else if (this._opcode === 9) { + this.emit('ping', data) + this._state = WAIT_MICROTASK + } else { + this.emit('pong', data) + this._state = WAIT_MICROTASK + } + } + } + module.exports = Receiver2 + function error(ErrorCtor, message, prefix, statusCode2, errorCode) { + const err = new ErrorCtor( + prefix ? `Invalid WebSocket frame: ${message}` : message + ) + Error.captureStackTrace(err, error) + err.code = errorCode + err[kStatusCode] = statusCode2 + return err + } + function queueMicrotaskShim(cb) { + promise.then(cb).catch(throwErrorNextTick) + } + function throwError(err) { + throw err + } + function throwErrorNextTick(err) { + process.nextTick(throwError, err) + } + }, +}) + +// ../node_modules/ws/lib/sender.js +var require_sender = __commonJS({ + '../node_modules/ws/lib/sender.js'(exports, module) { + 'use strict' + var { Duplex } = __require('stream') + var { randomFillSync } = __require('crypto') + var PerMessageDeflate = require_permessage_deflate() + var { EMPTY_BUFFER } = require_constants() + var { isValidStatusCode } = require_validation() + var { mask: applyMask, toBuffer } = require_buffer_util() + var kByteLength = Symbol('kByteLength') + var maskBuffer = Buffer.alloc(4) + var Sender2 = class { + /** + * Creates a Sender instance. + * + * @param {Duplex} socket The connection socket + * @param {Object} [extensions] An object containing the negotiated extensions + * @param {Function} [generateMask] The function used to generate the masking + * key + */ + constructor(socket, extensions, generateMask) { + this._extensions = extensions || {} + if (generateMask) { + this._generateMask = generateMask + this._maskBuffer = Buffer.alloc(4) + } + this._socket = socket + this._firstFragment = true + this._compress = false + this._bufferedBytes = 0 + this._deflating = false + this._queue = [] + } + /** + * Frames a piece of data according to the HyBi WebSocket protocol. + * + * @param {(Buffer|String)} data The data to frame + * @param {Object} options Options object + * @param {Boolean} [options.fin=false] Specifies whether or not to set the + * FIN bit + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Boolean} [options.mask=false] Specifies whether or not to mask + * `data` + * @param {Buffer} [options.maskBuffer] The buffer used to store the masking + * key + * @param {Number} options.opcode The opcode + * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be + * modified + * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the + * RSV1 bit + * @return {(Buffer|String)[]} The framed data + * @public + */ + static frame(data, options) { + let mask + let merge2 = false + let offset = 2 + let skipMasking = false + if (options.mask) { + mask = options.maskBuffer || maskBuffer + if (options.generateMask) { + options.generateMask(mask) + } else { + randomFillSync(mask, 0, 4) + } + skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0 + offset = 6 + } + let dataLength + if (typeof data === 'string') { + if ( + (!options.mask || skipMasking) && options[kByteLength] !== void 0 + ) { + dataLength = options[kByteLength] + } else { + data = Buffer.from(data) + dataLength = data.length + } + } else { + dataLength = data.length + merge2 = options.mask && options.readOnly && !skipMasking + } + let payloadLength = dataLength + if (dataLength >= 65536) { + offset += 8 + payloadLength = 127 + } else if (dataLength > 125) { + offset += 2 + payloadLength = 126 + } + const target = Buffer.allocUnsafe(merge2 ? dataLength + offset : offset) + target[0] = options.fin ? options.opcode | 128 : options.opcode + if (options.rsv1) { + target[0] |= 64 + } + target[1] = payloadLength + if (payloadLength === 126) { + target.writeUInt16BE(dataLength, 2) + } else if (payloadLength === 127) { + target[2] = target[3] = 0 + target.writeUIntBE(dataLength, 4, 6) + } + if (!options.mask) { + return [target, data] + } + target[1] |= 128 + target[offset - 4] = mask[0] + target[offset - 3] = mask[1] + target[offset - 2] = mask[2] + target[offset - 1] = mask[3] + if (skipMasking) { + return [target, data] + } + if (merge2) { + applyMask(data, mask, target, offset, dataLength) + return [target] + } + applyMask(data, mask, data, 0, dataLength) + return [target, data] + } + /** + * Sends a close message to the other peer. + * + * @param {Number} [code] The status code component of the body + * @param {(String|Buffer)} [data] The message component of the body + * @param {Boolean} [mask=false] Specifies whether or not to mask the message + * @param {Function} [cb] Callback + * @public + */ + close(code, data, mask, cb) { + let buf + if (code === void 0) { + buf = EMPTY_BUFFER + } else if (typeof code !== 'number' || !isValidStatusCode(code)) { + throw new TypeError( + 'First argument must be a valid error code number' + ) + } else if (data === void 0 || !data.length) { + buf = Buffer.allocUnsafe(2) + buf.writeUInt16BE(code, 0) + } else { + const length = Buffer.byteLength(data) + if (length > 123) { + throw new RangeError( + 'The message must not be greater than 123 bytes' + ) + } + buf = Buffer.allocUnsafe(2 + length) + buf.writeUInt16BE(code, 0) + if (typeof data === 'string') { + buf.write(data, 2) + } else { + buf.set(data, 2) + } + } + const options = { + [kByteLength]: buf.length, + fin: true, + generateMask: this._generateMask, + mask, + maskBuffer: this._maskBuffer, + opcode: 8, + readOnly: false, + rsv1: false, + } + if (this._deflating) { + this.enqueue([this.dispatch, buf, false, options, cb]) + } else { + this.sendFrame(Sender2.frame(buf, options), cb) + } + } + /** + * Sends a ping message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} [mask=false] Specifies whether or not to mask `data` + * @param {Function} [cb] Callback + * @public + */ + ping(data, mask, cb) { + let byteLength + let readOnly + if (typeof data === 'string') { + byteLength = Buffer.byteLength(data) + readOnly = false + } else { + data = toBuffer(data) + byteLength = data.length + readOnly = toBuffer.readOnly + } + if (byteLength > 125) { + throw new RangeError( + 'The data size must not be greater than 125 bytes' + ) + } + const options = { + [kByteLength]: byteLength, + fin: true, + generateMask: this._generateMask, + mask, + maskBuffer: this._maskBuffer, + opcode: 9, + readOnly, + rsv1: false, + } + if (this._deflating) { + this.enqueue([this.dispatch, data, false, options, cb]) + } else { + this.sendFrame(Sender2.frame(data, options), cb) + } + } + /** + * Sends a pong message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} [mask=false] Specifies whether or not to mask `data` + * @param {Function} [cb] Callback + * @public + */ + pong(data, mask, cb) { + let byteLength + let readOnly + if (typeof data === 'string') { + byteLength = Buffer.byteLength(data) + readOnly = false + } else { + data = toBuffer(data) + byteLength = data.length + readOnly = toBuffer.readOnly + } + if (byteLength > 125) { + throw new RangeError( + 'The data size must not be greater than 125 bytes' + ) + } + const options = { + [kByteLength]: byteLength, + fin: true, + generateMask: this._generateMask, + mask, + maskBuffer: this._maskBuffer, + opcode: 10, + readOnly, + rsv1: false, + } + if (this._deflating) { + this.enqueue([this.dispatch, data, false, options, cb]) + } else { + this.sendFrame(Sender2.frame(data, options), cb) + } + } + /** + * Sends a data message to the other peer. + * + * @param {*} data The message to send + * @param {Object} options Options object + * @param {Boolean} [options.binary=false] Specifies whether `data` is binary + * or text + * @param {Boolean} [options.compress=false] Specifies whether or not to + * compress `data` + * @param {Boolean} [options.fin=false] Specifies whether the fragment is the + * last one + * @param {Boolean} [options.mask=false] Specifies whether or not to mask + * `data` + * @param {Function} [cb] Callback + * @public + */ + send(data, options, cb) { + const perMessageDeflate = + this._extensions[PerMessageDeflate.extensionName] + let opcode = options.binary ? 2 : 1 + let rsv1 = options.compress + let byteLength + let readOnly + if (typeof data === 'string') { + byteLength = Buffer.byteLength(data) + readOnly = false + } else { + data = toBuffer(data) + byteLength = data.length + readOnly = toBuffer.readOnly + } + if (this._firstFragment) { + this._firstFragment = false + if ( + rsv1 && perMessageDeflate + && perMessageDeflate.params[ + perMessageDeflate._isServer + ? 'server_no_context_takeover' + : 'client_no_context_takeover' + ] + ) { + rsv1 = byteLength >= perMessageDeflate._threshold + } + this._compress = rsv1 + } else { + rsv1 = false + opcode = 0 + } + if (options.fin) { + this._firstFragment = true + } + if (perMessageDeflate) { + const opts = { + [kByteLength]: byteLength, + fin: options.fin, + generateMask: this._generateMask, + mask: options.mask, + maskBuffer: this._maskBuffer, + opcode, + readOnly, + rsv1, + } + if (this._deflating) { + this.enqueue([this.dispatch, data, this._compress, opts, cb]) + } else { + this.dispatch(data, this._compress, opts, cb) + } + } else { + this.sendFrame( + Sender2.frame(data, { + [kByteLength]: byteLength, + fin: options.fin, + generateMask: this._generateMask, + mask: options.mask, + maskBuffer: this._maskBuffer, + opcode, + readOnly, + rsv1: false, + }), + cb + ) + } + } + /** + * Dispatches a message. + * + * @param {(Buffer|String)} data The message to send + * @param {Boolean} [compress=false] Specifies whether or not to compress + * `data` + * @param {Object} options Options object + * @param {Boolean} [options.fin=false] Specifies whether or not to set the + * FIN bit + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Boolean} [options.mask=false] Specifies whether or not to mask + * `data` + * @param {Buffer} [options.maskBuffer] The buffer used to store the masking + * key + * @param {Number} options.opcode The opcode + * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be + * modified + * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the + * RSV1 bit + * @param {Function} [cb] Callback + * @private + */ + dispatch(data, compress, options, cb) { + if (!compress) { + this.sendFrame(Sender2.frame(data, options), cb) + return + } + const perMessageDeflate = + this._extensions[PerMessageDeflate.extensionName] + this._bufferedBytes += options[kByteLength] + this._deflating = true + perMessageDeflate.compress(data, options.fin, (_, buf) => { + if (this._socket.destroyed) { + const err = new Error( + 'The socket was closed while data was being compressed' + ) + if (typeof cb === 'function') { + cb(err) + } + for (let i = 0; i < this._queue.length; i++) { + const params = this._queue[i] + const callback = params[params.length - 1] + if (typeof callback === 'function') { + callback(err) + } + } + return + } + this._bufferedBytes -= options[kByteLength] + this._deflating = false + options.readOnly = false + this.sendFrame(Sender2.frame(buf, options), cb) + this.dequeue() + }) + } + /** + * Executes queued send operations. + * + * @private + */ + dequeue() { + while (!this._deflating && this._queue.length) { + const params = this._queue.shift() + this._bufferedBytes -= params[3][kByteLength] + Reflect.apply(params[0], this, params.slice(1)) + } + } + /** + * Enqueues a send operation. + * + * @param {Array} params Send operation parameters. + * @private + */ + enqueue(params) { + this._bufferedBytes += params[3][kByteLength] + this._queue.push(params) + } + /** + * Sends a frame. + * + * @param {Buffer[]} list The frame to send + * @param {Function} [cb] Callback + * @private + */ + sendFrame(list, cb) { + if (list.length === 2) { + this._socket.cork() + this._socket.write(list[0]) + this._socket.write(list[1], cb) + this._socket.uncork() + } else { + this._socket.write(list[0], cb) + } + } + } + module.exports = Sender2 + }, +}) + +// ../node_modules/ws/lib/event-target.js +var require_event_target = __commonJS({ + '../node_modules/ws/lib/event-target.js'(exports, module) { + 'use strict' + var { kForOnEventAttribute, kListener } = require_constants() + var kCode = Symbol('kCode') + var kData = Symbol('kData') + var kError = Symbol('kError') + var kMessage = Symbol('kMessage') + var kReason = Symbol('kReason') + var kTarget = Symbol('kTarget') + var kType = Symbol('kType') + var kWasClean = Symbol('kWasClean') + var Event = class { + /** + * Create a new `Event`. + * + * @param {String} type The name of the event + * @throws {TypeError} If the `type` argument is not specified + */ + constructor(type) { + this[kTarget] = null + this[kType] = type + } + /** + * @type {*} + */ + get target() { + return this[kTarget] + } + /** + * @type {String} + */ + get type() { + return this[kType] + } + } + Object.defineProperty(Event.prototype, 'target', { enumerable: true }) + Object.defineProperty(Event.prototype, 'type', { enumerable: true }) + var CloseEvent = class extends Event { + /** + * Create a new `CloseEvent`. + * + * @param {String} type The name of the event + * @param {Object} [options] A dictionary object that allows for setting + * attributes via object members of the same name + * @param {Number} [options.code=0] The status code explaining why the + * connection was closed + * @param {String} [options.reason=''] A human-readable string explaining why + * the connection was closed + * @param {Boolean} [options.wasClean=false] Indicates whether or not the + * connection was cleanly closed + */ + constructor(type, options = {}) { + super(type) + this[kCode] = options.code === void 0 ? 0 : options.code + this[kReason] = options.reason === void 0 ? '' : options.reason + this[kWasClean] = options.wasClean === void 0 ? false : options.wasClean + } + /** + * @type {Number} + */ + get code() { + return this[kCode] + } + /** + * @type {String} + */ + get reason() { + return this[kReason] + } + /** + * @type {Boolean} + */ + get wasClean() { + return this[kWasClean] + } + } + Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true }) + Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true }) + Object.defineProperty(CloseEvent.prototype, 'wasClean', { + enumerable: true, + }) + var ErrorEvent = class extends Event { + /** + * Create a new `ErrorEvent`. + * + * @param {String} type The name of the event + * @param {Object} [options] A dictionary object that allows for setting + * attributes via object members of the same name + * @param {*} [options.error=null] The error that generated this event + * @param {String} [options.message=''] The error message + */ + constructor(type, options = {}) { + super(type) + this[kError] = options.error === void 0 ? null : options.error + this[kMessage] = options.message === void 0 ? '' : options.message + } + /** + * @type {*} + */ + get error() { + return this[kError] + } + /** + * @type {String} + */ + get message() { + return this[kMessage] + } + } + Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true }) + Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true }) + var MessageEvent = class extends Event { + /** + * Create a new `MessageEvent`. + * + * @param {String} type The name of the event + * @param {Object} [options] A dictionary object that allows for setting + * attributes via object members of the same name + * @param {*} [options.data=null] The message content + */ + constructor(type, options = {}) { + super(type) + this[kData] = options.data === void 0 ? null : options.data + } + /** + * @type {*} + */ + get data() { + return this[kData] + } + } + Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true }) + var EventTarget = { + /** + * Register an event listener. + * + * @param {String} type A string representing the event type to listen for + * @param {(Function|Object)} handler The listener to add + * @param {Object} [options] An options object specifies characteristics about + * the event listener + * @param {Boolean} [options.once=false] A `Boolean` indicating that the + * listener should be invoked at most once after being added. If `true`, + * the listener would be automatically removed when invoked. + * @public + */ + addEventListener(type, handler, options = {}) { + for (const listener of this.listeners(type)) { + if ( + !options[kForOnEventAttribute] && listener[kListener] === handler + && !listener[kForOnEventAttribute] + ) { + return + } + } + let wrapper + if (type === 'message') { + wrapper = function onMessage(data, isBinary) { + const event = new MessageEvent('message', { + data: isBinary ? data : data.toString(), + }) + event[kTarget] = this + callListener(handler, this, event) + } + } else if (type === 'close') { + wrapper = function onClose(code, message) { + const event = new CloseEvent('close', { + code, + reason: message.toString(), + wasClean: this._closeFrameReceived && this._closeFrameSent, + }) + event[kTarget] = this + callListener(handler, this, event) + } + } else if (type === 'error') { + wrapper = function onError(error) { + const event = new ErrorEvent('error', { + error, + message: error.message, + }) + event[kTarget] = this + callListener(handler, this, event) + } + } else if (type === 'open') { + wrapper = function onOpen() { + const event = new Event('open') + event[kTarget] = this + callListener(handler, this, event) + } + } else { + return + } + wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute] + wrapper[kListener] = handler + if (options.once) { + this.once(type, wrapper) + } else { + this.on(type, wrapper) + } + }, + /** + * Remove an event listener. + * + * @param {String} type A string representing the event type to remove + * @param {(Function|Object)} handler The listener to remove + * @public + */ + removeEventListener(type, handler) { + for (const listener of this.listeners(type)) { + if ( + listener[kListener] === handler && !listener[kForOnEventAttribute] + ) { + this.removeListener(type, listener) + break + } + } + }, + } + module.exports = { + CloseEvent, + ErrorEvent, + Event, + EventTarget, + MessageEvent, + } + function callListener(listener, thisArg, event) { + if (typeof listener === 'object' && listener.handleEvent) { + listener.handleEvent.call(listener, event) + } else { + listener.call(thisArg, event) + } + } + }, +}) + +// ../node_modules/ws/lib/extension.js +var require_extension = __commonJS({ + '../node_modules/ws/lib/extension.js'(exports, module) { + 'use strict' + var { tokenChars } = require_validation() + function push(dest, name, elem) { + if (dest[name] === void 0) { + dest[name] = [elem] + } else { + dest[name].push(elem) + } + } + function parse3(header) { + const offers = /* @__PURE__ */ Object.create(null) + let params = /* @__PURE__ */ Object.create(null) + let mustUnescape = false + let isEscaping = false + let inQuotes = false + let extensionName + let paramName + let start = -1 + let code = -1 + let end = -1 + let i = 0 + for (; i < header.length; i++) { + code = header.charCodeAt(i) + if (extensionName === void 0) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) { + start = i + } + } else if (i !== 0 && (code === 32 || code === 9)) { + if (end === -1 && start !== -1) { + end = i + } + } else if (code === 59 || code === 44) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`) + } + if (end === -1) { + end = i + } + const name = header.slice(start, end) + if (code === 44) { + push(offers, name, params) + params = /* @__PURE__ */ Object.create(null) + } else { + extensionName = name + } + start = end = -1 + } else { + throw new SyntaxError(`Unexpected character at index ${i}`) + } + } else if (paramName === void 0) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) { + start = i + } + } else if (code === 32 || code === 9) { + if (end === -1 && start !== -1) { + end = i + } + } else if (code === 59 || code === 44) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`) + } + if (end === -1) { + end = i + } + push(params, header.slice(start, end), true) + if (code === 44) { + push(offers, extensionName, params) + params = /* @__PURE__ */ Object.create(null) + extensionName = void 0 + } + start = end = -1 + } else if (code === 61 && start !== -1 && end === -1) { + paramName = header.slice(start, i) + start = end = -1 + } else { + throw new SyntaxError(`Unexpected character at index ${i}`) + } + } else { + if (isEscaping) { + if (tokenChars[code] !== 1) { + throw new SyntaxError(`Unexpected character at index ${i}`) + } + if (start === -1) { + start = i + } else if (!mustUnescape) { + mustUnescape = true + } + isEscaping = false + } else if (inQuotes) { + if (tokenChars[code] === 1) { + if (start === -1) { + start = i + } + } else if (code === 34 && start !== -1) { + inQuotes = false + end = i + } else if (code === 92) { + isEscaping = true + } else { + throw new SyntaxError(`Unexpected character at index ${i}`) + } + } else if (code === 34 && header.charCodeAt(i - 1) === 61) { + inQuotes = true + } else if (end === -1 && tokenChars[code] === 1) { + if (start === -1) { + start = i + } + } else if (start !== -1 && (code === 32 || code === 9)) { + if (end === -1) { + end = i + } + } else if (code === 59 || code === 44) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`) + } + if (end === -1) { + end = i + } + let value = header.slice(start, end) + if (mustUnescape) { + value = value.replace(/\\/g, '') + mustUnescape = false + } + push(params, paramName, value) + if (code === 44) { + push(offers, extensionName, params) + params = /* @__PURE__ */ Object.create(null) + extensionName = void 0 + } + paramName = void 0 + start = end = -1 + } else { + throw new SyntaxError(`Unexpected character at index ${i}`) + } + } + } + if (start === -1 || inQuotes || code === 32 || code === 9) { + throw new SyntaxError('Unexpected end of input') + } + if (end === -1) { + end = i + } + const token = header.slice(start, end) + if (extensionName === void 0) { + push(offers, token, params) + } else { + if (paramName === void 0) { + push(params, token, true) + } else if (mustUnescape) { + push(params, paramName, token.replace(/\\/g, '')) + } else { + push(params, paramName, token) + } + push(offers, extensionName, params) + } + return offers + } + function format(extensions) { + return Object.keys(extensions).map((extension2) => { + let configurations = extensions[extension2] + if (!Array.isArray(configurations)) { + configurations = [configurations] + } + return configurations.map((params) => { + return [extension2].concat( + Object.keys(params).map((k) => { + let values = params[k] + if (!Array.isArray(values)) { + values = [values] + } + return values.map((v) => v === true ? k : `${k}=${v}`).join('; ') + }) + ).join('; ') + }).join(', ') + }).join(', ') + } + module.exports = { format, parse: parse3 } + }, +}) + +// ../node_modules/ws/lib/websocket.js +var require_websocket = __commonJS({ + '../node_modules/ws/lib/websocket.js'(exports, module) { + 'use strict' + var EventEmitter = __require('events') + var https = __require('https') + var http = __require('http') + var net = __require('net') + var tls = __require('tls') + var { randomBytes, createHash } = __require('crypto') + var { Duplex, Readable: Readable6 } = __require('stream') + var { URL: URL2 } = __require('url') + var PerMessageDeflate = require_permessage_deflate() + var Receiver2 = require_receiver() + var Sender2 = require_sender() + var { + BINARY_TYPES, + EMPTY_BUFFER, + GUID, + kForOnEventAttribute, + kListener, + kStatusCode, + kWebSocket, + NOOP, + } = require_constants() + var { + EventTarget: { addEventListener, removeEventListener }, + } = require_event_target() + var { format, parse: parse3 } = require_extension() + var { toBuffer } = require_buffer_util() + var closeTimeout = 30 * 1e3 + var kAborted = Symbol('kAborted') + var protocolVersions = [8, 13] + var readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'] + var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/ + var WebSocket2 = class extends EventEmitter { + /** + * Create a new `WebSocket`. + * + * @param {(String|URL)} address The URL to which to connect + * @param {(String|String[])} [protocols] The subprotocols + * @param {Object} [options] Connection options + */ + constructor(address, protocols, options) { + super() + this._binaryType = BINARY_TYPES[0] + this._closeCode = 1006 + this._closeFrameReceived = false + this._closeFrameSent = false + this._closeMessage = EMPTY_BUFFER + this._closeTimer = null + this._extensions = {} + this._paused = false + this._protocol = '' + this._readyState = WebSocket2.CONNECTING + this._receiver = null + this._sender = null + this._socket = null + if (address !== null) { + this._bufferedAmount = 0 + this._isServer = false + this._redirects = 0 + if (protocols === void 0) { + protocols = [] + } else if (!Array.isArray(protocols)) { + if (typeof protocols === 'object' && protocols !== null) { + options = protocols + protocols = [] + } else { + protocols = [protocols] + } + } + initAsClient(this, address, protocols, options) + } else { + this._isServer = true + } + } + /** + * This deviates from the WHATWG interface since ws doesn't support the + * required default "blob" type (instead we define a custom "nodebuffer" + * type). + * + * @type {String} + */ + get binaryType() { + return this._binaryType + } + set binaryType(type) { + if (!BINARY_TYPES.includes(type)) { + return + } + this._binaryType = type + if (this._receiver) { + this._receiver._binaryType = type + } + } + /** + * @type {Number} + */ + get bufferedAmount() { + if (!this._socket) { + return this._bufferedAmount + } + return this._socket._writableState.length + this._sender._bufferedBytes + } + /** + * @type {String} + */ + get extensions() { + return Object.keys(this._extensions).join() + } + /** + * @type {Boolean} + */ + get isPaused() { + return this._paused + } + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onclose() { + return null + } + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onerror() { + return null + } + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onopen() { + return null + } + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onmessage() { + return null + } + /** + * @type {String} + */ + get protocol() { + return this._protocol + } + /** + * @type {Number} + */ + get readyState() { + return this._readyState + } + /** + * @type {String} + */ + get url() { + return this._url + } + /** + * Set up the socket and the internal resources. + * + * @param {Duplex} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Object} options Options object + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Number} [options.maxPayload=0] The maximum allowed message size + * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or + * not to skip UTF-8 validation for text and close messages + * @private + */ + setSocket(socket, head, options) { + const receiver = new Receiver2({ + binaryType: this.binaryType, + extensions: this._extensions, + isServer: this._isServer, + maxPayload: options.maxPayload, + skipUTF8Validation: options.skipUTF8Validation, + }) + this._sender = new Sender2( + socket, + this._extensions, + options.generateMask + ) + this._receiver = receiver + this._socket = socket + receiver[kWebSocket] = this + socket[kWebSocket] = this + receiver.on('conclude', receiverOnConclude) + receiver.on('drain', receiverOnDrain) + receiver.on('error', receiverOnError) + receiver.on('message', receiverOnMessage) + receiver.on('ping', receiverOnPing) + receiver.on('pong', receiverOnPong) + if (socket.setTimeout) { + socket.setTimeout(0) + } + if (socket.setNoDelay) { + socket.setNoDelay() + } + if (head.length > 0) { + socket.unshift(head) + } + socket.on('close', socketOnClose) + socket.on('data', socketOnData) + socket.on('end', socketOnEnd) + socket.on('error', socketOnError) + this._readyState = WebSocket2.OPEN + this.emit('open') + } + /** + * Emit the `'close'` event. + * + * @private + */ + emitClose() { + if (!this._socket) { + this._readyState = WebSocket2.CLOSED + this.emit('close', this._closeCode, this._closeMessage) + return + } + if (this._extensions[PerMessageDeflate.extensionName]) { + this._extensions[PerMessageDeflate.extensionName].cleanup() + } + this._receiver.removeAllListeners() + this._readyState = WebSocket2.CLOSED + this.emit('close', this._closeCode, this._closeMessage) + } + /** + * Start a closing handshake. + * + * +----------+ +-----------+ +----------+ + * - - -|ws.close()|-->|close frame|-->|ws.close()|- - - + * | +----------+ +-----------+ +----------+ | + * +----------+ +-----------+ | + * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING + * +----------+ +-----------+ | + * | | | +---+ | + * +------------------------+-->|fin| - - - - + * | +---+ | +---+ + * - - - - -|fin|<---------------------+ + * +---+ + * + * @param {Number} [code] Status code explaining why the connection is closing + * @param {(String|Buffer)} [data] The reason why the connection is + * closing + * @public + */ + close(code, data) { + if (this.readyState === WebSocket2.CLOSED) { + return + } + if (this.readyState === WebSocket2.CONNECTING) { + const msg = + 'WebSocket was closed before the connection was established' + abortHandshake(this, this._req, msg) + return + } + if (this.readyState === WebSocket2.CLOSING) { + if ( + this._closeFrameSent + && (this._closeFrameReceived + || this._receiver._writableState.errorEmitted) + ) { + this._socket.end() + } + return + } + this._readyState = WebSocket2.CLOSING + this._sender.close(code, data, !this._isServer, (err) => { + if (err) { + return + } + this._closeFrameSent = true + if ( + this._closeFrameReceived + || this._receiver._writableState.errorEmitted + ) { + this._socket.end() + } + }) + this._closeTimer = setTimeout( + this._socket.destroy.bind(this._socket), + closeTimeout + ) + } + /** + * Pause the socket. + * + * @public + */ + pause() { + if ( + this.readyState === WebSocket2.CONNECTING + || this.readyState === WebSocket2.CLOSED + ) { + return + } + this._paused = true + this._socket.pause() + } + /** + * Send a ping. + * + * @param {*} [data] The data to send + * @param {Boolean} [mask] Indicates whether or not to mask `data` + * @param {Function} [cb] Callback which is executed when the ping is sent + * @public + */ + ping(data, mask, cb) { + if (this.readyState === WebSocket2.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)') + } + if (typeof data === 'function') { + cb = data + data = mask = void 0 + } else if (typeof mask === 'function') { + cb = mask + mask = void 0 + } + if (typeof data === 'number') { + data = data.toString() + } + if (this.readyState !== WebSocket2.OPEN) { + sendAfterClose(this, data, cb) + return + } + if (mask === void 0) { + mask = !this._isServer + } + this._sender.ping(data || EMPTY_BUFFER, mask, cb) + } + /** + * Send a pong. + * + * @param {*} [data] The data to send + * @param {Boolean} [mask] Indicates whether or not to mask `data` + * @param {Function} [cb] Callback which is executed when the pong is sent + * @public + */ + pong(data, mask, cb) { + if (this.readyState === WebSocket2.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)') + } + if (typeof data === 'function') { + cb = data + data = mask = void 0 + } else if (typeof mask === 'function') { + cb = mask + mask = void 0 + } + if (typeof data === 'number') { + data = data.toString() + } + if (this.readyState !== WebSocket2.OPEN) { + sendAfterClose(this, data, cb) + return + } + if (mask === void 0) { + mask = !this._isServer + } + this._sender.pong(data || EMPTY_BUFFER, mask, cb) + } + /** + * Resume the socket. + * + * @public + */ + resume() { + if ( + this.readyState === WebSocket2.CONNECTING + || this.readyState === WebSocket2.CLOSED + ) { + return + } + this._paused = false + if (!this._receiver._writableState.needDrain) { + this._socket.resume() + } + } + /** + * Send a data message. + * + * @param {*} data The message to send + * @param {Object} [options] Options object + * @param {Boolean} [options.binary] Specifies whether `data` is binary or + * text + * @param {Boolean} [options.compress] Specifies whether or not to compress + * `data` + * @param {Boolean} [options.fin=true] Specifies whether the fragment is the + * last one + * @param {Boolean} [options.mask] Specifies whether or not to mask `data` + * @param {Function} [cb] Callback which is executed when data is written out + * @public + */ + send(data, options, cb) { + if (this.readyState === WebSocket2.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)') + } + if (typeof options === 'function') { + cb = options + options = {} + } + if (typeof data === 'number') { + data = data.toString() + } + if (this.readyState !== WebSocket2.OPEN) { + sendAfterClose(this, data, cb) + return + } + const opts = { + binary: typeof data !== 'string', + mask: !this._isServer, + compress: true, + fin: true, + ...options, + } + if (!this._extensions[PerMessageDeflate.extensionName]) { + opts.compress = false + } + this._sender.send(data || EMPTY_BUFFER, opts, cb) + } + /** + * Forcibly close the connection. + * + * @public + */ + terminate() { + if (this.readyState === WebSocket2.CLOSED) { + return + } + if (this.readyState === WebSocket2.CONNECTING) { + const msg = + 'WebSocket was closed before the connection was established' + abortHandshake(this, this._req, msg) + return + } + if (this._socket) { + this._readyState = WebSocket2.CLOSING + this._socket.destroy() + } + } + } + Object.defineProperty(WebSocket2, 'CONNECTING', { + enumerable: true, + value: readyStates.indexOf('CONNECTING'), + }) + Object.defineProperty(WebSocket2.prototype, 'CONNECTING', { + enumerable: true, + value: readyStates.indexOf('CONNECTING'), + }) + Object.defineProperty(WebSocket2, 'OPEN', { + enumerable: true, + value: readyStates.indexOf('OPEN'), + }) + Object.defineProperty(WebSocket2.prototype, 'OPEN', { + enumerable: true, + value: readyStates.indexOf('OPEN'), + }) + Object.defineProperty(WebSocket2, 'CLOSING', { + enumerable: true, + value: readyStates.indexOf('CLOSING'), + }) + Object.defineProperty(WebSocket2.prototype, 'CLOSING', { + enumerable: true, + value: readyStates.indexOf('CLOSING'), + }) + Object.defineProperty(WebSocket2, 'CLOSED', { + enumerable: true, + value: readyStates.indexOf('CLOSED'), + }) + Object.defineProperty(WebSocket2.prototype, 'CLOSED', { + enumerable: true, + value: readyStates.indexOf('CLOSED'), + }) + ;[ + 'binaryType', + 'bufferedAmount', + 'extensions', + 'isPaused', + 'protocol', + 'readyState', + 'url', + ].forEach((property) => { + Object.defineProperty(WebSocket2.prototype, property, { + enumerable: true, + }) + }) + ;['open', 'error', 'close', 'message'].forEach((method) => { + Object.defineProperty(WebSocket2.prototype, `on${method}`, { + enumerable: true, + get() { + for (const listener of this.listeners(method)) { + if (listener[kForOnEventAttribute]) { + return listener[kListener] + } + } + return null + }, + set(handler) { + for (const listener of this.listeners(method)) { + if (listener[kForOnEventAttribute]) { + this.removeListener(method, listener) + break + } + } + if (typeof handler !== 'function') { + return + } + this.addEventListener(method, handler, { + [kForOnEventAttribute]: true, + }) + }, + }) + }) + WebSocket2.prototype.addEventListener = addEventListener + WebSocket2.prototype.removeEventListener = removeEventListener + module.exports = WebSocket2 + function initAsClient(websocket, address, protocols, options) { + const opts = { + protocolVersion: protocolVersions[1], + maxPayload: 100 * 1024 * 1024, + skipUTF8Validation: false, + perMessageDeflate: true, + followRedirects: false, + maxRedirects: 10, + ...options, + createConnection: void 0, + socketPath: void 0, + hostname: void 0, + protocol: void 0, + timeout: void 0, + method: 'GET', + host: void 0, + path: void 0, + port: void 0, + } + if (!protocolVersions.includes(opts.protocolVersion)) { + throw new RangeError( + `Unsupported protocol version: ${opts.protocolVersion} (supported versions: ${ + protocolVersions.join(', ') + })` + ) + } + let parsedUrl + if (address instanceof URL2) { + parsedUrl = address + } else { + try { + parsedUrl = new URL2(address) + } catch (e) { + throw new SyntaxError(`Invalid URL: ${address}`) + } + } + if (parsedUrl.protocol === 'http:') { + parsedUrl.protocol = 'ws:' + } else if (parsedUrl.protocol === 'https:') { + parsedUrl.protocol = 'wss:' + } + websocket._url = parsedUrl.href + const isSecure = parsedUrl.protocol === 'wss:' + const isIpcUrl = parsedUrl.protocol === 'ws+unix:' + let invalidUrlMessage + if (parsedUrl.protocol !== 'ws:' && !isSecure && !isIpcUrl) { + invalidUrlMessage = + `The URL's protocol must be one of "ws:", "wss:", "http:", "https", or "ws+unix:"` + } else if (isIpcUrl && !parsedUrl.pathname) { + invalidUrlMessage = "The URL's pathname is empty" + } else if (parsedUrl.hash) { + invalidUrlMessage = 'The URL contains a fragment identifier' + } + if (invalidUrlMessage) { + const err = new SyntaxError(invalidUrlMessage) + if (websocket._redirects === 0) { + throw err + } else { + emitErrorAndClose(websocket, err) + return + } + } + const defaultPort = isSecure ? 443 : 80 + const key = randomBytes(16).toString('base64') + const request = isSecure ? https.request : http.request + const protocolSet = /* @__PURE__ */ new Set() + let perMessageDeflate + opts.createConnection = isSecure ? tlsConnect : netConnect + opts.defaultPort = opts.defaultPort || defaultPort + opts.port = parsedUrl.port || defaultPort + opts.host = parsedUrl.hostname.startsWith('[') + ? parsedUrl.hostname.slice(1, -1) + : parsedUrl.hostname + opts.headers = { + ...opts.headers, + 'Sec-WebSocket-Version': opts.protocolVersion, + 'Sec-WebSocket-Key': key, + Connection: 'Upgrade', + Upgrade: 'websocket', + } + opts.path = parsedUrl.pathname + parsedUrl.search + opts.timeout = opts.handshakeTimeout + if (opts.perMessageDeflate) { + perMessageDeflate = new PerMessageDeflate( + opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, + false, + opts.maxPayload + ) + opts.headers['Sec-WebSocket-Extensions'] = format({ + [PerMessageDeflate.extensionName]: perMessageDeflate.offer(), + }) + } + if (protocols.length) { + for (const protocol of protocols) { + if ( + typeof protocol !== 'string' || !subprotocolRegex.test(protocol) + || protocolSet.has(protocol) + ) { + throw new SyntaxError( + 'An invalid or duplicated subprotocol was specified' + ) + } + protocolSet.add(protocol) + } + opts.headers['Sec-WebSocket-Protocol'] = protocols.join(',') + } + if (opts.origin) { + if (opts.protocolVersion < 13) { + opts.headers['Sec-WebSocket-Origin'] = opts.origin + } else { + opts.headers.Origin = opts.origin + } + } + if (parsedUrl.username || parsedUrl.password) { + opts.auth = `${parsedUrl.username}:${parsedUrl.password}` + } + if (isIpcUrl) { + const parts = opts.path.split(':') + opts.socketPath = parts[0] + opts.path = parts[1] + } + let req + if (opts.followRedirects) { + if (websocket._redirects === 0) { + websocket._originalIpc = isIpcUrl + websocket._originalSecure = isSecure + websocket._originalHostOrSocketPath = isIpcUrl + ? opts.socketPath + : parsedUrl.host + const headers = options && options.headers + options = { ...options, headers: {} } + if (headers) { + for (const [key2, value] of Object.entries(headers)) { + options.headers[key2.toLowerCase()] = value + } + } + } else if (websocket.listenerCount('redirect') === 0) { + const isSameHost = isIpcUrl + ? websocket._originalIpc + ? opts.socketPath === websocket._originalHostOrSocketPath + : false + : websocket._originalIpc + ? false + : parsedUrl.host === websocket._originalHostOrSocketPath + if (!isSameHost || websocket._originalSecure && !isSecure) { + delete opts.headers.authorization + delete opts.headers.cookie + if (!isSameHost) { + delete opts.headers.host + } + opts.auth = void 0 + } + } + if (opts.auth && !options.headers.authorization) { + options.headers.authorization = 'Basic ' + + Buffer.from(opts.auth).toString('base64') + } + req = websocket._req = request(opts) + if (websocket._redirects) { + websocket.emit('redirect', websocket.url, req) + } + } else { + req = websocket._req = request(opts) + } + if (opts.timeout) { + req.on('timeout', () => { + abortHandshake(websocket, req, 'Opening handshake has timed out') + }) + } + req.on('error', (err) => { + if (req === null || req[kAborted]) { + return + } + req = websocket._req = null + emitErrorAndClose(websocket, err) + }) + req.on('response', (res) => { + const location = res.headers.location + const statusCode2 = res.statusCode + if ( + location && opts.followRedirects && statusCode2 >= 300 + && statusCode2 < 400 + ) { + if (++websocket._redirects > opts.maxRedirects) { + abortHandshake(websocket, req, 'Maximum redirects exceeded') + return + } + req.abort() + let addr + try { + addr = new URL2(location, address) + } catch (e) { + const err = new SyntaxError(`Invalid URL: ${location}`) + emitErrorAndClose(websocket, err) + return + } + initAsClient(websocket, addr, protocols, options) + } else if (!websocket.emit('unexpected-response', req, res)) { + abortHandshake( + websocket, + req, + `Unexpected server response: ${res.statusCode}` + ) + } + }) + req.on('upgrade', (res, socket, head) => { + websocket.emit('upgrade', res) + if (websocket.readyState !== WebSocket2.CONNECTING) { + return + } + req = websocket._req = null + if (res.headers.upgrade.toLowerCase() !== 'websocket') { + abortHandshake(websocket, socket, 'Invalid Upgrade header') + return + } + const digest = createHash('sha1').update(key + GUID).digest('base64') + if (res.headers['sec-websocket-accept'] !== digest) { + abortHandshake( + websocket, + socket, + 'Invalid Sec-WebSocket-Accept header' + ) + return + } + const serverProt = res.headers['sec-websocket-protocol'] + let protError + if (serverProt !== void 0) { + if (!protocolSet.size) { + protError = 'Server sent a subprotocol but none was requested' + } else if (!protocolSet.has(serverProt)) { + protError = 'Server sent an invalid subprotocol' + } + } else if (protocolSet.size) { + protError = 'Server sent no subprotocol' + } + if (protError) { + abortHandshake(websocket, socket, protError) + return + } + if (serverProt) { + websocket._protocol = serverProt + } + const secWebSocketExtensions = res.headers['sec-websocket-extensions'] + if (secWebSocketExtensions !== void 0) { + if (!perMessageDeflate) { + const message = + 'Server sent a Sec-WebSocket-Extensions header but no extension was requested' + abortHandshake(websocket, socket, message) + return + } + let extensions + try { + extensions = parse3(secWebSocketExtensions) + } catch (err) { + const message = 'Invalid Sec-WebSocket-Extensions header' + abortHandshake(websocket, socket, message) + return + } + const extensionNames = Object.keys(extensions) + if ( + extensionNames.length !== 1 + || extensionNames[0] !== PerMessageDeflate.extensionName + ) { + const message = + 'Server indicated an extension that was not requested' + abortHandshake(websocket, socket, message) + return + } + try { + perMessageDeflate.accept( + extensions[PerMessageDeflate.extensionName] + ) + } catch (err) { + const message = 'Invalid Sec-WebSocket-Extensions header' + abortHandshake(websocket, socket, message) + return + } + websocket._extensions[PerMessageDeflate.extensionName] = + perMessageDeflate + } + websocket.setSocket(socket, head, { + generateMask: opts.generateMask, + maxPayload: opts.maxPayload, + skipUTF8Validation: opts.skipUTF8Validation, + }) + }) + if (opts.finishRequest) { + opts.finishRequest(req, websocket) + } else { + req.end() + } + } + function emitErrorAndClose(websocket, err) { + websocket._readyState = WebSocket2.CLOSING + websocket.emit('error', err) + websocket.emitClose() + } + function netConnect(options) { + options.path = options.socketPath + return net.connect(options) + } + function tlsConnect(options) { + options.path = void 0 + if (!options.servername && options.servername !== '') { + options.servername = net.isIP(options.host) ? '' : options.host + } + return tls.connect(options) + } + function abortHandshake(websocket, stream, message) { + websocket._readyState = WebSocket2.CLOSING + const err = new Error(message) + Error.captureStackTrace(err, abortHandshake) + if (stream.setHeader) { + stream[kAborted] = true + stream.abort() + if (stream.socket && !stream.socket.destroyed) { + stream.socket.destroy() + } + process.nextTick(emitErrorAndClose, websocket, err) + } else { + stream.destroy(err) + stream.once('error', websocket.emit.bind(websocket, 'error')) + stream.once('close', websocket.emitClose.bind(websocket)) + } + } + function sendAfterClose(websocket, data, cb) { + if (data) { + const length = toBuffer(data).length + if (websocket._socket) { + websocket._sender._bufferedBytes += length + } else { + websocket._bufferedAmount += length + } + } + if (cb) { + const err = new Error( + `WebSocket is not open: readyState ${websocket.readyState} (${ + readyStates[websocket.readyState] + })` + ) + process.nextTick(cb, err) + } + } + function receiverOnConclude(code, reason) { + const websocket = this[kWebSocket] + websocket._closeFrameReceived = true + websocket._closeMessage = reason + websocket._closeCode = code + if (websocket._socket[kWebSocket] === void 0) { + return + } + websocket._socket.removeListener('data', socketOnData) + process.nextTick(resume, websocket._socket) + if (code === 1005) { + websocket.close() + } else { + websocket.close(code, reason) + } + } + function receiverOnDrain() { + const websocket = this[kWebSocket] + if (!websocket.isPaused) { + websocket._socket.resume() + } + } + function receiverOnError(err) { + const websocket = this[kWebSocket] + if (websocket._socket[kWebSocket] !== void 0) { + websocket._socket.removeListener('data', socketOnData) + process.nextTick(resume, websocket._socket) + websocket.close(err[kStatusCode]) + } + websocket.emit('error', err) + } + function receiverOnFinish() { + this[kWebSocket].emitClose() + } + function receiverOnMessage(data, isBinary) { + this[kWebSocket].emit('message', data, isBinary) + } + function receiverOnPing(data) { + const websocket = this[kWebSocket] + websocket.pong(data, !websocket._isServer, NOOP) + websocket.emit('ping', data) + } + function receiverOnPong(data) { + this[kWebSocket].emit('pong', data) + } + function resume(stream) { + stream.resume() + } + function socketOnClose() { + const websocket = this[kWebSocket] + this.removeListener('close', socketOnClose) + this.removeListener('data', socketOnData) + this.removeListener('end', socketOnEnd) + websocket._readyState = WebSocket2.CLOSING + let chunk + if ( + !this._readableState.endEmitted && !websocket._closeFrameReceived + && !websocket._receiver._writableState.errorEmitted + && (chunk = websocket._socket.read()) !== null + ) { + websocket._receiver.write(chunk) + } + websocket._receiver.end() + this[kWebSocket] = void 0 + clearTimeout(websocket._closeTimer) + if ( + websocket._receiver._writableState.finished + || websocket._receiver._writableState.errorEmitted + ) { + websocket.emitClose() + } else { + websocket._receiver.on('error', receiverOnFinish) + websocket._receiver.on('finish', receiverOnFinish) + } + } + function socketOnData(chunk) { + if (!this[kWebSocket]._receiver.write(chunk)) { + this.pause() + } + } + function socketOnEnd() { + const websocket = this[kWebSocket] + websocket._readyState = WebSocket2.CLOSING + websocket._receiver.end() + this.end() + } + function socketOnError() { + const websocket = this[kWebSocket] + this.removeListener('error', socketOnError) + this.on('error', NOOP) + if (websocket) { + websocket._readyState = WebSocket2.CLOSING + this.destroy() + } + } + }, +}) + +// ../node_modules/ws/lib/subprotocol.js +var require_subprotocol = __commonJS({ + '../node_modules/ws/lib/subprotocol.js'(exports, module) { + 'use strict' + var { tokenChars } = require_validation() + function parse3(header) { + const protocols = /* @__PURE__ */ new Set() + let start = -1 + let end = -1 + let i = 0 + for (i; i < header.length; i++) { + const code = header.charCodeAt(i) + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) { + start = i + } + } else if (i !== 0 && (code === 32 || code === 9)) { + if (end === -1 && start !== -1) { + end = i + } + } else if (code === 44) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`) + } + if (end === -1) { + end = i + } + const protocol2 = header.slice(start, end) + if (protocols.has(protocol2)) { + throw new SyntaxError( + `The "${protocol2}" subprotocol is duplicated` + ) + } + protocols.add(protocol2) + start = end = -1 + } else { + throw new SyntaxError(`Unexpected character at index ${i}`) + } + } + if (start === -1 || end !== -1) { + throw new SyntaxError('Unexpected end of input') + } + const protocol = header.slice(start, i) + if (protocols.has(protocol)) { + throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`) + } + protocols.add(protocol) + return protocols + } + module.exports = { parse: parse3 } + }, +}) + +// ../node_modules/ws/lib/websocket-server.js +var require_websocket_server = __commonJS({ + '../node_modules/ws/lib/websocket-server.js'(exports, module) { + 'use strict' + var EventEmitter = __require('events') + var http = __require('http') + var { Duplex } = __require('stream') + var { createHash } = __require('crypto') + var extension2 = require_extension() + var PerMessageDeflate = require_permessage_deflate() + var subprotocol = require_subprotocol() + var WebSocket2 = require_websocket() + var { GUID, kWebSocket } = require_constants() + var keyRegex = /^[+/0-9A-Za-z]{22}==$/ + var RUNNING = 0 + var CLOSING = 1 + var CLOSED = 2 + var WebSocketServer2 = class extends EventEmitter { + /** + * Create a `WebSocketServer` instance. + * + * @param {Object} options Configuration options + * @param {Number} [options.backlog=511] The maximum length of the queue of + * pending connections + * @param {Boolean} [options.clientTracking=true] Specifies whether or not to + * track clients + * @param {Function} [options.handleProtocols] A hook to handle protocols + * @param {String} [options.host] The hostname where to bind the server + * @param {Number} [options.maxPayload=104857600] The maximum allowed message + * size + * @param {Boolean} [options.noServer=false] Enable no server mode + * @param {String} [options.path] Accept only connections matching this path + * @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable + * permessage-deflate + * @param {Number} [options.port] The port where to bind the server + * @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S + * server to use + * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or + * not to skip UTF-8 validation for text and close messages + * @param {Function} [options.verifyClient] A hook to reject connections + * @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket` + * class to use. It must be the `WebSocket` class or class that extends it + * @param {Function} [callback] A listener for the `listening` event + */ + constructor(options, callback) { + super() + options = { + maxPayload: 100 * 1024 * 1024, + skipUTF8Validation: false, + perMessageDeflate: false, + handleProtocols: null, + clientTracking: true, + verifyClient: null, + noServer: false, + backlog: null, + // use default (511 as implemented in net.js) + server: null, + host: null, + path: null, + port: null, + WebSocket: WebSocket2, + ...options, + } + if ( + options.port == null && !options.server && !options.noServer + || options.port != null && (options.server || options.noServer) + || options.server && options.noServer + ) { + throw new TypeError( + 'One and only one of the "port", "server", or "noServer" options must be specified' + ) + } + if (options.port != null) { + this._server = http.createServer((req, res) => { + const body = http.STATUS_CODES[426] + res.writeHead(426, { + 'Content-Length': body.length, + 'Content-Type': 'text/plain', + }) + res.end(body) + }) + this._server.listen( + options.port, + options.host, + options.backlog, + callback + ) + } else if (options.server) { + this._server = options.server + } + if (this._server) { + const emitConnection = this.emit.bind(this, 'connection') + this._removeListeners = addListeners(this._server, { + listening: this.emit.bind(this, 'listening'), + error: this.emit.bind(this, 'error'), + upgrade: (req, socket, head) => { + this.handleUpgrade(req, socket, head, emitConnection) + }, + }) + } + if (options.perMessageDeflate === true) { + options.perMessageDeflate = {} + } + if (options.clientTracking) { + this.clients = /* @__PURE__ */ new Set() + this._shouldEmitClose = false + } + this.options = options + this._state = RUNNING + } + /** + * Returns the bound address, the address family name, and port of the server + * as reported by the operating system if listening on an IP socket. + * If the server is listening on a pipe or UNIX domain socket, the name is + * returned as a string. + * + * @return {(Object|String|null)} The address of the server + * @public + */ + address() { + if (this.options.noServer) { + throw new Error('The server is operating in "noServer" mode') + } + if (!this._server) { + return null + } + return this._server.address() + } + /** + * Stop the server from accepting new connections and emit the `'close'` event + * when all existing connections are closed. + * + * @param {Function} [cb] A one-time listener for the `'close'` event + * @public + */ + close(cb) { + if (this._state === CLOSED) { + if (cb) { + this.once('close', () => { + cb(new Error('The server is not running')) + }) + } + process.nextTick(emitClose, this) + return + } + if (cb) { + this.once('close', cb) + } + if (this._state === CLOSING) { + return + } + this._state = CLOSING + if (this.options.noServer || this.options.server) { + if (this._server) { + this._removeListeners() + this._removeListeners = this._server = null + } + if (this.clients) { + if (!this.clients.size) { + process.nextTick(emitClose, this) + } else { + this._shouldEmitClose = true + } + } else { + process.nextTick(emitClose, this) + } + } else { + const server = this._server + this._removeListeners() + this._removeListeners = this._server = null + server.close(() => { + emitClose(this) + }) + } + } + /** + * See if a given request should be handled by this server instance. + * + * @param {http.IncomingMessage} req Request object to inspect + * @return {Boolean} `true` if the request is valid, else `false` + * @public + */ + shouldHandle(req) { + if (this.options.path) { + const index = req.url.indexOf('?') + const pathname = index !== -1 ? req.url.slice(0, index) : req.url + if (pathname !== this.options.path) { + return false + } + } + return true + } + /** + * Handle a HTTP Upgrade request. + * + * @param {http.IncomingMessage} req The request object + * @param {Duplex} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @public + */ + handleUpgrade(req, socket, head, cb) { + socket.on('error', socketOnError) + const key = req.headers['sec-websocket-key'] + const version2 = +req.headers['sec-websocket-version'] + if (req.method !== 'GET') { + const message = 'Invalid HTTP method' + abortHandshakeOrEmitwsClientError(this, req, socket, 405, message) + return + } + if (req.headers.upgrade.toLowerCase() !== 'websocket') { + const message = 'Invalid Upgrade header' + abortHandshakeOrEmitwsClientError(this, req, socket, 400, message) + return + } + if (!key || !keyRegex.test(key)) { + const message = 'Missing or invalid Sec-WebSocket-Key header' + abortHandshakeOrEmitwsClientError(this, req, socket, 400, message) + return + } + if (version2 !== 8 && version2 !== 13) { + const message = 'Missing or invalid Sec-WebSocket-Version header' + abortHandshakeOrEmitwsClientError(this, req, socket, 400, message) + return + } + if (!this.shouldHandle(req)) { + abortHandshake(socket, 400) + return + } + const secWebSocketProtocol = req.headers['sec-websocket-protocol'] + let protocols = /* @__PURE__ */ new Set() + if (secWebSocketProtocol !== void 0) { + try { + protocols = subprotocol.parse(secWebSocketProtocol) + } catch (err) { + const message = 'Invalid Sec-WebSocket-Protocol header' + abortHandshakeOrEmitwsClientError(this, req, socket, 400, message) + return + } + } + const secWebSocketExtensions = req.headers['sec-websocket-extensions'] + const extensions = {} + if ( + this.options.perMessageDeflate && secWebSocketExtensions !== void 0 + ) { + const perMessageDeflate = new PerMessageDeflate( + this.options.perMessageDeflate, + true, + this.options.maxPayload + ) + try { + const offers = extension2.parse(secWebSocketExtensions) + if (offers[PerMessageDeflate.extensionName]) { + perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]) + extensions[PerMessageDeflate.extensionName] = perMessageDeflate + } + } catch (err) { + const message = + 'Invalid or unacceptable Sec-WebSocket-Extensions header' + abortHandshakeOrEmitwsClientError(this, req, socket, 400, message) + return + } + } + if (this.options.verifyClient) { + const info = { + origin: req + .headers[ + `${version2 === 8 ? 'sec-websocket-origin' : 'origin'}` + ], + secure: !!(req.socket.authorized || req.socket.encrypted), + req, + } + if (this.options.verifyClient.length === 2) { + this.options.verifyClient( + info, + (verified, code, message, headers) => { + if (!verified) { + return abortHandshake(socket, code || 401, message, headers) + } + this.completeUpgrade( + extensions, + key, + protocols, + req, + socket, + head, + cb + ) + } + ) + return + } + if (!this.options.verifyClient(info)) { + return abortHandshake(socket, 401) + } + } + this.completeUpgrade(extensions, key, protocols, req, socket, head, cb) + } + /** + * Upgrade the connection to WebSocket. + * + * @param {Object} extensions The accepted extensions + * @param {String} key The value of the `Sec-WebSocket-Key` header + * @param {Set} protocols The subprotocols + * @param {http.IncomingMessage} req The request object + * @param {Duplex} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @throws {Error} If called more than once with the same socket + * @private + */ + completeUpgrade(extensions, key, protocols, req, socket, head, cb) { + if (!socket.readable || !socket.writable) { + return socket.destroy() + } + if (socket[kWebSocket]) { + throw new Error( + 'server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration' + ) + } + if (this._state > RUNNING) { + return abortHandshake(socket, 503) + } + const digest = createHash('sha1').update(key + GUID).digest('base64') + const headers = [ + 'HTTP/1.1 101 Switching Protocols', + 'Upgrade: websocket', + 'Connection: Upgrade', + `Sec-WebSocket-Accept: ${digest}`, + ] + const ws = new this.options.WebSocket(null) + if (protocols.size) { + const protocol = this.options.handleProtocols + ? this.options.handleProtocols(protocols, req) + : protocols.values().next().value + if (protocol) { + headers.push(`Sec-WebSocket-Protocol: ${protocol}`) + ws._protocol = protocol + } + } + if (extensions[PerMessageDeflate.extensionName]) { + const params = extensions[PerMessageDeflate.extensionName].params + const value = extension2.format({ + [PerMessageDeflate.extensionName]: [params], + }) + headers.push(`Sec-WebSocket-Extensions: ${value}`) + ws._extensions = extensions + } + this.emit('headers', headers, req) + socket.write(headers.concat('\r\n').join('\r\n')) + socket.removeListener('error', socketOnError) + ws.setSocket(socket, head, { + maxPayload: this.options.maxPayload, + skipUTF8Validation: this.options.skipUTF8Validation, + }) + if (this.clients) { + this.clients.add(ws) + ws.on('close', () => { + this.clients.delete(ws) + if (this._shouldEmitClose && !this.clients.size) { + process.nextTick(emitClose, this) + } + }) + } + cb(ws, req) + } + } + module.exports = WebSocketServer2 + function addListeners(server, map) { + for (const event of Object.keys(map)) { + server.on(event, map[event]) + } + return function removeListeners() { + for (const event of Object.keys(map)) { + server.removeListener(event, map[event]) + } + } + } + function emitClose(server) { + server._state = CLOSED + server.emit('close') + } + function socketOnError() { + this.destroy() + } + function abortHandshake(socket, code, message, headers) { + message = message || http.STATUS_CODES[code] + headers = { + Connection: 'close', + 'Content-Type': 'text/html', + 'Content-Length': Buffer.byteLength(message), + ...headers, + } + socket.once('finish', socket.destroy) + socket.end( + `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r +` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join('\r\n') + + '\r\n\r\n' + message + ) + } + function abortHandshakeOrEmitwsClientError( + server, + req, + socket, + code, + message + ) { + if (server.listenerCount('wsClientError')) { + const err = new Error(message) + Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError) + server.emit('wsClientError', err, socket, req) + } else { + abortHandshake(socket, code, message) + } + } + }, +}) + +// ../streams/dist/node.mjs +import { + PassThrough, + Readable as Readable2, + Writable as Writable2, +} from 'stream' +import { Readable, Transform, Writable } from 'stream' +import { Transform as Transform2 } from 'stream' +import { Transform as Transform3 } from 'stream' +import { Transform as Transform4 } from 'stream' +import { Transform as Transform5 } from 'stream' +import { Transform as Transform6 } from 'stream' +import { Transform as Transform7 } from 'stream' +import { Transform as Transform8 } from 'stream' +import { createWriteStream } from 'fs' +import { join } from 'path' +import { readFileSync } from 'node:fs' +import { join as join2 } from 'node:path' +import { Readable as Readable3, Writable as Writable3 } from 'stream' +import { Transform as Transform9 } from 'stream' +import { Transform as Transform10 } from 'stream' +import { connect } from 'node:net' +import { Readable as Readable4, Writable as Writable4 } from 'stream' +import { Readable as Readable5, Writable as Writable5 } from 'stream' + +// ../node_modules/ws/wrapper.mjs +var import_stream = __toESM(require_stream(), 1) +var import_receiver = __toESM(require_receiver(), 1) +var import_sender = __toESM(require_sender(), 1) +var import_websocket = __toESM(require_websocket(), 1) +var import_websocket_server = __toESM(require_websocket_server(), 1) + +// ../streams/dist/node.mjs +var __create2 = Object.create +var __defProp2 = Object.defineProperty +var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor +var __getOwnPropNames2 = Object.getOwnPropertyNames +var __getProtoOf2 = Object.getPrototypeOf +var __hasOwnProp2 = Object.prototype.hasOwnProperty +var __require2 = /* @__PURE__ */ ((x) => + typeof __require !== 'undefined' + ? __require + : typeof Proxy !== 'undefined' + ? new Proxy(x, { + get: (a, b) => (typeof __require !== 'undefined' ? __require : a)[b], + }) + : x)(function(x) { + if (typeof __require !== 'undefined') { + return __require.apply(this, arguments) + } + throw new Error('Dynamic require of "' + x + '" is not supported') + }) +var __commonJS2 = (cb, mod) => + function __require22() { + return mod + || (0, cb[__getOwnPropNames2(cb)[0]])( + (mod = { exports: {} }).exports, + mod + ), + mod.exports + } +var __export = (target, all) => { + for (var name in all) { + __defProp2(target, name, { get: all[name], enumerable: true }) + } +} +var __copyProps2 = (to, from, except, desc) => { + if (from && typeof from === 'object' || typeof from === 'function') { + for (let key of __getOwnPropNames2(from)) { + if (!__hasOwnProp2.call(to, key) && key !== except) { + __defProp2(to, key, { + get: () => from[key], + enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable, + }) + } + } + } + return to +} +var __toESM2 = ( + mod, + isNodeMode, + target +) => (target = mod != null ? __create2(__getProtoOf2(mod)) : {}, + __copyProps2( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule + ? __defProp2(target, 'default', { value: mod, enumerable: true }) + : target, + mod + )) +var require_md5 = __commonJS2({ + '../node_modules/ts-md5/dist/cjs/md5.js'(exports) { + 'use strict' + Object.defineProperty(exports, '__esModule', { value: true }) + exports.Md5 = void 0 + var Md5 = ( + /** @class */ + function() { + function Md52() { + this._dataLength = 0 + this._bufferLength = 0 + this._state = new Int32Array(4) + this._buffer = new ArrayBuffer(68) + this._buffer8 = new Uint8Array(this._buffer, 0, 68) + this._buffer32 = new Uint32Array(this._buffer, 0, 17) + this.start() + } + Md52.hashStr = function(str, raw) { + if (raw === void 0) { + raw = false + } + return this.onePassHasher.start().appendStr(str).end(raw) + } + Md52.hashAsciiStr = function(str, raw) { + if (raw === void 0) { + raw = false + } + return this.onePassHasher.start().appendAsciiStr(str).end(raw) + } + Md52._hex = function(x) { + var hc = Md52.hexChars + var ho = Md52.hexOut + var n + var offset + var j + var i + for (i = 0; i < 4; i += 1) { + offset = i * 8 + n = x[i] + for (j = 0; j < 8; j += 2) { + ho[offset + 1 + j] = hc.charAt(n & 15) + n >>>= 4 + ho[offset + 0 + j] = hc.charAt(n & 15) + n >>>= 4 + } + } + return ho.join('') + } + Md52._md5cycle = function(x, k) { + var a = x[0] + var b = x[1] + var c = x[2] + var d = x[3] + a += (b & c | ~b & d) + k[0] - 680876936 | 0 + a = (a << 7 | a >>> 25) + b | 0 + d += (a & b | ~a & c) + k[1] - 389564586 | 0 + d = (d << 12 | d >>> 20) + a | 0 + c += (d & a | ~d & b) + k[2] + 606105819 | 0 + c = (c << 17 | c >>> 15) + d | 0 + b += (c & d | ~c & a) + k[3] - 1044525330 | 0 + b = (b << 22 | b >>> 10) + c | 0 + a += (b & c | ~b & d) + k[4] - 176418897 | 0 + a = (a << 7 | a >>> 25) + b | 0 + d += (a & b | ~a & c) + k[5] + 1200080426 | 0 + d = (d << 12 | d >>> 20) + a | 0 + c += (d & a | ~d & b) + k[6] - 1473231341 | 0 + c = (c << 17 | c >>> 15) + d | 0 + b += (c & d | ~c & a) + k[7] - 45705983 | 0 + b = (b << 22 | b >>> 10) + c | 0 + a += (b & c | ~b & d) + k[8] + 1770035416 | 0 + a = (a << 7 | a >>> 25) + b | 0 + d += (a & b | ~a & c) + k[9] - 1958414417 | 0 + d = (d << 12 | d >>> 20) + a | 0 + c += (d & a | ~d & b) + k[10] - 42063 | 0 + c = (c << 17 | c >>> 15) + d | 0 + b += (c & d | ~c & a) + k[11] - 1990404162 | 0 + b = (b << 22 | b >>> 10) + c | 0 + a += (b & c | ~b & d) + k[12] + 1804603682 | 0 + a = (a << 7 | a >>> 25) + b | 0 + d += (a & b | ~a & c) + k[13] - 40341101 | 0 + d = (d << 12 | d >>> 20) + a | 0 + c += (d & a | ~d & b) + k[14] - 1502002290 | 0 + c = (c << 17 | c >>> 15) + d | 0 + b += (c & d | ~c & a) + k[15] + 1236535329 | 0 + b = (b << 22 | b >>> 10) + c | 0 + a += (b & d | c & ~d) + k[1] - 165796510 | 0 + a = (a << 5 | a >>> 27) + b | 0 + d += (a & c | b & ~c) + k[6] - 1069501632 | 0 + d = (d << 9 | d >>> 23) + a | 0 + c += (d & b | a & ~b) + k[11] + 643717713 | 0 + c = (c << 14 | c >>> 18) + d | 0 + b += (c & a | d & ~a) + k[0] - 373897302 | 0 + b = (b << 20 | b >>> 12) + c | 0 + a += (b & d | c & ~d) + k[5] - 701558691 | 0 + a = (a << 5 | a >>> 27) + b | 0 + d += (a & c | b & ~c) + k[10] + 38016083 | 0 + d = (d << 9 | d >>> 23) + a | 0 + c += (d & b | a & ~b) + k[15] - 660478335 | 0 + c = (c << 14 | c >>> 18) + d | 0 + b += (c & a | d & ~a) + k[4] - 405537848 | 0 + b = (b << 20 | b >>> 12) + c | 0 + a += (b & d | c & ~d) + k[9] + 568446438 | 0 + a = (a << 5 | a >>> 27) + b | 0 + d += (a & c | b & ~c) + k[14] - 1019803690 | 0 + d = (d << 9 | d >>> 23) + a | 0 + c += (d & b | a & ~b) + k[3] - 187363961 | 0 + c = (c << 14 | c >>> 18) + d | 0 + b += (c & a | d & ~a) + k[8] + 1163531501 | 0 + b = (b << 20 | b >>> 12) + c | 0 + a += (b & d | c & ~d) + k[13] - 1444681467 | 0 + a = (a << 5 | a >>> 27) + b | 0 + d += (a & c | b & ~c) + k[2] - 51403784 | 0 + d = (d << 9 | d >>> 23) + a | 0 + c += (d & b | a & ~b) + k[7] + 1735328473 | 0 + c = (c << 14 | c >>> 18) + d | 0 + b += (c & a | d & ~a) + k[12] - 1926607734 | 0 + b = (b << 20 | b >>> 12) + c | 0 + a += (b ^ c ^ d) + k[5] - 378558 | 0 + a = (a << 4 | a >>> 28) + b | 0 + d += (a ^ b ^ c) + k[8] - 2022574463 | 0 + d = (d << 11 | d >>> 21) + a | 0 + c += (d ^ a ^ b) + k[11] + 1839030562 | 0 + c = (c << 16 | c >>> 16) + d | 0 + b += (c ^ d ^ a) + k[14] - 35309556 | 0 + b = (b << 23 | b >>> 9) + c | 0 + a += (b ^ c ^ d) + k[1] - 1530992060 | 0 + a = (a << 4 | a >>> 28) + b | 0 + d += (a ^ b ^ c) + k[4] + 1272893353 | 0 + d = (d << 11 | d >>> 21) + a | 0 + c += (d ^ a ^ b) + k[7] - 155497632 | 0 + c = (c << 16 | c >>> 16) + d | 0 + b += (c ^ d ^ a) + k[10] - 1094730640 | 0 + b = (b << 23 | b >>> 9) + c | 0 + a += (b ^ c ^ d) + k[13] + 681279174 | 0 + a = (a << 4 | a >>> 28) + b | 0 + d += (a ^ b ^ c) + k[0] - 358537222 | 0 + d = (d << 11 | d >>> 21) + a | 0 + c += (d ^ a ^ b) + k[3] - 722521979 | 0 + c = (c << 16 | c >>> 16) + d | 0 + b += (c ^ d ^ a) + k[6] + 76029189 | 0 + b = (b << 23 | b >>> 9) + c | 0 + a += (b ^ c ^ d) + k[9] - 640364487 | 0 + a = (a << 4 | a >>> 28) + b | 0 + d += (a ^ b ^ c) + k[12] - 421815835 | 0 + d = (d << 11 | d >>> 21) + a | 0 + c += (d ^ a ^ b) + k[15] + 530742520 | 0 + c = (c << 16 | c >>> 16) + d | 0 + b += (c ^ d ^ a) + k[2] - 995338651 | 0 + b = (b << 23 | b >>> 9) + c | 0 + a += (c ^ (b | ~d)) + k[0] - 198630844 | 0 + a = (a << 6 | a >>> 26) + b | 0 + d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0 + d = (d << 10 | d >>> 22) + a | 0 + c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0 + c = (c << 15 | c >>> 17) + d | 0 + b += (d ^ (c | ~a)) + k[5] - 57434055 | 0 + b = (b << 21 | b >>> 11) + c | 0 + a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0 + a = (a << 6 | a >>> 26) + b | 0 + d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0 + d = (d << 10 | d >>> 22) + a | 0 + c += (a ^ (d | ~b)) + k[10] - 1051523 | 0 + c = (c << 15 | c >>> 17) + d | 0 + b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0 + b = (b << 21 | b >>> 11) + c | 0 + a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0 + a = (a << 6 | a >>> 26) + b | 0 + d += (b ^ (a | ~c)) + k[15] - 30611744 | 0 + d = (d << 10 | d >>> 22) + a | 0 + c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0 + c = (c << 15 | c >>> 17) + d | 0 + b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0 + b = (b << 21 | b >>> 11) + c | 0 + a += (c ^ (b | ~d)) + k[4] - 145523070 | 0 + a = (a << 6 | a >>> 26) + b | 0 + d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0 + d = (d << 10 | d >>> 22) + a | 0 + c += (a ^ (d | ~b)) + k[2] + 718787259 | 0 + c = (c << 15 | c >>> 17) + d | 0 + b += (d ^ (c | ~a)) + k[9] - 343485551 | 0 + b = (b << 21 | b >>> 11) + c | 0 + x[0] = a + x[0] | 0 + x[1] = b + x[1] | 0 + x[2] = c + x[2] | 0 + x[3] = d + x[3] | 0 + } + Md52.prototype.start = function() { + this._dataLength = 0 + this._bufferLength = 0 + this._state.set(Md52.stateIdentity) + return this + } + Md52.prototype.appendStr = function(str) { + var buf8 = this._buffer8 + var buf32 = this._buffer32 + var bufLen = this._bufferLength + var code + var i + for (i = 0; i < str.length; i += 1) { + code = str.charCodeAt(i) + if (code < 128) { + buf8[bufLen++] = code + } else if (code < 2048) { + buf8[bufLen++] = (code >>> 6) + 192 + buf8[bufLen++] = code & 63 | 128 + } else if (code < 55296 || code > 56319) { + buf8[bufLen++] = (code >>> 12) + 224 + buf8[bufLen++] = code >>> 6 & 63 | 128 + buf8[bufLen++] = code & 63 | 128 + } else { + code = (code - 55296) * 1024 + (str.charCodeAt(++i) - 56320) + + 65536 + if (code > 1114111) { + throw new Error( + 'Unicode standard supports code points up to U+10FFFF' + ) + } + buf8[bufLen++] = (code >>> 18) + 240 + buf8[bufLen++] = code >>> 12 & 63 | 128 + buf8[bufLen++] = code >>> 6 & 63 | 128 + buf8[bufLen++] = code & 63 | 128 + } + if (bufLen >= 64) { + this._dataLength += 64 + Md52._md5cycle(this._state, buf32) + bufLen -= 64 + buf32[0] = buf32[16] + } + } + this._bufferLength = bufLen + return this + } + Md52.prototype.appendAsciiStr = function(str) { + var buf8 = this._buffer8 + var buf32 = this._buffer32 + var bufLen = this._bufferLength + var i + var j = 0 + for (;;) { + i = Math.min(str.length - j, 64 - bufLen) + while (i--) { + buf8[bufLen++] = str.charCodeAt(j++) + } + if (bufLen < 64) { + break + } + this._dataLength += 64 + Md52._md5cycle(this._state, buf32) + bufLen = 0 + } + this._bufferLength = bufLen + return this + } + Md52.prototype.appendByteArray = function(input) { + var buf8 = this._buffer8 + var buf32 = this._buffer32 + var bufLen = this._bufferLength + var i + var j = 0 + for (;;) { + i = Math.min(input.length - j, 64 - bufLen) + while (i--) { + buf8[bufLen++] = input[j++] + } + if (bufLen < 64) { + break + } + this._dataLength += 64 + Md52._md5cycle(this._state, buf32) + bufLen = 0 + } + this._bufferLength = bufLen + return this + } + Md52.prototype.getState = function() { + var s = this._state + return { + buffer: String.fromCharCode.apply(null, Array.from(this._buffer8)), + buflen: this._bufferLength, + length: this._dataLength, + state: [s[0], s[1], s[2], s[3]], + } + } + Md52.prototype.setState = function(state) { + var buf = state.buffer + var x = state.state + var s = this._state + var i + this._dataLength = state.length + this._bufferLength = state.buflen + s[0] = x[0] + s[1] = x[1] + s[2] = x[2] + s[3] = x[3] + for (i = 0; i < buf.length; i += 1) { + this._buffer8[i] = buf.charCodeAt(i) + } + } + Md52.prototype.end = function(raw) { + if (raw === void 0) { + raw = false + } + var bufLen = this._bufferLength + var buf8 = this._buffer8 + var buf32 = this._buffer32 + var i = (bufLen >> 2) + 1 + this._dataLength += bufLen + var dataBitsLen = this._dataLength * 8 + buf8[bufLen] = 128 + buf8[bufLen + 1] = buf8[bufLen + 2] = buf8[bufLen + 3] = 0 + buf32.set(Md52.buffer32Identity.subarray(i), i) + if (bufLen > 55) { + Md52._md5cycle(this._state, buf32) + buf32.set(Md52.buffer32Identity) + } + if (dataBitsLen <= 4294967295) { + buf32[14] = dataBitsLen + } else { + var matches = dataBitsLen.toString(16).match(/(.*?)(.{0,8})$/) + if (matches === null) { + return + } + var lo = parseInt(matches[2], 16) + var hi = parseInt(matches[1], 16) || 0 + buf32[14] = lo + buf32[15] = hi + } + Md52._md5cycle(this._state, buf32) + return raw ? this._state : Md52._hex(this._state) + } + Md52.stateIdentity = new Int32Array([ + 1732584193, + -271733879, + -1732584194, + 271733878, + ]) + Md52.buffer32Identity = new Int32Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]) + Md52.hexChars = '0123456789abcdef' + Md52.hexOut = [] + Md52.onePassHasher = new Md52() + return Md52 + }() + ) + exports.Md5 = Md5 + if (Md5.hashStr('hello') !== '5d41402abc4b2a76b9719d911017c592') { + throw new Error('Md5 self test failed.') + } + }, +}) +var require_md5_file_hasher = __commonJS2({ + '../node_modules/ts-md5/dist/cjs/md5_file_hasher.js'(exports) { + 'use strict' + Object.defineProperty(exports, '__esModule', { value: true }) + exports.Md5FileHasher = void 0 + var md5_1 = require_md5() + var Md5FileHasher = ( + /** @class */ + function() { + function Md5FileHasher2(_callback, _async, _partSize) { + if (_async === void 0) { + _async = true + } + if (_partSize === void 0) { + _partSize = 1048576 + } + this._callback = _callback + this._async = _async + this._partSize = _partSize + this._configureReader() + } + Md5FileHasher2.prototype.hash = function(blob) { + var self = this + self._blob = blob + self._part = 0 + self._md5 = new md5_1.Md5() + self._processPart() + } + Md5FileHasher2.prototype._fail = function() { + this._callback({ + success: false, + result: 'data read failed', + }) + } + Md5FileHasher2.prototype._hashData = function(e) { + var self = this + self._md5.appendByteArray(new Uint8Array(e.target.result)) + if (self._part * self._partSize >= self._blob.size) { + self._callback({ + success: true, + result: self._md5.end(), + }) + } else { + self._processPart() + } + } + Md5FileHasher2.prototype._processPart = function() { + var self = this + var endbyte = 0 + var current_part + self._part += 1 + if (self._blob.size > self._partSize) { + endbyte = self._part * self._partSize + if (endbyte > self._blob.size) { + endbyte = self._blob.size + } + current_part = self._blob.slice( + (self._part - 1) * self._partSize, + endbyte + ) + } else { + current_part = self._blob + } + if (self._async) { + self._reader.readAsArrayBuffer(current_part) + } else { + setTimeout(function() { + try { + self._hashData({ + target: { + result: self._reader.readAsArrayBuffer(current_part), + }, + }) + } catch (e) { + self._fail() + } + }, 0) + } + } + Md5FileHasher2.prototype._configureReader = function() { + var self = this + if (self._async) { + self._reader = new FileReader() + self._reader.onload = self._hashData.bind(self) + self._reader.onerror = self._fail.bind(self) + self._reader.onabort = self._fail.bind(self) + } else { + self._reader = new FileReaderSync() + } + } + return Md5FileHasher2 + }() + ) + exports.Md5FileHasher = Md5FileHasher + }, +}) +var require_parallel_hasher = __commonJS2({ + '../node_modules/ts-md5/dist/cjs/parallel_hasher.js'(exports) { + 'use strict' + Object.defineProperty(exports, '__esModule', { value: true }) + exports.ParallelHasher = void 0 + var ParallelHasher = ( + /** @class */ + function() { + function ParallelHasher2(workerUri, workerOptions) { + this._queue = [] + this._ready = true + var self = this + if (Worker) { + self._hashWorker = new Worker(workerUri, workerOptions) + self._hashWorker.onmessage = self._recievedMessage.bind(self) + self._hashWorker.onerror = function(err) { + self._ready = false + console.error('Hash worker failure', err) + } + } else { + self._ready = false + console.error('Web Workers are not supported in this browser') + } + } + ParallelHasher2.prototype.hash = function(blob) { + var self = this + var promise + promise = new Promise(function(resolve, reject) { + self._queue.push({ + blob, + resolve, + reject, + }) + self._processNext() + }) + return promise + } + ParallelHasher2.prototype.terminate = function() { + this._ready = false + this._hashWorker.terminate() + } + ParallelHasher2.prototype._processNext = function() { + if (this._ready && !this._processing && this._queue.length > 0) { + this._processing = this._queue.pop() + this._hashWorker.postMessage(this._processing.blob) + } + } + ParallelHasher2.prototype._recievedMessage = function(evt) { + var _a, _b + var data = evt.data + if (data.success) { + ;(_a = this._processing) === null || _a === void 0 + ? void 0 + : _a.resolve(data.result) + } else { + ;(_b = this._processing) === null || _b === void 0 + ? void 0 + : _b.reject(data.result) + } + this._processing = void 0 + this._processNext() + } + return ParallelHasher2 + }() + ) + exports.ParallelHasher = ParallelHasher + }, +}) +var require_cjs = __commonJS2({ + '../node_modules/ts-md5/dist/cjs/index.js'(exports) { + 'use strict' + Object.defineProperty(exports, '__esModule', { value: true }) + exports.ParallelHasher = exports.Md5FileHasher = exports.Md5 = void 0 + var md5_1 = require_md5() + Object.defineProperty(exports, 'Md5', { + enumerable: true, + get: function() { + return md5_1.Md5 + }, + }) + var md5_file_hasher_1 = require_md5_file_hasher() + Object.defineProperty(exports, 'Md5FileHasher', { + enumerable: true, + get: function() { + return md5_file_hasher_1.Md5FileHasher + }, + }) + var parallel_hasher_1 = require_parallel_hasher() + Object.defineProperty(exports, 'ParallelHasher', { + enumerable: true, + get: function() { + return parallel_hasher_1.ParallelHasher + }, + }) + }, +}) +var require_ms = __commonJS2({ + '../node_modules/ms/index.js'(exports, module) { + var s = 1e3 + var m = s * 60 + var h = m * 60 + var d = h * 24 + var w = d * 7 + var y = d * 365.25 + module.exports = function(val, options) { + options = options || {} + var type = typeof val + if (type === 'string' && val.length > 0) { + return parse3(val) + } else if (type === 'number' && isFinite(val)) { + return options.long ? fmtLong(val) : fmtShort(val) + } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ) + } + function parse3(str) { + str = String(str) + if (str.length > 100) { + return + } + var match = + /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i + .exec( + str + ) + if (!match) { + return + } + var n = parseFloat(match[1]) + var type = (match[2] || 'ms').toLowerCase() + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y + case 'weeks': + case 'week': + case 'w': + return n * w + case 'days': + case 'day': + case 'd': + return n * d + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n + default: + return void 0 + } + } + function fmtShort(ms) { + var msAbs = Math.abs(ms) + if (msAbs >= d) { + return Math.round(ms / d) + 'd' + } + if (msAbs >= h) { + return Math.round(ms / h) + 'h' + } + if (msAbs >= m) { + return Math.round(ms / m) + 'm' + } + if (msAbs >= s) { + return Math.round(ms / s) + 's' + } + return ms + 'ms' + } + function fmtLong(ms) { + var msAbs = Math.abs(ms) + if (msAbs >= d) { + return plural(ms, msAbs, d, 'day') + } + if (msAbs >= h) { + return plural(ms, msAbs, h, 'hour') + } + if (msAbs >= m) { + return plural(ms, msAbs, m, 'minute') + } + if (msAbs >= s) { + return plural(ms, msAbs, s, 'second') + } + return ms + ' ms' + } + function plural(ms, msAbs, n, name) { + var isPlural = msAbs >= n * 1.5 + return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '') + } + }, +}) +var require_common = __commonJS2({ + '../node_modules/debug/src/common.js'(exports, module) { + function setup(env) { + createDebug.debug = createDebug + createDebug.default = createDebug + createDebug.coerce = coerce + createDebug.disable = disable + createDebug.enable = enable + createDebug.enabled = enabled + createDebug.humanize = require_ms() + createDebug.destroy = destroy + Object.keys(env).forEach((key) => { + createDebug[key] = env[key] + }) + createDebug.names = [] + createDebug.skips = [] + createDebug.formatters = {} + function selectColor(namespace) { + let hash = 0 + for (let i = 0; i < namespace.length; i++) { + hash = (hash << 5) - hash + namespace.charCodeAt(i) + hash |= 0 + } + return createDebug.colors[Math.abs(hash) % createDebug.colors.length] + } + createDebug.selectColor = selectColor + function createDebug(namespace) { + let prevTime + let enableOverride = null + let namespacesCache + let enabledCache + function debug6(...args) { + if (!debug6.enabled) { + return + } + const self = debug6 + const curr = Number(/* @__PURE__ */ new Date()) + const ms = curr - (prevTime || curr) + self.diff = ms + self.prev = prevTime + self.curr = curr + prevTime = curr + args[0] = createDebug.coerce(args[0]) + if (typeof args[0] !== 'string') { + args.unshift('%O') + } + let index = 0 + args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => { + if (match === '%%') { + return '%' + } + index++ + const formatter = createDebug.formatters[format] + if (typeof formatter === 'function') { + const val = args[index] + match = formatter.call(self, val) + args.splice(index, 1) + index-- + } + return match + }) + createDebug.formatArgs.call(self, args) + const logFn = self.log || createDebug.log + logFn.apply(self, args) + } + debug6.namespace = namespace + debug6.useColors = createDebug.useColors() + debug6.color = createDebug.selectColor(namespace) + debug6.extend = extend + debug6.destroy = createDebug.destroy + Object.defineProperty(debug6, 'enabled', { + enumerable: true, + configurable: false, + get: () => { + if (enableOverride !== null) { + return enableOverride + } + if (namespacesCache !== createDebug.namespaces) { + namespacesCache = createDebug.namespaces + enabledCache = createDebug.enabled(namespace) + } + return enabledCache + }, + set: (v) => { + enableOverride = v + }, + }) + if (typeof createDebug.init === 'function') { + createDebug.init(debug6) + } + return debug6 + } + function extend(namespace, delimiter) { + const newDebug = createDebug( + this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + + namespace + ) + newDebug.log = this.log + return newDebug + } + function enable(namespaces) { + createDebug.save(namespaces) + createDebug.namespaces = namespaces + createDebug.names = [] + createDebug.skips = [] + let i + const split = (typeof namespaces === 'string' ? namespaces : '').split( + /[\s,]+/ + ) + const len = split.length + for (i = 0; i < len; i++) { + if (!split[i]) { + continue + } + namespaces = split[i].replace(/\*/g, '.*?') + if (namespaces[0] === '-') { + createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$')) + } else { + createDebug.names.push(new RegExp('^' + namespaces + '$')) + } + } + } + function disable() { + const namespaces = [ + ...createDebug.names.map(toNamespace), + ...createDebug.skips.map(toNamespace).map((namespace) => + '-' + namespace + ), + ].join(',') + createDebug.enable('') + return namespaces + } + function enabled(name) { + if (name[name.length - 1] === '*') { + return true + } + let i + let len + for (i = 0, len = createDebug.skips.length; i < len; i++) { + if (createDebug.skips[i].test(name)) { + return false + } + } + for (i = 0, len = createDebug.names.length; i < len; i++) { + if (createDebug.names[i].test(name)) { + return true + } + } + return false + } + function toNamespace(regexp) { + return regexp.toString().substring(2, regexp.toString().length - 2) + .replace(/\.\*\?$/, '*') + } + function coerce(val) { + if (val instanceof Error) { + return val.stack || val.message + } + return val + } + function destroy() { + console.warn( + 'Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.' + ) + } + createDebug.enable(createDebug.load()) + return createDebug + } + module.exports = setup + }, +}) +var require_browser = __commonJS2({ + '../node_modules/debug/src/browser.js'(exports, module) { + exports.formatArgs = formatArgs + exports.save = save + exports.load = load + exports.useColors = useColors + exports.storage = localstorage() + exports.destroy = (() => { + let warned = false + return () => { + if (!warned) { + warned = true + console.warn( + 'Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.' + ) + } + } + })() + exports.colors = [ + '#0000CC', + '#0000FF', + '#0033CC', + '#0033FF', + '#0066CC', + '#0066FF', + '#0099CC', + '#0099FF', + '#00CC00', + '#00CC33', + '#00CC66', + '#00CC99', + '#00CCCC', + '#00CCFF', + '#3300CC', + '#3300FF', + '#3333CC', + '#3333FF', + '#3366CC', + '#3366FF', + '#3399CC', + '#3399FF', + '#33CC00', + '#33CC33', + '#33CC66', + '#33CC99', + '#33CCCC', + '#33CCFF', + '#6600CC', + '#6600FF', + '#6633CC', + '#6633FF', + '#66CC00', + '#66CC33', + '#9900CC', + '#9900FF', + '#9933CC', + '#9933FF', + '#99CC00', + '#99CC33', + '#CC0000', + '#CC0033', + '#CC0066', + '#CC0099', + '#CC00CC', + '#CC00FF', + '#CC3300', + '#CC3333', + '#CC3366', + '#CC3399', + '#CC33CC', + '#CC33FF', + '#CC6600', + '#CC6633', + '#CC9900', + '#CC9933', + '#CCCC00', + '#CCCC33', + '#FF0000', + '#FF0033', + '#FF0066', + '#FF0099', + '#FF00CC', + '#FF00FF', + '#FF3300', + '#FF3333', + '#FF3366', + '#FF3399', + '#FF33CC', + '#FF33FF', + '#FF6600', + '#FF6633', + '#FF9900', + '#FF9933', + '#FFCC00', + '#FFCC33', + ] + function useColors() { + if ( + typeof window !== 'undefined' && window.process + && (window.process.type === 'renderer' || window.process.__nwjs) + ) { + return true + } + if ( + typeof navigator !== 'undefined' && navigator.userAgent + && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/) + ) { + return false + } + return typeof document !== 'undefined' && document.documentElement + && document.documentElement.style + && document.documentElement.style.WebkitAppearance // Is firebug? http://stackoverflow.com/a/398120/376773 + || typeof window !== 'undefined' && window.console + && (window.console.firebug + || window.console.exception && window.console.table) // Is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + || typeof navigator !== 'undefined' && navigator.userAgent + && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) + && parseInt(RegExp.$1, 10) >= 31 // Double check webkit in userAgent just in case we are in a worker + || typeof navigator !== 'undefined' && navigator.userAgent + && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/) + } + function formatArgs(args) { + args[0] = (this.useColors ? '%c' : '') + this.namespace + + (this.useColors ? ' %c' : ' ') + args[0] + + (this.useColors ? '%c ' : ' ') + '+' + + module.exports.humanize(this.diff) + if (!this.useColors) { + return + } + const c = 'color: ' + this.color + args.splice(1, 0, c, 'color: inherit') + let index = 0 + let lastC = 0 + args[0].replace(/%[a-zA-Z%]/g, (match) => { + if (match === '%%') { + return + } + index++ + if (match === '%c') { + lastC = index + } + }) + args.splice(lastC, 0, c) + } + exports.log = console.debug || console.log || (() => { + }) + function save(namespaces) { + try { + if (namespaces) { + exports.storage.setItem('debug', namespaces) + } else { + exports.storage.removeItem('debug') + } + } catch (error) { + } + } + function load() { + let r + try { + r = exports.storage.getItem('debug') + } catch (error) { + } + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG + } + return r + } + function localstorage() { + try { + return localStorage + } catch (error) { + } + } + module.exports = require_common()(exports) + var { formatters } = module.exports + formatters.j = function(v) { + try { + return JSON.stringify(v) + } catch (error) { + return '[UnexpectedJSONParseError]: ' + error.message + } + } + }, +}) +var require_has_flag = __commonJS2({ + '../node_modules/has-flag/index.js'(exports, module) { + 'use strict' + module.exports = (flag, argv = process.argv) => { + const prefix = flag.startsWith('-') ? '' : flag.length === 1 ? '-' : '--' + const position = argv.indexOf(prefix + flag) + const terminatorPosition = argv.indexOf('--') + return position !== -1 + && (terminatorPosition === -1 || position < terminatorPosition) + } + }, +}) +var require_supports_color = __commonJS2({ + '../node_modules/supports-color/index.js'(exports, module) { + 'use strict' + var os = __require2('os') + var tty = __require2('tty') + var hasFlag = require_has_flag() + var { env } = process + var forceColor + if ( + hasFlag('no-color') || hasFlag('no-colors') || hasFlag('color=false') + || hasFlag('color=never') + ) { + forceColor = 0 + } else if ( + hasFlag('color') || hasFlag('colors') || hasFlag('color=true') + || hasFlag('color=always') + ) { + forceColor = 1 + } + if ('FORCE_COLOR' in env) { + if (env.FORCE_COLOR === 'true') { + forceColor = 1 + } else if (env.FORCE_COLOR === 'false') { + forceColor = 0 + } else { + forceColor = env.FORCE_COLOR.length === 0 + ? 1 + : Math.min(parseInt(env.FORCE_COLOR, 10), 3) + } + } + function translateLevel(level) { + if (level === 0) { + return false + } + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3, + } + } + function supportsColor(haveStream, streamIsTTY) { + if (forceColor === 0) { + return 0 + } + if ( + hasFlag('color=16m') || hasFlag('color=full') + || hasFlag('color=truecolor') + ) { + return 3 + } + if (hasFlag('color=256')) { + return 2 + } + if (haveStream && !streamIsTTY && forceColor === void 0) { + return 0 + } + const min = forceColor || 0 + if (env.TERM === 'dumb') { + return min + } + if (process.platform === 'win32') { + const osRelease = os.release().split('.') + if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) { + return Number(osRelease[2]) >= 14931 ? 3 : 2 + } + return 1 + } + if ('CI' in env) { + if ( + [ + 'TRAVIS', + 'CIRCLECI', + 'APPVEYOR', + 'GITLAB_CI', + 'GITHUB_ACTIONS', + 'BUILDKITE', + ].some((sign) => sign in env) || env.CI_NAME === 'codeship' + ) { + return 1 + } + return min + } + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) + ? 1 + : 0 + } + if (env.COLORTERM === 'truecolor') { + return 3 + } + if ('TERM_PROGRAM' in env) { + const version2 = parseInt( + (env.TERM_PROGRAM_VERSION || '').split('.')[0], + 10 + ) + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + return version2 >= 3 ? 3 : 2 + case 'Apple_Terminal': + return 2 + } + } + if (/-256(color)?$/i.test(env.TERM)) { + return 2 + } + if ( + /^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test( + env.TERM + ) + ) { + return 1 + } + if ('COLORTERM' in env) { + return 1 + } + return min + } + function getSupportLevel(stream) { + const level = supportsColor(stream, stream && stream.isTTY) + return translateLevel(level) + } + module.exports = { + supportsColor: getSupportLevel, + stdout: translateLevel(supportsColor(true, tty.isatty(1))), + stderr: translateLevel(supportsColor(true, tty.isatty(2))), + } + }, +}) +var require_node = __commonJS2({ + '../node_modules/debug/src/node.js'(exports, module) { + var tty = __require2('tty') + var util = __require2('util') + exports.init = init + exports.log = log + exports.formatArgs = formatArgs + exports.save = save + exports.load = load + exports.useColors = useColors + exports.destroy = util.deprecate( + () => { + }, + 'Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.' + ) + exports.colors = [6, 2, 3, 4, 5, 1] + try { + const supportsColor = require_supports_color() + if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) { + exports.colors = [ + 20, + 21, + 26, + 27, + 32, + 33, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 56, + 57, + 62, + 63, + 68, + 69, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 92, + 93, + 98, + 99, + 112, + 113, + 128, + 129, + 134, + 135, + 148, + 149, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 178, + 179, + 184, + 185, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 214, + 215, + 220, + 221, + ] + } + } catch (error) { + } + exports.inspectOpts = Object.keys(process.env).filter((key) => { + return /^debug_/i.test(key) + }).reduce((obj, key) => { + const prop = key.substring(6).toLowerCase().replace( + /_([a-z])/g, + (_, k) => { + return k.toUpperCase() + } + ) + let val = process.env[key] + if (/^(yes|on|true|enabled)$/i.test(val)) { + val = true + } else if (/^(no|off|false|disabled)$/i.test(val)) { + val = false + } else if (val === 'null') { + val = null + } else { + val = Number(val) + } + obj[prop] = val + return obj + }, {}) + function useColors() { + return 'colors' in exports.inspectOpts + ? Boolean(exports.inspectOpts.colors) + : tty.isatty(process.stderr.fd) + } + function formatArgs(args) { + const { namespace: name, useColors: useColors2 } = this + if (useColors2) { + const c = this.color + const colorCode = '\x1B[3' + (c < 8 ? c : '8;5;' + c) + const prefix = ` ${colorCode};1m${name} \x1B[0m` + args[0] = prefix + args[0].split('\n').join('\n' + prefix) + args.push( + colorCode + 'm+' + module.exports.humanize(this.diff) + '\x1B[0m' + ) + } else { + args[0] = getDate() + name + ' ' + args[0] + } + } + function getDate() { + if (exports.inspectOpts.hideDate) { + return '' + } + return (/* @__PURE__ */ new Date()).toISOString() + ' ' + } + function log(...args) { + return process.stderr.write(util.format(...args) + '\n') + } + function save(namespaces) { + if (namespaces) { + process.env.DEBUG = namespaces + } else { + delete process.env.DEBUG + } + } + function load() { + return process.env.DEBUG + } + function init(debug6) { + debug6.inspectOpts = {} + const keys = Object.keys(exports.inspectOpts) + for (let i = 0; i < keys.length; i++) { + debug6.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]] + } + } + module.exports = require_common()(exports) + var { formatters } = module.exports + formatters.o = function(v) { + this.inspectOpts.colors = this.useColors + return util.inspect(v, this.inspectOpts).split('\n').map((str) => + str.trim() + ).join(' ') + } + formatters.O = function(v) { + this.inspectOpts.colors = this.useColors + return util.inspect(v, this.inspectOpts) + } + }, +}) +var require_src = __commonJS2({ + '../node_modules/debug/src/index.js'(exports, module) { + if ( + typeof process === 'undefined' || process.type === 'renderer' + || process.browser === true || process.__nwjs + ) { + module.exports = require_browser() + } else { + module.exports = require_node() + } + }, +}) +var index_node_exports = {} +__export(index_node_exports, { + AACDepay: () => AACDepay, + Auth: () => Auth, + BasicDepay: () => BasicDepay, + H264Depay: () => H264Depay, + Inspector: () => Inspector, + JPEGDepay: () => JPEGDepay, + MessageType: () => MessageType, + Mp4Capture: () => Mp4Capture, + Mp4Muxer: () => Mp4Muxer, + ONVIFDepay: () => ONVIFDepay, + RTSPResponseError: () => RTSPResponseError, + RTSP_METHOD: () => RTSP_METHOD, + Recorder: () => Recorder, + Replayer: () => Replayer, + RtspParser: () => RtspParser, + RtspSession: () => RtspSession, + Sink: () => Sink, + Source: () => Source, + TcpSource: () => TcpSource, + Tube: () => Tube, + WSSink: () => WSSink, + createTransform: () => createTransform, +}) +var POS = [128, 64, 32, 16, 8, 4, 2, 1] +var version = (buffer) => { + return buffer[0] >>> 6 +} +var padding = (buffer) => { + return !!(buffer[0] & POS[2]) +} +var extension = (buffer) => { + return !!(buffer[0] & POS[3]) +} +var cSrcCount = (buffer) => { + return buffer[0] & 15 +} +var marker = (buffer) => { + return !!(buffer[1] & POS[0]) +} +var payloadType = (buffer) => { + return buffer[1] & 127 +} +var sequenceNumber = (buffer) => { + return buffer.readUInt16BE(2) +} +var timestamp = (buffer) => { + return buffer.readUInt32BE(4) +} +var sSrc = (buffer) => { + return buffer.readUInt32BE(8) +} +var cSrc = (buffer, rank = 0) => { + return cSrcCount(buffer) > rank ? buffer.readUInt32BE(12 + rank * 4) : 0 +} +var extHeaderLength = (buffer) => { + return !extension(buffer) + ? 0 + : buffer.readUInt16BE(12 + cSrcCount(buffer) * 4 + 2) +} +var extHeader = (buffer) => { + return extHeaderLength(buffer) === 0 ? Buffer.from([]) : buffer.slice( + 12 + cSrcCount(buffer) * 4, + 12 + cSrcCount(buffer) * 4 + 4 + extHeaderLength(buffer) * 4 + ) +} +var payload = (buffer) => { + return !extension(buffer) + ? buffer.slice(12 + cSrcCount(buffer) * 4) + : buffer.slice(12 + cSrcCount(buffer) * 4 + 4 + extHeaderLength(buffer) * 4) +} +var StreamFactory = class { + /** + * Creates a writable stream that sends all messages written to the stream + * to a callback function and then considers it written. + * @param fn The callback to be invoked on the message + */ + static consumer(fn = () => { + }) { + return new Writable({ + objectMode: true, + write(msg, _encoding, callback) { + fn(msg) + callback() + }, + }) + } + static peeker(fn) { + if (typeof fn !== 'function') { + throw new Error('you must supply a function') + } + return new Transform({ + objectMode: true, + transform(msg, _encoding, callback) { + fn(msg) + callback(void 0, msg) + }, + }) + } + /** + * Creates a readable stream that sends a message for each element of an array. + * @param arr The array with elements to be turned into a stream. + */ + static producer(messages) { + let counter = 0 + return new Readable({ + objectMode: true, + read() { + if (messages !== void 0) { + if (counter < messages.length) { + this.push(messages[counter++]) + } else { + this.push(null) + } + } + }, + }) + } + static recorder(type, fileStream) { + return new Transform({ + objectMode: true, + transform(msg, encoding, callback) { + const timestamp2 = Date.now() + const message = Object.assign({}, msg, { + data: msg.data.toString('base64'), + }) + fileStream.write( + JSON.stringify({ type, timestamp: timestamp2, message }, null, 2) + ) + fileStream.write(',\n') + callback(void 0, msg) + }, + }) + } + /** + * Yield binary messages from JSON packet array until depleted. + * @return {Generator} Returns a JSON packet iterator. + */ + static replayer(packets) { + let packetCounter = 0 + let lastTimestamp = packets[0].timestamp + return new Readable({ + objectMode: true, + read() { + const packet = packets[packetCounter++] + if (packet) { + const { type, timestamp: timestamp2, message } = packet + const delay = timestamp2 - lastTimestamp + lastTimestamp = timestamp2 + if (message) { + const data = message.data + ? Buffer.from(message.data, 'base64') + : Buffer.alloc(0) + const msg = Object.assign({}, message, { data }) + this.push({ type, delay, msg }) + } else { + this.push({ type, delay, msg: null }) + } + } else { + this.push(null) + } + }, + }) + } +} +var AbstractComponent = class { + _incomingErrorHandler + _outgoingErrorHandler +} +var Source = class extends AbstractComponent { + /** + * Set up a source component that has a message list as data source. + * + * @param messages - List of objects (with data property) to emit on the + * incoming stream + */ + static fromMessages(messages) { + const component = new Source( + StreamFactory.producer(messages), + StreamFactory.consumer() + ) + return component + } + incoming + outgoing + next + prev + constructor( + incoming = new Readable2({ objectMode: true }), + outgoing = new Writable2({ objectMode: true }) + ) { + super() + this.incoming = incoming + this.outgoing = outgoing + this.next = null + this.prev = null + } + /** + * Attach another component so the the 'down' stream flows into the + * next component 'down' stream and the 'up' stream of the other component + * flows into the 'up' stream of this component. This is what establishes the + * meaning of 'up' and 'down'. + * @param next - The component to connect. + * @return A reference to the connected component. + * + * -------------- pipe -------------- + * <- | outgoing | <- | outgoing | <- + * | this | | next | + * -> | incoming | -> | incoming | -> + * -------------- pipe -------------- + */ + connect(next) { + if (next === null) { + return this + } else if (this.next !== null || next.prev !== null) { + throw new Error('connection failed: component(s) already connected') + } + if (!this.incoming.readable || !this.outgoing.writable) { + throw new Error('connection failed: this component not compatible') + } + if (!next.incoming.writable || !next.outgoing.readable) { + throw new Error('connection failed: next component not compatible') + } + try { + this.incoming.pipe(next.incoming) + next.outgoing.pipe(this.outgoing) + } catch (e) { + throw new Error(`connection failed: ${e.message}`) + } + const incomingErrorHandler = (err) => { + this.incoming.emit('error', err) + } + next.incoming.on('error', incomingErrorHandler) + const outgoingErrorHandler = (err) => { + next.outgoing.emit('error', err) + } + this.outgoing.on('error', outgoingErrorHandler) + this.next = next + next.prev = this + this._incomingErrorHandler = incomingErrorHandler + this._outgoingErrorHandler = outgoingErrorHandler + return next + } + /** + * Disconnect the next connected component. When there is no next component + * the function will just do nothing. + * @return {Component} - A reference to this component. + */ + disconnect() { + const next = this.next + if (next !== null) { + this.incoming.unpipe(next.incoming) + next.outgoing.unpipe(this.outgoing) + if (typeof this._incomingErrorHandler !== 'undefined') { + next.incoming.removeListener('error', this._incomingErrorHandler) + } + if (typeof this._outgoingErrorHandler !== 'undefined') { + this.outgoing.removeListener('error', this._outgoingErrorHandler) + } + this.next = null + next.prev = null + delete this._incomingErrorHandler + delete this._outgoingErrorHandler + } + return this + } +} +var Tube = class extends Source { + /** + * Create a component that calls a handler function for each message passing + * through, but otherwise just passes data through. + * + * Can be used to log messages passing through a pipeline. + */ + static fromHandlers(fnIncoming, fnOutgoing) { + const incomingStream = fnIncoming + ? StreamFactory.peeker(fnIncoming) + : void 0 + const outgoingStream = fnOutgoing + ? StreamFactory.peeker(fnOutgoing) + : void 0 + return new Tube(incomingStream, outgoingStream) + } + incoming + outgoing + constructor( + incoming = new PassThrough({ objectMode: true }), + outgoing = new PassThrough({ objectMode: true }) + ) { + super(incoming, outgoing) + this.incoming = incoming + this.outgoing = outgoing + } +} +var Sink = class extends AbstractComponent { + /** + * Create a component that swallows incoming data (calling fn on it). To + * print data, you would use fn = console.log. + * + * @param fn - The callback to use for the incoming data. + */ + static fromHandler(fn) { + const component = new Sink( + StreamFactory.consumer(fn), + StreamFactory.producer(void 0) + ) + component.incoming.on('finish', () => { + component.outgoing.push(null) + }) + return component + } + incoming + outgoing + next + prev + constructor( + incoming = new Writable2({ objectMode: true }), + outgoing = new Readable2({ objectMode: true }) + ) { + super() + this.incoming = incoming + this.outgoing = outgoing + this.next = null + this.prev = null + } + connect() { + throw new Error('connection failed: attempting to connect after a sink') + } + disconnect() { + return this + } +} +var MessageType = /* @__PURE__ */ ((MessageType2) => { + MessageType2[MessageType2['UNKNOWN'] = 0] = 'UNKNOWN' + MessageType2[MessageType2['RAW'] = 1] = 'RAW' + MessageType2[MessageType2['RTP'] = 2] = 'RTP' + MessageType2[MessageType2['RTCP'] = 3] = 'RTCP' + MessageType2[MessageType2['RTSP'] = 4] = 'RTSP' + MessageType2[MessageType2['SDP'] = 5] = 'SDP' + MessageType2[MessageType2['ELEMENTARY'] = 6] = 'ELEMENTARY' + MessageType2[MessageType2['H264'] = 7] = 'H264' + MessageType2[MessageType2['ISOM'] = 8] = 'ISOM' + MessageType2[MessageType2['XML'] = 9] = 'XML' + MessageType2[MessageType2['JPEG'] = 10] = 'JPEG' + return MessageType2 +})(MessageType || {}) +var createTransform = (transform) => { + return new Transform2({ + objectMode: true, + transform, + }) +} +function parse(rtp, hasHeader, callback) { + const buffer = payload(rtp.data) + let headerLength = 0 + if (hasHeader) { + const auHeaderLengthInBits = buffer.readUInt16BE(0) + headerLength = 2 + (auHeaderLengthInBits + auHeaderLengthInBits % 8) / 8 + } + const packet = { + type: 6, + data: buffer.slice(headerLength), + payloadType: payloadType(rtp.data), + timestamp: timestamp(rtp.data), + ntpTimestamp: rtp.ntpTimestamp, + } + callback(packet) +} +var AACDepay = class extends Tube { + constructor() { + let AACPayloadType + let hasHeader + const incoming = createTransform(function(msg, encoding, callback) { + if (msg.type === 5) { + let validMedia + for (const media of msg.sdp.media) { + if ( + media.type === 'audio' && media.fmtp && media.fmtp.parameters + && media.fmtp.parameters.mode === 'AAC-hbr' + ) { + validMedia = media + } + } + if (validMedia && validMedia.rtpmap !== void 0) { + AACPayloadType = Number(validMedia.rtpmap.payloadType) + const parameters = validMedia.fmtp.parameters + const sizeLength = Number(parameters.sizelength) || 0 + const indexLength = Number(parameters.indexlength) || 0 + const indexDeltaLength = Number(parameters.indexdeltalength) || 0 + const CTSDeltaLength = Number(parameters.ctsdeltalength) || 0 + const DTSDeltaLength = Number(parameters.dtsdeltalength) || 0 + const RandomAccessIndication = + Number(parameters.randomaccessindication) || 0 + const StreamStateIndication = Number(parameters.streamstateindication) + || 0 + const AuxiliaryDataSizeLength = + Number(parameters.auxiliarydatasizelength) || 0 + hasHeader = sizeLength + Math.max(indexLength, indexDeltaLength) + + CTSDeltaLength + DTSDeltaLength + RandomAccessIndication + + StreamStateIndication + AuxiliaryDataSizeLength > 0 + } + callback(void 0, msg) + } else if (msg.type === 2 && payloadType(msg.data) === AACPayloadType) { + parse(msg, hasHeader, this.push.bind(this)) + callback() + } else { + callback(void 0, msg) + } + }) + super(incoming) + } +} +var merge = (template, override) => { + let cleanOverride + if (override !== void 0) { + if (typeof override !== 'object') { + throw new Error('merge expects override to be an object!') + } else { + cleanOverride = Object.keys(override).reduce( + (acc, key) => { + if (override[key] !== void 0) { + acc[key] = override[key] + } + return acc + }, + {} + ) + } + } + return Object.assign({}, template, cleanOverride) +} +var extractHeaderValue = (buffer, header) => { + const anchor = ` +${header.toLowerCase()}: ` + const start = buffer.toString().toLowerCase().indexOf(anchor) + if (start >= 0) { + const end = buffer.indexOf('\n', start + anchor.length) + const headerValue = buffer.toString('ascii', start + anchor.length, end) + .trim() + return headerValue + } + return null +} +var sequence = (buffer) => { + const val = extractHeaderValue(buffer, 'CSeq') + if (val !== null) { + return Number(val) + } + return null +} +var sessionId = (buffer) => { + const val = extractHeaderValue(buffer, 'Session') + return val ? val.split(';')[0] : null +} +var sessionTimeout = (buffer) => { + const val = extractHeaderValue(buffer, 'Session') + if (val === null) { + return null + } + const defaultTimeout = 60 + const timeoutToken = 'timeout=' + const timeoutPosition = val.toLowerCase().indexOf(timeoutToken) + if (timeoutPosition !== -1) { + let timeoutVal = val.substring(timeoutPosition + timeoutToken.length) + timeoutVal = timeoutVal.split(';')[0] + const parsedTimeout = parseInt(timeoutVal) + return isNaN(parsedTimeout) ? defaultTimeout : parsedTimeout + } + return defaultTimeout +} +var statusCode = (buffer) => { + return Number(buffer.toString('ascii', 9, 12)) +} +var contentBase = (buffer) => { + return extractHeaderValue(buffer, 'Content-Base') +} +var contentLocation = (buffer) => { + return extractHeaderValue(buffer, 'Content-Location') +} +var connectionEnded = (buffer) => { + const connectionToken = extractHeaderValue(buffer, 'Connection') + return connectionToken !== null && connectionToken.toLowerCase() === 'close' +} +var range = (buffer) => { + const npt = extractHeaderValue(buffer, 'Range') + if (npt !== null) { + return npt.split('=')[1].split('-') + } + return void 0 +} +var bodyOffset = (chunk) => { + const bodyOffsets = ['\n\n', '\r\r', '\r\n\r\n'].map((s) => { + const offset = chunk.indexOf(s) + if (offset !== -1) { + return offset + s.length + } + return offset + }).filter((offset) => offset !== -1) + if (bodyOffsets.length > 0) { + return bodyOffsets.reduce((acc, offset) => { + return Math.min(acc, offset) + }) + } + return -1 +} +var import_ts_md5 = __toESM2(require_cjs()) +function md5Hash(s) { + const hash = new import_ts_md5.Md5().appendStr(s).end() + if (hash === void 0) { + throw new Error('empty MD5 hash') + } + return hash.toString() +} +var DigestAuth = class { + realm + nonce + opaque + algorithm + qop + username + ha1Base + count + constructor(params, username, password) { + const realm = params.get('realm') + if (realm === void 0) { + throw new Error('no realm in digest challenge') + } + this.realm = realm + this.ha1Base = md5Hash(`${username}:${realm}:${password}`) + const nonce = params.get('nonce') + if (nonce === void 0) { + throw new Error('no nonce in digest challenge') + } + this.nonce = nonce + this.opaque = params.get('opaque') + const algorithm = params.get('algorithm') + if (algorithm !== void 0) { + if (algorithm === 'md5') { + this.algorithm = 'md5' + } else if (algorithm === 'md5-sess') { + this.algorithm = 'md5-sess' + } + } else { + this.algorithm = 'md5' + } + const qop = params.get('qop') + if (qop !== void 0) { + const possibleQops = qop.split(',').map((qopType) => qopType.trim()) + if (possibleQops.some((qopValue) => qopValue === 'auth')) { + this.qop = 'auth' + } else if (possibleQops.some((qopValue) => qopValue === 'auth-int')) { + this.qop = 'auth-int' + } + } + this.count = 0 + this.username = username + } + nc = () => { + ;++this.count + return this.count.toString(16).padStart(8, '0') + } + cnonce = () => { + return new Array(4).fill(0).map(() => Math.floor(Math.random() * 256)).map(( + n + ) => n.toString(16)).join('') + } + ha1 = (cnonce) => { + let ha1 = this.ha1Base + if (this.algorithm === 'md5-sess') { + ha1 = md5Hash(`${ha1}:${this.nonce}:${cnonce}`) + } + return ha1 + } + ha2 = (method, uri, body = '') => { + let ha2 = md5Hash(`${method}:${uri}`) + if (this.algorithm === 'md5-sess') { + const hbody = md5Hash(body) + ha2 = md5Hash(`${method}:${uri}:${hbody}`) + } + return ha2 + } + authorization = (method = 'GET', uri = '', body) => { + const nc = this.nc() + const cnonce = this.cnonce() + const ha1 = this.ha1(cnonce) + const ha2 = this.ha2(method, uri, body) + const response = this.qop === void 0 + ? md5Hash(`${ha1}:${this.nonce}:${ha2}`) + : md5Hash(`${ha1}:${this.nonce}:${nc}:${cnonce}:${this.qop}:${ha2}`) + const authorizationParams = [] + authorizationParams.push(`username="${this.username}"`) + authorizationParams.push(`realm="${this.realm}"`) + authorizationParams.push(`nonce="${this.nonce}"`) + authorizationParams.push(`uri="${uri}"`) + if (this.qop !== void 0) { + authorizationParams.push(`qop=${this.qop}`) + authorizationParams.push(`nc=${nc}`) + authorizationParams.push(`cnonce="${cnonce}"`) + } + authorizationParams.push(`response="${response}"`) + if (this.opaque !== void 0) { + authorizationParams.push(`opaque="${this.opaque}"`) + } + return `Digest ${authorizationParams.join(', ')}` + } +} +var parseWWWAuthenticate = (header) => { + const [, type, ...challenge] = header.split(' ') + const pairs = [] + const re = /\s*([^=]+)="([^"]*)",?/gm + let match + do { + match = re.exec(challenge.join(' ')) + if (match !== null) { + const [, key, value] = match + pairs.push([key, value]) + } + } while (match !== null) + const params = new Map(pairs) + return { type: type.toLowerCase(), params } +} +var UNAUTHORIZED = 401 +var DEFAULT_CONFIG = { + username: 'root', + password: 'pass', +} +var Auth = class extends Tube { + constructor(config = {}) { + const { username, password } = merge(DEFAULT_CONFIG, config) + if (username === void 0 || password === void 0) { + throw new Error('need username and password') + } + let lastSentMessage + let authHeader + const outgoing = createTransform(function(msg, encoding, callback) { + if (msg.type === 4) { + lastSentMessage = msg + if (authHeader && msg.headers) { + msg.headers.Authorization = authHeader + } + } + callback(void 0, msg) + }) + const incoming = createTransform(function(msg, encoding, callback) { + if (msg.type === 4 && statusCode(msg.data) === UNAUTHORIZED) { + const headers = msg.data.toString().split('\n') + const wwwAuth = headers.find((header) => /WWW-Auth/i.test(header)) + if (wwwAuth === void 0) { + throw new Error('cannot find WWW-Authenticate header') + } + const challenge = parseWWWAuthenticate(wwwAuth) + if (challenge.type === 'basic') { + authHeader = `Basic ${ + Buffer.from(`${username}:${password}`).toString( + 'base64' + ) + }` + } else if (challenge.type === 'digest') { + const digest = new DigestAuth(challenge.params, username, password) + authHeader = digest.authorization( + lastSentMessage.method, + lastSentMessage.uri + ) + } else { + return + } + outgoing.write(lastSentMessage, () => callback()) + } else { + callback(void 0, msg) + } + }) + super(incoming, outgoing) + } +} +var BasicDepay = class extends Tube { + constructor(rtpPayloadType) { + if (rtpPayloadType === void 0) { + throw new Error('you must supply a payload type to BasicDepayComponent') + } + let buffer = Buffer.alloc(0) + const incoming = createTransform(function(msg, encoding, callback) { + if (msg.type === 2 && payloadType(msg.data) === rtpPayloadType) { + const rtpPayload = payload(msg.data) + buffer = Buffer.concat([buffer, rtpPayload]) + if (marker(msg.data)) { + if (buffer.length > 0) { + this.push({ + data: buffer, + timestamp: timestamp(msg.data), + ntpTimestamp: msg.ntpTimestamp, + payloadType: payloadType(msg.data), + type: 6, + /* ELEMENTARY */ + }) + } + buffer = Buffer.alloc(0) + } + callback() + } else { + callback(void 0, msg) + } + }) + super(incoming) + } +} +var import_debug = __toESM2(require_src()) +var h264Debug = (0, import_debug.default)('msl:h264depay') +var H264DepayParser = class { + _buffer + constructor() { + this._buffer = Buffer.alloc(0) + } + parse(rtp) { + const rtpPayload = payload(rtp.data) + const type = rtpPayload[0] & 31 + if (type === 28) { + const fuIndicator = rtpPayload[0] + const fuHeader = rtpPayload[1] + const startBit = !!(fuHeader >> 7) + const nalType = fuHeader & 31 + const nal = fuIndicator & 224 | nalType + const stopBit = fuHeader & 64 + if (startBit) { + this._buffer = Buffer.concat([ + Buffer.from([0, 0, 0, 0, nal]), + rtpPayload.slice(2), + ]) + return null + } else if (stopBit) { + const h264frame = Buffer.concat([ + this._buffer, + rtpPayload.slice(2), + ]) + h264frame.writeUInt32BE(h264frame.length - 4, 0) + const msg = { + data: h264frame, + type: 7, + timestamp: timestamp(rtp.data), + ntpTimestamp: rtp.ntpTimestamp, + payloadType: payloadType(rtp.data), + nalType, + } + this._buffer = Buffer.alloc(0) + return msg + } + this._buffer = Buffer.concat([this._buffer, rtpPayload.slice(2)]) + return null + } else if ((type === 1 || type === 5) && this._buffer.length === 0) { + const h264frame = Buffer.concat([ + Buffer.from([0, 0, 0, 0]), + rtpPayload, + ]) + h264frame.writeUInt32BE(h264frame.length - 4, 0) + const msg = { + data: h264frame, + type: 7, + timestamp: timestamp(rtp.data), + ntpTimestamp: rtp.ntpTimestamp, + payloadType: payloadType(rtp.data), + nalType: type, + } + this._buffer = Buffer.alloc(0) + return msg + } + h264Debug( + `H264depayComponent can only extract types 1,5 and 28, got ${type}` + ) + this._buffer = Buffer.alloc(0) + return null + } +} +var H264Depay = class extends Tube { + constructor() { + let h264PayloadType + let idrFound = false + let packets = [] + const h264DepayParser = new H264DepayParser() + const incoming = new Transform3({ + objectMode: true, + transform(msg, _encoding, callback) { + if (msg.type === 5) { + const h264Media = msg.sdp.media.find((media) => { + return media.type === 'video' && media.rtpmap !== void 0 + && media.rtpmap.encodingName === 'H264' + }) + if (h264Media !== void 0 && h264Media.rtpmap !== void 0) { + h264PayloadType = h264Media.rtpmap.payloadType + } + callback(void 0, msg) + } else if ( + msg.type === 2 && payloadType(msg.data) === h264PayloadType + ) { + const endOfFrame = marker(msg.data) + const h264Message = h264DepayParser.parse(msg) + if (h264Message === null || !idrFound && h264Message.nalType !== 5) { + callback() + return + } + idrFound = true + packets.push(h264Message.data) + if (endOfFrame) { + this.push({ + ...h264Message, + data: packets.length === 1 ? packets[0] : Buffer.concat(packets), + }) + packets = [] + } + callback() + } else { + callback(void 0, msg) + } + }, + }) + super(incoming) + } +} +var generateLogger = (prefix, type) => { + let lastTimestamp = Date.now() + const log = (msg) => { + const timestamp2 = Date.now() + console.log(`${prefix}: +${timestamp2 - lastTimestamp}ms`, msg) + lastTimestamp = timestamp2 + } + if (type === void 0) { + return log + } + return (msg) => msg.type === type && log(msg) +} +var Inspector = class extends Tube { + /** + * Create a new inspector component. + * @argument {String} type The type of message to log (default is to log all). + * @return {undefined} + */ + constructor(type) { + const incomingLogger = generateLogger('incoming', type) + const incoming = new Transform4({ + objectMode: true, + transform(msg, encoding, callback) { + incomingLogger(msg) + callback(void 0, msg) + }, + }) + const outgoingLogger = generateLogger('outgoing', type) + const outgoing = new Transform4({ + objectMode: true, + transform(msg, encoding, callback) { + outgoingLogger(msg) + callback(void 0, msg) + }, + }) + super(incoming, outgoing) + } +} +function makeImageHeader() { + return Buffer.from([255, 216]) +} +function makeQuantHeader(precision, qTable) { + const lumSize = precision & 1 ? 128 : 64 + const chmSize = precision & 2 ? 128 : 64 + if (qTable.length !== lumSize + chmSize) { + throw new Error('invalid quantization table') + } + const lumaPrefix = Buffer.from([255, 219, 0, lumSize + 3, 0]) + const chromaPrefix = Buffer.from([255, 219, 0, chmSize + 3, 1]) + return Buffer.concat([ + lumaPrefix, + qTable.slice(0, lumSize), + chromaPrefix, + qTable.slice(lumSize), + ]) +} +function makeFrameHeader(width, height, type) { + return Buffer.from([ + 255, + 192, + // SOF_0 (Start Of Frame) + 0, + 17, + 8, + height >> 8, + height, + width >> 8, + width, + 3, + 0, + type === 0 ? 33 : 34, + 0, + 1, + 17, + 1, + 2, + 17, + 1, + ]) +} +var LUM_DC_CODELENS = [ + 0, + 1, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +] +var LUM_DC_SYMBOLS = [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, +] +var LUM_AC_CODELENS = [ + 0, + 2, + 1, + 3, + 3, + 2, + 4, + 3, + 5, + 5, + 4, + 4, + 0, + 0, + 1, + 125, +] +var LUM_AC_SYMBOLS = [ + 1, + 2, + 3, + 0, + 4, + 17, + 5, + 18, + 33, + 49, + 65, + 6, + 19, + 81, + 97, + 7, + 34, + 113, + 20, + 50, + 129, + 145, + 161, + 8, + 35, + 66, + 177, + 193, + 21, + 82, + 209, + 240, + 36, + 51, + 98, + 114, + 130, + 9, + 10, + 22, + 23, + 24, + 25, + 26, + 37, + 38, + 39, + 40, + 41, + 42, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, +] +var CHM_DC_CODELENS = [ + 0, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, +] +var CHM_DC_SYMBOLS = [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, +] +var CHM_AC_CODELENS = [ + 0, + 2, + 1, + 2, + 4, + 4, + 3, + 4, + 7, + 5, + 4, + 4, + 0, + 1, + 2, + 119, +] +var CHM_AC_SYMBOLS = [ + 0, + 1, + 2, + 3, + 17, + 4, + 5, + 33, + 49, + 6, + 18, + 65, + 81, + 7, + 97, + 113, + 19, + 34, + 50, + 129, + 8, + 20, + 66, + 145, + 161, + 177, + 193, + 9, + 35, + 51, + 82, + 240, + 21, + 98, + 114, + 209, + 10, + 22, + 36, + 52, + 225, + 37, + 241, + 23, + 24, + 25, + 26, + 38, + 39, + 40, + 41, + 42, + 53, + 54, + 55, + 56, + 57, + 58, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, +] +function makeHuffmanHeader() { + const LUM_DC_BUFFER = [ + [ + 255, + 196, + 0, + 3 + LUM_DC_CODELENS.length + LUM_DC_SYMBOLS.length, + 0 << 4 | 0, + ], + LUM_DC_CODELENS, + LUM_DC_SYMBOLS, + ] + const LUM_AC_BUFFER = [ + [ + 255, + 196, + 0, + 3 + LUM_AC_CODELENS.length + LUM_AC_SYMBOLS.length, + 1 << 4 | 0, + ], + LUM_AC_CODELENS, + LUM_AC_SYMBOLS, + ] + const CHM_DC_BUFFER = [ + [ + 255, + 196, + 0, + 3 + CHM_DC_CODELENS.length + CHM_DC_SYMBOLS.length, + 0 << 4 | 1, + ], + CHM_DC_CODELENS, + CHM_DC_SYMBOLS, + ] + const CHM_AC_BUFFER = [ + [ + 255, + 196, + 0, + 3 + CHM_AC_CODELENS.length + CHM_AC_SYMBOLS.length, + 1 << 4 | 1, + ], + CHM_AC_CODELENS, + CHM_AC_SYMBOLS, + ] + return Buffer.concat([ + ...LUM_DC_BUFFER.map(Buffer.from), + ...LUM_AC_BUFFER.map(Buffer.from), + ...CHM_DC_BUFFER.map(Buffer.from), + ...CHM_AC_BUFFER.map(Buffer.from), + ]) +} +function makeScanHeader() { + return Buffer.from([ + 255, + 218, + // SOS (Start Of Scan) + 0, + 12, + 3, + 0, + 0, + 1, + 17, + 2, + 17, + 0, + 63, + 0, + ]) +} +function makeDRIHeader(dri) { + return Buffer.from([255, 221, 0, 4, dri >> 8, dri & 255]) +} +function clamp(val, min, max) { + return val > max ? max : val < min ? min : val +} +var jpegLumaQuantizer = [ + 16, + 11, + 12, + 14, + 12, + 10, + 16, + 14, + 13, + 14, + 18, + 17, + 16, + 19, + 24, + 40, + 26, + 24, + 22, + 22, + 24, + 49, + 35, + 37, + 29, + 40, + 58, + 51, + 61, + 60, + 57, + 51, + 56, + 55, + 64, + 72, + 92, + 78, + 64, + 68, + 87, + 69, + 55, + 56, + 80, + 109, + 81, + 87, + 95, + 98, + 103, + 104, + 103, + 62, + 77, + 113, + 121, + 112, + 100, + 120, + 92, + 101, + 103, + 99, +] +var jpeChromaQuantizer = [ + 17, + 18, + 18, + 24, + 21, + 24, + 47, + 26, + 26, + 47, + 99, + 66, + 56, + 66, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, + 99, +] +function makeQtable(Q) { + const factor = clamp(Q, 1, 99) + const buffer = Buffer.alloc(128) + const S = Q < 50 ? Math.floor(5e3 / factor) : 200 - factor * 2 + for (let i = 0; i < 64; i++) { + const lq = Math.floor((jpegLumaQuantizer[i] * S + 50) / 100) + const cq = Math.floor((jpeChromaQuantizer[i] * S + 50) / 100) + buffer.writeUInt8(clamp(lq, 1, 255), i) + buffer.writeUInt8(clamp(cq, 1, 255), i + 64) + } + return buffer +} +function jpegDepayFactory(defaultWidth = 0, defaultHeight = 0) { + const IMAGE_HEADER = makeImageHeader() + const HUFFMAN_HEADER = makeHuffmanHeader() + const SCAN_HEADER = makeScanHeader() + return function jpegDepay(packets) { + let metadata + const fragments = [] + for (const packet of packets) { + let fragment = payload(packet) + const typeSpecific = fragment.readUInt8(0) + const fragmentOffset = fragment.readUInt8(1) << 16 + | fragment.readUInt8(2) << 8 | fragment.readUInt8(3) + const type2 = fragment.readUInt8(4) + const Q = fragment.readUInt8(5) + const width2 = fragment.readUInt8(6) * 8 || defaultWidth + const height2 = fragment.readUInt8(7) * 8 || defaultHeight + fragment = fragment.slice(8) + let DRI = 0 + if (type2 >= 64 && type2 <= 127) { + DRI = fragment.readUInt16BE(0) + fragment = fragment.slice(4) + } + if (Q >= 128 && fragmentOffset === 0) { + const precision2 = fragment.readUInt8(1) + const length = fragment.readUInt16BE(2) + const qTable2 = fragment.slice(4, 4 + length) + metadata = { + typeSpecific, + type: type2, + width: width2, + height: height2, + DRI, + precision: precision2, + qTable: qTable2, + } + fragment = fragment.slice(4 + length) + } else if (Q < 128 && fragmentOffset === 0) { + const precision2 = 0 + const qTable2 = makeQtable(Q) + metadata = { + typeSpecific, + type: type2, + width: width2, + height: height2, + DRI, + precision: precision2, + qTable: qTable2, + } + } + fragments.push(fragment) + } + if (metadata === void 0) { + throw new Error('no quantization header present') + } + const { precision, qTable, type, width, height } = metadata + const quantHeader = makeQuantHeader(precision, qTable) + const driHeader = metadata.DRI === 0 + ? Buffer.alloc(0) + : makeDRIHeader(metadata.DRI) + const frameHeader = makeFrameHeader(width, height, type) + return { + size: { width, height }, + data: Buffer.concat([ + IMAGE_HEADER, + quantHeader, + driHeader, + frameHeader, + HUFFMAN_HEADER, + SCAN_HEADER, + ...fragments, + ]), + } + } +} +var JPEGDepay = class extends Tube { + constructor() { + let jpegPayloadType + let packets = [] + let jpegDepay + const incoming = new Transform5({ + objectMode: true, + transform(msg, encoding, callback) { + if (msg.type === 5) { + const jpegMedia = msg.sdp.media.find((media) => { + return media.type === 'video' && media.rtpmap !== void 0 + && media.rtpmap.encodingName === 'JPEG' + }) + if (jpegMedia !== void 0 && jpegMedia.rtpmap !== void 0) { + jpegPayloadType = Number(jpegMedia.rtpmap.payloadType) + const framesize = jpegMedia.framesize + if (framesize !== void 0) { + const [width, height] = framesize + jpegDepay = jpegDepayFactory(width, height) + } else { + jpegDepay = jpegDepayFactory() + } + } + callback(void 0, msg) + } else if ( + msg.type === 2 && payloadType(msg.data) === jpegPayloadType + ) { + packets.push(msg.data) + if (marker(msg.data) && packets.length > 0) { + const jpegFrame = jpegDepay(packets) + this.push({ + timestamp: timestamp(msg.data), + ntpTimestamp: msg.ntpTimestamp, + payloadType: payloadType(msg.data), + data: jpegFrame.data, + framesize: jpegFrame.size, + type: 10, + /* JPEG */ + }) + packets = [] + } + callback() + } else { + callback(void 0, msg) + } + }, + }) + super(incoming) + } +} +var import_debug2 = __toESM2(require_src()) +var MAX_CAPTURE_BYTES = 225e6 +var Mp4Capture = class extends Tube { + _active + _capture + _captureCallback + _bufferOffset + _bufferSize + _buffer + constructor(maxSize = MAX_CAPTURE_BYTES) { + const incoming = new Transform6({ + objectMode: true, + transform: (msg, _encoding, callback) => { + if (this._active && msg.type === 8 && msg.tracks !== void 0) { + this._capture = true + } + if (this._capture && msg.type === 8) { + if ( + this._bufferOffset < this._buffer.byteLength - msg.data.byteLength + ) { + msg.data.copy(this._buffer, this._bufferOffset) + this._bufferOffset += msg.data.byteLength + } else { + this.stop() + } + } + callback(void 0, msg) + }, + }) + incoming.on('finish', () => { + this.stop() + }) + super(incoming) + this._buffer = Buffer.allocUnsafe(0) + this._bufferSize = maxSize + this._bufferOffset = 0 + this._active = false + this._capture = false + this._captureCallback = () => { + } + } + /** + * Activate video capture. The capture will begin when a new movie starts, + * and will terminate when the movie ends or when the buffer is full. On + * termination, the callback you passed will be called with the captured + * data as argument. + * @param callback Will be called when data is captured. + */ + start(callback) { + if (!this._active) { + ;(0, import_debug2.default)('msl:capture:start')(callback) + this._captureCallback = callback + this._buffer = Buffer.allocUnsafe(this._bufferSize) + this._bufferOffset = 0 + this._active = true + } + } + /** + * Deactivate video capture. This ends an ongoing capture and prevents + * any further capturing. + */ + stop() { + if (this._active) { + ;(0, import_debug2.default)('msl:capture:stop')( + `captured bytes: ${this._bufferOffset}` + ) + try { + this._captureCallback(this._buffer.slice(0, this._bufferOffset)) + } catch (e) { + console.error(e) + } + this._buffer = Buffer.allocUnsafe(0) + this._bufferOffset = 0 + this._active = false + this._capture = false + } + } +} +var import_debug3 = __toESM2(require_src()) +var UINT32_RANGE = Math.pow(2, 32) +var BoxElement = class { + byteLength + value + constructor(size) { + this.byteLength = size + } +} +var Empty = class extends BoxElement { + constructor(size = 0) { + super(size) + } + copy = (buffer, offset) => { + buffer.fill(0, offset, offset + this.byteLength) + } + load() { + } +} +var CharArray = class extends BoxElement { + value + constructor(s) { + super(s.length) + this.value = s + } + copy = (buffer, offset) => { + for (let i = 0; i < this.byteLength; i += 1) { + buffer[offset + i] = this.value.charCodeAt(i) + } + } + load = (buffer, offset) => { + this.value = buffer.slice(offset, offset + this.byteLength).toString( + 'ascii' + ) + } +} +var UInt8 = class extends BoxElement { + value + constructor(scalar = 0) { + super(1) + this.value = scalar + } + copy = (buffer, offset) => { + buffer.writeUInt8(this.value, offset) + } + load = (buffer, offset) => { + this.value = buffer.readUInt8(offset) + } +} +var UInt8Array = class extends BoxElement { + value + constructor(array) { + super(array.length) + this.value = array + } + copy = (buffer, offset) => { + for (let i = 0; i < this.value.length; ++i) { + buffer.writeUInt8(this.value[i], offset + i) + } + } + load = (buffer, offset) => { + for (let i = 0; i < this.value.length; ++i) { + this.value[i] = buffer.readUInt8(offset + i) + } + } +} +var UInt16BE = class extends BoxElement { + value + constructor(scalar = 0) { + super(2) + this.value = scalar + } + copy = (buffer, offset) => { + buffer.writeUInt16BE(this.value, offset) + } + load = (buffer, offset) => { + this.value = buffer.readUInt16BE(offset) + } +} +var UInt24BE = class extends BoxElement { + value + constructor(scalar = 0) { + super(3) + this.value = scalar + } + copy = (buffer, offset) => { + buffer.writeUInt8(this.value >> 16 & 255, offset) + buffer.writeUInt8(this.value >> 8 & 255, offset + 1) + buffer.writeUInt8(this.value & 255, offset + 2) + } + load = (buffer, offset) => { + this.value = buffer.readUInt8(offset) << 16 + buffer.readUInt8(offset + 1) + << 8 + buffer.readUInt8(offset + 2) + } +} +var UInt16BEArray = class extends BoxElement { + value + constructor(array) { + super(array.length * 2) + this.value = array + } + copy = (buffer, offset) => { + for (let i = 0; i < this.value.length; ++i) { + buffer.writeUInt16BE(this.value[i], offset + 2 * i) + } + } + load = (buffer, offset) => { + for (let i = 0; i < this.value.length; ++i) { + this.value[i] = buffer.readUInt16BE(offset + 2 * i) + } + } +} +var UInt32BE = class extends BoxElement { + value + constructor(scalar = 0) { + super(4) + this.value = scalar + } + copy = (buffer, offset) => { + buffer.writeUInt32BE(this.value, offset) + } + load = (buffer, offset) => { + this.value = buffer.readUInt32BE(offset) + } +} +var UInt32BEArray = class extends BoxElement { + value + constructor(array) { + super(array.length * 4) + this.value = array + } + copy = (buffer, offset) => { + for (let i = 0; i < this.value.length; ++i) { + buffer.writeUInt32BE(this.value[i], offset + 4 * i) + } + } + load = (buffer, offset) => { + for (let i = 0; i < this.value.length; ++i) { + this.value[i] = buffer.readUInt32BE(offset + 4 * i) + } + } +} +var UInt64BE = class extends BoxElement { + value + constructor(scalar = 0) { + super(8) + this.value = scalar + } + copy = (buffer, offset) => { + const high = this.value / UINT32_RANGE | 0 + const low = this.value - high * UINT32_RANGE + buffer.writeUInt32BE(high, offset) + buffer.writeUInt32BE(low, offset + 4) + } + load = (buffer, offset) => { + const high = buffer.readUInt32BE(offset) + const low = buffer.readUInt32BE(offset + 4) + this.value = high * UINT32_RANGE + low + } +} +var createParameterSetArrayClass = function(sizeMask = 0) { + return class ParameterSetArray extends BoxElement { + value + /** + * Takes an array of byte-arrays + * @param array The array of byte arrays + */ + constructor(array) { + super(0) + this.value = array.reduce( + (flatArray, byteArray) => { + return flatArray.concat( + new UInt16BE(byteArray.length), + new UInt8Array(byteArray) + ) + }, + [new UInt8(sizeMask | array.length)] + ) + this.byteLength = this.value.reduce( + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands + (total, element) => total + element.byteLength, + 0 + ) + } + copy = (buffer, offset) => { + let i = 0 + for (const element of this.value) { + element.copy(buffer, offset + i) + i += element.byteLength + } + } + load = () => { + } + } +} +var BOXSPEC = { + // File Type Box + ftyp: { + container: 'file', + mandatory: true, + quantity: 'one', + box: 'Box', + is_container: true, + body: [ + ['major_brand', CharArray, 'isom'], + ['minor_version', UInt32BE, 0], + ['compatible_brands', CharArray, 'mp41'], + // ['compatible_brands1', CharArray, 'iso2'], + // ['compatible_brands2', CharArray, 'dash'], + ], + }, + // Movie Container + moov: { + container: 'file', + mandatory: true, + quantity: 'one', + box: 'Box', + is_container: true, + }, + // Movie Data Box + mdat: { + container: 'file', + mandatory: false, + quantity: 'any', + box: 'Box', + is_container: false, + body: [], + }, + // Movie Header Box + mvhd: { + container: 'moov', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: false, + body: [ + ['creation_time', UInt32BE, 0], + ['modification_time', UInt32BE, 0], + ['timescale', UInt32BE, 1e3], + // time-scale for entire presentation, default = milliseconds + ['duration', UInt32BE, 4294967295], + // length of entire presentation, default = undetermined + ['rate', UInt32BE, 65536], + // fixed point 16.16, preferred playback rate, default = 1.0 + ['volume', UInt16BE, 256], + // fixed point 8.8, preferred playback volume, default = 1.0 + ['reserved', Empty, 10], + // transformation matrix, default = unity + [ + 'matrix', + UInt32BEArray, + [65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824], + ], + ['pre_defined', Empty, 24], + ['next_track_ID', UInt32BE, 4294967295], + // next unused track ID, default = unknown + ], + }, + // Track Container + trak: { + container: 'moov', + mandatory: true, + quantity: 'one+', + box: 'Box', + is_container: true, + }, + // Track Header Box + tkhd: { + container: 'trak', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: false, + // Flag values for the track header: + // 0x000001 Track_enabled: track enabled (otherwise ignored) + // 0x000002 Track_in_movie: track used in presentation + // 0x000004 Track_in_preview: used when previewing presentation + config: { + flags: 3, + // track enabled and used in presentation + }, + body: [ + ['creation_time', UInt32BE, 0], + ['modification_time', UInt32BE, 0], + ['track_ID', UInt32BE, 1], + // Track identifier, cannot be 0 + ['reserved', Empty, 4], + ['duration', UInt32BE, 0], + // Duration of track using timescale of mvhd box + ['reserved2', Empty, 8], + ['layer', UInt16BE, 0], + // Front-to-back ordering, lower is closer to viewer + ['alternate_group', UInt16BE, 0], + // Possible grouping of tracks + ['volume', UInt16BE, 256], + // Track's relative audio volume 8.8 fixed point + ['reserved3', Empty, 2], + [ + 'matrix', + UInt32BEArray, + [65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824], + ], + ['width', UInt32BE, 0], + // Visual presentation width, 16.16 fixed point + ['height', UInt32BE, 0], + // Visual presentation height, 16.16 fixed point + ], + }, + // Track Reference Box + tref: { + container: 'trak', + mandatory: false, + quantity: 'one-', + box: 'Box', + is_container: false, + }, + // Media Container + mdia: { + container: 'trak', + mandatory: false, + quantity: 'one', + box: 'Box', + is_container: true, + }, + // Media Header Box + mdhd: { + container: 'mdia', + mandatory: false, + quantity: 'one', + box: 'FullBox', + is_container: false, + body: [ + ['creation_time', UInt32BE, 0], + ['modification_time', UInt32BE, 0], + ['timescale', UInt32BE, 1e3], + // time-scale for entire presentation, default = milliseconds + ['duration', UInt32BE, 4294967295], + // length of entire presentation, default = undetermined + ['language', UInt16BE, 0], + // ISO 639-2 lanugage code, three lower-case letters, stored as + ['pre_defined', UInt16BE, 0], + ], + }, + // Handler Reference Box + hdlr: { + container: 'mdia', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: false, + body: [ + ['predefined', UInt32BE, 0], + ['handler_type', CharArray, 'vide'], + // 'vide', 'soun', or 'hint' + ['reserved', Empty, 12], + ['name', CharArray, 'VideoHandler\0'], + ], + }, + // Media Information Container + minf: { + container: 'mdia', + mandatory: true, + quantity: 'one', + box: 'Box', + is_container: true, + }, + // Video Media Header Box + vmhd: { + container: 'minf', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: false, + config: { + flags: 1, + }, + body: [ + ['graphicsmode', UInt16BE, 0], + // Composition mode of the video track, 0 = overwrite + ['opcolor', UInt16BEArray, [0, 0, 0]], + // Red green blue, for use by graphics modes + ], + }, + // Sound Media Header Box + smhd: { + container: 'minf', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: false, + body: [ + // Place mono track in stereo space: + // 8.8 fixed point, 0 = center, -1.0 = left, 1.0 = right + ['balance', UInt16BE, 0], + ['reserved', UInt16BE], + ], + }, + // Data Information Container + dinf: { + container: 'minf', + mandatory: true, + quantity: 'one', + box: 'Box', + is_container: true, + }, + // Data Reference Box + dref: { + // When adding elements to this box, update the entry_count value! + container: 'dinf', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: true, + body: [ + ['entry_count', UInt32BE, 0], + // Number of entries. + ], + }, + 'url ': { + container: 'dref', + mandatory: true, + quantity: 'one+', + box: 'FullBox', + is_container: false, + // Flag values: + // 0x000001 Local reference, which means empty URL + config: { + flags: 1, + }, + body: [ + // ['location', CharArray, ''], + ], + }, + // Sample Table Container + stbl: { + container: 'minf', + mandatory: true, + quantity: 'one', + box: 'Box', + is_container: true, + }, + // Decoding Time to Sample Box + stts: { + container: 'stbl', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: false, + body: [ + ['entry_count', UInt32BE, 0], + // For each entry these two elements: + // ['sample_count', UInt32BE, 0], // Number of consecutive samples with same delta + // ['sample_delta', UInt32BE, 0], // Delta of each sample + ], + }, + stsd: { + container: 'stbl', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: true, + body: [ + ['entry_count', UInt32BE, 1], + // For each entry, one of these three boxes depending on the handler: + // VisualSampleEntry, AudioSampleEntry, HintSampleEntry + ], + }, + /* + ISO/IEC 14496-12:2005(E) 8.16.2 (pp. 28) + aligned(8) abstract class SampleEntry (unsigned int(32) format) + extends Box(format){ + const unsigned int(8)[6] reserved = 0; + unsigned int(16) data_reference_index; + } + class VisualSampleEntry(codingname) extends SampleEntry (codingname){ + unsigned int(16) pre_defined = 0; + const unsigned int(16) reserved = 0; + unsigned int(32)[3] pre_defined = 0; + unsigned int(16) width; + unsigned int(16) height; + template unsigned int(32) horizresolution = 0x00480000; // 72 dpi + template unsigned int(32) vertresolution = 0x00480000; // 72 dpi + const unsigned int(32) reserved = 0; + template unsigned int(16) frame_count = 1; + string[32] compressorname; + template unsigned int(16) depth = 0x0018; + int(16) pre_defined = -1; + } + ISO/IEC 14496-15:2004(E) 5.3.4.1 (pp. 14) + class AVCSampleEntry() extends VisualSampleEntry (โ€˜avc1โ€™){ + AVCConfigurationBox config; + MPEG4BitRateBox (); // optional + MPEG4ExtensionDescriptorsBox (); // optional + } + */ + avc1: { + container: 'stsd', + mandatory: false, + quantity: 'one', + box: 'Box', + is_container: true, + body: [ + ['reserved', Empty, 6], + ['data_reference_index', UInt16BE, 1], + ['pre_defined', UInt16BE, 0], + ['reserved2', Empty, 2], + ['pre_defined2', UInt32BEArray, [0, 0, 0]], + ['width', UInt16BE, 1920], + ['height', UInt16BE, 1080], + ['horizresolution', UInt32BE, 4718592], + ['vertresolution', UInt32BE, 4718592], + ['reserved3', UInt32BE, 0], + ['frame_count', UInt16BE, 1], + ['compressorname', UInt8Array, Buffer.alloc(32)], + ['depth', UInt16BE, 24], + ['pre_defined3', UInt16BE, 65535], + ], + }, + /* + class AVCConfigurationBox extends Box(โ€˜avcCโ€™) { + AVCDecoderConfigurationRecord() AVCConfig; + } + ISO/IEC 14496-15:2004(E) 5.2.4.1.1 (pp. 12) + aligned(8) class AVCDecoderConfigurationRecord { + unsigned int(8) configurationVersion = 1; + unsigned int(8) AVCProfileIndication; + unsigned int(8) profile_compatibility; + unsigned int(8) AVCLevelIndication; + bit(6) reserved = โ€˜111111โ€™b; + unsigned int(2) lengthSizeMinusOne; + bit(3) reserved = โ€˜111โ€™b; + unsigned int(5) numOfSequenceParameterSets; + for (i=0; i< numOfSequenceParameterSets; i++) { + unsigned int(16) sequenceParameterSetLength ; + bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit; + } + unsigned int(8) numOfPictureParameterSets; + for (i=0; i< numOfPictureParameterSets; i++) { + unsigned int(16) pictureParameterSetLength; + bit(8*pictureParameterSetLength) pictureParameterSetNALUnit; + } + } + */ + avcC: { + container: 'avc1', + mandatory: false, + quantity: 'one', + box: 'Box', + is_container: false, + body: [ + ['configurationVersion', UInt8, 1], + ['AVCProfileIndication', UInt8, 77], + ['profile_compatibility', UInt8, 0], + ['AVCLevelIndication', UInt8, 41], + // size = reserved 0b111111 + 0b11 NALUnitLength (0b11 = 4-byte) + ['lengthSizeMinusOne', UInt8, 255], + // Example SPS (length 20): + // [0x67, 0x4d, 0x00, 0x29, 0xe2, 0x90, 0x0f, 0x00, + // 0x44, 0xfc, 0xb8, 0x0b, 0x70, 0x10, 0x10, 0x1a, + // 0x41, 0xe2, 0x44, 0x54] + // number of sets = reserved 0b111 + number of SPS (0b00001 = 1) + // ['numOfSequenceParameterSets', UInt8, 0b11100001], + // ['sequenceParameterSetLength', UInt16BE, 0], // Lenght in bytes of the SPS that follows + // ['sequenceParameterSetNALUnit', UInt8Array, []], + // These are packed in a single custom element: + ['sequenceParameterSets', createParameterSetArrayClass(224), []], + // Example PPS (length 4): + // [0x68, 0xee, 0x3c, 0x80] + // ['numOfPictureParameterSets', UInt8, 1], // number of PPS + // ['pictureParameterSetLength', UInt16BE, 0], // Length in bytes of the PPS that follows + // ['pictureParameterSetNALUnit', UInt8Array, []] + // These are packed in a single custom element: + ['pictureParameterSets', createParameterSetArrayClass(), []], + ], + }, + /* + ISO/IEC 14496-12:2005(E) 8.16.2 (pp. 28) + aligned(8) abstract class SampleEntry (unsigned int(32) format) + extends Box(format){ + const unsigned int(8)[6] reserved = 0; + unsigned int(16) data_reference_index; + } + class AudioSampleEntry(codingname) extends SampleEntry (codingname){ + const unsigned int(32)[2] reserved = 0; + template unsigned int(16) channelcount = 2; + template unsigned int(16) samplesize = 16; + unsigned int(16) pre_defined = 0; + const unsigned int(16) reserved = 0 ; + template unsigned int(32) samplerate = {timescale of media}<<16; + } + */ + mp4a: { + container: 'stsd', + mandatory: false, + quantity: 'one', + box: 'Box', + is_container: true, + body: [ + ['reserved', Empty, 6], + ['data_reference_index', UInt16BE, 1], + ['reserved2', UInt32BEArray, [0, 0]], + ['channelcount', UInt16BE, 2], + ['samplesize', UInt16BE, 16], + ['pre_defined', UInt16BE, 0], + ['reserved3', UInt16BE, 0], + ['samplerate', UInt32BE, 0], + // 16.16 bit floating point + ], + }, + /* Elementary stream descriptor + basic box that holds only an ESDescriptor + reference: 'https://developer.apple.com/library/content/documentation/QuickTime/ + QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-124774' + Descriptors have a tag that identifies them, specified in ISO/IEC 14496-1 8.3.12 + ISO/IEC 14496-1 8.3.3 (pp. 24) ES_Descriptor + aligned(8) class ES_Descriptor : bit(8) tag=ES_DescrTag { + bit(8) length; + bit(16) ES_ID; + bit(1) streamDependenceFlag; + bit(1) URL_Flag; + const bit(1) reserved=1; + bit(5) streamPriority; + if (streamDependenceFlag) + bit(16) dependsOn_ES_ID; + if (URL_Flag) + bit(8) URLstring[length-3-(streamDependencFlag*2)]; + ExtensionDescriptor extDescr[0 .. 255]; + LanguageDescriptor langDescr[0 .. 1]; + DecoderConfigDescriptor decConfigDescr; + SLConfigDescriptor slConfigDescr; + IPI_DescPointer ipiPtr[0 .. 1]; + IP_IdentificationDataSet ipIDS[0 .. 1]; + QoS_Descriptor qosDescr[0 .. 1]; + } + aligned(8) class DecoderConfigDescriptor + : bit(8) tag=DecoderConfigDescrTag { + bit(8) length; + bit(8) objectProfileIndication; + bit(6) streamType; + bit(1) upStream; + const bit(1) reserved=1; + bit(24) bufferSizeDB; + bit(32) maxBitrate; + bit(32) avgBitrate; + DecoderSpecificInfo decSpecificInfo[]; + } + aligned(8) class DecoderSpecificInfoShort extends DecoderSpecificInfo + : bit(8) tag=DecSpecificInfoShortTag + { + bit(8) length; + bit(8) specificInfo[length]; + } + aligned(8) class SLConfigDescriptor : bit(8) tag=SLConfigDescrTag { + bit(8) length; + bit(8) predefined; + if (predefined==0) { + bit(1) useAccessUnitStartFlag; + bit(1) useAccessUnitEndFlag; + bit(1) useRandomAccessPointFlag; + bit(1) usePaddingFlag; + bit(1) useTimeStampsFlag; + bit(1) useWallClockTimeStampFlag; + bit(1) useIdleFlag; + bit(1) durationFlag; + bit(32) timeStampResolution; + bit(32) OCRResolution; + bit(8) timeStampLength; // must be less than 64 + bit(8) OCRLength; + // must be less than 64 + bit(8) AU_Length; + // must be less than 32 + bit(8) instantBitrateLength; + bit(4) degradationPriorityLength; + bit(4) seqNumLength; + if (durationFlag) { + bit(32) timeScale; + bit(16) accessUnitDuration; + bit(16) compositionUnitDuration; + } + if (!useTimeStampsFlag) { + if (useWallClockTimeStampFlag) + double(64) wallClockTimeStamp; + bit(timeStampLength) startDecodingTimeStamp; + bit(timeStampLength) startCompositionTimeStamp; + } + } + aligned(8) bit(1) OCRstreamFlag; + const bit(7) reserved=0b1111.111; + if (OCRstreamFlag) + bit(16) OCR_ES_Id; + } + */ + esds: { + container: 'mp4a', + mandatory: false, + quantity: 'one', + box: 'FullBox', + is_container: false, + body: [ + ['ES_DescrTag', UInt8, 3], + // length of the remainder of this descriptor in byte, + // excluding trailing embedded descriptors. + ['ES_DescrLength', UInt8, 25], + ['ES_ID', UInt16BE, 1], + ['flagsAndStreamPriority', UInt8, 0], + ['DecoderConfigDescrTag', UInt8, 4], + // length of the remainder of this descriptor in bytes, + // excluding trailing embedded descriptors. + ['DecoderConfigDescrLength', UInt8, 17], + ['objectProfileIndication', UInt8, 64], + ['streamTypeUpstreamReserved', UInt8, 21], + ['bufferSizeDB', UInt8Array, [0, 0, 0]], + ['maxBitRate', UInt32BE, 0], + ['avgBitRate', UInt32BE, 0], + ['DecSpecificInfoShortTag', UInt8, 5], + ['DecSpecificInfoShortLength', UInt8, 2], + ['audioConfigBytes', UInt16BE, 0], + ['SLConfigDescrTag', UInt8, 6], + ['SLConfigDescrLength', UInt8, 1], + ['SLConfigDescrPredefined', UInt8, 2], + // ISO use + ], + }, + // Sample Size Box + stsz: { + container: 'stbl', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: false, + body: [ + ['sample_size', UInt32BE, 0], + ['sample_count', UInt32BE, 0], + // For each sample up to sample_count, append an entry_size: + // ['entry_size', UInt32BE, ], + ], + }, + // Sample To Chunk Box + stsc: { + container: 'stbl', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: false, + body: [ + ['entry_count', UInt32BE, 0], + // For each entry up to entry_count, append these elements: + // ['first_chunk', UInt32BE, ], + // ['samples_per_chunk', UInt32BE, ], + // ['samples_description_index', UInt32BE, ], + ], + }, + // Chunk Offset Box + stco: { + container: 'stbl', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: false, + body: [ + ['entry_count', UInt32BE, 0], + // For each entry up to entry_count, append an element: + // ['chunk_offset', UInt32BE, ], + ], + }, + // Sync Sample Box + stss: { + container: 'stbl', + mandatory: false, + quantity: 'one-', + box: 'FullBox', + is_container: false, + body: [ + ['entry_count', UInt32BE, 0], + // For each entry up to entry_count, append an element: + // ['sample_number', UInt32BE, ], + ], + }, + // Edit Box + edts: { + container: 'trak', + mandatory: false, + quantity: 'one-', + box: 'Box', + is_container: true, + }, + // Edit List Box + elst: { + container: 'edts', + mandatory: false, + quantity: 'one-', + box: 'FullBox', + is_container: false, + body: [ + ['entry_count', UInt32BE, 1], + ['segment_duration', UInt32BE, 0], + ['media_time', UInt32BE, 4294967295], + ['media_rate_integer', UInt16BE, 1], + ['media_rate_fraction', UInt16BE, 0], + ], + }, + mvex: { + container: 'moov', + mandatory: false, + quantity: 'one-', + box: 'Box', + is_container: true, + }, + mehd: { + container: 'mvex', + mandatory: false, + quantity: 'one-', + box: 'FullBox', + is_container: false, + body: [ + ['fragment_duration', UInt32BE, 0], + // Total duration of movie + ], + }, + trex: { + container: 'mvex', + mandatory: true, + quantity: 'one+', + box: 'FullBox', + is_container: false, + body: [ + ['track_ID', UInt32BE, 1], + // The track to which this data is applicable + ['default_sample_description_index', UInt32BE, 1], + ['default_sample_duration', UInt32BE, 0], + ['default_sample_size', UInt32BE, 0], + ['default_sample_flags', UInt32BE, 0], + ], + }, + moof: { + container: 'file', + mandatory: false, + quantity: 'zero+', + box: 'Box', + is_container: false, + }, + mfhd: { + container: 'moof', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: false, + body: [ + ['sequence_number', UInt32BE, 0], + // A number associated with this fragment + ], + }, + traf: { + container: 'moof', + mandatory: false, + quantity: 'zero+', + box: 'Box', + is_container: true, + }, + tfhd: { + container: 'traf', + mandatory: true, + quantity: 'one', + box: 'FullBox', + is_container: false, + // Flag values for the track fragment header: + // 0x000001 base-data-offset-present + // 0x000002 sample-description-index-present + // 0x000008 default-sample-duration-present + // 0x000010 default-sample-size-present + // 0x000020 default-sample-flags-present + // 0x010000 duration-is-empty + // 0x020000 default-base-is-moof + config: { + flags: 32, + // default sample flags present + }, + body: [ + ['track_ID', UInt32BE, 1], + // The track to which this data is applicable + // ['base_data_offset', UInt64BE, 0], + // ['default_sample_description_index', UInt32BE, 0], + // ['default_sample_duration', UInt32BE, 0], + // ['default_sample_size', UInt32BE, 0], + ['default_sample_flags', UInt32BE, 0], + ], + }, + tfdt: { + container: 'traf', + mandatory: false, + quantity: 'one-', + box: 'FullBox', + is_container: false, + config: { + version: 1, + // Version 1 uses 64-bit value for baseMediaDecodeTime + }, + body: [['baseMediaDecodeTime', UInt64BE, 0]], + }, + trun: { + container: 'traf', + mandatory: false, + quantity: 'zero+', + box: 'FullBox', + is_container: false, + // Flag values for the track fragment header: + // 0x000001 data-offset-present + // 0x000004 first-sample-flags-present + // 0x000100 sample-duration-present + // 0x000200 sample-size-present + // 0x000400 sample-flags-present + // 0x000800 sample-composition-time-offsets-present + config: { + flags: 773, + // default sample flags present + }, + body: [ + ['sample_count', UInt32BE, 1], + // How many samples there are + ['data_offset', UInt32BE, 0], + ['first_sample_flags', UInt32BE, 0], + ['sample_duration', UInt32BE, 0], + ['sample_size', UInt32BE, 0], + // ['sample_flags', UInt32BE, 0], + // ['sample_composition_time_offset', UInt32BE, 0], + ], + }, + // Unknown Box, used for parsing + '....': { + box: 'Box', + is_container: false, + body: [], + }, + // File Box, special box without any headers + file: { + box: 'None', + is_container: true, + mandatory: true, + quantity: 'one', + }, +} +var Header = class { + static None() { + return [] + } + static Box(type) { + return [ + ['size', UInt32BE, 0], + ['type', CharArray, type], + ] + } + static FullBox(type) { + return [].concat(this.Box(type), [ + ['version', UInt8, 0], + ['flags', UInt24BE, 0], + ]) + } +} +var Box = class extends BoxElement { + type + config + struct + /** + * Create a new Box. + * @param type 4-character ASCII string + * @param config Configuration holding (key: value) fields + */ + constructor(type, config) { + super(0) + this.type = type + const spec = BOXSPEC[this.type] + if (spec === void 0) { + throw new Error(`unknown box type: ${type}`) + } + this.config = Object.assign({}, spec.config, config) + const header = Header[spec.box](this.type) + const body = spec.body || [] + this.struct = /* @__PURE__ */ new Map() + let offset = 0 + for (const [key, Type, defaultValue] of [].concat(header, body)) { + if (this.has(key)) { + throw new Error('Trying to add existing key') + } + let value = defaultValue + if (this.config[key]) { + value = this.config[key] + } + const element = new Type(value) + this.struct.set(key, { offset, element }) + offset += element.byteLength + } + this.byteLength = offset + } + /** + * Get access to an element based on it's name. + * @param key The element's name + * @return Object with 'byteLength' property and 'copy' method + */ + element(key) { + const value = this.struct.get(key) + if (value === void 0) { + throw new Error('invalid key') + } + return value.element + } + /** + * Set an element's value. + * @param key The element's name + * @param value The element's (new) value + */ + set(key, value) { + this.element(key).value = value + } + /** + * Get an element's value. + * @param key The element's name + * @return The element's value + */ + get(key) { + return this.element(key).value + } + /** + * Get an element's offset. + * @param key The element's name + * @return The element's offset + */ + offset(key) { + const value = this.struct.get(key) + if (value === void 0) { + throw new Error('invalid key') + } + return value.offset + } + /** + * Check if a certain element exists + * @param key The element's name + * @return true if the element is known, false if not + */ + has(key) { + return this.struct.has(key) + } + /** + * Add a new element to the box. + * @param key A _new_ non-existing element name. + * @param element Something with a 'byteLength' property and 'copy' method. + * @return this box, so that 'add' can be used in a chain + */ + add(key, element) { + if (this.has(key)) { + throw new Error('Trying to add existing key') + } + this.struct.set(key, { offset: this.byteLength, element }) + this.byteLength += element.byteLength + return this + } + /** + * Create a buffer and copy all element values to it. + * @return Data representing the box. + */ + buffer() { + const buffer = Buffer.allocUnsafe(this.byteLength) + this.copy(buffer) + return buffer + } + /** + * Copy all values of the box into an existing buffer. + * @param buffer The target buffer to accept the box data + * @param [offset=0] The number of bytes into the target to start at. + */ + copy(buffer, offset = 0) { + this.set('size', this.byteLength) + for (const entry of this.struct.values()) { + entry.element.copy(buffer, offset + entry.offset) + } + } + /** + * Read element values from a box's data representation. + * @param buffer The source buffer with box data + * @param [offset=0] The number of bytes into the source to start at. + */ + load(buffer, offset = 0) { + for (const entry of this.struct.values()) { + if (entry.element.load !== void 0) { + entry.element.load(buffer, offset + entry.offset) + } + } + } + /** + * Pretty-format an entire box as an element/box hierarchy. + * @param [indent=0] How large an indentation to use for the hierarchy + */ + format(indent = 0) { + const lines = [`${' '.repeat(indent)}[${this.type}] (${this.byteLength})`] + for (const [key, entry] of this.struct) { + const element = entry.element + if (element.format !== void 0) { + lines.push(element.format(indent + 2)) + } else { + lines.push( + `${ + ' '.repeat(indent + 2) + }${key} = ${element.value} (${element.byteLength})` + ) + } + } + return lines.join('\n') + } + /** + * Pretty-print an entire box as an element/box hierarchy. + * @param [indent=0] How large an indentation to use for the hierarchy + */ + print(indent) { + console.warn(this.format(indent)) + } +} +var Container = class extends Box { + boxSize + /** + * Create a new container box + * @param type 4-character ASCII string + * @param config Configuration holding (key: value) fields + * @param boxes One or more boxes to append. + */ + constructor(type, config, ...boxes) { + super(type, config) + this.boxSize = 0 + this.append(...boxes) + } + /** + * Add one or more boxes to the container. + * @param boxes The box(es) to append + * @return this container, so that add can be used in a chain + */ + append(...boxes) { + for (const box of boxes) { + this.add(`box_${this.boxSize++}`, box) + } + return this + } + /** + * Parse a container box by looking for boxes that it contains, and + * recursively proceed when it is another container. + * + * FIXME: this cannot properly handle different versions of the FullBox, + * currenlty the loader is hardcoded to the version used in this file. + * Also, appearance of an esds box is assumed to be AAC audio information, + * while the avcC box signals H.264 video information. + * + * @param data The data to parse. + */ + parse(data) { + const tracks = [] + while (data.byteLength > 0) { + const type = new CharArray('....') + type.load(data, 4) + const boxType = type.value + const spec = BOXSPEC[boxType] + let box + if (spec !== void 0) { + if (spec.is_container) { + box = new Container(boxType) + box.load(data) + const boxTracks = box.parse( + data.slice(box.byteLength, box.get('size')) + ) + tracks.push(...boxTracks) + } else { + box = new Box(boxType) + box.load(data) + if (boxType === 'avcC') { + const profile = box.element('AVCProfileIndication').value.toString( + 16 + ).padStart(2, 0) + const compat = box.element('profile_compatibility').value.toString( + 16 + ).padStart(2, 0) + const level = box.element('AVCLevelIndication').value.toString(16) + .padStart(2, 0) + tracks.push({ + type: 'video', + mime: `avc1.${profile}${compat}${level}`, + }) + } else if (boxType === 'esds') { + const audioConfigBytes = box.element('audioConfigBytes').value + const objectTypeIndication = audioConfigBytes >>> 11 & 31 + tracks.push({ + type: 'audio', + mime: `mp4a.40.${objectTypeIndication}`, + }) + } + } + } else { + box = new Box('....') + box.load(data) + box.type = box.get('type') + } + this.append(box) + data = data.slice(box.get('size')) + } + return tracks + } +} +var AUDIO_OBJECT_TYPE_NAMES = { + 1: 'AAC Main', + 2: 'AAC LC', +} +var FREQUENCY_VALUES = { + 0: '96 kHz', + 1: '88.2 kHz', + 2: '64 kHz', + 3: '48 kHz', + 4: '44.1 kHz', + 5: '32 kHz', + 6: '24 kHz', + 7: '22.05 kHz', + 8: '16 kHz', + 9: '12 kHz', + 10: '11.025 kHz', + 11: '8 kHz', + 12: '7.35 kHz', +} +var CHANNEL_CONFIG_NAMES = { + 1: 'Mono', + 2: 'Stereo', +} +var aacEncodingName = (audioConfigBytes) => { + const audioObjectType = audioConfigBytes >>> 11 & 31 + const frequencyIndex = audioConfigBytes >>> 7 & 15 + const channelConfig = audioConfigBytes >>> 3 & 15 + const audioType = AUDIO_OBJECT_TYPE_NAMES[audioObjectType] + || `AAC (${audioObjectType})` + const samplingRate = FREQUENCY_VALUES[frequencyIndex] || 'unknown' + const channels = CHANNEL_CONFIG_NAMES[channelConfig] + || channelConfig.toString() + return { + coding: audioType, + samplingRate, + channels, + } +} +var aacSettings = (media, date, trackId) => { + const bitrate = Number(media.fmtp.parameters.bitrate) || 32e4 + const audioConfigBytes = parseInt(media.fmtp.parameters.config, 16) + const audioObjectType = audioConfigBytes >>> 11 & 31 + return { + tkhd: { + track_ID: trackId, + creation_time: date, + modification_time: date, + width: 0, + height: 0, + volume: 1, + }, + mdhd: { + timescale: Number(media.rtpmap.clockrate), + creation_time: date, + modification_time: date, + duration: 0, + }, + hdlr: { + handler_type: 'soun', + name: 'SoundHandler\0', + // 00 soundhandler, add 00 if things screws up + }, + mediaHeaderBox: new Box('smhd'), + sampleEntryBox: new Container( + 'mp4a', + { + samplerate: media.rtpmap.clockrate << 16 >>> 0, + // FIXME: Is this correct? + }, + new Box('esds', { + audioConfigBytes, + // Converting from hex string to int + maxBitRate: bitrate, + avgBitRate: bitrate, + }) + ), + /* + https://wiki.multimedia.cx/index.php/Understanding_AAC + AAC is a variable bitrate (VBR) block-based codec where each block decodes + to 1024 time-domain samples, which means that a single block (or frame?) is + 1024 ticks long, which we take as default here. + */ + defaultFrameDuration: 1024, + // MIME type + mime: `mp4a.40.${audioObjectType}`, + codec: aacEncodingName(audioConfigBytes), + } +} +var BufferReader = class { + _buffer + _dataView + _offset + _bitpos + _byte + constructor(buffer) { + this._buffer = buffer + this._dataView = new DataView(this._buffer) + this._offset = 0 + this._bitpos = 0 + this._byte = 0 + } + /** + * Reads 8-bit of data from the buffer. + * @method readUint8 + * @param offset - Index in the buffer. + * @return An unsigned 8-bit integer. + */ + readUint8(offset) { + return this._dataView.getUint8(offset) + } + /** + * Reads 16-bit of data from the buffer. + * @method readUint16 + * @param offset - Index in the buffer. + * @return An unsigned 16-bit integer. + */ + readUint16(offset) { + return this._dataView.getUint16(offset) + } + /** + * Reads 32-bit of data from the buffer. + * @method readUint32 + * @param offset - Index in the buffer. + * @return An unsigned 32-bit integer. + */ + readUint32(offset) { + return this._dataView.getUint32(offset) + } + /** + * Reads the next byte of data from the buffer and increaments the offset. + * @method readNext + * @return {Number} An unsigned 8-bit integer. + */ + readNext() { + const value = this.readUint8(this._offset) + this._offset += 1 + return value + } + readBits(length) { + if (length > 32 || length === 0) { + throw new Error('length has to be between 0 - 31 bits') + } + let result = 0 + for (let i = 1; i <= length; ++i) { + if (this._bitpos === 0) { + this._byte = this.readNext() + } + result = result << 1 | this._byte >> 8 - ++this._bitpos & 1 + this._bitpos %= 8 + } + return result + } + readUnsignedExpGolomb() { + let bitsToRead = 0 + while (this.readBits(1) !== 1) { + bitsToRead++ + } + if (bitsToRead === 0) { + return 0 + } + if (bitsToRead >= 31) { + throw new Error('read unsigned exponential Golomb: internal error') + } + let n = this.readBits(bitsToRead) + n |= 1 << bitsToRead + return n - 1 + } + readSignedExpGolomb() { + let r = this.readUnsignedExpGolomb() + if (r & 1) { + r = r + 1 >> 1 + } else { + r = -(r >> 1) + } + return r + } + /** + * Returns the size of the buffer + * @method readSize + * @return {Number} The buffer size. + */ + size() { + return this._buffer.byteLength + } + /** + * Returns an instance of the buffer as an unsigned 8-bit integer array. + * @method getUint8Array + * @return {Uint8Array} Unsigned 8-bit integer representation of the buffer + */ + getUint8Array() { + return new Uint8Array(this._buffer) + } + /** + * Returns the buffer object + * @method getArrayBuffer + * @return {ArrayBuffer} The buffer used the BufferReader + */ + getArrayBuffer() { + return this._buffer + } +} +var SPSParser = class { + reader + constructor(buffer) { + this.reader = new BufferReader(buffer) + } + parse() { + this.reader.readNext() + const profile = this.reader.readNext() + this.reader.readNext() + const level = this.reader.readNext() + this.reader.readUnsignedExpGolomb() + if ([100, 110, 122, 244, 44, 83, 86, 118].includes(profile)) { + const chromaFormat = this.reader.readUnsignedExpGolomb() + if (chromaFormat === 3) { + this.reader.readBits(1) + } + this.reader.readUnsignedExpGolomb() + this.reader.readUnsignedExpGolomb() + this.reader.readBits(1) + const seqScalingMatrix = this.reader.readBits(1) + if (seqScalingMatrix) { + for (let k = 0; k < (chromaFormat !== 3 ? 8 : 12); k++) { + this.reader.readBits(1) + } + } + } + this.reader.readUnsignedExpGolomb() + const picOrderCntType = this.reader.readUnsignedExpGolomb() + if (picOrderCntType === 0) { + this.reader.readUnsignedExpGolomb() + } else if (picOrderCntType === 1) { + let numRefFramesInPic = 0 + this.reader.readBits(1) + this.reader.readSignedExpGolomb() + this.reader.readSignedExpGolomb() + numRefFramesInPic = this.reader.readUnsignedExpGolomb() + for (let i = 0; i < numRefFramesInPic; i++) { + this.reader.readSignedExpGolomb() + } + } + this.reader.readUnsignedExpGolomb() + this.reader.readBits(1) + const picWidthInMbsMinus1 = this.reader.readUnsignedExpGolomb() + const picHeightInMapUnitsMinus1 = this.reader.readUnsignedExpGolomb() + const picFrameMbsOnlyFlag = this.reader.readBits(1) + this.reader.readBits(1) + const frameCroppingFlag = this.reader.readBits(1) + const frameCropLeftOffset = frameCroppingFlag + ? this.reader.readUnsignedExpGolomb() + : 0 + const frameCropRightOffset = frameCroppingFlag + ? this.reader.readUnsignedExpGolomb() + : 0 + const frameCropTopOffset = frameCroppingFlag + ? this.reader.readUnsignedExpGolomb() + : 0 + const frameCropBottomOffset = frameCroppingFlag + ? this.reader.readUnsignedExpGolomb() + : 0 + const w = (picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 + - frameCropRightOffset * 2 + const h = (2 - picFrameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 + - frameCropTopOffset * 2 - frameCropBottomOffset * 2 + return { + profile, + level: level / 10, + width: w, + height: h, + } + } +} +function b64ToUint6(nChr) { + return nChr > 64 && nChr < 91 + ? nChr - 65 + : nChr > 96 && nChr < 123 + ? nChr - 71 + : nChr > 47 && nChr < 58 + ? nChr + 4 + : nChr === 43 + ? 62 + : nChr === 47 + ? 63 + : 0 +} +function base64DecToArr(sBase64, nBlocksSize) { + const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, '') + const nInLen = sB64Enc.length + const nOutLen = nBlocksSize + ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize + : nInLen * 3 + 1 >> 2 + const taBytes = new Uint8Array(nOutLen) + let nMod3 + let nMod4 + let nUint24 = 0 + let nOutIdx = 0 + for (let nInIdx = 0; nInIdx < nInLen; nInIdx++) { + nMod4 = nInIdx & 3 + nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4 + if (nMod4 === 3 || nInLen - nInIdx === 1) { + for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { + taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255 + } + nUint24 = 0 + } + } + return taBytes +} +var PROFILE_NAMES = { + 66: 'Baseline', + 77: 'Main', + 100: 'High', +} +var h264EncodingName = (profileLevelId) => { + const profileCode = parseInt(profileLevelId.substr(0, 2), 16) + const levelCode = parseInt(profileLevelId.substr(4, 2), 16) + const profile = PROFILE_NAMES[profileCode] || profileCode.toString() + const level = (levelCode / 10).toFixed(1) + return { + coding: 'H.264', + profile, + level, + } +} +var h264Settings = (media, date, trackId) => { + const profileLevelId = media.fmtp.parameters['profile-level-id'] + const parameterSets = media.fmtp.parameters['sprop-parameter-sets'].split(',') + .map(base64DecToArr) + const sps = parameterSets.slice(0, 1) + const pps = parameterSets.slice(1) + const parsedSps = new SPSParser(sps[0].buffer).parse() + const FALLBACK_FRAME_DURATION = 3600 + return { + mediaHeaderBox: new Box('vmhd'), + sampleEntryBox: new Container( + 'avc1', + { + width: parsedSps.width, + height: parsedSps.height, + }, + new Box('avcC', { + AVCProfileIndication: sps[0][1], + profile_compatibility: sps[0][2], + AVCLevelIndication: sps[0][3], + sequenceParameterSets: sps, + pictureParameterSets: pps, + }) + ), + tkhd: { + track_ID: trackId, + creation_time: date, + modification_time: date, + width: parsedSps.width << 16, + height: parsedSps.height << 16, + volume: 0, + }, + hdlr: {}, + mdhd: { + timescale: media.rtpmap.clockrate, + creation_time: date, + modification_time: date, + duration: 0, + }, + // (ticks / s) / (frames / s) = ticks / frame, e.g. frame duration in ticks + defaultFrameDuration: media.framerate !== void 0 && media.framerate > 0 + ? Number(media.rtpmap.clockrate) / Number(media.framerate) + || FALLBACK_FRAME_DURATION + : FALLBACK_FRAME_DURATION, + // MIME type + mime: `avc1.${profileLevelId}`, + codec: h264EncodingName(profileLevelId), + } +} +var formatDefaults = { + 'MPEG4-GENERIC': aacSettings, + H264: h264Settings, +} +var createTrackData = () => { + return { + lastTimestamp: 0, + baseMediaDecodeTime: 0, + defaultFrameDuration: 0, + clockrate: 0, + bitrate: 0, + framerate: 0, + cumulativeByteLength: 0, + cumulativeDuration: 0, + cumulativeFrames: 0, + } +} +var updateRateInfo = (trackData, { byteLength, duration }) => { + trackData.cumulativeByteLength += byteLength + trackData.cumulativeDuration += duration + trackData.cumulativeFrames++ + if (trackData.cumulativeDuration >= trackData.clockrate) { + const bits = 8 * trackData.cumulativeByteLength + const frames = trackData.cumulativeFrames + const seconds = trackData.cumulativeDuration / trackData.clockrate + trackData.bitrate = bits / seconds + trackData.framerate = frames / seconds + trackData.cumulativeByteLength = 0 + trackData.cumulativeDuration = 0 + trackData.cumulativeFrames = 0 + } +} +var BoxBuilder = class { + trackIdMap + sequenceNumber + ntpPresentationTime + trackData + videoTrackId + constructor() { + this.trackIdMap = {} + this.sequenceNumber = 0 + this.ntpPresentationTime = 0 + this.trackData = [] + } + trak(settings) { + const trak = new Container('trak') + const mdia = new Container('mdia') + const minf = new Container('minf') + const dinf = new Container('dinf') + const dref = new Container('dref') + const stbl = new Container('stbl') + dref.set('entry_count', 1) + trak.append( + new Box('tkhd', settings.tkhd), + mdia.append( + new Box('mdhd', settings.mdhd), + new Box('hdlr', settings.hdlr), + minf.append( + settings.mediaHeaderBox, + // vmhd or smhd box (video or sound) + dinf.append(dref.append(new Box('url '))), + stbl.append( + new Container('stsd', void 0, settings.sampleEntryBox), + new Box('stts'), + new Box('stsc'), + new Box('stco'), + new Box('stsz'), + new Box('stss') + ) + ) + ) + ) + return trak + } + /** + * Creates a Moov box from the provided options. + * @method moov + * @param sdp - The session description protocol + * @param date - The creation/modification time of the movie + * @return Moov object + */ + moov(sdp, date) { + const moov = new Container('moov') + moov.append( + new Box('mvhd', { + creation_time: date, + modification_time: date, + duration: 0, + }) + ) + const mvex = new Container('mvex') + this.trackIdMap = {} + this.sequenceNumber = 0 + this.ntpPresentationTime = 0 + let trackId = 0 + this.trackData = [] + sdp.media.forEach((media) => { + if (media.rtpmap === void 0) { + return + } + const payloadType2 = media.rtpmap.payloadType + const encoding = media.rtpmap.encodingName + if (formatDefaults[encoding] !== void 0) { + this.trackIdMap[payloadType2] = ++trackId + if (media.type.toLowerCase() === 'video') { + this.videoTrackId = trackId + } + const settings = formatDefaults[encoding](media, date, trackId) + media.mime = settings.mime + media.codec = settings.codec + const trackData = createTrackData() + trackData.clockrate = media.rtpmap.clockrate + trackData.defaultFrameDuration = settings.defaultFrameDuration + this.trackData.push(trackData) + const trak = this.trak(settings) + moov.append(trak) + mvex.append(new Box('trex', { track_ID: trackId })) + } + }) + moov.append(mvex) + return moov + } + /** + * Boxes that carry actual elementary stream fragment metadata + data. + */ + /** + * Creates a moof box from the provided fragment metadata. + * @method moof + * @param metadata - Track ID, timestamp, bytelength + * @return moof Container + */ + moof(metadata) { + const { trackId, timestamp: timestamp2, byteLength } = metadata + const trackOffset = trackId - 1 + const trackData = this.trackData[trackOffset] + const duration = trackData.lastTimestamp !== 0 + ? timestamp2 - trackData.lastTimestamp | 0 + : trackData.defaultFrameDuration + trackData.lastTimestamp = timestamp2 + const moof = new Container('moof') + const traf = new Container('traf') + const trun = new Box('trun', { + sample_duration: duration, + sample_size: byteLength, + first_sample_flags: 64, + }) + moof.append( + new Box('mfhd', { sequence_number: this.sequenceNumber++ }), + traf.append( + new Box('tfhd', { track_ID: trackId }), + new Box('tfdt', { baseMediaDecodeTime: trackData.baseMediaDecodeTime }), + trun + ) + ) + trackData.baseMediaDecodeTime += duration + trun.set('data_offset', moof.byteLength + 8) + updateRateInfo(trackData, { byteLength, duration }) + return moof + } + /** + * Creates an mdat box containing the elementary stream data. + * @param data - Elementary stream data + * @return mdat Box + */ + mdat(data) { + const box = new Box('mdat') + box.add('data', data) + return box + } + setPresentationTime(trackId, ntpTimestamp) { + if ( + !this.ntpPresentationTime && ntpTimestamp && trackId === this.videoTrackId + ) { + const trackOffset = trackId - 1 + const trackData = this.trackData[trackOffset] + this.ntpPresentationTime = ntpTimestamp + - 1e3 * (trackData.baseMediaDecodeTime / trackData.clockrate) + } + } +} +var Mp4Muxer = class extends Tube { + boxBuilder + onSync + /** + * Create a new mp4muxer component. + * @return {undefined} + */ + constructor() { + const boxBuilder = new BoxBuilder() + const onSync = (ntpPresentationTime) => { + this.onSync && this.onSync(ntpPresentationTime) + } + const incoming = new Transform7({ + objectMode: true, + transform(msg, encoding, callback) { + if (msg.type === 5) { + const now = Math.floor( + (/* @__PURE__ */ new Date()).getTime() / 1e3 + 2082852e3 + ) + const ftyp = new Box('ftyp') + const moov = boxBuilder.moov(msg.sdp, now) + const data = Buffer.allocUnsafe(ftyp.byteLength + moov.byteLength) + ftyp.copy(data, 0) + moov.copy(data, ftyp.byteLength) + ;(0, import_debug3.default)('msl:mp4:isom')(`ftyp: ${ftyp.format()}`) + ;(0, import_debug3.default)('msl:mp4:isom')(`moov: ${moov.format()}`) + const tracks = msg.sdp.media.map((media) => { + return { + type: media.type, + encoding: media.rtpmap && media.rtpmap.encodingName, + mime: media.mime, + codec: media.codec, + } + }) + this.push({ type: 8, data, tracks, ftyp, moov }) + } else if (msg.type === 6 || msg.type === 7) { + const { + payloadType: payloadType2, + timestamp: timestamp2, + ntpTimestamp, + } = msg + const trackId = boxBuilder.trackIdMap[payloadType2] + if (trackId) { + if (!boxBuilder.ntpPresentationTime) { + boxBuilder.setPresentationTime(trackId, ntpTimestamp) + if (boxBuilder.ntpPresentationTime) { + onSync(boxBuilder.ntpPresentationTime) + } + } + let checkpointTime + const idrPicture = msg.type === 7 ? msg.nalType === 5 : void 0 + if ( + boxBuilder.ntpPresentationTime && idrPicture + && msg.ntpTimestamp !== void 0 + ) { + checkpointTime = + (msg.ntpTimestamp - boxBuilder.ntpPresentationTime) / 1e3 + } + const byteLength = msg.data.byteLength + const moof = boxBuilder.moof({ + trackId, + timestamp: timestamp2, + byteLength, + }) + const mdat = boxBuilder.mdat(msg.data) + const data = Buffer.allocUnsafe(moof.byteLength + mdat.byteLength) + moof.copy(data, 0) + mdat.copy(data, moof.byteLength) + this.push({ + type: 8, + data, + moof, + mdat, + ntpTimestamp, + checkpointTime, + }) + } + } else { + this.push(msg) + } + callback() + }, + }) + super(incoming) + this.boxBuilder = boxBuilder + } + get bitrate() { + return this.boxBuilder.trackData + && this.boxBuilder.trackData.map((data) => data.bitrate) + } + get framerate() { + return this.boxBuilder.trackData + && this.boxBuilder.trackData.map((data) => data.framerate) + } + get ntpPresentationTime() { + return this.boxBuilder.ntpPresentationTime + } +} +var ONVIFDepay = class extends Tube { + constructor() { + let XMLPayloadType + let packets = [] + const incoming = new Transform8({ + objectMode: true, + transform(msg, encoding, callback) { + if (msg.type === 5) { + let validMedia + for (const media of msg.sdp.media) { + if ( + media.type === 'application' && media.rtpmap + && media.rtpmap.encodingName === 'VND.ONVIF.METADATA' + ) { + validMedia = media + } + } + if (validMedia && validMedia.rtpmap) { + XMLPayloadType = Number(validMedia.rtpmap.payloadType) + } + callback(void 0, msg) + } else if (msg.type === 2 && payloadType(msg.data) === XMLPayloadType) { + packets.push(payload(msg.data)) + if (marker(msg.data) && packets.length > 0) { + const xmlMsg = { + timestamp: timestamp(msg.data), + ntpTimestamp: msg.ntpTimestamp, + payloadType: payloadType(msg.data), + data: Buffer.concat(packets), + type: 9, + /* XML */ + } + callback(void 0, xmlMsg) + packets = [] + return + } + callback() + } else { + callback(void 0, msg) + } + }, + }) + super(incoming) + } +} +var Recorder = class extends Tube { + /** + * Create a new recorder component that will record to a writable stream. + * @param fileStream - The stream to save the messages to. + */ + constructor(fileStream) { + const incoming = StreamFactory.recorder('incoming', fileStream) + const outgoing = StreamFactory.recorder('outgoing', fileStream) + const interleaved = { incoming, outgoing } + const streamsFinished = [] + for (const [key, value] of Object.entries(interleaved)) { + streamsFinished.push( + new Promise( + (resolve) => + value.on('finish', () => { + const timestamp2 = Date.now() + const message = null + const type = key + fileStream.write( + JSON.stringify( + { type, timestamp: timestamp2, message }, + null, + 2 + ) + ) + fileStream.write(',\n') + resolve() + }) + ) + ) + } + fileStream.write('[\n') + Promise.all(streamsFinished).then(() => { + fileStream.write(JSON.stringify(null)) + fileStream.write('\n]\n') + }).catch(() => { + }) + super(incoming, outgoing) + } + /** + * Create a new recorder component that will record to a file. + * @param filename - The name of the file (relative to cwd) + */ + static toFile(filename = 'data.json') { + const cwd = process.cwd() + const fileStream = createWriteStream(join(cwd, filename)) + return new Recorder(fileStream) + } +} +var sleep = async (ms) => { + return await new Promise((resolve) => { + setTimeout(resolve, ms) + }) +} +var Replayer = class extends Source { + /** + * Create a new replay component that will play provided data. + * The packets need to conform to the format: + * { + * type: 'incoming'/'outgoing', + * delay: Number, + * msg: Object (original message) + * } + * @param packetStream - The JSON data to replay. + */ + constructor(packetStream) { + let finished = false + const incoming = new Readable3({ + objectMode: true, + read() { + }, + }) + const start = async () => { + let packet = packetStream.read() + while (packet && packet.type === 'incoming') { + await sleep(packet.delay) + incoming.push(packet.msg) + packet = packetStream.read() + } + if (finished) { + incoming.push(null) + } + } + const outgoing = new Writable3({ + objectMode: true, + write(msg, encoding, callback) { + start().catch(() => { + }) + callback() + }, + }) + outgoing.on('finish', () => { + finished = true + }) + outgoing.on('pipe', async () => await start()) + super(incoming, outgoing) + } + /** + * Create a new replay component that will play from a file. + * @param filename - The name of the file (relative to cwd) + */ + static fromFile(filename = 'data.json') { + const cwd = process.cwd() + const data = readFileSync(join2(cwd, filename)) + const packets = JSON.parse(data.toString()) + const packetStream = StreamFactory.replayer(packets) + return new Replayer(packetStream) + } +} +var import_debug4 = __toESM2(require_src()) +var DEFAULT_PROTOCOL = 'RTSP/1.0' +var builder = (msg) => { + if (!msg.method || !msg.uri) { + throw new Error('message needs to contain a method and a uri') + } + const protocol = msg.protocol || DEFAULT_PROTOCOL + const headers = msg.headers || {} + const messageString = [ + `${msg.method} ${msg.uri} ${protocol}`, + Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join( + '\r\n' + ), + '\r\n', + ].join('\r\n') + ;(0, import_debug4.default)('msl:rtsp:outgoing')(messageString) + return Buffer.from(messageString) +} +var RTCPPacketType = /* @__PURE__ */ ((RTCPPacketType2) => { + RTCPPacketType2[RTCPPacketType2['SR'] = 200] = 'SR' + RTCPPacketType2[RTCPPacketType2['RR'] = 201] = 'RR' + RTCPPacketType2[RTCPPacketType2['SDES'] = 202] = 'SDES' + RTCPPacketType2[RTCPPacketType2['BYE'] = 203] = 'BYE' + RTCPPacketType2[RTCPPacketType2['APP'] = 204] = 'APP' + return RTCPPacketType2 +})(RTCPPacketType || {}) +var parseBase = (buffer) => ({ + version: buffer[0] >>> 6, + padding: !!(buffer[0] & POS[2]), + count: buffer[0] & 31, + packetType: buffer.readUInt8(1), + length: buffer.readUInt16BE(2), +}) +var parseRtcp = (buffer) => { + const base = parseBase(buffer) + switch (base.packetType) { + case 200: + return parseSR(buffer, base) + case 201: + return parseRR(buffer, base) + case 202: + return parseSDES(buffer, base) + case 203: + return parseBYE(buffer, base) + case 204: + return parseAPP(buffer, base) + default: + return base + } +} +var rtcpMessageFromBuffer = (channel, buffer) => { + return { + type: 3, + data: buffer, + channel, + rtcp: parseRtcp(buffer), + } +} +var SR = { + packetType: 200, +} +var parseReportBlocks = (count, buffer, offset) => { + const reports = [] + for (let reportNumber = 0; reportNumber < count; reportNumber++) { + const o = offset + reportNumber * 24 + reports.push({ + syncSource: buffer.readUInt32BE(o + 0), + fractionLost: buffer.readUInt8(o + 4), + cumulativeNumberOfPacketsLost: buffer.readUIntBE(o + 5, 3), + extendedHighestSequenceNumberReceived: buffer.readUInt32BE(o + 8), + interarrivalJitter: buffer.readUInt32BE(o + 12), + lastSRTimestamp: buffer.readUInt32BE(o + 16), + delaySinceLastSR: buffer.readUInt32BE(o + 20), + }) + } + return reports +} +var parseSR = (buffer, base) => ({ + ...base, + syncSource: buffer.readUInt32BE(4), + ntpMost: buffer.readUInt32BE(8), + ntpLeast: buffer.readUInt32BE(12), + rtpTimestamp: buffer.readUInt32BE(16), + sendersPacketCount: buffer.readUInt32BE(20), + sendersOctetCount: buffer.readUInt32BE(24), + reports: parseReportBlocks(base.count, buffer, 28), +}) +var isRtcpSR = (rtcp) => rtcp.packetType === 200 +var parseRR = (buffer, base) => ({ + ...base, + syncSource: buffer.readUInt32BE(4), + reports: parseReportBlocks(base.count, buffer, 8), +}) +var isRtcpRR = (rtcp) => rtcp.packetType === 201 +var SDESItem = /* @__PURE__ */ ((SDESItem2) => { + SDESItem2[SDESItem2['CNAME'] = 1] = 'CNAME' + SDESItem2[SDESItem2['NAME'] = 2] = 'NAME' + SDESItem2[SDESItem2['EMAIL'] = 3] = 'EMAIL' + SDESItem2[SDESItem2['PHONE'] = 4] = 'PHONE' + SDESItem2[SDESItem2['LOC'] = 5] = 'LOC' + SDESItem2[SDESItem2['TOOL'] = 6] = 'TOOL' + SDESItem2[SDESItem2['NOTE'] = 7] = 'NOTE' + SDESItem2[SDESItem2['PRIV'] = 8] = 'PRIV' + return SDESItem2 +})(SDESItem || {}) +var parseSDES = (buffer, base) => { + const sourceDescriptions = [] + let offset = 4 + for (let block = 0; block < base.count; block++) { + const chunk = { + source: buffer.readUInt32BE(offset), + items: [], + } + offset += 4 + while (true) { + const itemType = buffer.readUInt8(offset++) + if (itemType === 0) { + if (offset % 4 !== 0) { + offset += 4 - offset % 4 + } + break + } + const length = buffer.readUInt8(offset++) + if (itemType === 8) { + const prefixLength = buffer.readUInt8(offset) + const prefix = buffer.toString( + 'utf8', + offset + 1, + offset + 1 + prefixLength + ) + const value = buffer.toString( + 'utf8', + offset + 1 + prefixLength, + offset + length + ) + chunk.items.push([8, prefix, value]) + } else { + const value = buffer.toString('utf8', offset, offset + length) + chunk.items.push([itemType, value]) + } + offset += length + } + sourceDescriptions.push(chunk) + } + return { + ...base, + syncSource: buffer.readUInt32BE(4), + sourceDescriptions, + } +} +var isRtcpSDES = (rtcp) => rtcp.packetType === 202 +var parseBYE = (buffer, base) => { + const sources = [] + for (let block = 0; block < base.count; block++) { + sources.push(buffer.readUInt32BE(4 + 4 * block)) + } + let reason + if (base.length > base.count) { + const start = 4 + 4 * base.count + const length = buffer.readUInt8(start) + reason = buffer.toString('utf-8', start + 1, start + 1 + length) + } + return { + ...base, + sources, + reason, + } +} +var isRtcpBye = (rtcp) => rtcp.packetType === 203 +var parseAPP = (buffer, base) => { + return { + ...base, + subtype: base.count, + source: buffer.readUInt32BE(4), + name: buffer.toString('ascii', 8, 12), + data: buffer.slice(12), + } +} +var isRtcpApp = (rtcp) => rtcp.packetType === 204 +var extractLineVals = (buffer, lineStart, start = 0) => { + const anchor = ` +${lineStart}` + start = buffer.indexOf(anchor, start) + let end = 0 + const ret = [] + while (start >= 0) { + end = buffer.indexOf('\n', start + anchor.length) + ret.push(buffer.toString('ascii', start + anchor.length, end).trim()) + start = buffer.indexOf(anchor, end) + } + return ret +} +var newMediaLevel = (line) => { + return line.match(/^m=/) +} +var splitOnFirst = (c, text) => { + const p = text.indexOf(c) + if (p < 0) { + return [text.slice(0)] + } + return [text.slice(0, p), text.slice(p + 1)] +} +var attributeParsers = { + fmtp: (value) => { + const [format, stringParameters] = splitOnFirst(' ', value) + switch (format) { + default: { + const pairs = stringParameters.trim().split(';') + const parameters = {} + pairs.forEach((pair) => { + const [key, val] = splitOnFirst('=', pair) + const normalizedKey = key.trim().toLowerCase() + if (normalizedKey !== '') { + parameters[normalizedKey] = val.trim() + } + }) + return { format, parameters } + } + } + }, + framerate: Number, + rtpmap: (value) => { + const [payloadType2, encoding] = splitOnFirst(' ', value) + const [encodingName, clockrate, encodingParameters] = encoding.toUpperCase() + .split('/') + if (encodingParameters === void 0) { + return { + payloadType: Number(payloadType2), + encodingName, + clockrate: Number(clockrate), + } + } + return { + payloadType: Number(payloadType2), + encodingName, + clockrate: Number(clockrate), + encodingParameters, + } + }, + transform: (value) => { + return value.split(';').map((row) => row.split(',').map(Number)) + }, + 'x-sensor-transform': (value) => { + return value.split(';').map((row) => row.split(',').map(Number)) + }, + framesize: (value) => { + return value.split(' ')[1].split('-').map(Number) + }, +} +var parseAttribute = (body) => { + const [attribute, value] = splitOnFirst(':', body) + if (value === void 0) { + return { [attribute]: true } + } + if (attributeParsers[attribute] !== void 0) { + return { [attribute]: attributeParsers[attribute](value) } + } + return { [attribute]: value } +} +var extractField = (line) => { + const prefix = line.slice(0, 1) + const body = line.slice(2) + switch (prefix) { + case 'v': + return { version: body } + case 'o': { + const [ + username, + sessionId2, + sessionVersion, + netType, + addrType, + unicastAddress, + ] = body.split(' ') + return { + origin: { + addrType, + netType, + sessionId: sessionId2, + sessionVersion, + unicastAddress, + username, + }, + } + } + case 's': + return { sessionName: body } + case 'i': + return { sessionInformation: body } + case 'u': + return { uri: body } + case 'e': + return { email: body } + case 'p': + return { phone: body } + case 'c': { + const [connectionNetType, connectionAddrType, connectionAddress] = body + .split(' ') + return { + connectionData: { + addrType: connectionAddrType, + connectionAddress, + netType: connectionNetType, + }, + } + } + case 'b': { + const [bwtype, bandwidth] = body.split(':') + return { bwtype, bandwidth } + } + case 't': { + const [startTime, stopTime] = body.split(' ').map(Number) + return { time: { startTime, stopTime } } + } + case 'r': { + const [repeatInterval, activeDuration, ...offsets] = body.split(' ').map( + Number + ) + return { + repeatTimes: { repeatInterval, activeDuration, offsets }, + } + } + case 'z': + return + case 'k': + return + case 'a': + return parseAttribute(body) + case 'm': { + const [type, port, protocol, fmt] = body.split(' ') + return { type, port: Number(port), protocol, fmt: Number(fmt) } + } + default: + } +} +var extractURIs = (buffer) => { + const seekFrom = buffer.indexOf('\nm=') + return extractLineVals(buffer, 'a=control:', seekFrom) +} +var parse2 = (buffer) => { + const sdp = buffer.toString('ascii').split('\n').map((s) => s.trim()) + const struct = { session: {}, media: [] } + let mediaCounter = 0 + let current = struct.session + for (const line of sdp) { + if (newMediaLevel(line)) { + struct.media[mediaCounter] = {} + current = struct.media[mediaCounter] + ;++mediaCounter + } + current = Object.assign(current, extractField(line)) + } + return struct +} +var messageFromBuffer = (buffer) => { + return { + type: 5, + data: buffer, + sdp: parse2(buffer), + } +} +var INTERLEAVED_HEADER_BYTES = 4 +var ASCII_DOLLAR = 36 +var rtpPacketInfo = (chunks) => { + const header = Buffer.alloc(INTERLEAVED_HEADER_BYTES) + let i = 0 + let bytesRead = 0 + while (bytesRead < header.length) { + const chunk = chunks[i++] + const bytesToRead = Math.min(chunk.length, header.length - bytesRead) + chunk.copy(header, bytesRead, 0, bytesToRead) + bytesRead += bytesToRead + } + const channel = header[1] + const begin = header.length + const length = header.readUInt16BE(2) + const end = begin + length + return { channel, begin, end } +} +var Parser = class { + _chunks = [] + _length = 0 + _state = 0 + _packet + /** + * Create a new Parser object. + * @return {undefined} + */ + constructor() { + this._init() + } + /** + * Initialize the internal properties to their default starting + * values. + * @return {undefined} + */ + _init() { + this._chunks = [] + this._length = 0 + this._state = 0 + } + _push(chunk) { + this._chunks.push(chunk) + this._length += chunk.length + } + /** + * Extract RTSP messages. + * @return {Array} An array of messages, possibly empty. + */ + _parseRtsp() { + const messages = [] + const buffer = Buffer.concat(this._chunks) + const chunkBodyOffset = bodyOffset(buffer) + if (chunkBodyOffset === -1) { + return messages + } + const rtspHeaderLength = chunkBodyOffset + const contentLength = extractHeaderValue(buffer, 'Content-Length') + if ( + contentLength + && parseInt(contentLength) > buffer.length - rtspHeaderLength + ) { + return messages + } + this._init() + if ( + rtspHeaderLength === buffer.length + || buffer[rtspHeaderLength] === ASCII_DOLLAR + ) { + const packet = buffer.slice(0, rtspHeaderLength) + messages.push({ type: 4, data: packet }) + const trailing = buffer.slice(rtspHeaderLength) + this._push(trailing) + } else { + const packet = buffer + const body = buffer.slice(rtspHeaderLength) + messages.push({ type: 4, data: packet }) + messages.push(messageFromBuffer(body)) + } + return messages + } + /** + * Extract RTP/RTCP messages. + * @return {Array} An array of messages, possibly empty. + */ + _parseInterleaved() { + const messages = [] + if (this._length < INTERLEAVED_HEADER_BYTES) { + return messages + } + if (!this._packet) { + this._packet = rtpPacketInfo(this._chunks) + } + if (this._length < this._packet.end) { + return messages + } + const buffer = Buffer.concat(this._chunks) + const packet = buffer.slice(this._packet.begin, this._packet.end) + const trailing = buffer.slice(this._packet.end) + const channel = this._packet.channel + delete this._packet + this._init() + this._push(trailing) + if (channel % 2 === 0) { + messages.push({ type: 2, data: packet, channel }) + } else { + let rtcpPackets = packet + do { + const rtcpByteSize = rtcpPackets.readUInt16BE(2) * 4 + 4 + messages.push( + rtcpMessageFromBuffer(channel, rtcpPackets.slice(0, rtcpByteSize)) + ) + rtcpPackets = rtcpPackets.slice(rtcpByteSize) + } while (rtcpPackets.length > 0) + } + return messages + } + /** + * Set the internal state based on the type of the first chunk + */ + _setState() { + while (this._chunks.length > 0 && this._chunks[0].length === 0) { + this._chunks.shift() + } + const firstChunk = this._chunks[0] + if (this._chunks.length === 0) { + this._state = 0 + } else if (firstChunk[0] === ASCII_DOLLAR) { + this._state = 1 + } else if (firstChunk.toString('ascii', 0, 4) === 'RTSP') { + this._state = 2 + } else { + throw new Error(`Unknown chunk of length ${firstChunk.length}`) + } + } + /** + * Add the next chunk of data to the parser and extract messages. + * If no message can be extracted, an empty array is returned, otherwise + * an array of messages is returned. + * @param chunk - The next piece of data. + * @return An array of messages, possibly empty. + */ + parse(chunk) { + this._push(chunk) + if (this._state === 0) { + this._setState() + } + let messages = [] + let done = false + while (!done) { + let extracted = [] + switch (this._state) { + case 0: + break + case 1: + extracted = this._parseInterleaved() + break + case 2: + extracted = this._parseRtsp() + break + default: + throw new Error('internal error: unknown state') + } + if (extracted.length > 0) { + messages = messages.concat(extracted) + } else { + done = true + } + this._setState() + } + return messages + } +} +var RtspParser = class extends Tube { + constructor() { + const parser = new Parser() + const incoming = new Transform9({ + objectMode: true, + transform(msg, encoding, callback) { + if (msg.type === 1) { + try { + parser.parse(msg.data).forEach((message) => incoming.push(message)) + callback() + } catch (e) { + const err = e + callback(err) + } + } else { + callback(void 0, msg) + } + }, + }) + const outgoing = new Transform9({ + objectMode: true, + transform(msg, encoding, callback) { + if (msg.type === 4) { + const data = builder(msg) + callback(void 0, { type: 1, data }) + } else { + callback(void 0, msg) + } + }, + }) + super(incoming, outgoing) + } +} +var import_debug5 = __toESM2(require_src()) +var NTP_UNIX_EPOCH_OFFSET = Date.UTC(1900, 0, 1) +function getTime(ntpMost, ntpLeast) { + const ntpMilliSeconds = (ntpMost + ntpLeast / 4294967296) * 1e3 + return NTP_UNIX_EPOCH_OFFSET + ntpMilliSeconds +} +function isAbsolute(url) { + return /^[^:]+:\/\//.test(url) +} +var RTSP_METHOD = /* @__PURE__ */ ((RTSP_METHOD2) => { + RTSP_METHOD2['OPTIONS'] = 'OPTIONS' + RTSP_METHOD2['DESCRIBE'] = 'DESCRIBE' + RTSP_METHOD2['SETUP'] = 'SETUP' + RTSP_METHOD2['PLAY'] = 'PLAY' + RTSP_METHOD2['PAUSE'] = 'PAUSE' + RTSP_METHOD2['TEARDOWN'] = 'TEARDOWN' + return RTSP_METHOD2 +})(RTSP_METHOD || {}) +var MIN_SESSION_TIMEOUT = 5 +var defaultConfig = ( + hostname = typeof window === 'undefined' ? '' : window.location.hostname, + parameters = [] +) => { + const uri = parameters.length > 0 + ? `rtsp://${hostname}/axis-media/media.amp?${parameters.join('&')}` + : `rtsp://${hostname}/axis-media/media.amp` + return { uri } +} +var RTSPResponseError = class extends Error { + code + constructor(message, code) { + super(message) + this.name = 'RTSPResponseError' + this.code = code + } +} +var RtspSession = class extends Tube { + uri + headers + defaultHeaders + t0 + n0 + clockrates + startTime + onRtcp + onSdp + onError + onPlay + retry + _outgoingClosed + _sequence + _callStack + _callHistory + _state + _waiting + _contentBase + _contentLocation + _sessionId + _sessionControlURL + _renewSessionInterval + /** + * Create a new RTSP session controller component. + * @param [config] Details about the session. + * @param [config.hostname] The RTSP server hostname + * @param [config.parameters] The RTSP URI parameters + * @param [config.uri] The full RTSP URI (overrides any hostname/parameters) + * @param [config.defaultHeaders] Default headers to use (for all methods). + * @param [config.headers] Headers to use (mapped to each method). + */ + constructor(config = {}) { + const { uri, headers, defaultHeaders } = merge( + defaultConfig(config.hostname, config.parameters), + config + ) + const incoming = new Transform10({ + objectMode: true, + transform: (msg, _, callback) => { + if (msg.type === 4) { + this._onRtsp(msg) + callback() + } else if (msg.type === 3) { + this._onRtcp(msg) + this.onRtcp && this.onRtcp(msg.rtcp) + callback(void 0, msg) + } else if (msg.type === 2) { + this._onRtp(msg) + callback(void 0, msg) + } else if (msg.type === 5) { + this._onSdp(msg) + this.onSdp && this.onSdp(msg.sdp) + callback(void 0, msg) + } else { + callback(void 0, msg) + } + }, + }) + incoming.on('end', () => { + this._outgoingClosed = true + }) + super(incoming) + this._outgoingClosed = false + this._reset() + this.update(uri, headers, defaultHeaders) + this._sessionControlURL = this._controlURL() + } + /** + * Update the cached RTSP uri and headers. + * @param uri - The RTSP URI. + * @param headers - Maps commands to headers. + * @param defaultHeaders - Default headers. + */ + update(uri, headers = {}, defaultHeaders = {}) { + if (uri === void 0) { + throw new Error( + 'You must supply an uri when creating a RtspSessionComponent' + ) + } + this.uri = uri + this.defaultHeaders = defaultHeaders + this.headers = Object.assign( + { + [ + 'OPTIONS' + /* OPTIONS */ + ]: {}, + [ + 'PLAY' + /* PLAY */ + ]: {}, + [ + 'SETUP' + /* SETUP */ + ]: { Blocksize: '64000' }, + [ + 'DESCRIBE' + /* DESCRIBE */ + ]: { Accept: 'application/sdp' }, + [ + 'PAUSE' + /* PAUSE */ + ]: {}, + }, + headers + ) + } + /** + * Restore the initial values to the state they were in before any RTSP + * connection was made. + */ + _reset() { + this._sequence = 1 + this.retry = () => console.error("No request sent, can't retry") + this._callStack = [] + this._callHistory = [] + this._state = 'idle' + this._waiting = false + this._contentBase = null + this._sessionId = null + if (this._renewSessionInterval !== null) { + clearInterval(this._renewSessionInterval) + } + this._renewSessionInterval = null + this.t0 = void 0 + this.n0 = void 0 + this.clockrates = void 0 + } + _controlURL(attribute) { + if (attribute !== void 0 && isAbsolute(attribute)) { + return attribute + } + const baseURL = this._contentBase ?? this._contentLocation ?? this.uri + if (baseURL === null || baseURL === void 0) { + throw new Error( + 'relative or missing control attribute but no base URL available' + ) + } + if (attribute === void 0 || attribute === '*') { + return baseURL + } + return new URL(attribute, baseURL).href + } + /** + * Handles incoming RTSP messages and send the next command in the queue. + * @param msg - An incoming RTSP message. + */ + _onRtsp(msg) { + this._waiting = false + const status = statusCode(msg.data) + const ended = connectionEnded(msg.data) + const seq = sequence(msg.data) + if (seq === null) { + throw new Error('rtsp: expected sequence number') + } + if (this._callHistory === void 0) { + throw new Error('rtsp: internal error') + } + const method = this._callHistory[seq - 1] + ;(0, import_debug5.default)('msl:rtsp:incoming')(`${msg.data}`) + if (!this._sessionId && !ended) { + this._sessionId = sessionId(msg.data) + const _sessionTimeout = sessionTimeout(msg.data) + if (_sessionTimeout !== null) { + if (this._renewSessionInterval !== null) { + clearInterval(this._renewSessionInterval) + } + this._renewSessionInterval = setInterval( + () => { + this._enqueue({ + method: 'OPTIONS', + /* OPTIONS */ + }) + this._dequeue() + }, + Math.max(MIN_SESSION_TIMEOUT, _sessionTimeout - 5) * 1e3 + ) + } + } + if (!this._contentBase) { + this._contentBase = contentBase(msg.data) + } + if (!this._contentLocation) { + this._contentLocation = contentLocation(msg.data) + } + if (status >= 400) { + this.onError && this.onError( + new RTSPResponseError(msg.data.toString('ascii'), status) + ) + } + if (method === 'PLAY') { + this.onPlay && this.onPlay(range(msg.data)) + } + if (ended) { + ;(0, import_debug5.default)('msl:rtsp:incoming')( + `RTSP Session ${this._sessionId} ended with statusCode: ${status}` + ) + this._sessionId = null + } + this._dequeue() + } + _onRtcp(msg) { + if (this.t0 === void 0 || this.n0 === void 0) { + throw new Error('rtsp: internal error') + } + if (isRtcpSR(msg.rtcp)) { + const rtpChannel = msg.channel - 1 + this.t0[rtpChannel] = msg.rtcp.rtpTimestamp + this.n0[rtpChannel] = getTime(msg.rtcp.ntpMost, msg.rtcp.ntpLeast) + } + } + _onRtp(msg) { + if ( + this.t0 === void 0 || this.n0 === void 0 || this.clockrates === void 0 + ) { + throw new Error('rtsp: internal error') + } + const rtpChannel = msg.channel + const t0 = this.t0[rtpChannel] + const n0 = this.n0[rtpChannel] + if (typeof t0 !== 'undefined' && typeof n0 !== 'undefined') { + const clockrate = this.clockrates[rtpChannel] + const t = timestamp(msg.data) + const dt = t - t0 | 0 + msg.ntpTimestamp = dt / clockrate * 1e3 + n0 + } + } + /** + * Handles incoming SDP messages, reply with SETUP and optionally PLAY. + * @param msg - An incoming SDP message. + */ + _onSdp(msg) { + this.n0 = {} + this.t0 = {} + this.clockrates = {} + this._sessionControlURL = this._controlURL(msg.sdp.session.control) + msg.sdp.media.forEach((media, index) => { + if (media.rtpmap === void 0) { + return + } + const { clockrate } = media.rtpmap + const rtp = index * 2 + const rtcp = rtp + 1 + const uri = media.control === void 0 + ? this._sessionControlURL + : this._controlURL(media.control) + this._enqueue({ + method: 'SETUP', + headers: { + Transport: `RTP/AVP/TCP;unicast;interleaved=${rtp}-${rtcp}`, + }, + uri, + }) + if (this.clockrates === void 0) { + return + } + this.clockrates[rtp] = clockrate + }) + if (this._state === 'playing') { + this._enqueue({ + method: 'PLAY', + headers: { + Range: `npt=${this.startTime || 0}-`, + }, + uri: this._sessionControlURL, + }) + } + this._dequeue() + } + /** + * Set up command queue in order to start playing, i.e. PLAY optionally + * preceeded by OPTIONS/DESCRIBE commands. If not waiting, immediately + * start sending. + * @param startTime - Time (seconds) at which to start playing + */ + play(startTime = 0) { + if (this._state === 'idle') { + this.startTime = Number(startTime) || 0 + this._enqueue({ + method: 'OPTIONS', + /* OPTIONS */ + }) + this._enqueue({ + method: 'DESCRIBE', + /* DESCRIBE */ + }) + } else if (this._state === 'paused') { + if (this._sessionId === null || this._sessionId === void 0) { + throw new Error('rtsp: internal error') + } + this._enqueue({ + method: 'PLAY', + headers: { + Session: this._sessionId, + }, + uri: this._sessionControlURL, + }) + } + this._state = 'playing' + this._dequeue() + } + /** + * Queue a pause command, and send if not waiting. + * @return {undefined} + */ + pause() { + this._enqueue({ + method: 'PAUSE', + /* PAUSE */ + }) + this._state = 'paused' + this._dequeue() + } + /** + * End the session if there is one, otherwise just cancel + * any outstanding calls on the stack. + * @return {undefined} + */ + stop() { + if (this._sessionId) { + this._enqueue({ + method: 'TEARDOWN', + /* TEARDOWN */ + }) + } else { + this._callStack = [] + } + this._state = 'idle' + if (this._renewSessionInterval !== null) { + clearInterval(this._renewSessionInterval) + this._renewSessionInterval = null + } + this._dequeue() + } + /** + * Pushes an RTSP request onto the outgoing stream. + * @param cmd - The details about the command to send. + */ + send(cmd) { + const { method, headers, uri } = cmd + if (method === void 0) { + throw new Error('missing method when send request') + } + this._waiting = true + this.retry = this.send.bind(this, cmd) + if ( + this._sequence === void 0 || this.headers === void 0 + || this._callHistory === void 0 + ) { + throw new Error('rtsp: internal error') + } + const message = Object.assign( + { + type: 4, + uri: uri || this._sessionControlURL, + data: Buffer.alloc(0), + // data is a mandatory field. Not used by session -> parser messages. + }, + { method, headers }, + { + headers: Object.assign( + { CSeq: this._sequence++ }, + this.defaultHeaders, + // default headers (for all methods) + this.headers[method], + // preset headers for this method + headers + // headers that came with the invokation + ), + } + ) + this._sessionId && (message.headers.Session = this._sessionId) + this._callHistory.push(method) + if (!this._outgoingClosed) { + this.outgoing.push(message) + } else { + ;(0, import_debug5.default)('msl:rtsp:outgoing')( + `Unable to send ${method}, connection closed` + ) + } + } + /** + * Push a command onto the call stack. + * @param cmd - The command to queue + */ + _enqueue(cmd) { + if (this._callStack === void 0) { + throw new Error('rtsp: internal error') + } + this._callStack.push(cmd) + } + /** + * If possible, send the next command on the call stack. + */ + _dequeue() { + if (this._callStack === void 0) { + throw new Error('rtsp: internal error') + } + if (!this._waiting && this._callStack.length > 0) { + const cmd = this._callStack.shift() + if (cmd !== void 0) { + this.send(cmd) + } + } + } +} +var TcpSource = class extends Source { + /** + * Create a TCP component. + * A TCP socket will be created from parsing the URL of the first outgoing message. + * @param host Force RTSP host (overrides OPTIONS URL) + */ + constructor(host) { + let socket + const incoming = new Readable4({ + objectMode: true, + read() { + }, + }) + const outgoing = new Writable4({ + objectMode: true, + write(msg, encoding, callback) { + const b = msg.data + if (!socket) { + let url + if (host === void 0) { + const firstSpace = b.indexOf(' ') + const secondSpace = b.indexOf(' ', firstSpace + 1) + url = b.slice(firstSpace, secondSpace).toString('ascii') + } else { + url = `rtsp://${host}` + } + const { hostname, port } = new URL(url) + socket = connect( + Number(port) || 554, + hostname === null ? void 0 : hostname + ) + socket.on('error', (e) => { + console.error('TCP socket error:', e) + socket.destroy() + incoming.push(null) + }) + socket.on('data', (buffer) => { + if ( + !incoming.push({ + data: buffer, + type: 1, + /* RAW */ + }) + ) { + console.warn( + 'TCP Component internal error: not allowed to push more data' + ) + } + }) + socket.on('end', () => { + console.warn('socket ended') + incoming.push(null) + }) + } + try { + socket.write(msg.data, encoding, callback) + } catch (e) { + console.warn('message lost during send:', msg) + } + }, + }) + incoming.on('error', (e) => { + console.log('closing TCP socket due to incoming error', e) + socket && socket.end() + }) + incoming.on('finish', () => { + socket && socket.end() + }) + outgoing.on('error', (e) => { + console.warn('error during TCP send, ignoring:', e) + }) + outgoing.on('finish', () => { + socket && socket.end() + }) + super(incoming, outgoing) + } +} +var WSSink = class extends Sink { + constructor(socket) { + const outgoing = new Readable5({ + objectMode: true, + read: () => { + }, + }) + const incoming = new Writable5({ + objectMode: true, + write: (msg, encoding, callback) => { + try { + socket.send(msg.data) + } catch (e) { + console.warn('message lost during send:', msg) + } + callback() + }, + }) + socket.on('message', function(data) { + outgoing.push({ + data, + type: 1, + /* RAW */ + }) + }) + socket.on('close', function() { + outgoing.push(null) + }) + socket.on('error', (e) => { + console.error('WebSocket error:', e) + socket.terminate() + outgoing.push(null) + }) + incoming.on('error', (e) => { + console.log('closing WebSocket due to incoming error', e) + socket && socket.close && socket.close() + }) + incoming.on('finish', () => { + socket && socket.close && socket.close() + }) + outgoing.on('error', (e) => { + console.warn('error during WebSocket send, ignoring:', e) + }) + outgoing.on('finish', () => { + socket && socket.close && socket.close() + }) + super(incoming, outgoing) + } +} +var index_node_exports2 = {} +__export(index_node_exports2, { + CliMjpegPipeline: () => CliMjpegPipeline, + CliMp4Pipeline: () => CliMp4Pipeline, + Pipeline: () => Pipeline, + RtspMjpegPipeline: () => RtspMjpegPipeline, + RtspMp4Pipeline: () => RtspMp4Pipeline, + RtspPipeline: () => RtspPipeline, + TcpWsProxyPipeline: () => TcpWsProxyPipeline, +}) +var Pipeline = class { + firstComponent + lastComponent + _set + /** + * @param components - The components of the pipeline in order. + */ + constructor(...components) { + const [car, ...cdr] = components + this._set = new Set(components) + this.firstComponent = car + this.lastComponent = cdr.reduce((last, component) => { + return last.connect(component) + }, car) + } + /** + * @param components - The components of the pipeline in order. + */ + init(...components) { + const [car, ...cdr] = components + this._set = new Set(components) + this.firstComponent = car + this.lastComponent = cdr.reduce((last, component) => { + return last.connect(component) + }, car) + } + /** + * Inserts a component into the pipeline. + * + * @param component - Tube or Source behind which to insert a new component. + * @param component - Tube or Sink to insert. + */ + insertAfter(component, newComponent) { + if (!this._set.has(component)) { + throw new Error('insertion point not part of pipeline') + } + if (this._set.has(newComponent)) { + throw new Error('new component already in the pipeline') + } + const cdr = component.next + if (cdr === null) { + component.connect(newComponent) + this.lastComponent = newComponent + } else { + component.disconnect() + component.connect(newComponent).connect(cdr) + } + this._set.add(newComponent) + return this + } + /** + * Inserts a component into the pipeline. + * + * @param component - Tube or Sink in front of which to insert a new component. + * @param component - Tube or Source to insert. + */ + insertBefore(component, newComponent) { + if (!this._set.has(component)) { + throw new Error('insertion point not part of pipeline') + } + if (this._set.has(newComponent)) { + throw new Error('new component already in the pipeline') + } + const car = component.prev + if (car === null) { + newComponent.connect(component) + this.firstComponent = newComponent + } else { + car.disconnect() + car.connect(newComponent).connect(component) + } + this._set.add(newComponent) + return this + } + /** + * Removes a component from the pipeline. + * + * @param component - Component to remove. + */ + remove(component) { + if (!this._set.has(component)) { + throw new Error('component not part of pipeline') + } + const car = component.prev + const cdr = component.next + if (car === null && cdr === null) { + throw new Error('cannot remove last component') + } else if (car === null && cdr !== null) { + component.disconnect() + this.firstComponent = cdr + } else if (car !== null && cdr === null) { + car.disconnect() + this.lastComponent = car + } else if (car !== null && cdr !== null) { + car.disconnect() + const cmp = component + cmp.disconnect() + car.connect(cdr) + } + this._set.delete(component) + return this + } + /** + * Inserts a component at the end of the pipeline. + * + * @param component - Tube or Sink to insert. + */ + append(...components) { + components.forEach((component) => { + this.insertAfter(this.lastComponent, component) + }) + return this + } + /** + * Inserts a component at the beginning of the pipeline. + * + * @param component - Tube or Source to insert. + */ + prepend(...components) { + components.forEach((component) => { + this.insertBefore(this.firstComponent, component) + }) + return this + } +} +var RtspPipeline = class extends Pipeline { + onSdp + onPlay + rtsp + constructor(rtspConfig) { + const rtspParser = new RtspParser() + const rtspSession = new RtspSession(rtspConfig) + rtspSession.onSdp = (sdp) => { + this.onSdp && this.onSdp(sdp) + } + rtspSession.onPlay = (range2) => { + this.onPlay && this.onPlay(range2) + } + super(rtspParser, rtspSession) + this.rtsp = rtspSession + } +} +var RtspMjpegPipeline = class extends RtspPipeline { + constructor(rtspConfig) { + super(rtspConfig) + const jpegDepay = new JPEGDepay() + this.append(jpegDepay) + } +} +var RtspMp4Pipeline = class extends RtspPipeline { + onSync + _mp4Muxer + constructor(rtspConfig) { + super(rtspConfig) + const h264Depay = new H264Depay() + const aacDepay = new AACDepay() + const mp4Muxer = new Mp4Muxer() + mp4Muxer.onSync = (ntpPresentationTime) => { + this.onSync && this.onSync(ntpPresentationTime) + } + this.append(h264Depay, aacDepay, mp4Muxer) + this._mp4Muxer = mp4Muxer + } + get bitrate() { + return this._mp4Muxer.bitrate + } + get framerate() { + return this._mp4Muxer.framerate + } +} +var CliMjpegPipeline = class extends RtspMjpegPipeline { + constructor(config) { + const { rtsp: rtspConfig, auth: authConfig } = config + super(rtspConfig) + const auth = new Auth(authConfig) + this.insertBefore(this.rtsp, auth) + const tcpSource = new TcpSource() + const dataSaver = process.stdout.isTTY + ? (msg) => console.log(msg.type, msg.data) + : (msg) => msg.type === 10 && process.stdout.write(msg.data) + const videoSink = Sink.fromHandler(dataSaver) + this.prepend(tcpSource) + this.append(videoSink) + } +} +var CliMp4Pipeline = class extends RtspMp4Pipeline { + constructor(config) { + const { rtsp: rtspConfig, auth: authConfig } = config + super(rtspConfig) + const auth = new Auth(authConfig) + this.insertBefore(this.rtsp, auth) + const tcpSource = new TcpSource() + const dataSaver = process.stdout.isTTY + ? (msg) => console.log(msg.type, msg.data) + : (msg) => msg.type === 8 && process.stdout.write(msg.data) + const videoSink = Sink.fromHandler(dataSaver) + this.prepend(tcpSource) + this.append(videoSink) + } +} +var TcpWsProxyPipeline = class extends Pipeline { + wss + constructor(config = {}) { + const { wsOptions, rtspHost } = config + const wss = new import_websocket_server.default(wsOptions) + wss.on('connection', (socket) => { + const wsSink = new WSSink(socket) + const tcpSource = new TcpSource(rtspHost) + this.init(tcpSource, wsSink) + }) + super() + this.wss = wss + } +} +var index_node_exports3 = {} +__export(index_node_exports3, { + Clock: () => Clock, + RTCPPacketType: () => RTCPPacketType, + SDESItem: () => SDESItem, + SR: () => SR, + Scheduler: () => Scheduler, + addRTSPRetry: () => addRTSPRetry, + bodyOffset: () => bodyOffset, + cSrc: () => cSrc, + cSrcCount: () => cSrcCount, + connectionEnded: () => connectionEnded, + contentBase: () => contentBase, + contentLocation: () => contentLocation, + extHeader: () => extHeader, + extHeaderLength: () => extHeaderLength, + extension: () => extension, + extractHeaderValue: () => extractHeaderValue, + extractURIs: () => extractURIs, + getTime: () => getTime, + isRtcpApp: () => isRtcpApp, + isRtcpBye: () => isRtcpBye, + isRtcpRR: () => isRtcpRR, + isRtcpSDES: () => isRtcpSDES, + isRtcpSR: () => isRtcpSR, + marker: () => marker, + messageFromBuffer: () => messageFromBuffer, + padding: () => padding, + parse: () => parse2, + parseRtcp: () => parseRtcp, + payload: () => payload, + payloadType: () => payloadType, + range: () => range, + rtcpMessageFromBuffer: () => rtcpMessageFromBuffer, + sSrc: () => sSrc, + sequence: () => sequence, + sequenceNumber: () => sequenceNumber, + sessionId: () => sessionId, + sessionTimeout: () => sessionTimeout, + statusCode: () => statusCode, + timestamp: () => timestamp, + version: () => version, +}) +var Clock = class { + started + stopped + elapsed + constructor() { + this.elapsed = 0 + this.started = 0 + this.stopped = true + } + start() { + if (this.stopped) { + this.started = window.performance.now() + this.stopped = false + } + } + stop() { + if (!this.stopped) { + this.elapsed = this.now() + this.stopped = true + } + } + reset() { + this.elapsed = 0 + this.started = 0 + this.stopped = true + } + // Gives the elapsed time in milliseconds since the + // clock was first started (after last reset). + now() { + if (this.stopped) { + return this.elapsed + } + return this.elapsed + (window.performance.now() - this.started) + } + play() { + this.start() + } + pause() { + this.stop() + } + // Gives the elapsed time in seconds since last reset. + get currentTime() { + return this.now() / 1e3 + } +} +var addRTSPRetry = ( + rtspSession, + { maxRetries, errors } = { maxRetries: 20, errors: [503] } +) => { + let retries = 0 + const oldOnError = rtspSession.onError + rtspSession.onError = (err) => { + oldOnError == null ? void 0 : oldOnError(err) + if (!errors.includes(err.code)) { + return + } + if ((retries += 1) > maxRetries) { + console.log('retry, too many', retries, maxRetries) + return + } + setTimeout(() => { + var _a + return (_a = rtspSession.retry) == null ? void 0 : _a.call(rtspSession) + }, retries * 100) + } +} +var DEFAULT_TOLERANCE = 10 +var Scheduler = class { + _clock + _handler + _tolerance + _nextRun + _nextPlay + _fifo + _ntpPresentationTime + _suspended + /** + * Creates an instance of Scheduler. + * @param clock - The clock to use (so we can control playback) + * @param handler - The callback to invoke when a message is in sync + * @param tolerance - The milliseconds defining "in sync" (default = 10) + */ + constructor(clock, handler, tolerance = DEFAULT_TOLERANCE) { + this._clock = clock + this._handler = handler + this._tolerance = tolerance + this._nextRun = 0 + this._nextPlay = 0 + this._fifo = [] + this._ntpPresentationTime = 0 + this._suspended = false + } + /** + * Bring the scheduler back to it's initial state. + */ + reset() { + clearTimeout(this._nextRun) + clearTimeout(this._nextPlay) + this._fifo = [] + this._ntpPresentationTime = 0 + this._suspended = false + } + /** + * Initialize the scheduler. + * + * @param ntpPresentationTime - The offset representing the start of the presentation + */ + init(ntpPresentationTime) { + this._ntpPresentationTime = ntpPresentationTime + } + /** + * Suspend the scheduler. + * + * This releases control of the clock and stops any scheduling activity. + * Note that this doesn't mean the clock will be in a particular state + * (could be started or stopped), just that the scheduler will no longer + * control it. + */ + suspend() { + clearTimeout(this._nextPlay) + this._suspended = true + } + /** + * Resume the scheduler. + * + * This gives back control of the clock and the ability + * to schedule messages. The scheduler will immediately + * try to do that on resume. + */ + resume() { + this._suspended = false + this.run(void 0) + } + /** + * Run the scheduler. + * + * @param newMessage - New message to schedule. + */ + run(newMessage) { + clearTimeout(this._nextRun) + if (typeof this._ntpPresentationTime === 'undefined') { + return + } + if (typeof newMessage !== 'undefined') { + this._fifo.push(newMessage) + } + if (this._suspended) { + return + } + if (this._fifo.length === 0) { + return + } + let timeToPresent = 0 + let currentMessage + do { + const msg = this._fifo.shift() + if (msg === void 0) { + throw new Error('internal error: message should never be undefined') + } + currentMessage = msg + const ntpTimestamp = currentMessage.ntpTimestamp + if (ntpTimestamp === void 0) { + continue + } + const presentationTime = ntpTimestamp - this._ntpPresentationTime + timeToPresent = presentationTime - this._clock.currentTime * 1e3 + if (Math.abs(timeToPresent) < this._tolerance) { + this._handler && this._handler(currentMessage) + } + } while (timeToPresent < this._tolerance && this._fifo.length > 0) + if (timeToPresent < -this._tolerance) { + clearTimeout(this._nextPlay) + this._clock.pause() + this._nextPlay = window.setTimeout( + () => this._clock.play(), + -timeToPresent + ) + } else if (timeToPresent > this._tolerance) { + this._fifo.unshift(currentMessage) + this._nextRun = window.setTimeout( + () => this.run(void 0), + timeToPresent + ) + } + } +} + +// src/tcp-ws-proxy.ts +import { binary, command, number, option, optional, run, string } from 'cmd-ts' +var cli = command({ + name: 'changelog', + description: `Generate a changelog for a range of commits.`, + args: { + port: option({ + description: 'websocket port (default: 8854)', + long: 'port', + short: 'p', + defaultValue: () => 8854, + type: number, + }), + rtsp: option({ + description: 'RTSP host', + long: 'rtsp', + type: optional(string), + }), + }, + handler(args) { + console.log(`WebSocket server at ws://localhost:${args.port}`) + console.log(index_node_exports2, index_node_exports2.TcpWsProxyPipeline) + return new index_node_exports2.TcpWsProxyPipeline({ + wsOptions: { host: '::', port: args.port }, + rtspHost: args.rtsp, + }) + }, +}) +await run(binary(cli), process.argv) diff --git a/tools/src/changelog/changeset.ts b/tools/src/changelog/changeset.ts new file mode 100644 index 000000000..fd1862b89 --- /dev/null +++ b/tools/src/changelog/changeset.ts @@ -0,0 +1,133 @@ +import { commitUrl, compareUrl, gitLogFromRange, shortSha } from './git' + +const GroupTitles = { + 'build': '๐Ÿ‘ท Build', + 'chore': '๐Ÿšง Maintenance', + 'ci': '๐Ÿšฆ Continous integration', + 'docs': '๐Ÿ“ Documentation', + 'feat': 'โœจ Features', + 'fix': '๐Ÿ› Bug fixes', + 'perf': '๐ŸŽ๏ธ Performance', + 'refactor': 'โ™ป๏ธ Refactoring', + 'revert': 'โช๏ธ Reverts', + 'style': '๐Ÿ’„ Styling', + 'test': '๐Ÿงช Test', +} as const +const GroupKeys = new Set(Object.keys(GroupTitles)) as ReadonlySet< + keyof typeof GroupTitles +> + +interface ChangesetArgs { + readonly date: string + readonly name: string + readonly range: string + readonly scope?: string + readonly url?: string +} +export function changeset({ + date, + name, + range, + scope, + url, +}: ChangesetArgs) { + return [ + changesetHeader({ date, name, range, url }), + changesetBody({ range, scope, url }), + ] + .join('') +} + +interface ChangesetHeaderArgs { + readonly date: string + readonly name: string + readonly range: string + readonly url?: string +} +function changesetHeader({ date, name, range, url }: ChangesetHeaderArgs) { + if (url !== undefined) { + return `## [${name}](${compareUrl(url, range)}) (${date})\n` + } + + return `## ${name} (${date})\n` +} + +interface ChangesetBodyArgs { + readonly range: string + readonly scope?: string + readonly url?: string +} +function changesetBody( + { + range, + scope, + url, + }: ChangesetBodyArgs +): string { + const outputChunks = [] + + const groups: Record> = {} + for (const [sha, msg] of gitLogFromRange(range)) { + const cc = parseConventionalCommitMessage(msg) + if (scope !== undefined && cc.scope !== scope) { + continue + } + if (groups[cc.group] === undefined) { + groups[cc.group] = [] + } + groups[cc.group].push([sha, cc]) + } + + for (const group of GroupKeys) { + if (groups[group] === undefined) { + continue + } + outputChunks.push(`\n### ${GroupTitles[group]}\n\n`) + for (const [sha, cc] of groups[group]) { + const scopePrefix = scope === undefined && cc.scope !== undefined + ? ` **${cc.scope}**:` + : '' + const breakingPrefix = cc.breaking ? ` **BREAKING**` : '' + const link = url !== undefined + ? `([${shortSha(sha)}](${commitUrl(url, sha)}))` + : `(${shortSha(sha)})` + outputChunks.push( + ` -${scopePrefix}${breakingPrefix} ${cc.title} ${link}\n` + ) + } + } + + return outputChunks.join('') +} + +// Conventional commits + +interface ConventionalCommit { + readonly group: string + readonly scope?: string + readonly breaking: boolean + readonly title: string +} + +function parseConventionalCommitMessage(msg: string): ConventionalCommit { + try { + const match = msg.match(/^([^:!(]+)(?:\(([^)]+)\))?(!)?: (.*)$/) + if (match === null) { + throw new Error('no matches found') + } + const [_, group, scope, breaking, title] = match + return { + group, + scope, + breaking: breaking !== undefined, + title, + } + } catch { + process.stderr.write(`invalid conventional commit message: ${msg}\n`) + } + return { + group: 'chore', + breaking: false, + title: msg, + } +} diff --git a/tools/src/changelog/cli.ts b/tools/src/changelog/cli.ts new file mode 100644 index 000000000..dd262d0aa --- /dev/null +++ b/tools/src/changelog/cli.ts @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs' + +import { + binary, + command, + option, + optional, + positional, + run, + string, +} from 'cmd-ts' + +import { changeset } from './changeset' + +const changesetCli = command({ + name: 'changelog', + description: `Generate a changelog for a range of commits.`, + args: { + name: positional({ + type: string, + description: 'name for the changes (used in header)', + displayName: 'name', + }), + range: positional({ + type: string, + description: 'range of the changes (git revision range)', + displayName: 'range', + }), + date: option({ + description: 'date of the changeset', + long: 'date', + short: 'd', + defaultValue: () => (new Date()).toISOString(), + type: string, + }), + outfile: option({ + description: 'output file to write to', + long: 'outfile', + short: 'o', + type: optional(string), + }), + scope: option({ + description: + 'Only include conventional commits that match "...(): ..."', + long: 'scope', + short: 's', + type: optional(string), + }), + url: option({ + description: 'GitHub URL of the repository', + long: 'url', + short: 'u', + type: optional(string), + }), + }, + + handler(args) { + const contents = changeset({ + date: args.date, + name: args.name, + range: args.range, + scope: args.scope, + url: args.url, + }) + if (args.outfile !== undefined) { + fs.writeFileSync(args.outfile, contents) + } else { + process.stdout.write(contents) + } + }, +}) + +await run(binary(changesetCli), process.argv) diff --git a/tools/src/changelog/git.ts b/tools/src/changelog/git.ts new file mode 100644 index 000000000..7739739fd --- /dev/null +++ b/tools/src/changelog/git.ts @@ -0,0 +1,32 @@ +import { execSync } from 'node:child_process' + +function sanitizeGitHubUrl(url: string) { + if ((new URL(url)).hostname !== 'github.com') { + throw new Error('not implemented: only GitHub repositories are supported') + } + return url.replace(/\.git$/, '') +} +export const compareUrl = (url: string, range: string) => + `${sanitizeGitHubUrl(url)}/compare/${range}` +export const commitUrl = (url: string, commit: string) => + `${sanitizeGitHubUrl(url)}/commit/${commit}` + +export function gitLogFromRange(range: string) { + try { + const logOut = execSync( + `git log --no-merges --date-order --format="%H%x09%s" ${range}` + ) + const lines = logOut.toString().trim().split('\n') + const shaMessagePairs = lines.map((line) => line.split('\t')) + return shaMessagePairs + } catch (err) { + console.warn( + `git log failed on range ${range}, one of those tags probably does not exist` + ) + return [] + } +} + +export function shortSha(sha: string) { + return execSync(`git log -1 --format=%h ${sha}`).toString().trim() +} diff --git a/tools/src/tcp-ws-proxy.ts b/tools/src/tcp-ws-proxy.ts new file mode 100755 index 000000000..6aa59cc23 --- /dev/null +++ b/tools/src/tcp-ws-proxy.ts @@ -0,0 +1,41 @@ +#!/usr/bin/env node +import { pipelines } from 'media-stream-library' + +import { + binary, + command, + number, + option, + optional, + run, + string, +} from 'cmd-ts' + +const cli = command({ + name: 'changelog', + description: `Generate a changelog for a range of commits.`, + args: { + port: option({ + description: 'websocket port (default: 8854)', + long: 'port', + short: 'p', + defaultValue: () => 8854, + type: number, + }), + rtsp: option({ + description: 'RTSP host', + long: 'rtsp', + type: optional(string), + }), + }, + handler(args) { + console.log(`WebSocket server at ws://localhost:${args.port}`) + console.log(pipelines, pipelines.TcpWsProxyPipeline) + return new pipelines.TcpWsProxyPipeline({ + wsOptions: { host: '::', port: args.port }, + rtspHost: args.rtsp, + }) + }, +}) + +await run(binary(cli), process.argv) diff --git a/tools/tsconfig.eslint.json b/tools/tsconfig.eslint.json new file mode 100644 index 000000000..452cbc274 --- /dev/null +++ b/tools/tsconfig.eslint.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "allowJs": true + }, + "include": [ + "*.json", + ".eslintrc.cjs" + ] +} diff --git a/tools/tsconfig.json b/tools/tsconfig.json new file mode 100644 index 000000000..0f6809db0 --- /dev/null +++ b/tools/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "customConditions": ["node"] + }, + "extends": "../tsconfig.base.json", + "include": ["./src/**/*"] +}