diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d9249c60..82c10dc3 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -14,6 +14,11 @@ jobs: uses: actions/checkout@v4 with: submodules: recursive + - name: Install JDK + uses: actions/setup-java@v4 + with: + distribution: 'zulu' # See 'Supported distributions' for available options + java-version: '21' - name: Checkout actions uses: actions/checkout@v4 with: diff --git a/.github/workflows/run-validations.yml b/.github/workflows/run-validations.yml index c56a8890..fb829673 100644 --- a/.github/workflows/run-validations.yml +++ b/.github/workflows/run-validations.yml @@ -43,6 +43,11 @@ jobs: uses: actions/checkout@v4 with: submodules: recursive + - name: Install JDK + uses: actions/setup-java@v4 + with: + distribution: 'zulu' # See 'Supported distributions' for available options + java-version: '21' - name: Cache uses: actions/cache@v4 with: diff --git a/config/ktlint/baseline.xml b/config/ktlint/baseline.xml new file mode 100644 index 00000000..98142077 --- /dev/null +++ b/config/ktlint/baseline.xml @@ -0,0 +1,3 @@ + + + diff --git a/gradle.properties b/gradle.properties index 6c65e707..341f9ad9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,7 +32,7 @@ POM_DEVELOPER_URL=support@pubnub.com IOS_SIMULATOR_ID=iPhone 15 Pro SWIFT_PATH=pubnub-kotlin/swift -ENABLE_TARGET_JS=false +ENABLE_TARGET_JS=true ENABLE_TARGET_IOS_OTHER=false ENABLE_TARGET_IOS_SIMULATOR_ARM64=true # alternatively (e.g. for release): diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6a985487..c358654d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] nexus = "2.0.0" -kotlin = "2.0.20" +kotlin = "2.0.21" vanniktech = "0.29.0" ktlint = "12.1.0" dokka = "1.9.20" diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index 50d10e23..01dfff30 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -2,16 +2,432 @@ # yarn lockfile v1 +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@js-joda/core@3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1" + integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" + integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== + "@tootallnate/quickjs-emscripten@^0.23.0": version "0.23.0" resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== +"@types/body-parser@*": + version "1.19.5" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/cookie@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" + integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== + +"@types/cors@^2.8.12": + version "2.8.17" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b" + integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" + integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.5": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^5.0.0": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz#3c9997ae9d00bc236e45c6374e84f2596458d9db" + integrity sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express-serve-static-core@^4.17.33": + version "4.19.6" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz#e01324c2a024ff367d92c66f48553ced0ab50267" + integrity sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.0.tgz#13a7d1f75295e90d19ed6e74cab3678488eaa96c" + integrity sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^5.0.0" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/express@^4.17.13": + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== + +"@types/http-proxy@^1.17.8": + version "1.17.15" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.15.tgz#12118141ce9775a6499ecb4c01d02f90fc839d36" + integrity sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ== + dependencies: + "@types/node" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + +"@types/node-forge@^1.3.0": + version "1.3.11" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" + integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== + dependencies: + "@types/node" "*" + +"@types/node@*", "@types/node@>=10.0.0": + version "22.9.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.1.tgz#bdf91c36e0e7ecfb7257b2d75bf1b206b308ca71" + integrity sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg== + dependencies: + undici-types "~6.19.8" + +"@types/qs@*": + version "6.9.17" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.17.tgz#fc560f60946d0aeff2f914eb41679659d3310e1a" + integrity sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.1": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.13.10": + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@types/sockjs@^0.3.33": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== + dependencies: + "@types/node" "*" + +"@types/ws@^8.5.5": + version "8.5.13" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.13.tgz#6414c280875e2691d0d1e080b05addbf5cb91e20" + integrity sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA== + dependencies: + "@types/node" "*" + +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.12.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== + dependencies: + "@webassemblyjs/helper-numbers" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + +"@webassemblyjs/floating-point-hex-parser@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== + +"@webassemblyjs/helper-api-error@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== + +"@webassemblyjs/helper-buffer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== + +"@webassemblyjs/helper-numbers@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.13.2" + "@webassemblyjs/helper-api-error" "1.13.2" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== + +"@webassemblyjs/helper-wasm-section@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/wasm-gen" "1.14.1" + +"@webassemblyjs/ieee754@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== + +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/helper-wasm-section" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-opt" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + "@webassemblyjs/wast-printer" "1.14.1" + +"@webassemblyjs/wasm-gen@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wasm-opt@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-api-error" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wast-printer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== + +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== + +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +accepts@~1.3.4, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + +acorn@^8.7.1, acorn@^8.8.2: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" @@ -26,11 +442,55 @@ agentkeepalive@^3.5.2: dependencies: humanize-ms "^1.2.1" +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.9.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ansi-colors@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -56,6 +516,11 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + ast-types@^0.13.4: version "0.13.4" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" @@ -78,16 +543,60 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +base64id@2.0.0, base64id@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + basic-ftp@^5.0.2: version "5.0.5" resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + binary-extensions@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== +body-parser@1.20.3, body-parser@^1.19.0: + version "1.20.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.13.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.0.11: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.3.0.tgz#80d867430b5a0da64e82a8047fc1e355bdb71722" + integrity sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA== + dependencies: + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + brace-expansion@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" @@ -95,6 +604,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +braces@^3.0.2, braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -107,6 +623,16 @@ browser-stdout@^1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +browserslist@^4.21.10: + version "4.24.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== + dependencies: + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" + node-releases "^2.0.18" + update-browserslist-db "^1.1.1" + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -128,11 +654,32 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + camelcase@^6.0.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +caniuse-lite@^1.0.30001669: + version "1.0.30001680" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz#5380ede637a33b9f9f1fc6045ea99bd142f3da5e" + integrity sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA== + cbor-js@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/cbor-js/-/cbor-js-0.1.0.tgz#c80ce6120f387e8faa74370dfda21d965b8fc7f9" @@ -151,7 +698,7 @@ chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@^3.5.3: +chokidar@^3.5.1, chokidar@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -166,6 +713,11 @@ chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -175,6 +727,15 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -187,6 +748,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colorette@^2.0.10, colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -194,11 +760,127 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +compressible@~2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.5.tgz#fdd256c0a642e39e314c478f6c2cd654edd74c93" + integrity sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q== + dependencies: + bytes "3.1.2" + compressible "~2.0.18" + debug "2.6.9" + negotiator "~0.6.4" + on-headers "~1.0.2" + safe-buffer "5.2.1" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +connect@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" + integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== + +cookie@~0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@~2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^7.0.3: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +custom-event@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== + data-uri-to-buffer@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b" integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== +date-format@^4.0.14: + version "4.0.14" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" + integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + debug@4, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -206,7 +888,7 @@ debug@4, debug@^4.3.4: dependencies: ms "2.1.2" -debug@^4.3.5: +debug@^4.1.0, debug@^4.3.5, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: version "4.3.7" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== @@ -218,6 +900,27 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + degenerator@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" @@ -232,21 +935,151 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== + diff@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== +dns-packet@^5.2.2: + version "5.6.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" + integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +dom-serialize@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ== + dependencies: + custom-event "~1.0.0" + ent "~2.2.0" + extend "^3.0.0" + void-elements "^2.0.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.5.41: + version "1.5.63" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.63.tgz#69444d592fbbe628d129866c2355691ea93eda3e" + integrity sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + +engine.io-parser@~5.2.1: + version "5.2.3" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f" + integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q== + +engine.io@~6.6.0: + version "6.6.2" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.6.2.tgz#32bd845b4db708f8c774a4edef4e5c8a98b3da72" + integrity sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw== + dependencies: + "@types/cookie" "^0.4.1" + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.7.2" + cors "~2.8.5" + debug "~4.3.1" + engine.io-parser "~5.2.1" + ws "~8.17.1" + +enhanced-resolve@^5.17.0: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +ent@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.1.tgz#68dc99a002f115792c26239baedaaea9e70c0ca2" + integrity sha512-QHuXVeZx9d+tIQAz/XztU0ZwZf2Agg9CcXcgE1rurqvdBeDBrpSwjl8/6XUqMg7tw2Y7uAdKb2sRv+bSEFqQ5A== + dependencies: + punycode "^1.4.1" + +envinfo@^7.7.3: + version "7.14.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" + integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.2.1: + version "1.5.4" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== + escalade@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -263,11 +1096,31 @@ escodegen@^2.1.0: optionalDependencies: source-map "~0.6.1" +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + estraverse@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" @@ -278,6 +1131,105 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +express@^4.17.3: + version "4.21.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281" + integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.3" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.7.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.3.1" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.3" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.10" + proxy-addr "~2.0.7" + qs "6.13.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.19.0" + serve-static "1.16.2" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-uri@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" + integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -285,6 +1237,47 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== + dependencies: + debug "2.6.9" + encodeurl "~2.0.0" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -298,6 +1291,16 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== +flatted@^3.2.7: + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== + +follow-redirects@^1.0.0: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -312,6 +1315,16 @@ format-util@^1.0.5: resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + fs-extra@^11.2.0: version "11.2.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" @@ -321,6 +1334,20 @@ fs-extra@^11.2.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-monkey@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.6.tgz#8ead082953e88d992cf3ff844faa907b26756da2" + integrity sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -331,11 +1358,32 @@ fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-uri@^6.0.1: version "6.0.3" resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.3.tgz#0d26697bc13cf91092e519aa63aa60ee5b6f385a" @@ -353,6 +1401,23 @@ glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.1.3, glob@^7.1.7: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" @@ -364,21 +1429,103 @@ glob@^8.1.0: minimatch "^5.0.1" once "^1.3.0" -graceful-fs@^4.1.6, graceful-fs@^4.2.0: +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +hasown@^2.0.0, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^2.3.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" + integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1: version "7.0.2" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" @@ -387,6 +1534,26 @@ http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1: agent-base "^7.1.0" debug "^4.3.4" +http-proxy-middleware@^2.0.3: + version "2.0.7" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz#915f236d92ae98ef48278a95dedf17e991936ec6" + integrity sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + https-proxy-agent@^7.0.2, https-proxy-agent@^7.0.3: version "7.0.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" @@ -395,6 +1562,11 @@ https-proxy-agent@^7.0.2, https-proxy-agent@^7.0.3: agent-base "^7.0.2" debug "4" +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -402,11 +1574,33 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -415,11 +1609,21 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + ip-address@^9.0.5: version "9.0.5" resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" @@ -428,6 +1632,16 @@ ip-address@^9.0.5: jsbn "1.1.0" sprintf-js "^1.1.3" +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -435,6 +1649,18 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-core-module@^2.13.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -462,11 +1688,64 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isbinaryfile@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" + integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -479,6 +1758,28 @@ jsbn@1.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -488,11 +1789,96 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +karma-chrome-launcher@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9" + integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q== + dependencies: + which "^1.2.1" + +karma-mocha@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" + integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== + dependencies: + minimist "^1.2.3" + +karma-sourcemap-loader@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz#b01d73f8f688f533bcc8f5d273d43458e13b5488" + integrity sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA== + dependencies: + graceful-fs "^4.2.10" + +karma-webpack@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.1.tgz#4eafd31bbe684a747a6e8f3e4ad373e53979ced4" + integrity sha512-oo38O+P3W2mSPCSUrQdySSPv1LvPpXP+f+bBimNomS5sW+1V4SuhCuW8TfJzV+rDv921w2fDSDw0xJbPe6U+kQ== + dependencies: + glob "^7.1.3" + minimatch "^9.0.3" + webpack-merge "^4.1.5" + +karma@6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.3.tgz#763e500f99597218bbb536de1a14acc4ceea7ce8" + integrity sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q== + dependencies: + "@colors/colors" "1.5.0" + body-parser "^1.19.0" + braces "^3.0.2" + chokidar "^3.5.1" + connect "^3.7.0" + di "^0.0.1" + dom-serialize "^2.2.1" + glob "^7.1.7" + graceful-fs "^4.2.6" + http-proxy "^1.18.1" + isbinaryfile "^4.0.8" + lodash "^4.17.21" + log4js "^6.4.1" + mime "^2.5.2" + minimatch "^3.0.4" + mkdirp "^0.5.5" + qjobs "^1.2.0" + range-parser "^1.2.1" + rimraf "^3.0.2" + socket.io "^4.7.2" + source-map "^0.6.1" + tmp "^0.2.1" + ua-parser-js "^0.7.30" + yargs "^16.1.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +launch-editor@^2.6.0: + version "2.9.1" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.9.1.tgz#253f173bd441e342d4344b4dae58291abb425047" + integrity sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.8.1" + lil-uuid@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/lil-uuid/-/lil-uuid-0.1.1.tgz#f9edcf23f00e42bf43f0f843d98d8b53f3341f16" integrity sha512-GhWI8f61tBlMeqULZ1QWhFiiyFIFdPlg//S8Udq1wjq1FJhpFKTfnbduSxAQjueofeUtpr7UvQ/lIK/sKUF8dg== +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -500,6 +1886,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash@^4.17.15, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" @@ -508,23 +1899,101 @@ log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +log4js@^6.4.1: + version "6.9.1" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6" + integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + flatted "^3.2.7" + rfdc "^1.3.0" + streamroller "^3.1.5" + lru-cache@^7.14.1: version "7.18.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memfs@^3.4.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" + integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== + dependencies: + fs-monkey "^1.0.4" + +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.2: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12: +"mime-db@>= 1.43.0 < 2": + version "1.53.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" + integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimatch@^5.0.1, minimatch@^5.1.6: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" @@ -532,6 +2001,25 @@ minimatch@^5.0.1, minimatch@^5.1.6: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.3: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.3, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + mocha@10.7.0: version "10.7.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.0.tgz#9e5cbed8fa9b37537a25bd1f7fb4f6fc45458b9a" @@ -558,16 +2046,44 @@ mocha@10.7.0: yargs-parser "^20.2.9" yargs-unparser "^2.0.0" +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.3: +ms@2.1.3, ms@^2.0.0, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +negotiator@~0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + netmask@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" @@ -580,11 +2096,62 @@ node-fetch@^2.7.0: dependencies: whatwg-url "^5.0.0" +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a" + integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA== + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -592,6 +2159,29 @@ once@^1.3.0: dependencies: wrappy "1" +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^8.0.9: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -599,6 +2189,13 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + p-locate@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" @@ -606,6 +2203,19 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + pac-proxy-agent@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz#6b9ddc002ec3ff0ba5fdf4a8a21d363bcc612d75" @@ -628,16 +2238,66 @@ pac-resolver@^7.0.0: degenerator "^5.0.0" netmask "^2.0.2" +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -picomatch@^2.0.4, picomatch@^2.2.1: +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" + integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== + +picocolors@^1.0.0, picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + proxy-agent@^6.3.0: version "6.4.0" resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.4.0.tgz#b4e2dd51dee2b377748aef8d45604c2d7608652d" @@ -673,11 +2333,28 @@ pubnub@8.2.8: react-native-url-polyfill "^2.0.0" text-encoding "^0.7.0" -punycode@^2.1.1: +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +punycode@^2.1.0, punycode@^2.1.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +qjobs@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" + integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== + +qs@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -685,6 +2362,21 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + react-native-url-polyfill@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/react-native-url-polyfill/-/react-native-url-polyfill-2.0.0.tgz#db714520a2985cff1d50ab2e66279b9f91ffd589" @@ -692,6 +2384,28 @@ react-native-url-polyfill@^2.0.0: dependencies: whatwg-url-without-unicode "8.0.0-3" +readable-stream@^2.0.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -699,28 +2413,266 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -safe-buffer@^5.1.0: +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +rfdc@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -serialize-javascript@^6.0.2: +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.1.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" + integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== + dependencies: + "@types/node-forge" "^1.3.0" + node-forge "^1" + +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.1, serialize-javascript@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== + dependencies: + encodeurl "~2.0.0" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.19.0" + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== +socket.io-adapter@~2.5.2: + version "2.5.5" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz#c7a1f9c703d7756844751b6ff9abfc1780664082" + integrity sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg== + dependencies: + debug "~4.3.4" + ws "~8.17.1" + +socket.io-parser@~4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" + integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + +socket.io@^4.7.2: + version "4.8.1" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.8.1.tgz#fa0eaff965cc97fdf4245e8d4794618459f7558a" + integrity sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg== + dependencies: + accepts "~1.3.4" + base64id "~2.0.0" + cors "~2.8.5" + debug "~4.3.2" + engine.io "~6.6.0" + socket.io-adapter "~2.5.2" + socket.io-parser "~4.2.4" + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + socks-proxy-agent@^8.0.2: version "8.0.3" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz#6b2da3d77364fde6292e810b496cb70440b9b89d" @@ -738,7 +2690,20 @@ socks@^2.7.1: ip-address "^9.0.5" smart-buffer "^4.2.0" -source-map-support@0.5.21: +source-map-js@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map-loader@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-5.0.0.tgz#f593a916e1cc54471cfc8851b905c8a845fc7e38" + integrity sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA== + dependencies: + iconv-lite "^0.6.3" + source-map-js "^1.0.2" + +source-map-support@0.5.21, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -746,16 +2711,58 @@ source-map-support@0.5.21: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + sprintf-js@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +streamroller@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" + integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + fs-extra "^8.1.0" + string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -765,6 +2772,20 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -772,6 +2793,11 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -784,18 +2810,59 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.1.1: +supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.26.0: + version "5.36.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.36.0.tgz#8b0dbed459ac40ff7b4c9fd5a3a2029de105180e" + integrity sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + text-encoding@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.7.0.tgz#f895e836e45990624086601798ea98e8f36ee643" integrity sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA== +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +tmp@^0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" + integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -803,6 +2870,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -813,16 +2885,99 @@ tslib@^2.0.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + typescript@5.5.4: version "5.5.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== +ua-parser-js@^0.7.30: + version "0.7.39" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.39.tgz#c71efb46ebeabc461c4612d22d54f88880fabe7e" + integrity sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w== + +undici-types@~6.19.8: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + universalify@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== + +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -833,6 +2988,137 @@ webidl-conversions@^5.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== +webpack-cli@5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-dev-middleware@^5.3.4: + version "5.3.4" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz#eb7b39281cbce10e104eb2b8bf2b63fce49a3517" + integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@4.15.2: + version "4.15.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz#9e0c70a42a012560860adb186986da1248333173" + integrity sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.5" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + launch-editor "^2.6.0" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.4" + ws "^8.13.0" + +webpack-merge@^4.1.5: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@5.93.0: + version "5.93.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.93.0.tgz#2e89ec7035579bdfba9760d26c63ac5c3462a5e5" + integrity sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" + acorn "^8.7.1" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" + webpack-sources "^3.2.3" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + whatwg-url-without-unicode@8.0.0-3: version "8.0.0-3" resolved "https://registry.yarnpkg.com/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz#ab6df4bf6caaa6c85a59f6e82c026151d4bb376b" @@ -850,6 +3136,25 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +which@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + workerpool@^6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" @@ -869,6 +3174,16 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +ws@^8.13.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + +ws@~8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -889,7 +3204,7 @@ yargs-unparser@^2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" -yargs@^16.2.0: +yargs@^16.1.1, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== diff --git a/pubnub-chat-api/api/pubnub-chat-api.api b/pubnub-chat-api/api/pubnub-chat-api.api index 50ca314b..51a83709 100644 --- a/pubnub-chat-api/api/pubnub-chat-api.api +++ b/pubnub-chat-api/api/pubnub-chat-api.api @@ -189,6 +189,7 @@ public abstract interface class com/pubnub/chat/Message { public abstract fun getChat ()Lcom/pubnub/chat/Chat; public abstract fun getContent ()Lcom/pubnub/chat/types/EventContent$TextMessageContent; public abstract fun getDeleted ()Z + public abstract fun getError ()Lcom/pubnub/api/PubNubError; public abstract fun getFiles ()Ljava/util/List; public abstract fun getHasThread ()Z public abstract fun getMentionedUsers ()Ljava/util/Map; @@ -411,10 +412,10 @@ public final class com/pubnub/chat/membership/MembersResponse { } public final class com/pubnub/chat/membership/MembershipsResponse { - public fun (Lcom/pubnub/api/models/consumer/objects/PNPage;Lcom/pubnub/api/models/consumer/objects/PNPage;IILjava/util/List;)V + public fun (Lcom/pubnub/api/models/consumer/objects/PNPage$PNNext;Lcom/pubnub/api/models/consumer/objects/PNPage$PNPrev;IILjava/util/List;)V public final fun getMemberships ()Ljava/util/List; - public final fun getNext ()Lcom/pubnub/api/models/consumer/objects/PNPage; - public final fun getPrev ()Lcom/pubnub/api/models/consumer/objects/PNPage; + public final fun getNext ()Lcom/pubnub/api/models/consumer/objects/PNPage$PNNext; + public final fun getPrev ()Lcom/pubnub/api/models/consumer/objects/PNPage$PNPrev; public final fun getStatus ()I public final fun getTotal ()I } @@ -861,9 +862,17 @@ public final class com/pubnub/chat/types/QuotedMessage$Companion { public final class com/pubnub/chat/types/TextLink { public static final field Companion Lcom/pubnub/chat/types/TextLink$Companion; public fun (IILjava/lang/String;)V + public final fun component1 ()I + public final fun component2 ()I + public final fun component3 ()Ljava/lang/String; + public final fun copy (IILjava/lang/String;)Lcom/pubnub/chat/types/TextLink; + public static synthetic fun copy$default (Lcom/pubnub/chat/types/TextLink;IILjava/lang/String;ILjava/lang/Object;)Lcom/pubnub/chat/types/TextLink; + public fun equals (Ljava/lang/Object;)Z public final fun getEndIndex ()I public final fun getLink ()Ljava/lang/String; public final fun getStartIndex ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public synthetic class com/pubnub/chat/types/TextLink$$serializer : kotlinx/serialization/internal/GeneratedSerializer { diff --git a/pubnub-chat-api/config/ktlint/baseline.xml b/pubnub-chat-api/config/ktlint/baseline.xml new file mode 100644 index 00000000..98142077 --- /dev/null +++ b/pubnub-chat-api/config/ktlint/baseline.xml @@ -0,0 +1,3 @@ + + + diff --git a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Channel.kt b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Channel.kt index 1f9f3746..3514622e 100644 --- a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Channel.kt +++ b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Channel.kt @@ -141,7 +141,7 @@ interface Channel { * } * ``` */ - fun startTyping(): PNFuture + fun startTyping(): PNFuture /** * Deactivates a typing indicator on a given channel. @@ -160,7 +160,7 @@ interface Channel { * } * ``` */ - fun stopTyping(): PNFuture + fun stopTyping(): PNFuture /** * Enables continuous tracking of typing activity within the [Channel]. diff --git a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Membership.kt b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Membership.kt index 27e79f23..8b3b6bd8 100644 --- a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Membership.kt +++ b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Membership.kt @@ -58,7 +58,7 @@ interface Membership { * @param custom Any custom properties or metadata associated with the channel-user membership in a `Map`. Values must be scalar only; arrays or objects are not supported. App Context filtering language doesn’t support filtering by custom properties. * @return A [PNFuture] that returns an updated [Membership] object. */ - fun update(custom: CustomObject): PNFuture + fun update(custom: CustomObject?): PNFuture /** * Setting the last read message for users lets you implement the Read Receipts feature and monitor which channel member read which message. diff --git a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Message.kt b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Message.kt index 83f706fd..36893ccb 100644 --- a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Message.kt +++ b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Message.kt @@ -1,5 +1,6 @@ package com.pubnub.chat +import com.pubnub.api.PubNubError import com.pubnub.api.models.consumer.PNPublishResult import com.pubnub.api.models.consumer.history.PNFetchMessageItem.Action import com.pubnub.api.models.consumer.message_actions.PNRemoveMessageActionResult @@ -89,6 +90,11 @@ interface Message { */ val reactions: Map> + /** + * Error associated with the message, if any. + */ + val error: PubNubError? + /** * List of included text links and their position. */ diff --git a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/config/ChatConfiguration.kt b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/config/ChatConfiguration.kt index 8ce48d39..0cce4a37 100644 --- a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/config/ChatConfiguration.kt +++ b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/config/ChatConfiguration.kt @@ -69,7 +69,7 @@ interface ChatConfiguration { } fun ChatConfiguration( - logLevel: LogLevel = LogLevel.OFF, + logLevel: LogLevel = LogLevel.WARN, typingTimeout: Duration = 5.seconds, storeUserActivityInterval: Duration = 600.seconds, storeUserActivityTimestamps: Boolean = false, diff --git a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/membership/MembershipsResponse.kt b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/membership/MembershipsResponse.kt index 9705466a..9355c887 100644 --- a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/membership/MembershipsResponse.kt +++ b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/membership/MembershipsResponse.kt @@ -13,8 +13,8 @@ import com.pubnub.chat.Membership * @property memberships A list of [Membership] objects representing the channels the user is a member of. */ class MembershipsResponse( - val next: PNPage?, - val prev: PNPage?, + val next: PNPage.PNNext?, + val prev: PNPage.PNPrev?, val total: Int, val status: Int, val memberships: List diff --git a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/MessageMentionedUser.kt b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/MessageMentionedUser.kt index 8d48cd1d..7f2bf818 100644 --- a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/MessageMentionedUser.kt +++ b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/MessageMentionedUser.kt @@ -1,8 +1,10 @@ package com.pubnub.chat.types import kotlinx.serialization.Serializable +import kotlin.js.JsExport @Serializable +@JsExport class MessageMentionedUser(val id: String, val name: String) typealias MessageMentionedUsers = Map diff --git a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/MessageReferencedChannel.kt b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/MessageReferencedChannel.kt index 4bdfde6f..dd641c51 100644 --- a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/MessageReferencedChannel.kt +++ b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/MessageReferencedChannel.kt @@ -1,8 +1,13 @@ +@file:OptIn(ExperimentalJsExport::class) + package com.pubnub.chat.types import kotlinx.serialization.Serializable +import kotlin.js.ExperimentalJsExport +import kotlin.js.JsExport @Serializable +@JsExport class MessageReferencedChannel(val id: String, val name: String) typealias MessageReferencedChannels = Map diff --git a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/TextLink.kt b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/TextLink.kt index 3b708002..a0b312bd 100644 --- a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/TextLink.kt +++ b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/TextLink.kt @@ -1,6 +1,11 @@ +@file:OptIn(ExperimentalJsExport::class) + package com.pubnub.chat.types import kotlinx.serialization.Serializable +import kotlin.js.ExperimentalJsExport +import kotlin.js.JsExport @Serializable -class TextLink(val startIndex: Int, val endIndex: Int, val link: String) +@JsExport +data class TextLink(val startIndex: Int, val endIndex: Int, val link: String) diff --git a/pubnub-chat-impl/build.gradle.kts b/pubnub-chat-impl/build.gradle.kts index 9a2203b2..1a9e32c1 100644 --- a/pubnub-chat-impl/build.gradle.kts +++ b/pubnub-chat-impl/build.gradle.kts @@ -1,5 +1,9 @@ +@file:OptIn(ExperimentalKotlinGradlePluginApi::class) + import com.pubnub.gradle.enableAnyIosTarget import com.pubnub.gradle.enableJsTarget +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JsModuleKind import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension plugins { @@ -13,6 +17,25 @@ plugins { } kotlin { + if (enableJsTarget) { + js { +// keep this in here for ad-hoc testing +// browser { +// testTask { +// useMocha { +// timeout = "15s" +// } +// } +// } + + compilerOptions { + target.set("es2015") + moduleKind.set(JsModuleKind.MODULE_UMD) + } + binaries.library() + } + } + sourceSets { val commonMain by getting { dependencies { diff --git a/pubnub-chat-impl/config/ktlint/baseline.xml b/pubnub-chat-impl/config/ktlint/baseline.xml new file mode 100644 index 00000000..8aa069b6 --- /dev/null +++ b/pubnub-chat-impl/config/ktlint/baseline.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pubnub-chat-impl/src/appleMain/kotlin/com/pubnub/chat/internal/ChatImpl.ios.kt b/pubnub-chat-impl/src/appleMain/kotlin/com/pubnub/chat/internal/ChatImpl.ios.kt new file mode 100644 index 00000000..e78dc7b4 --- /dev/null +++ b/pubnub-chat-impl/src/appleMain/kotlin/com/pubnub/chat/internal/ChatImpl.ios.kt @@ -0,0 +1,7 @@ +package com.pubnub.chat.internal + +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid + +@OptIn(ExperimentalUuidApi::class) +actual fun generateRandomUuid(): String = Uuid.random().toString() diff --git a/pubnub-chat-impl/src/appleMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.ios.kt b/pubnub-chat-impl/src/appleMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.ios.kt index c9decf6f..a8a77fd5 100644 --- a/pubnub-chat-impl/src/appleMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.ios.kt +++ b/pubnub-chat-impl/src/appleMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.ios.kt @@ -10,7 +10,7 @@ import platform.Foundation.NSTextCheckingResult import platform.Foundation.matchesInString private val userMentionRegex = NSRegularExpression( - pattern = """(?<=^|\p{Space})(@[\p{Alpha}\-]+)""", + pattern = """(?<=^|\p{Space})(@[\p{Alpha}\-\d]+)""", options = 0u, error = null ) diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt index 5e965bca..fe0b0843 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt @@ -113,8 +113,6 @@ import kotlinx.datetime.Clock import kotlinx.datetime.Instant import kotlin.reflect.KClass import kotlin.time.Duration.Companion.seconds -import kotlin.uuid.ExperimentalUuidApi -import kotlin.uuid.Uuid class ChatImpl( override val config: ChatConfiguration, @@ -421,7 +419,7 @@ class ChatImpl( if (!isValidId(channelId)) { return log.logErrorAndReturnException(CHANNEL_ID_IS_REQUIRED).asFuture() } - return pubNub.getChannelMetadata(channel = channelId) + return pubNub.getChannelMetadata(channel = channelId, includeCustom = true) .then { pnChannelMetadataResult: PNChannelMetadataResult -> ChannelImpl.fromDTO(this, pnChannelMetadataResult.data) }.catch { exception -> @@ -505,7 +503,6 @@ class ChatImpl( } } - @OptIn(ExperimentalUuidApi::class) override fun createPublicConversation( channelId: String?, channelName: String?, @@ -513,7 +510,7 @@ class ChatImpl( channelCustom: CustomObject?, channelStatus: String? ): PNFuture { - val finalChannelId: String = channelId ?: Uuid.random().toString() + val finalChannelId: String = channelId ?: generateRandomUuid() return createChannel( id = finalChannelId, @@ -573,7 +570,6 @@ class ChatImpl( } } - @OptIn(ExperimentalUuidApi::class) override fun createGroupConversation( invitedUsers: Collection, channelId: String?, @@ -584,7 +580,7 @@ class ChatImpl( membershipCustom: CustomObject? ): PNFuture { val user = this.currentUser - val finalChannelId = channelId ?: Uuid.random().toString() + val finalChannelId = channelId ?: generateRandomUuid() return getChannel(finalChannelId).thenAsync { channel -> channel?.asFuture() ?: createChannel( finalChannelId, @@ -980,7 +976,8 @@ class ChatImpl( ).thenAsync { getEventsHistoryResult: GetEventsHistoryResult -> isMore = getEventsHistoryResult.isMore getEventsHistoryResult.events - .filterIsInstance>() + .filter { it.payload is EventContent.Mention } // this performs actual filtering + .filterIsInstance>() // this adjusts the type passed into map() below .map { mentionEvent: Event -> val mentionTimetoken = mentionEvent.payload.messageTimetoken val mentionChannelId = mentionEvent.payload.channel @@ -1249,3 +1246,5 @@ class ChatImpl( } } } + +internal expect fun generateRandomUuid(): String diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MembershipImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MembershipImpl.kt index f1af8a9e..f3305698 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MembershipImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MembershipImpl.kt @@ -45,7 +45,7 @@ data class MembershipImpl( return setLastReadMessageTimetoken(message.timetoken) } - override fun update(custom: CustomObject): PNFuture { + override fun update(custom: CustomObject?): PNFuture { return exists().thenAsync { exists -> if (!exists) { log.pnError(NO_SUCH_MEMBERSHIP_EXISTS) diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.kt index aa0d212d..5b093038 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.kt @@ -1,6 +1,6 @@ package com.pubnub.chat.internal -import com.pubnub.api.PubNubException +import co.touchlab.kermit.Logger import com.pubnub.api.models.consumer.PNPublishResult import com.pubnub.chat.Channel import com.pubnub.chat.MentionTarget @@ -11,6 +11,7 @@ import com.pubnub.chat.MessageElement import com.pubnub.chat.SuggestedMention import com.pubnub.chat.User import com.pubnub.chat.internal.error.PubNubErrorMessage +import com.pubnub.chat.internal.util.pnError import com.pubnub.chat.types.ChannelType import com.pubnub.chat.types.InputFile import com.pubnub.kmp.PNFuture @@ -36,6 +37,12 @@ class MessageDraftImpl( override val channelLimit: Int = 10 ) : MessageDraft { override var quotedMessage: Message? = null + set(value) { + if (value != null && value.channelId != this.channel.id) { + log.pnError(PubNubErrorMessage.CANNOT_QUOTE_MESSAGE_FROM_OTHER_CHANNELS) + } + field = value + } override val files: MutableList = mutableListOf() private val listeners = atomic(setOf()) @@ -76,7 +83,7 @@ class MessageDraftImpl( override fun insertSuggestedMention(mention: SuggestedMention, text: String) { if (messageText.substring(mention.offset, mention.offset + mention.replaceFrom.length) != mention.replaceFrom) { - throw PubNubException(PubNubErrorMessage.MENTION_SUGGESTION_INVALID) + log.pnError(PubNubErrorMessage.MENTION_SUGGESTION_INVALID) } removeTextInternal(mention.offset, mention.replaceFrom.length) insertTextInternal(mention.offset, text) @@ -91,7 +98,7 @@ class MessageDraftImpl( } override fun removeMention(offset: Int) { - mentions.removeAll { it.start == offset } + mentions.removeAll { offset in it.start until it.endExclusive } fireMessageElementsChanged() } @@ -121,16 +128,18 @@ class MessageDraftImpl( shouldStore: Boolean, usePost: Boolean, ttl: Int? - ): PNFuture = channel.sendText( - text = render(getMessageElements()), - meta = meta, - shouldStore = shouldStore, - usePost = usePost, - ttl = ttl, - quotedMessage = quotedMessage, - files = files, - usersToMention = mentions.mapNotNull { it.target as? MentionTarget.User }.map { it.userId } - ) + ): PNFuture { + return channel.sendText( + text = render(getMessageElements()), + meta = meta, + shouldStore = shouldStore, + usePost = usePost, + ttl = ttl, + quotedMessage = quotedMessage, + files = files, + usersToMention = mentions.mapNotNull { it.target as? MentionTarget.User }.map { it.userId } + ) + } private fun fireMessageElementsChanged() { val listeners = listeners.value @@ -146,7 +155,7 @@ class MessageDraftImpl( private val RegexMatchResult.matchStart get() = range.first - private fun getSuggestedMentions(): PNFuture> { + internal fun getSuggestedMentions(): PNFuture> { val allUserMentions = findUserMentionMatches(messageText) val allChannelMentions = findChannelMentionMatches(messageText) @@ -243,8 +252,9 @@ class MessageDraftImpl( mentions.add(mention) } - internal fun getMessageElements(): List = - getMessageElements(messageText, mentions) + internal fun getMessageElements(): List { + return getMessageElements(messageText, mentions) + } private fun getSuggestedUsers(searchText: String): PNFuture> { return if (userSuggestionSource == MessageDraft.UserSuggestionSource.CHANNEL) { @@ -261,7 +271,8 @@ class MessageDraftImpl( } companion object { -// private val linkRegex = Regex("""\[(?(?:[^]]*?(?:\\\\)*(?:\\])*)+?)]\((?(?:[^)]*?(?:\\\\)*(?:\\\))*)+?)\)""") + private val log = Logger.withTag("MessageDraftImpl") + private val linkRegex = Regex("""\[(?(?:[^\]]*?(?:\\\\)*(?:\\\])*)+?)\]\((?(?:[^)]*?(?:\\\\)*(?:\\\))*)+?)\)""") internal fun escapeLinkText(text: String) = text.replace("\\", "\\\\").replace("]", "\\]") diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/UserImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/UserImpl.kt index ae6550a1..6213fc35 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/UserImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/UserImpl.kt @@ -19,7 +19,6 @@ import com.pubnub.chat.User import com.pubnub.chat.internal.error.PubNubErrorMessage import com.pubnub.chat.internal.error.PubNubErrorMessage.CAN_NOT_STREAM_USER_UPDATES_ON_EMPTY_LIST import com.pubnub.chat.internal.error.PubNubErrorMessage.MODERATION_CAN_BE_SET_ONLY_BY_CLIENT_HAVING_SECRET_KEY -import com.pubnub.chat.internal.error.PubNubErrorMessage.STORE_USER_ACTIVITY_INTERVAL_IS_FALSE import com.pubnub.chat.internal.restrictions.RestrictionImpl import com.pubnub.chat.internal.util.logErrorAndReturnException import com.pubnub.chat.internal.util.pnError @@ -51,9 +50,6 @@ data class UserImpl( ) : User { override val active: Boolean get() { - if (!chat.config.storeUserActivityTimestamps) { - log.pnError(STORE_USER_ACTIVITY_INTERVAL_IS_FALSE) - } return lastActiveTimestamp?.let { lastActiveTimestampNonNull -> Clock.System.now() - Instant.fromEpochMilliseconds(lastActiveTimestampNonNull) <= chat.config.storeUserActivityInterval } ?: false @@ -99,7 +95,7 @@ data class UserImpl( sort: Collection>, ): PNFuture { val internalModerationFilter = "!(channel.id LIKE '${INTERNAL_MODERATION_PREFIX}*')" - val effectiveFilter: String = filter?.let { "$internalModerationFilter && $filter" } ?: internalModerationFilter + val effectiveFilter: String = filter?.let { "$internalModerationFilter && ($filter)" } ?: internalModerationFilter return chat.pubNub.getMemberships( uuid = id, @@ -109,6 +105,7 @@ data class UserImpl( sort = sort, includeCount = true, includeCustom = true, + includeType = true, includeChannelDetails = getChannelDetailsType(true) ).then { pnChannelMembershipArrayResult -> MembershipsResponse( diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt index a9b4a1dd..03a61b9c 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt @@ -145,7 +145,7 @@ abstract class BaseChannel( return chat.forwardMessage(message, this.id) } - override fun startTyping(): PNFuture { + override fun startTyping(): PNFuture { if (type == ChannelType.PUBLIC) { return log.logErrorAndReturnException(TYPING_INDICATORS_NO_SUPPORTED_IN_PUBLIC_CHATS).asFuture() } @@ -153,7 +153,7 @@ abstract class BaseChannel( typingSent?.let { typingSentNotNull: Instant -> if (!timeoutElapsed(typingTimeout - typingTimoutMargin, typingSentNotNull, now)) { - return Unit.asFuture() + return null.asFuture() } } @@ -161,7 +161,7 @@ abstract class BaseChannel( return sendTypingSignal(true) } - override fun stopTyping(): PNFuture { + override fun stopTyping(): PNFuture { if (type == ChannelType.PUBLIC) { return log.logErrorAndReturnException(TYPING_INDICATORS_NO_SUPPORTED_IN_PUBLIC_CHATS).asFuture() } @@ -169,9 +169,9 @@ abstract class BaseChannel( typingSent?.let { typingSentNotNull: Instant -> val now = clock.now() if (timeoutElapsed(typingTimeout, typingSentNotNull, now)) { - return Unit.asFuture() + return null.asFuture() } - } ?: return Unit.asFuture() + } ?: return null.asFuture() typingSent = null return sendTypingSignal(false) @@ -743,11 +743,11 @@ abstract class BaseChannel( return channelFactory(chat, toPNChannelMetadata() + update) } - private fun sendTypingSignal(value: Boolean): PNFuture { + private fun sendTypingSignal(value: Boolean): PNFuture { return chat.emitEvent( channelId = this.id, payload = EventContent.Typing(value), - ).then { } + ) } internal abstract fun copyWithStatusDeleted(): C diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/error/PubNubErrorMessage.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/error/PubNubErrorMessage.kt index c666009b..0a17ef2e 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/error/PubNubErrorMessage.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/error/PubNubErrorMessage.kt @@ -25,8 +25,6 @@ internal object PubNubErrorMessage { "Channel invites are not supported in Public chats." internal const val MODERATION_CAN_BE_SET_ONLY_BY_CLIENT_HAVING_SECRET_KEY = "Moderation restrictions can only be set by clients initialized with a Secret Key." - internal const val STORE_USER_ACTIVITY_INTERVAL_IS_FALSE = - "storeUserActivityTimestamps config property is set to false so can not provide info about user being active" internal const val STORE_USER_ACTIVITY_INTERVAL_SHOULD_BE_AT_LEAST_1_MIN = "storeUserActivityInterval must be at least 60000ms" internal const val APNS_TOPIC_SHOULD_BE_DEFINED_WHEN_DEVICE_GATEWAY_IS_SET_TO_APNS2 = diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/BaseMessage.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/BaseMessage.kt index e47499ee..25bc5ecc 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/BaseMessage.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/BaseMessage.kt @@ -2,7 +2,7 @@ package com.pubnub.chat.internal.message import co.touchlab.kermit.Logger import com.pubnub.api.JsonElement -import com.pubnub.api.PubNubException +import com.pubnub.api.PubNubError import com.pubnub.api.asMap import com.pubnub.api.decode import com.pubnub.api.endpoints.message_actions.RemoveMessageAction @@ -28,7 +28,6 @@ import com.pubnub.chat.internal.error.PubNubErrorMessage.CANNOT_STREAM_MESSAGE_U import com.pubnub.chat.internal.error.PubNubErrorMessage.KEY_IS_NOT_VALID_INTEGER import com.pubnub.chat.internal.error.PubNubErrorMessage.THIS_MESSAGE_HAS_NOT_BEEN_DELETED import com.pubnub.chat.internal.serialization.PNDataEncoder -import com.pubnub.chat.internal.util.logWarnAndReturnException import com.pubnub.chat.internal.util.pnError import com.pubnub.chat.types.EventContent import com.pubnub.chat.types.File @@ -58,6 +57,7 @@ abstract class BaseMessage( override val userId: String, override val actions: Map>>? = null, private val metaInternal: JsonElement? = null, + override val error: PubNubError? = null, ) : Message { override val meta: Map? get() = metaInternal?.decode() as? Map override val quotedMessage: QuotedMessage? get() = metaInternal.extractQuotedMessage() @@ -187,9 +187,9 @@ abstract class BaseMessage( } override fun report(reason: String): PNFuture { - val channelId = "$INTERNAL_MODERATION_PREFIX$channelId" + val reportChannelId = "$INTERNAL_MODERATION_PREFIX$channelId" return chat.emitEvent( - channelId, + reportChannelId, EventContent.Report( text, reason, @@ -214,7 +214,7 @@ abstract class BaseMessage( uuid = chat.currentUser.id } val newActions = if (existingReaction != null) { - chat.pubNub.removeMessageAction(channelId, timetoken, existingReaction.actionTimetoken.toLong()) + chat.pubNub.removeMessageAction(channelId, timetoken, existingReaction.actionTimetoken) .then { filterAction(actions, messageAction) } } else { chat.pubNub.addMessageAction(channelId, messageAction) @@ -231,7 +231,7 @@ abstract class BaseMessage( override fun restore(): PNFuture { val deleteActions: List = getDeleteActions() - ?: return PubNubException(THIS_MESSAGE_HAS_NOT_BEEN_DELETED).logWarnAndReturnException(log).asFuture() + ?: return this.also { log.w(THIS_MESSAGE_HAS_NOT_BEEN_DELETED) }.asFuture() var updatedActions: Actions? = actions?.filterNot { it.key == chat.deleteMessageActionName } @@ -281,6 +281,8 @@ abstract class BaseMessage( internal abstract fun copyWithActions(actions: Actions?): T + internal abstract fun copyWithContent(content: EventContent.TextMessageContent): T + companion object { private val log = Logger.withTag("BaseMessageImpl") diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/MessageImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/MessageImpl.kt index e111d384..0c176e83 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/MessageImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/MessageImpl.kt @@ -1,6 +1,7 @@ package com.pubnub.chat.internal.message import com.pubnub.api.JsonElement +import com.pubnub.api.PubNubError import com.pubnub.api.models.consumer.history.PNFetchMessageItem import com.pubnub.api.models.consumer.history.PNFetchMessageItem.Action import com.pubnub.api.models.consumer.pubsub.PNMessageResult @@ -16,7 +17,8 @@ data class MessageImpl( override val channelId: String, override val userId: String, override val actions: Map>>? = null, - val metaInternal: JsonElement? = null + val metaInternal: JsonElement? = null, + override val error: PubNubError? = null, ) : BaseMessage( chat = chat, timetoken = timetoken, @@ -24,10 +26,13 @@ data class MessageImpl( channelId = channelId, userId = userId, actions = actions, - metaInternal = metaInternal + metaInternal = metaInternal, + error = error ) { override fun copyWithActions(actions: Actions?): Message = copy(actions = actions) + override fun copyWithContent(content: EventContent.TextMessageContent): Message = copy(content = content) + companion object { internal fun fromDTO(chat: ChatInternal, pnMessageResult: PNMessageResult): Message { val content = @@ -35,12 +40,13 @@ data class MessageImpl( ?: defaultGetMessageResponseBody(pnMessageResult.message) ?: EventContent.UnknownMessageFormat(pnMessageResult.message) return MessageImpl( - chat, - pnMessageResult.timetoken!!, - content, - pnMessageResult.channel, - pnMessageResult.publisher!!, - metaInternal = pnMessageResult.userMetadata + chat = chat, + timetoken = pnMessageResult.timetoken!!, + content = content, + channelId = pnMessageResult.channel, + userId = pnMessageResult.publisher!!, + metaInternal = pnMessageResult.userMetadata, + error = pnMessageResult.error, ) } @@ -51,13 +57,14 @@ data class MessageImpl( ?: EventContent.UnknownMessageFormat(messageItem.message) return MessageImpl( - chat, - messageItem.timetoken!!, - content, - channelId, - messageItem.uuid!!, - messageItem.actions, - messageItem.meta + chat = chat, + timetoken = messageItem.timetoken!!, + content = content, + channelId = channelId, + userId = messageItem.uuid!!, + actions = messageItem.actions, + metaInternal = messageItem.meta, + error = messageItem.error, ) } } diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/ThreadMessageImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/ThreadMessageImpl.kt index c95fecfe..2a4a3486 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/ThreadMessageImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/message/ThreadMessageImpl.kt @@ -2,6 +2,7 @@ package com.pubnub.chat.internal.message import co.touchlab.kermit.Logger import com.pubnub.api.JsonElement +import com.pubnub.api.PubNubError import com.pubnub.api.models.consumer.history.PNFetchMessageItem import com.pubnub.api.models.consumer.history.PNFetchMessageItem.Action import com.pubnub.api.models.consumer.pubsub.PNMessageResult @@ -27,6 +28,7 @@ data class ThreadMessageImpl( override val userId: String, override val actions: Map>>? = null, val metaInternal: JsonElement? = null, + override val error: PubNubError? = null, ) : BaseMessage( chat = chat, timetoken = timetoken, @@ -35,10 +37,13 @@ data class ThreadMessageImpl( userId = userId, actions = actions, metaInternal = metaInternal, + error = error ), ThreadMessage { override fun copyWithActions(actions: Actions?): ThreadMessage = copy(actions = actions) + override fun copyWithContent(content: EventContent.TextMessageContent): ThreadMessage = copy(content = content) + companion object { private val log = Logger.withTag("ThreadMessageImpl") @@ -55,6 +60,7 @@ data class ThreadMessageImpl( pnMessageResult.channel, pnMessageResult.publisher!!, metaInternal = pnMessageResult.userMetadata, + error = pnMessageResult.error ) } @@ -72,7 +78,8 @@ data class ThreadMessageImpl( channelId = channelId, userId = messageItem.uuid!!, actions = messageItem.actions, - metaInternal = messageItem.meta + metaInternal = messageItem.meta, + error = messageItem.error ) } } diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt index bb561e22..7ebe6031 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt @@ -602,6 +602,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { fun can_getMessageReportsHistory() = runTest { val pnPublishResult = channel01.sendText(text = "message1").await() val timetoken = pnPublishResult.timetoken + delayInMillis(250) val message = channel01.getMessage(timetoken).await()!! // report messages @@ -652,7 +653,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { val reportReason = reportEvent.payload.reason assertTrue(reportReason == reason01 || reportReason == reason02) assertEquals(messageText, reportEvent.payload.text) - assertTrue(reportEvent.payload.reportedMessageChannelId?.contains(INTERNAL_MODERATION_PREFIX)!!) + assertEquals(message.channelId, reportEvent.payload.reportedMessageChannelId) assertTrue(reportEvent.channelId.contains(INTERNAL_MODERATION_PREFIX)) if (numberOfReports.value == 2) { assertionErrorInCallback.complete(null) diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/MessageIntegrationTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/MessageIntegrationTest.kt index daca313a..a044177d 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/MessageIntegrationTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/MessageIntegrationTest.kt @@ -12,7 +12,10 @@ import com.pubnub.chat.internal.message.MessageImpl import com.pubnub.chat.listenForEvents import com.pubnub.chat.types.EventContent import com.pubnub.chat.types.HistoryResponse +import com.pubnub.chat.types.InputFile import com.pubnub.chat.types.MessageActionType +import com.pubnub.internal.PLATFORM +import com.pubnub.kmp.Uploadable import com.pubnub.kmp.createCustomObject import com.pubnub.test.await import com.pubnub.test.randomString @@ -25,6 +28,29 @@ import kotlin.test.assertFalse import kotlin.test.assertTrue class MessageIntegrationTest : BaseChatIntegrationTest() { + @Test + fun sendingFiles() = runTest { + if (PLATFORM == "iOS") { // TODO investigate why it doesn't work + return@runTest + } + val tt = channel01.sendText( + "message", + files = listOf( + InputFile("name.txt", "text/plain", generateFileContent()) + ) + ).await() + + delayInMillis(250) + val message: Message = channel01.getMessage(tt.timetoken).await()!! + assertEquals(1, message.files.size) + val file = message.files.first() + assertEquals("text/plain", file.type) + assertEquals("name.txt", file.name) + assertTrue(file.url.startsWith("https://")) + + pubnub.deleteFile(channel01.id, file.name, file.id).await() + } + @Test fun createMessageThenSoftDeleteThenRestore() = runTest { val messageText = "messageText_${randomString()}" @@ -75,6 +101,7 @@ class MessageIntegrationTest : BaseChatIntegrationTest() { val messageText = "messageText_${randomString()}" val pnPublishResult = channel01.sendText(text = messageText).await() val publishTimetoken = pnPublishResult.timetoken + delayInMillis(300) val message: Message = channel01.getMessage(publishTimetoken).await()!! val threadChannel: ThreadChannel = message.createThread().await() // we need to call sendText because addMessageAction is called in sendText that stores details about thread @@ -87,6 +114,7 @@ class MessageIntegrationTest : BaseChatIntegrationTest() { assertTrue(messageWithThread.hasThread) messageWithThread.removeThread().await() + delayInMillis(300) // we need to call getMessage to get message with indication that it has no Thread val messageWithNoThread: Message = channel01.getMessage(publishTimetoken).await()!! @@ -202,7 +230,7 @@ class MessageIntegrationTest : BaseChatIntegrationTest() { try { // we need to have try/catch here because assertion error will not cause test to fail assertEquals(reason, event.payload.reason) - assertEquals(channelId, event.payload.reportedMessageChannelId) + assertEquals(channel01.id, event.payload.reportedMessageChannelId) assertEquals(channelId, event.channelId) assertEquals(someUser.id, event.payload.reportedUserId) assertEquals(timetoken, event.payload.reportedMessageTimetoken) @@ -237,3 +265,5 @@ class MessageIntegrationTest : BaseChatIntegrationTest() { private fun Message.asImpl(): MessageImpl { return this as MessageImpl } + +expect fun generateFileContent(): Uploadable diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/UserIntegrationTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/UserIntegrationTest.kt index f72f20a0..2cfc5c8f 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/UserIntegrationTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/UserIntegrationTest.kt @@ -1,6 +1,5 @@ package com.pubnub.integration -import com.pubnub.api.PubNubException import com.pubnub.api.models.consumer.objects.PNMembershipKey import com.pubnub.api.models.consumer.objects.PNSortKey import com.pubnub.chat.Chat @@ -18,7 +17,6 @@ import com.pubnub.test.test import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertFailsWith import kotlin.test.assertTrue class UserIntegrationTest : BaseChatIntegrationTest() { @@ -159,18 +157,6 @@ class UserIntegrationTest : BaseChatIntegrationTest() { assertEquals(expectedUpdates, actualUpdates) } - @Test - fun calling_active_should_throw_exception_when_storeUserActivityTimestamps_is_false() = runTest { - val e = assertFailsWith { - someUser.active - } - - assertEquals( - "storeUserActivityTimestamps config property is set to false so can not provide info about user being active", - e.message - ) - } - @Test fun whenUserDoesNotExist_init_should_create_it_with_lastActiveTimestamp() = runTest { // set up storeUserActivityTimestamps diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChannelTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChannelTest.kt index 2580c242..57384916 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChannelTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChannelTest.kt @@ -41,6 +41,7 @@ import com.pubnub.chat.types.MessageMentionedUser import com.pubnub.chat.types.MessageReferencedChannel import com.pubnub.internal.PLATFORM import com.pubnub.kmp.utils.BaseTest +import com.pubnub.kmp.utils.get import com.pubnub.test.await import com.pubnub.test.randomString import dev.mokkery.MockMode @@ -177,7 +178,7 @@ class ChannelTest : BaseTest() { objectUnderTest.startTyping().async { result -> // then assertTrue(result.isSuccess) - assertEquals(Unit, result.getOrNull()) + assertEquals(null, result.getOrNull()) } verify(exactly(0)) { chat.emitEvent(any(), any()) } @@ -199,7 +200,7 @@ class ChannelTest : BaseTest() { objectUnderTest.startTyping().async { result -> // then assertTrue(result.isSuccess) - assertEquals(Unit, result.getOrNull()) + assertNotNull(result.getOrNull()) } verify { @@ -217,7 +218,7 @@ class ChannelTest : BaseTest() { // when objectUnderTest.startTyping().async { result -> assertTrue(result.isSuccess) - assertEquals(Unit, result.getOrNull()) + assertNotNull(result.getOrNull()) } // then @@ -244,7 +245,7 @@ class ChannelTest : BaseTest() { objectUnderTest.startTyping().async { result -> assertTrue(result.isSuccess) - assertEquals(Unit, result.getOrNull()) + assertEquals(null, result.getOrNull()) } verify(exactly(0)) { chat.emitEvent(any(), any()) } @@ -264,7 +265,7 @@ class ChannelTest : BaseTest() { fun whenStopTypingAlreadySentStopTypingShouldImmediatelyResultSuccess() { objectUnderTest.stopTyping().async { result -> assertTrue(result.isSuccess) - assertEquals(Unit, result.getOrNull()) + assertEquals(null, result.getOrNull()) } verify(exactly(0)) { chat.emitEvent(any(), any()) } @@ -284,7 +285,7 @@ class ChannelTest : BaseTest() { objectUnderTest.stopTyping().async { result -> assertTrue(result.isSuccess) - assertEquals(Unit, result.getOrNull()) + assertEquals(null, result.getOrNull()) } verify(exactly(0)) { chat.emitEvent(any(), any()) } @@ -306,7 +307,7 @@ class ChannelTest : BaseTest() { // when objectUnderTest.stopTyping().async { result -> assertTrue(result.isSuccess) - assertEquals(Unit, result.getOrNull()) + assertNotNull(result.getOrNull()) } // then @@ -690,7 +691,7 @@ class ChannelTest : BaseTest() { objectUnderTest.startTyping().async { result -> assertTrue(result.isSuccess) - assertEquals(Unit, result.getOrNull()) + assertNotNull(result.getOrNull()) } assertEquals(currentTimeStampInMillis, objectUnderTest.typingSent) verify(exactly(1)) { chat.emitEvent(any(), any()) } @@ -712,7 +713,7 @@ class ChannelTest : BaseTest() { objectUnderTest.startTyping().async { result -> assertTrue(result.isSuccess) - assertEquals(Unit, result.getOrNull()) + assertEquals(null, result.getOrNull()) } assertEquals(typingSent, objectUnderTest.typingSent) verify(exactly(0)) { chat.emitEvent(any(), any()) } @@ -734,7 +735,7 @@ class ChannelTest : BaseTest() { objectUnderTest.startTyping().async { result -> assertTrue(result.isSuccess) - assertEquals(Unit, result.getOrNull()) + assertNotNull(result.getOrNull()) } assertEquals(currentTimeStampInMillis, objectUnderTest.typingSent) verify(exactly(1)) { chat.emitEvent(any(), any()) } @@ -883,8 +884,8 @@ class ChannelTest : BaseTest() { } val actualCustomMetadata = customSlot.get() - val actualPinnedMessageTimtoken = actualCustomMetadata["pinnedMessageTimetoken"] - val actualPinnedMessageChannelId = actualCustomMetadata["pinnedMessageChannelID"] + val actualPinnedMessageTimtoken = actualCustomMetadata.get("pinnedMessageTimetoken") + val actualPinnedMessageChannelId = actualCustomMetadata.get("pinnedMessageChannelID") assertEquals(timetoken.toString(), actualPinnedMessageTimtoken) assertEquals(channelId, actualPinnedMessageChannelId) } @@ -902,7 +903,7 @@ class ChannelTest : BaseTest() { objectUnderTest = createChannel(type = type, custom = customData) val timetoken = 9999999L val channelId = "adfjaldf" - val customSlot = Capture.slot>() + val customSlot = Capture.slot() every { pubNub.setChannelMetadata( channel = any(), @@ -923,8 +924,8 @@ class ChannelTest : BaseTest() { } val actualCustomMetadata = customSlot.get() - assertFalse(actualCustomMetadata.contains("pinnedMessageTimetoken")) - assertFalse(actualCustomMetadata.contains("pinnedMessageChannelID")) + assertNull(actualCustomMetadata.get("pinnedMessageTimetoken")) + assertNull(actualCustomMetadata.get("pinnedMessageChannelID")) } } diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt index 4103362c..240edb93 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt @@ -65,6 +65,7 @@ import com.pubnub.chat.types.EventContent import com.pubnub.chat.types.GetEventsHistoryResult import com.pubnub.chat.user.GetUsersResponse import com.pubnub.kmp.utils.BaseTest +import com.pubnub.kmp.utils.get import dev.mokkery.MockMode import dev.mokkery.answering.calls import dev.mokkery.answering.returns @@ -430,7 +431,7 @@ class ChatTest : BaseTest() { @Test fun shouldResultErrorWhenSetChannelMetadataResultError() { - every { pubnub.getChannelMetadata(any()) } returns getChannelMetadataEndpoint + every { pubnub.getChannelMetadata(any(), any()) } returns getChannelMetadataEndpoint every { getChannelMetadataEndpoint.async(any()) } calls { (callback1: Consumer>) -> callback1.accept(Result.success(getPNChannelMetadataResult())) } @@ -459,7 +460,7 @@ class ChatTest : BaseTest() { @Test fun shouldResultErrorWhenUpdatingChannelThatDoesNotExist() { - every { pubnub.getChannelMetadata(any()) } returns getChannelMetadataEndpoint + every { pubnub.getChannelMetadata(any(), any()) } returns getChannelMetadataEndpoint every { getChannelMetadataEndpoint.async(any()) } calls { (callback1: Consumer>) -> callback1.accept(Result.failure(pnException404)) } @@ -495,7 +496,7 @@ class ChatTest : BaseTest() { val updatedType = ChannelType.GROUP.stringValue val updatedStatus = "updatedStatus" - every { pubnub.getChannelMetadata(any()) } returns getChannelMetadataEndpoint + every { pubnub.getChannelMetadata(any(), any()) } returns getChannelMetadataEndpoint every { getChannelMetadataEndpoint.async(any()) } calls { (callback1: Consumer>) -> callback1.accept(Result.success(getPNChannelMetadataResult())) } @@ -703,7 +704,7 @@ class ChatTest : BaseTest() { @Test fun whenChannelNotFoundShouldReturnProperMessage() { - every { pubnub.getChannelMetadata(any()) } returns getChannelMetadataEndpoint + every { pubnub.getChannelMetadata(any(), any()) } returns getChannelMetadataEndpoint every { getChannelMetadataEndpoint.async(any()) } calls { (callback1: Consumer>) -> callback1.accept(Result.failure(pnException404)) } @@ -716,7 +717,7 @@ class ChatTest : BaseTest() { @Test fun getChannelShouldResultSuccessWhenChannelExists() { - every { pubnub.getChannelMetadata(any()) } returns getChannelMetadataEndpoint + every { pubnub.getChannelMetadata(any(), any()) } returns getChannelMetadataEndpoint every { getChannelMetadataEndpoint.async(any()) } calls { (callback1: Consumer>) -> callback1.accept( Result.success( @@ -745,7 +746,7 @@ class ChatTest : BaseTest() { @Test fun createChannelShouldResultFailureWhenChannelExists() { - every { pubnub.getChannelMetadata(any()) } returns getChannelMetadataEndpoint + every { pubnub.getChannelMetadata(any(), any()) } returns getChannelMetadataEndpoint every { getChannelMetadataEndpoint.async(any()) } calls { (callback1: Consumer>) -> callback1.accept(Result.success(getPNChannelMetadataResult())) } @@ -758,7 +759,7 @@ class ChatTest : BaseTest() { @Test fun createChannelShouldResultSuccessWhenChannelDoesNotExist() { - every { pubnub.getChannelMetadata(any()) } returns getChannelMetadataEndpoint + every { pubnub.getChannelMetadata(any(), any()) } returns getChannelMetadataEndpoint every { getChannelMetadataEndpoint.async(any()) } calls { (callback1: Consumer>) -> callback1.accept(Result.failure(pnException404)) } @@ -1167,7 +1168,7 @@ class ChatTest : BaseTest() { fun whenCreatingPublicConversationWithChannelIdShouldUseIt() { val channelId = id // - every { pubnub.getChannelMetadata(any()) } returns getChannelMetadataEndpoint + every { pubnub.getChannelMetadata(any(), any()) } returns getChannelMetadataEndpoint every { getChannelMetadataEndpoint.async(any()) } calls { (callback1: Consumer>) -> callback1.accept(Result.failure(pnException404)) } @@ -1210,7 +1211,7 @@ class ChatTest : BaseTest() { @OptIn(ExperimentalUuidApi::class) @Test fun whenCreatingPublicConversationWithoutChannelIdShouldGenerateIt() { - every { pubnub.getChannelMetadata(any()) } returns getChannelMetadataEndpoint + every { pubnub.getChannelMetadata(any(), any()) } returns getChannelMetadataEndpoint every { getChannelMetadataEndpoint.async(any()) } calls { (callback1: Consumer>) -> callback1.accept(Result.failure(pnException404)) } @@ -1401,12 +1402,12 @@ class ChatTest : BaseTest() { } val actualRestrictedChannelId: String = channelIdSlot.get() - val actualRestriction = userIdsSlot.get()[0].custom as Map + val actualRestriction = userIdsSlot.get()[0].custom val actualModerationEventChannelId = userIdSlot.get() val actualEncodedMessageSlot = encodedMessageSlot.get() - assertTrue(actualRestriction["ban"] as Boolean) - assertEquals(reason, actualRestriction["reason"]) - assertEquals("banned", actualEncodedMessageSlot.get("restriction")) + assertTrue(actualRestriction?.get("ban") as Boolean) + assertEquals(reason, actualRestriction.get("reason")) + assertEquals("banned", actualEncodedMessageSlot["restriction"]) assertEquals("PUBNUB_INTERNAL_MODERATION_$restrictedChannelId", actualRestrictedChannelId) assertEquals(restrictedUserId, actualModerationEventChannelId) } diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/MessageDraftTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/MessageDraftTest.kt index 56b0c0ce..d26046dc 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/MessageDraftTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/MessageDraftTest.kt @@ -399,7 +399,7 @@ class MessageDraftTest { "@user" to "@user", "@us()er" to "@us", "@user @fsjdoif" to "@user", - "@123aaa @user" to "@user", + "@,123aaa @user" to "@user", "@user aaa" to "@user", "aaa @user aaa" to "@user", "aaa @user," to "@user", diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/MessageTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/MessageTest.kt deleted file mode 100644 index 0b3a3a7f..00000000 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/MessageTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.pubnub.kmp - -import com.pubnub.api.models.consumer.history.PNFetchMessageItem.Action -import com.pubnub.api.v2.callbacks.Result -import com.pubnub.chat.Message -import com.pubnub.chat.internal.ChatInternal -import com.pubnub.chat.internal.message.MessageImpl -import com.pubnub.chat.types.EventContent -import dev.mokkery.MockMode -import dev.mokkery.answering.returns -import dev.mokkery.every -import dev.mokkery.mock -import kotlin.test.BeforeTest -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -class MessageTest { - private lateinit var objectUnderTest: MessageImpl - private val chat: ChatInternal = mock(MockMode.strict) - private val timetoken: Long = 123457 - private val messageTest = "this is my message" - private val messageContent: EventContent.TextMessageContent = EventContent.TextMessageContent( - text = messageTest, - ) - private val channelId = "testId" - private val userId = "myUserId" - - @BeforeTest - fun setUp() { - objectUnderTest = MessageImpl( - chat = chat, - timetoken = timetoken, - content = messageContent, - channelId = channelId, - userId = userId, - ) - } - - @Test - fun shouldReturnErrorWhenRestoringMessageThatDoesNotExists() { - val actionsWithEntryIndicatingThatMessageHasBeenDeleted: Map>> = - mapOf("deleted" to mapOf("deleted" to listOf(Action("user1", 1234L)))) - objectUnderTest = MessageImpl( - chat = chat, - timetoken = timetoken, - content = messageContent, - channelId = channelId, - userId = userId, - actions = actionsWithEntryIndicatingThatMessageHasBeenDeleted - ) - every { chat.deleteMessageActionName } returns "DELETED" - - objectUnderTest.restore().async { result: Result -> - assertTrue(result.isFailure) - assertEquals("This message has not been deleted", result.exceptionOrNull()?.message) - println(result) - } - } -} diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/UserTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/UserTest.kt index febe8c4c..ab111a28 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/UserTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/UserTest.kt @@ -197,7 +197,8 @@ class UserTest { sort = any(), includeCount = any(), includeCustom = any(), - includeChannelDetails = any() + includeChannelDetails = any(), + includeType = any() ) } returns getMembershipsEndpoint every { getMembershipsEndpoint.async(any()) } calls { (callback1: Consumer>) -> @@ -218,7 +219,7 @@ class UserTest { val limit = 10 val page = PNPage.PNNext("nextPageHash") val filter = "channel.name LIKE '*super*'" - val expectedFilter = "!(channel.id LIKE '$INTERNAL_MODERATION_PREFIX*') && $filter" + val expectedFilter = "!(channel.id LIKE '$INTERNAL_MODERATION_PREFIX*') && ($filter)" val sort = listOf(PNSortKey.PNAsc(PNMembershipKey.CHANNEL_ID)) val getMembershipsEndpoint: GetMemberships = mock(MockMode.strict) every { chat.pubNub } returns pubNub @@ -231,7 +232,8 @@ class UserTest { sort = any(), includeCount = any(), includeCustom = any(), - includeChannelDetails = any() + includeChannelDetails = any(), + includeType = any() ) } returns getMembershipsEndpoint every { getMembershipsEndpoint.async(any()) } calls { (callback1: Consumer>) -> @@ -256,7 +258,8 @@ class UserTest { sort = sort, includeCount = true, includeCustom = true, - includeChannelDetails = PNChannelDetailsLevel.CHANNEL_WITH_CUSTOM + includeChannelDetails = PNChannelDetailsLevel.CHANNEL_WITH_CUSTOM, + includeType = true ) } } diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/utils/BaseTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/utils/BaseTest.kt index db66f5d9..ddd1f77b 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/utils/BaseTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/utils/BaseTest.kt @@ -8,6 +8,7 @@ import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadataResult import com.pubnub.api.utils.PatchValue import com.pubnub.chat.types.ChannelType +import com.pubnub.kmp.CustomObject open class BaseTest { internal val id = "testId" @@ -59,3 +60,5 @@ open class BaseTest { return PNChannelMetadataResult(status = 200, data = pnChannelMetadata) } } + +internal expect fun CustomObject.get(key: String): Any? diff --git a/pubnub-chat-impl/src/iosTest/kotlin/com/pubnub/integration/MessageIntegrationTest.ios.kt b/pubnub-chat-impl/src/iosTest/kotlin/com/pubnub/integration/MessageIntegrationTest.ios.kt new file mode 100644 index 00000000..f12b87d5 --- /dev/null +++ b/pubnub-chat-impl/src/iosTest/kotlin/com/pubnub/integration/MessageIntegrationTest.ios.kt @@ -0,0 +1,17 @@ +package com.pubnub.integration + +import com.pubnub.kmp.DataUploadContent +import com.pubnub.kmp.Uploadable +import kotlinx.cinterop.BetaInteropApi +import platform.Foundation.NSString +import platform.Foundation.NSUTF8StringEncoding +import platform.Foundation.create +import platform.Foundation.dataUsingEncoding + +@OptIn(BetaInteropApi::class) +actual fun generateFileContent(): Uploadable { + return DataUploadContent( + NSString.create(string = "some text").dataUsingEncoding(NSUTF8StringEncoding)!!, + "text/plain" + ) +} diff --git a/pubnub-chat-impl/src/iosTest/kotlin/com/pubnub/kmp/utils/BaseTest.ios.kt b/pubnub-chat-impl/src/iosTest/kotlin/com/pubnub/kmp/utils/BaseTest.ios.kt new file mode 100644 index 00000000..9d2dec78 --- /dev/null +++ b/pubnub-chat-impl/src/iosTest/kotlin/com/pubnub/kmp/utils/BaseTest.ios.kt @@ -0,0 +1,7 @@ +package com.pubnub.kmp.utils + +import com.pubnub.kmp.CustomObject + +internal actual fun CustomObject.get(key: String): Any? { + return (value as Map)[key] +} diff --git a/pubnub-chat-impl/src/jsMain/kotlin/ChannelJs.kt b/pubnub-chat-impl/src/jsMain/kotlin/ChannelJs.kt new file mode 100644 index 00000000..abe175af --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/ChannelJs.kt @@ -0,0 +1,331 @@ +@file:OptIn(ExperimentalJsExport::class, ExperimentalJsStatic::class) + +import com.pubnub.chat.Channel +import com.pubnub.chat.MessageDraft +import com.pubnub.chat.internal.MessageDraftImpl +import com.pubnub.chat.internal.channel.BaseChannel +import com.pubnub.chat.types.ChannelType +import com.pubnub.chat.types.InputFile +import com.pubnub.kmp.JsMap +import com.pubnub.kmp.UploadableImpl +import com.pubnub.kmp.asFuture +import com.pubnub.kmp.createJsObject +import com.pubnub.kmp.then +import com.pubnub.kmp.toJsMap +import com.pubnub.kmp.toMap +import kotlin.js.Json +import kotlin.js.Promise +import kotlin.js.json + +@JsExport +@JsName("Channel") +open class ChannelJs internal constructor(internal val channel: Channel, internal val chatJs: ChatJs) : ChannelFields { + override val id: String by channel::id + override val name: String? by channel::name + override val custom: Any? get() = channel.custom?.toJsMap() + override val description: String? by channel::description + override val updated: String? by channel::updated + override val status: String? by channel::status + override val type: String? get() = channel.type?.stringValue + + fun update(data: ChannelFields): Promise { + return channel.update( + data.name, + convertToCustomObject(data.custom), + data.description, + data.status, + ChannelType.from(data.type) + ).then { + it.asJs(chatJs) + }.asPromise() + } + + fun delete(options: DeleteParameters?): Promise { + return channel.delete(options?.soft ?: false).then { + if (it != null) { + DeleteChannelResult(it.asJs(chatJs)) + } else { + DeleteChannelResult(true) + } + }.asPromise() + } + + fun streamUpdates(callback: (channel: ChannelJs?) -> Unit): () -> Unit { + val closeable = channel.streamUpdates { + callback(it?.asJs(chatJs)) + } + return closeable::close + } + + fun sendText(text: String, options: SendTextOptionParams?): Promise { + @Suppress("USELESS_CAST") // cast required to be able to call "let" extension function + val files = (options?.files as? Any)?.let { files -> + val filesArray = + files as? Array<*> ?: arrayOf(files) + filesArray.filterNotNull().map { file -> + InputFile("", file.asDynamic().type ?: file.asDynamic().mimeType ?: "", UploadableImpl(file)) + } + } ?: listOf() + return channel.sendText( + text = text, + meta = options?.meta?.unsafeCast>()?.toMap(), + shouldStore = options?.storeInHistory ?: true, + usePost = options?.sendByPost ?: false, + ttl = options?.ttl?.toInt(), + mentionedUsers = options?.mentionedUsers?.toMap()?.mapKeys { it.key.toInt() }, + referencedChannels = options?.referencedChannels?.toMap()?.mapKeys { it.key.toInt() }, + textLinks = options?.textLinks?.toList(), + quotedMessage = options?.quotedMessage?.message, + files = files + ).then { result -> + result.toPublishResponse() + }.asPromise() + } + + fun forwardMessage(message: MessageJs): Promise { + return channel.forwardMessage(message.message).then { it.toPublishResponse() }.asPromise() + } + + fun startTyping(): Promise { + return channel.startTyping().then { it?.toPublishResponse() ?: undefined }.asPromise() + } + + fun stopTyping(): Promise { + return channel.stopTyping().then { it?.toPublishResponse() ?: undefined }.asPromise() + } + + fun getTyping(callback: (Array) -> Unit): () -> Unit { + return channel.getTyping { callback(it.toTypedArray()) } + .let { autoCloseable -> + autoCloseable::close + } + } + + fun connect(callback: (MessageJs) -> Unit): () -> Unit { + return channel.connect { + callback(it.asJs(chatJs)) + }::close + } + + fun whoIsPresent(): Promise> { + return channel.whoIsPresent().then { it.toTypedArray() }.asPromise() + } + + fun isPresent(userId: String): Promise { + return channel.isPresent(userId).asPromise() + } + + fun streamPresence(callback: (Array) -> Unit): Promise<() -> Unit> { + return channel.streamPresence { callback(it.toTypedArray()) }.let { + it::close + }.asFuture().asPromise() + } + + open fun getHistory(params: GetHistoryParams?): Promise { + return channel.getHistory( + params?.startTimetoken?.toLong(), + params?.endTimetoken?.toLong(), + params?.count?.toInt() ?: 25 + ).then { result -> + createJsObject { + this.isMore = result.isMore + this.messages = result.messages.map { it.asJs(chatJs) }.toTypedArray() + } + }.asPromise() + } + + fun getMessage(timetoken: String): Promise { + return channel.getMessage(timetoken.tryLong()!!).then { it!!.asJs(chatJs) }.asPromise() + } + + fun join(callback: (MessageJs) -> Unit, params: PubNub.SetMembershipsParameters?): Promise { + return channel.join { callback(it.asJs(chatJs)) }.then { + createJsObject { + membership = it.membership.asJs(chatJs) + disconnect = it.disconnect?.let { autoCloseable -> autoCloseable::close } ?: {} + } + }.asPromise() + } + + fun leave(): Promise { + return channel.leave().then { true }.asPromise() + } + + fun getMembers(params: PubNub.GetChannelMembersParameters?): Promise { + return channel.getMembers( + params?.limit?.toInt() ?: 100, + params?.page?.toKmp(), + params?.filter, + extractSortKeys(params?.sort) + ).then { result -> + createJsObject { + this.page = MetadataPage(result.next, result.prev) + this.total = result.total + this.status = result.status + this.members = result.members.map { it.asJs(chatJs) }.toTypedArray() + } + }.asPromise() + } + + fun invite(user: UserJs): Promise { + return channel.invite(user.user).then { it.asJs(chatJs) }.asPromise() + } + + fun inviteMultiple(users: Array): Promise> { + return channel.inviteMultiple(users.map { it.user }) + .then { memberships -> + memberships.map { it.asJs(chatJs) }.toTypedArray() + }.asPromise() + } + + open fun pinMessage(message: MessageJs): Promise { + return channel.pinMessage(message.message).then { it.asJs(chatJs) }.asPromise() + } + + open fun unpinMessage(): Promise { + return channel.unpinMessage().then { it.asJs(chatJs) }.asPromise() + } + + fun getPinnedMessage(): Promise { + return channel.getPinnedMessage().then { it?.asJs(chatJs) }.asPromise() + } + + fun createMessageDraft(config: MessageDraftConfig?): MessageDraftV1Js { + return MessageDraftV1Js( + chatJs, + this, + config + ) + } + + fun createMessageDraftV2(config: MessageDraftConfig?): MessageDraftV2Js { + return MessageDraftV2Js( + MessageDraftImpl( + this.channel, + config?.userSuggestionSource?.let { + userSuggestionSourceFrom(it) + } ?: MessageDraft.UserSuggestionSource.CHANNEL, + config?.isTypingIndicatorTriggered ?: (channel.type != ChannelType.PUBLIC), + config?.userLimit ?: 10, + config?.channelLimit ?: 10 + ), + config + ) + } + + fun registerForPush(): Promise { + return channel.registerForPush().asPromise() + } + + fun unregisterFromPush(): Promise { + return channel.unregisterFromPush().asPromise() + } + + fun streamReadReceipts(callback: (JsMap>) -> Unit): Promise<() -> Unit> { + return channel.streamReadReceipts { + callback( + it + .mapKeys { entry -> entry.key.toString() } + .mapValues { entry -> entry.value.toTypedArray() } + .toJsMap() + ) + }.let { autoCloseable -> + autoCloseable::close.asFuture().asPromise() + } + } + + fun getFiles(params: PubNub.ListFilesParameters?): Promise { + return channel.getFiles( + params?.limit?.toInt() ?: 100, + params?.next + ).then { result -> + createJsObject { + this.files = result.files.toTypedArray() + this.next = result.next + this.total = result.total + } + }.asPromise() + } + + fun deleteFile(params: DeleteFileParams): Promise { + return channel.deleteFile(params.id, params.name).then { + createJsObject { + this.status = it.status + } + }.asPromise() + } + + fun setRestrictions(user: UserJs, params: RestrictionJs): Promise { + return channel.setRestrictions( + user.user, + params.ban ?: false, + params.mute ?: false, + params.reason?.toString() + ).asPromise() + } + + fun getUserRestrictions(user: UserJs): Promise { + return channel.getUserRestrictions(user.user).then { it.asJs() }.asPromise() + } + + fun getUsersRestrictions(params: PubNub.GetChannelMembersParameters?): Promise { + return channel.getUsersRestrictions( + params?.limit?.toInt(), + params?.page?.toKmp(), + extractSortKeys(params?.sort), + ).then { + it.toJs() + }.asPromise() + } + + @Deprecated("Only for internal MessageDraft V1 use") + fun getUserSuggestions(text: String, options: GetSuggestionsParams?): Promise> { + val limit = options?.limit + val cacheKey = MessageElementsUtils.getPhraseToLookFor(text) ?: return Promise.resolve(emptyArray()) + return channel.getUserSuggestions(cacheKey, limit?.toInt() ?: 10).then { memberships -> + memberships.map { it.asJs(chatJs) }.toTypedArray() + }.asPromise() + } + + fun getMessageReportsHistory(params: GetHistoryParams?): Promise { + return this.channel.getMessageReportsHistory( + params?.startTimetoken?.toLong(), + params?.endTimetoken?.toLong(), + params?.count ?: 100 + ).then { result -> + createJsObject { + events = result.events.map { it.toJs(chatJs) }.toTypedArray() + isMore = result.isMore + } + }.asPromise() + } + + fun toJSON(): Json { + return json( + "id" to id, + "name" to name, + "custom" to custom, + "description" to description, + "updated" to updated, + "status" to status, + "type" to type + ) + } + + companion object { + @JsStatic + fun streamUpdatesOn(channels: Array, callback: (Array) -> Unit): () -> Unit { + val chatJs = channels.first().chatJs + val closeable = BaseChannel.streamUpdatesOn(channels.map { jsChannel -> jsChannel.channel }) { + callback(it.map { kmpChannel -> ChannelJs(kmpChannel, chatJs) }.toTypedArray()) + } + return closeable::close + } + } +} + +internal fun Channel.asJs(chat: ChatJs) = ChannelJs(this, chat) + +private fun userSuggestionSourceFrom(lowercaseString: String): MessageDraft.UserSuggestionSource { + return MessageDraft.UserSuggestionSource.entries.first { it.name.lowercase() == lowercaseString } +} diff --git a/pubnub-chat-impl/src/jsMain/kotlin/ChatJs.kt b/pubnub-chat-impl/src/jsMain/kotlin/ChatJs.kt new file mode 100644 index 00000000..7f935d93 --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/ChatJs.kt @@ -0,0 +1,393 @@ +@file:OptIn(ExperimentalJsExport::class, ExperimentalJsStatic::class) + +import com.pubnub.api.PubNubImpl +import com.pubnub.api.createJsonElement +import com.pubnub.chat.internal.ChatImpl +import com.pubnub.chat.internal.ChatInternal +import com.pubnub.chat.internal.serialization.PNDataEncoder +import com.pubnub.chat.restrictions.Restriction +import com.pubnub.chat.types.ChannelMentionData +import com.pubnub.chat.types.ChannelType +import com.pubnub.chat.types.CreateDirectConversationResult +import com.pubnub.chat.types.CreateGroupConversationResult +import com.pubnub.chat.types.EmitEventMethod +import com.pubnub.chat.types.EventContent +import com.pubnub.chat.types.ThreadMentionData +import com.pubnub.kmp.createJsObject +import com.pubnub.kmp.then +import kotlin.js.Json +import kotlin.js.Promise +import kotlin.js.json + +@JsExport +@JsName("Chat") +class ChatJs internal constructor(val chat: ChatInternal, val config: ChatConfig) { + val currentUser: UserJs get() = chat.currentUser.asJs(this@ChatJs) + + val sdk: PubNub get() = (chat.pubNub as PubNubImpl).jsPubNub + + fun emitEvent(event: dynamic): Promise { + val channel: String = event.channel ?: event.user + val type = event.type + val payload = event.payload + payload.type = type + return chat.emitEvent( + channel, + PNDataEncoder.decode(createJsonElement(payload)) + ).then { it.toPublishResponse() }.asPromise() + } + + fun listenForEvents(event: ListenForEventsParams): () -> Unit { + val klass = when (event.type) { + "typing" -> EventContent.Typing::class + "report" -> EventContent.Report::class + "receipt" -> EventContent.Receipt::class + "mention" -> EventContent.Mention::class + "invite" -> EventContent.Invite::class + "custom" -> EventContent.Custom::class + "moderation" -> EventContent.Moderation::class + else -> throw IllegalArgumentException("Unknown event type ${event.type}") + } + val channel: String = event.channel ?: event.user!! + val method = if (event.method == "signal") { + EmitEventMethod.SIGNAL + } else { + EmitEventMethod.PUBLISH + } + return chat.listenForEvents( + klass, + channel, + method + ) { + event.callback(it.toJs(this)) + }::close + } + + fun getEventsHistory(params: GetEventsHistoryParams): Promise { + return chat.getEventsHistory( + params.channel, + params.startTimetoken?.toLong(), + params.endTimetoken?.toLong(), + params.count?.toInt() ?: 100 + ).then { result -> + createJsObject { + events = result.events.map { it.toJs(this@ChatJs) }.toTypedArray() + isMore = result.isMore + } + }.asPromise() + } + + fun getUser(id: String): Promise { + return chat.getUser(id).then { it?.asJs(this@ChatJs) }.asPromise() + } + + fun createUser(id: String, data: UserFields): Promise { + return chat.createUser( + id, + data.name, + data.externalId, + data.profileUrl, + data.email, + convertToCustomObject(data.custom), + data.status, + data.type + ).then { it.asJs(this@ChatJs) }.asPromise() + } + + fun updateUser(id: String, data: UserFields): Promise { + return chat.updateUser( + id, + data.name, + data.externalId, + data.profileUrl, + data.email, + convertToCustomObject(data.custom), + data.status, + data.type + ).then { it.asJs(this@ChatJs) }.asPromise() + } + + fun deleteUser(id: String, params: DeleteParameters?): Promise { + return chat.deleteUser(id, params?.soft ?: false) + .then { + if (it != null) { + DeleteUserResult(it.asJs(this@ChatJs)) + } else { + DeleteUserResult(true) + } + }.asPromise() + } + + fun getUsers(params: PubNub.GetAllMetadataParameters?): Promise { + return chat.getUsers( + params?.filter, + extractSortKeys(params), + params?.limit?.toInt(), + params?.page?.toKmp() + ).then { result -> + createJsObject { + this.users = result.users.map { it.asJs(this@ChatJs) }.toTypedArray() + this.page = MetadataPage(result.next, result.prev) + this.total = result.total + } + }.asPromise() + } + + fun getChannel(id: String): Promise { + return chat.getChannel(id).then { it?.asJs(this) }.asPromise() + } + + fun updateChannel(id: String, data: ChannelFields): Promise { + return chat.updateChannel( + id, + data.name, + convertToCustomObject(data.custom), + data.description, + data.status, + ChannelType.from(data.type) + ).then { it.asJs(this@ChatJs) }.asPromise() + } + + fun getChannels(params: PubNub.GetAllMetadataParameters?): Promise { + return chat.getChannels( + params?.filter, + extractSortKeys(params), + params?.limit?.toInt(), + params?.page?.toKmp() + ).then { result -> + createJsObject { + this.users = result.channels.map { it.asJs(this@ChatJs) }.toTypedArray() + this.page = MetadataPage(result.next, result.prev) + this.total = result.total + } + }.asPromise() + } + + fun deleteChannel(id: String, params: DeleteParameters?): Promise { + return chat.deleteChannel(id, params?.soft ?: false) + .then { + if (it != null) { + DeleteChannelResult(it.asJs(this@ChatJs)) + } else { + DeleteChannelResult(true) + } + }.asPromise() + } + + // internal + fun createChannel(id: String, data: PubNub.ChannelMetadata?): Promise { + return chat.createChannel( + id, + data?.name, + data?.description, + convertToCustomObject(data?.custom), + ChannelType.from(data?.type), + data?.status + ).then { it.asJs(this@ChatJs) }.asPromise() + } + + // internal + fun getThreadId(channelId: String, messageId: String) = + ChatImpl.getThreadId(channelId, messageId.toLong()) + + fun createPublicConversation(params: CreateChannelParams?): Promise { + val channelId: String? = params?.channelId + val data: PubNub.ChannelMetadata? = params?.channelData + return chat.createPublicConversation( + channelId, + data?.name, + data?.description, + convertToCustomObject(data?.custom), + data?.status + ).then { it.asJs(this@ChatJs) }.asPromise() + } + + fun createDirectConversation(params: CreateDirectConversationParams): Promise { + val user: UserJs = params.user + val channelId: String? = params.channelId + val data: PubNub.ChannelMetadata? = params.channelData + val membershipCustom: Any? = params.membershipData?.custom + return chat.createDirectConversation( + user.user, + channelId, + data?.name, + data?.description, + convertToCustomObject(data?.custom), + data?.status, + convertToCustomObject(membershipCustom), + ).then { result -> + result.toJs() + }.asPromise() + } + + fun createGroupConversation(params: CreateGroupConversationParams): Promise { + val users: Array = params.users + val channelId: String? = params.channelId + val data: PubNub.ChannelMetadata? = params.channelData + val membershipCustom: Any? = params.membershipData?.custom + return chat.createGroupConversation( + users.map { it.user }, + channelId, + data?.name, + data?.description, + convertToCustomObject(data?.custom), + data?.status, + convertToCustomObject(membershipCustom), + ).then { result -> + result.toJs() + }.asPromise() + } + + fun wherePresent(id: String): Promise> { + return chat.wherePresent(id).then { it.toTypedArray() }.asPromise() + } + + fun whoIsPresent(id: String): Promise> { + return chat.whoIsPresent(id).then { it.toTypedArray() }.asPromise() + } + + fun isPresent(userId: String, channelId: String): Promise { + return chat.isPresent(userId, channelId).asPromise() + } + + fun registerPushChannels(channels: Array): Promise { + return chat.registerPushChannels(channels.toList()).asPromise() + } + + fun unregisterPushChannels(channels: Array): Promise { + return chat.unregisterPushChannels(channels.toList()).asPromise() + } + + fun unregisterAllPushChannels(): Promise { + return chat.unregisterAllPushChannels().asPromise() + } + + fun getPushChannels(): Promise> { + return chat.getPushChannels().then { it.toTypedArray() }.asPromise() + } + + fun getCurrentUserMentions( + params: GetHistoryParams? + ): Promise { + return chat.getCurrentUserMentions( + params?.startTimetoken?.toLong(), + params?.endTimetoken?.toLong(), + params?.count?.toInt() ?: 100 + ).then { result -> + createJsObject { + this.isMore = result.isMore + this.enhancedMentionsData = result.enhancedMentionsData.map { + when (it) { + is ChannelMentionData -> createJsObject { + this.userId = it.userId + this.channelId = it.channelId + this.event = it.event.toJs(this@ChatJs) + this.message = it.message.asJs(this@ChatJs) + } + + is ThreadMentionData -> createJsObject { + this.userId = it.userId + this.parentChannelId = it.parentChannelId + this.threadChannelId = it.threadChannelId + this.event = it.event.toJs(this@ChatJs) + this.message = it.message.asJs(this@ChatJs) + } + } + }.toTypedArray() + } + }.asPromise() + } + + fun getUnreadMessagesCounts(params: PubNub.GetMembershipsParametersv2?): Promise> { + return chat.getUnreadMessagesCounts( + params?.limit?.toInt(), + params?.page?.toKmp(), + params?.filter, + extractSortKeys(params?.sort) + ).then { result -> + result.map { unreadMessagesCount -> + createJsObject { + this.channel = unreadMessagesCount.channel.asJs(this@ChatJs) + this.membership = unreadMessagesCount.membership.asJs(this@ChatJs) + this.count = unreadMessagesCount.count.toDouble() + } + }.toTypedArray() + }.asPromise() + } + + fun markAllMessagesAsRead(params: PubNub.GetMembershipsParametersv2): Promise { + return chat.markAllMessagesAsRead( + params.limit?.toInt(), + params.page?.toKmp(), + params.filter, + extractSortKeys(params.sort) + ).then { result -> + createJsObject { + this.page = MetadataPage(result.next, result.prev) + this.total = result.total + this.status = result.status + this.memberships = result.memberships.map { it.asJs(this@ChatJs) }.toTypedArray() + } + }.asPromise() + } + + fun setRestrictions(userId: String, channelId: String, params: RestrictionJs): Promise { + return chat.setRestrictions( + Restriction( + userId, + channelId, + params.ban ?: false, + params.mute ?: false, + params.reason?.toString() + ) + ).asPromise() + } + + private fun CreateDirectConversationResult.toJs() = + createJsObject { + this.channel = this@toJs.channel.asJs(this@ChatJs) + this.hostMembership = this@toJs.hostMembership.asJs(this@ChatJs) + this.inviteeMembership = this@toJs.inviteeMembership.asJs(this@ChatJs) + } + + private fun CreateGroupConversationResult.toJs() = + createJsObject { + this.channel = this@toJs.channel.asJs(this@ChatJs) + this.hostMembership = this@toJs.hostMembership.asJs(this@ChatJs) + this.inviteesMemberships = this@toJs.inviteeMemberships.map { it.asJs(this@ChatJs) }.toTypedArray() + } + + fun toJSON(): Json { + return json("config" to config, "currentUser" to currentUser) + } + + @Deprecated("Only for internal MessageDraft V1 use") + fun getUserSuggestions(text: String, options: GetSuggestionsParams?): Promise> { + val limit = options?.limit + val cacheKey = MessageElementsUtils.getPhraseToLookFor(text) ?: return Promise.resolve(emptyArray()) + return chat.getUserSuggestions(cacheKey, limit?.toInt() ?: 10).then { users -> + users.map { it.asJs(this@ChatJs) }.toTypedArray() + }.asPromise() + } + + @Deprecated("Only for internal MessageDraft V1 use") + fun getChannelSuggestions(text: String, options: GetSuggestionsParams?): Promise> { + val limit = options?.limit + val cacheKey = MessageElementsUtils.getChannelPhraseToLookFor(text) ?: return Promise.resolve(emptyArray()) + return chat.getChannelSuggestions(cacheKey, limit?.toInt() ?: 10).then { channels -> + channels.map { it.asJs(this@ChatJs) }.toTypedArray() + }.asPromise() + } + + companion object { + @JsStatic + fun init(config: ChatConstructor): Promise { + return ChatImpl(config.toChatConfiguration(), PubNubImpl(PubNub(config))).initialize().then { + ChatJs(it as ChatInternal, config) + }.asPromise() + } + } +} + +@JsExport +val INTERNAL_MODERATION_PREFIX = "PUBNUB_INTERNAL_MODERATION_" diff --git a/pubnub-chat-impl/src/jsMain/kotlin/CryptoUtils.kt b/pubnub-chat-impl/src/jsMain/kotlin/CryptoUtils.kt new file mode 100644 index 00000000..d15c60f4 --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/CryptoUtils.kt @@ -0,0 +1,31 @@ +@file:OptIn(ExperimentalJsExport::class, ExperimentalJsStatic::class) + +import com.pubnub.api.createJsonElement +import com.pubnub.chat.ThreadMessage +import com.pubnub.chat.internal.message.BaseMessage +import com.pubnub.chat.internal.serialization.PNDataEncoder +import com.pubnub.chat.types.EventContent + +@JsExport +class CryptoUtils { + companion object { + @JsStatic + fun decrypt(params: DecryptParams): MessageJs { + val decryptedContentJs = params.decryptor(params.message.message.text) + val decryptedContent: EventContent.TextMessageContent = PNDataEncoder.decode(createJsonElement(decryptedContentJs)) + + val message = params.message.message as BaseMessage<*> + val newMessage = message.copyWithContent(decryptedContent) + return (newMessage as? ThreadMessage)?.asJs(params.chat) ?: newMessage.asJs(params.chat) + } + } +} + +external interface DecryptParams { + val chat: ChatJs + val message: MessageJs + val decryptor: (String) -> Any +} + +@JsExport +val CryptoModule = PubNub.CryptoModule diff --git a/pubnub-chat-impl/src/jsMain/kotlin/EventJs.kt b/pubnub-chat-impl/src/jsMain/kotlin/EventJs.kt new file mode 100644 index 00000000..5e23161e --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/EventJs.kt @@ -0,0 +1,12 @@ +@file:OptIn(ExperimentalJsExport::class) + +@JsExport +@JsName("Event") +class EventJs( + val chat: ChatJs, + val timetoken: String, + val type: String, + val payload: dynamic, + val channelId: String, + val userId: String, +) diff --git a/pubnub-chat-impl/src/jsMain/kotlin/MembershipJs.kt b/pubnub-chat-impl/src/jsMain/kotlin/MembershipJs.kt new file mode 100644 index 00000000..1890228b --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/MembershipJs.kt @@ -0,0 +1,76 @@ +@file:OptIn(ExperimentalJsExport::class, ExperimentalJsStatic::class) + +import com.pubnub.chat.Membership +import com.pubnub.chat.internal.MembershipImpl +import com.pubnub.kmp.then +import com.pubnub.kmp.toJsMap +import kotlin.js.Json +import kotlin.js.Promise +import kotlin.js.json + +@JsExport +@JsName("Membership") +class MembershipJs internal constructor(internal val membership: Membership, internal val chatJs: ChatJs) { + val channel: ChannelJs get() = membership.channel.asJs(chatJs) + val user: UserJs get() = membership.user.asJs(chatJs) + val custom get() = membership.custom?.toJsMap() + val updated by membership::updated + val eTag by membership::eTag + + val lastReadMessageTimetoken: String? get() = membership.lastReadMessageTimetoken?.toString() + + fun update(custom: dynamic): Promise { + return membership.update(convertToCustomObject(custom?.custom)) + .then { it.asJs(chatJs) } + .asPromise() + } + + fun streamUpdates(callback: (MembershipJs?) -> Unit): () -> Unit { + return streamUpdatesOn(arrayOf(this)) { + callback(it.firstOrNull()) + } + } + + fun setLastReadMessage(message: MessageJs): Promise { + return membership.setLastReadMessage(message.message).then { it.asJs(chatJs) }.asPromise() + } + + fun setLastReadMessageTimetoken(timetoken: String): Promise { + return membership.setLastReadMessageTimetoken(timetoken.toLong()).then { it.asJs(chatJs) }.asPromise() + } + + fun getUnreadMessagesCount(): Promise { + return membership.getUnreadMessagesCount().then { + if (it != null) { + GetUnreadMessagesCountResult(it.toInt()) + } else { + GetUnreadMessagesCountResult(false) + } + }.asPromise() + } + + fun toJSON(): Json { + return json( + "channel" to channel, + "user" to user, + "custom" to custom, + "updated" to updated, + "eTag" to eTag, + "lastReadMessageTimetoken" to lastReadMessageTimetoken + ) + } + + companion object { + @JsStatic + fun streamUpdatesOn(memberships: Array, callback: (Array) -> Unit): () -> Unit { + val chatJs = memberships.first().chatJs + return MembershipImpl.streamUpdatesOn(memberships.map { it.membership }) { + callback(it.map { it.asJs(chatJs) }.toTypedArray()) + }.let { + it::close + } + } + } +} + +internal fun Membership.asJs(chat: ChatJs) = MembershipJs(this, chat) diff --git a/pubnub-chat-impl/src/jsMain/kotlin/MessageDraftV1Js.kt b/pubnub-chat-impl/src/jsMain/kotlin/MessageDraftV1Js.kt new file mode 100644 index 00000000..acc89e0d --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/MessageDraftV1Js.kt @@ -0,0 +1,944 @@ +// This file was auto-translated from message-draft.ts and message-element-utils.ts + +@file:OptIn(ExperimentalJsExport::class) + +import com.pubnub.api.PubNubException +import com.pubnub.chat.types.MessageMentionedUser +import com.pubnub.chat.types.MessageReferencedChannel +import com.pubnub.chat.types.TextLink +import com.pubnub.kmp.JsMap +import com.pubnub.kmp.combine +import com.pubnub.kmp.createJsObject +import com.pubnub.kmp.toJsMap +import kotlin.js.Promise +import kotlin.math.abs + +@JsExport +sealed class MixedTextTypedElement(val type: String) { + data class Text(val content: TextContent) : MixedTextTypedElement("text") + + data class TextLink(val content: TextLinkContent) : MixedTextTypedElement("textLink") + + data class PlainLink(val content: PlainLinkContent) : MixedTextTypedElement("plainLink") + + data class Mention(val content: MentionContent) : MixedTextTypedElement("mention") + + data class ChannelReference(val content: ChannelReferenceContent) : MixedTextTypedElement("channelReference") +} + +@JsExport +data class TextContent(val text: String) + +@JsExport +data class TextLinkContent(val link: String, val text: String) + +@JsExport +data class PlainLinkContent(val link: String) + +@JsExport +data class MentionContent(val id: String, val name: String) + +@JsExport +data class ChannelReferenceContent(val id: String, val name: String) + +object Validator { + private val validProtocols = listOf("http://", "https://", "www.") + private val urlRegex = + """(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})""".toRegex( + RegexOption.IGNORE_CASE + ) + + fun isUrl(potentialUrl: String): Boolean { + if (validProtocols.none { potentialUrl.startsWith(it) }) { + return false + } + + if (potentialUrl.split(".").filter { it.isNotEmpty() }.size < + if (potentialUrl.startsWith("www.")) { + 3 + } else { + 2 + } + ) { + return false + } + + return urlRegex.matches(potentialUrl.replace("\n", "")) + } +} + +object MessageElementsUtils { + fun getPhraseToLookFor(text: String): String? { + val lastAtIndex = text.lastIndexOf("@") + val charactersAfterAt = text.split("@").lastOrNull() ?: "" + + if (lastAtIndex == -1 || charactersAfterAt.length < 3) { + return null + } + + val splitWords = charactersAfterAt.split(" ") + + if (splitWords.size > 2) { + return null + } + + return splitWords[0] + (splitWords.getOrNull(1)?.let { " $it" } ?: "") + } + + fun getChannelPhraseToLookFor(text: String): String? { + val lastAtIndex = text.lastIndexOf("#") + val charactersAfterHash = text.split("#").lastOrNull() ?: "" + + if (lastAtIndex == -1 || charactersAfterHash.length < 3) { + return null + } + + val splitWords = charactersAfterHash.split(" ") + + if (splitWords.size > 2) { + return null + } + + return splitWords[0] + (splitWords.getOrNull(1)?.let { " $it" } ?: "") + } + + fun getMessageElements( + text: String, + mentionedUsers: Map, + textLinks: List, + referencedChannels: Map, + ): Array { + var resultWithTextLinks = "" + val indicesOfWordsWithTextLinks = mutableListOf() + + val textLinkRanges = textLinks.map { textLink -> + (textLink.startIndex..textLink.endIndex step 1).toList() + } + val allIndices = textLinkRanges.flatten() + val startIndices = textLinkRanges.map { it.first() } + val endIndices = textLinkRanges.map { it.last() } + var spacesSoFar = 0 + + text.forEachIndexed { i, letter -> + if (letter == ' ') { + spacesSoFar++ + } + if (i in startIndices) { + val relevantIndex = startIndices.indexOf(i) + val substring = text.substring(i, endIndices[relevantIndex]) + + resultWithTextLinks += substring + + indicesOfWordsWithTextLinks.add( + WordWithTextLink( + start = spacesSoFar, + end = spacesSoFar + substring.split(" ").size, + link = textLinks[relevantIndex].link, + substring = substring + ) + ) + return@forEachIndexed + } + if (i in allIndices && i !in endIndices) { + return@forEachIndexed + } + resultWithTextLinks += letter + } + + var counter = 0 + var channelCounter = 0 + val indicesToSkip = mutableListOf() + val channelIndicesToSkip = mutableListOf() + + val splitText = resultWithTextLinks.split(" ") + val arrayOfTextElements = mutableListOf() + + splitText.forEachIndexed { index, word -> + if (!word.startsWith("@") && !word.startsWith("#")) { + if (index in indicesToSkip || index in channelIndicesToSkip) { + return@forEachIndexed + } + + val foundTextLink = indicesOfWordsWithTextLinks.find { it.start == index } + + if (foundTextLink != null) { + val substring = splitText.slice(foundTextLink.start until foundTextLink.end).joinToString(" ") + val additionalPunctuation = substring.replace(foundTextLink.substring, "") + + arrayOfTextElements.add( + MixedTextTypedElement.TextLink( + TextLinkContent( + link = foundTextLink.link, + text = foundTextLink.substring + ) + ) + ) + if (additionalPunctuation.isNotEmpty()) { + arrayOfTextElements.add( + MixedTextTypedElement.Text( + TextContent(additionalPunctuation) + ) + ) + } + arrayOfTextElements.add(MixedTextTypedElement.Text(TextContent(" "))) + indicesToSkip.addAll((index until index + substring.split(" ").size).toList()) + return@forEachIndexed + } + + if (Validator.isUrl(word)) { + val lastCharacter = word.lastOrNull()?.toString() ?: "" + if (lastCharacter in listOf("!", "?", ".", ",")) { + arrayOfTextElements.add( + MixedTextTypedElement.PlainLink( + PlainLinkContent(word.dropLast(1)) + ) + ) + arrayOfTextElements.add( + MixedTextTypedElement.Text( + TextContent(lastCharacter) + ) + ) + arrayOfTextElements.add(MixedTextTypedElement.Text(TextContent(" "))) + } else { + arrayOfTextElements.add( + MixedTextTypedElement.PlainLink( + PlainLinkContent(word) + ) + ) + arrayOfTextElements.add(MixedTextTypedElement.Text(TextContent(" "))) + } + return@forEachIndexed + } + + if (word.isNotEmpty()) { + arrayOfTextElements.add(MixedTextTypedElement.Text(TextContent(word))) + arrayOfTextElements.add(MixedTextTypedElement.Text(TextContent(" "))) + } + } else if (word.startsWith("@")) { + val mentionFound = counter in mentionedUsers + + if (!mentionFound) { + counter++ + arrayOfTextElements.add(MixedTextTypedElement.Text(TextContent(word))) + arrayOfTextElements.add(MixedTextTypedElement.Text(TextContent(" "))) + } else { + val userId = mentionedUsers[counter]!!.id + val userName = mentionedUsers[counter]!!.name + val userNameWords = userName.split(" ") + + var additionalPunctuationCharacters = "" + + if (userNameWords.size > 1) { + indicesToSkip.addAll((index until index + userNameWords.size).toList()) + additionalPunctuationCharacters = splitText[indicesToSkip.last()] + .replace(userNameWords.last(), "") + } else { + additionalPunctuationCharacters = word.replace("@", "").replace(userName, "") + } + if (additionalPunctuationCharacters.isNotEmpty()) { + additionalPunctuationCharacters += " " + } + if (additionalPunctuationCharacters.isEmpty()) { + additionalPunctuationCharacters = " " + } + + counter++ + arrayOfTextElements.add( + MixedTextTypedElement.Mention( + MentionContent( + id = userId, + name = userName + ) + ) + ) + arrayOfTextElements.add( + MixedTextTypedElement.Text( + TextContent(additionalPunctuationCharacters) + ) + ) + } + } else { + val channelReferenceFound = channelCounter in referencedChannels + + if (!channelReferenceFound) { + channelCounter++ + arrayOfTextElements.add(MixedTextTypedElement.Text(TextContent(word))) + arrayOfTextElements.add(MixedTextTypedElement.Text(TextContent(" "))) + } else { + val channelId = referencedChannels[channelCounter]!!.id + val channelName = referencedChannels[channelCounter]!!.name + val channelNameWords = channelName.split(" ") + + var additionalPunctuationCharacters = "" + + if (channelNameWords.size > 1) { + channelIndicesToSkip.addAll((index until index + channelNameWords.size).toList()) + additionalPunctuationCharacters = splitText[channelIndicesToSkip.last()] + .replace(channelNameWords.last(), "") + } else { + additionalPunctuationCharacters = word.replace("#", "").replace(channelName, "") + } + if (additionalPunctuationCharacters.isNotEmpty()) { + additionalPunctuationCharacters += " " + } + if (additionalPunctuationCharacters.isEmpty()) { + additionalPunctuationCharacters = " " + } + + channelCounter++ + arrayOfTextElements.add( + MixedTextTypedElement.ChannelReference( + ChannelReferenceContent( + id = channelId, + name = channelName + ) + ) + ) + arrayOfTextElements.add( + MixedTextTypedElement.Text( + TextContent(additionalPunctuationCharacters) + ) + ) + } + } + } + + if (arrayOfTextElements.lastOrNull() is MixedTextTypedElement.Text) { + val lastTextElement = arrayOfTextElements.last() as MixedTextTypedElement.Text + arrayOfTextElements[arrayOfTextElements.lastIndex] = + lastTextElement.copy(content = lastTextElement.content.copy(text = lastTextElement.content.text.trim())) + lastTextElement.copy(content = lastTextElement.content.copy(text = lastTextElement.content.text.trim())) + } + if (arrayOfTextElements.lastOrNull() is MixedTextTypedElement.Text && + (arrayOfTextElements.last() as MixedTextTypedElement.Text).content.text in listOf(" ", "") + ) { + arrayOfTextElements.removeLast() + } + + return arrayOfTextElements.fold(listOf()) { acc: List, curr: MixedTextTypedElement -> + val previousObject = acc.lastOrNull() + if (curr is MixedTextTypedElement.Text && previousObject is MixedTextTypedElement.Text) { + acc.dropLast(1) + MixedTextTypedElement.Text( + TextContent(previousObject.content.text + curr.content.text) + ) + } else { + acc + curr + } + }.toTypedArray() + } +} + +data class WordWithTextLink( + val start: Int, + val end: Int, + val substring: String, + val link: String, +) + +external interface AddLinkedTextParams { + val text: String + val link: String + val positionInInput: Int +} + +fun range(start: Int, stop: Int, step: Int = 1): List { + return (start..stop step step).toList() +} + +@JsExport +class MessageDraftV1Js(private val chat: ChatJs, private val channel: ChannelJs, config: MessageDraftConfig? = null) { + private var previousValue = "" + private val mentionedUsers: MutableMap = mutableMapOf() + private val referencedChannels: MutableMap = mutableMapOf() + private val _textLinks: MutableList = mutableListOf() + + var value = "" + val textLinks get() = _textLinks.toTypedArray() + var quotedMessage: MessageJs? = null + val config: MessageDraftConfig = createJsObject { + this.userSuggestionSource = config?.userSuggestionSource ?: "channel" + this.isTypingIndicatorTriggered = config?.isTypingIndicatorTriggered ?: true + this.userLimit = config?.userLimit ?: 10 + this.channelLimit = config?.channelLimit ?: 10 + } + var files: Any? = null // Adjust type as needed + + private fun reindexTextLinks() { + if (value.startsWith(previousValue)) { + // a user keeps adding text to the end; nothing to reindex + return + } + if (value == previousValue) { + // nothing changed so there is nothing to reindex + return + } + val lengthDifference = abs(previousValue.length - value.length) + + var newLinks = _textLinks.toMutableList() + val indicesToFilterOut: MutableList = mutableListOf() + + // cut from the end + if (previousValue.startsWith(value)) { + val differenceStartsAtIndex = value.length + + newLinks.forEachIndexed { i, textLink -> + if (textLink.endIndex < differenceStartsAtIndex) { + return@forEachIndexed + } + // this word was cut + if (textLink.startIndex >= differenceStartsAtIndex) { + indicesToFilterOut.add(i) + return@forEachIndexed + } + // second part of this word was cut + if (textLink.startIndex < differenceStartsAtIndex) { + newLinks[i] = newLinks[i].copy(endIndex = value.length) + } + } + + newLinks = + newLinks.filterIndexed { linkIndex, _ -> !indicesToFilterOut.contains(linkIndex) }.toMutableList() + _textLinks.clear() + _textLinks.addAll(newLinks) + } + + // a user cut text from the beginning + else if (previousValue.endsWith(value)) { + newLinks = _textLinks.toMutableList() + indicesToFilterOut.clear() + val differenceEndsAtIndex = lengthDifference + + newLinks.forEachIndexed { i, textLink -> + // this word is intact + if (textLink.startIndex >= differenceEndsAtIndex) { + newLinks[i] = newLinks[i].copy( + startIndex = newLinks[i].startIndex - lengthDifference, + endIndex = newLinks[i].endIndex - lengthDifference + ) + return@forEachIndexed + } + // this word was cut + if (textLink.endIndex <= differenceEndsAtIndex) { + indicesToFilterOut.add(i) + return@forEachIndexed + } + // first part of this word was cut + if (textLink.startIndex < differenceEndsAtIndex) { + newLinks[i] = newLinks[i].copy(startIndex = 0, endIndex = newLinks[i].endIndex - lengthDifference) + } + } + newLinks = + newLinks.filterIndexed { linkIndex, _ -> !indicesToFilterOut.contains(linkIndex) }.toMutableList() + _textLinks.clear() + _textLinks.addAll(newLinks) + } + + // a user cut from the middle of the text + else if (previousValue.length > value.length) { + newLinks = _textLinks.toMutableList() + indicesToFilterOut.clear() + var differenceStartsAtIndex = -1 + var differenceEndsAtIndex = -1 + + for ((index, letter) in previousValue.withIndex()) { + if (value.getOrElse(index) { ' ' } != letter && differenceStartsAtIndex == -1) { + differenceStartsAtIndex = index + } + if ( + value.getOrElse(value.length - 1 - index) { ' ' } != + previousValue.getOrElse(previousValue.length - 1 - index) { ' ' } && + differenceEndsAtIndex == -1 + ) { + differenceEndsAtIndex = previousValue.length - index + } + } + + newLinks.forEachIndexed { i, textLink -> + // this word was cut + if ( + differenceStartsAtIndex <= textLink.startIndex && + differenceEndsAtIndex >= textLink.endIndex + ) { + indicesToFilterOut.add(i) + return@forEachIndexed + } + // the middle part of this word was cut + if ( + differenceStartsAtIndex > textLink.startIndex && + differenceEndsAtIndex < textLink.endIndex + ) { + newLinks[i] = newLinks[i].copy(endIndex = newLinks[i].endIndex - lengthDifference) + return@forEachIndexed + } + // second part of this word was cut + if ( + differenceStartsAtIndex >= textLink.startIndex && + differenceEndsAtIndex >= textLink.endIndex && + differenceStartsAtIndex < textLink.endIndex + ) { + newLinks[i] = newLinks[i].copy(endIndex = differenceStartsAtIndex) + return@forEachIndexed + } + // first part of this word was cut + if ( + differenceEndsAtIndex > textLink.startIndex && + differenceStartsAtIndex <= textLink.startIndex + ) { + newLinks[i] = newLinks[i].copy( + endIndex = newLinks[i].endIndex - lengthDifference, + startIndex = differenceStartsAtIndex + ) + return@forEachIndexed + } + // this word is intact + if (differenceEndsAtIndex < textLink.endIndex) { + newLinks[i] = newLinks[i].copy( + startIndex = newLinks[i].startIndex - lengthDifference, + endIndex = newLinks[i].endIndex - lengthDifference + ) + return@forEachIndexed + } + } + + newLinks = + newLinks.filterIndexed { linkIndex, _ -> !indicesToFilterOut.contains(linkIndex) }.toMutableList() + _textLinks.clear() + _textLinks.addAll(newLinks) + } + // a user keeps adding text to the beginning + else if (value.endsWith(previousValue)) { + newLinks = _textLinks.toMutableList() + indicesToFilterOut.clear() + + newLinks.forEachIndexed { i, newLink -> + newLinks[i] = newLinks[i].copy( + endIndex = newLinks[i].endIndex + lengthDifference, + startIndex = newLinks[i].startIndex + lengthDifference + ) + } + + _textLinks.clear() + _textLinks.addAll(newLinks) + } + // a user keeps adding text in the middle + else if (value.length > previousValue.length) { + newLinks = _textLinks.toMutableList() + indicesToFilterOut.clear() + var differenceStartsAtIndex = -1 + var differenceEndsAtIndex = -1 + + for ((index, letter) in previousValue.withIndex()) { + if (value.getOrElse(index) { ' ' } != letter && differenceStartsAtIndex == -1) { + differenceStartsAtIndex = index + } + if ( + value.getOrElse(value.length - 1 - index) { ' ' } != + previousValue.getOrElse(previousValue.length - 1 - index) { ' ' } && + differenceEndsAtIndex == -1 + ) { + differenceEndsAtIndex = previousValue.length - index + } + } + + newLinks.forEachIndexed { i, textLink -> + // text was added before this link + if (differenceEndsAtIndex <= textLink.startIndex) { + newLinks[i] = newLinks[i].copy( + startIndex = newLinks[i].startIndex + lengthDifference, + endIndex = newLinks[i].endIndex + lengthDifference + ) + return@forEachIndexed + } + // text was added in the middle of the link + if ( + differenceStartsAtIndex > textLink.startIndex && + differenceEndsAtIndex < textLink.endIndex + ) { + newLinks[i] = newLinks[i].copy(endIndex = newLinks[i].endIndex + lengthDifference) + return@forEachIndexed + } + if ( + differenceStartsAtIndex <= textLink.startIndex && + differenceEndsAtIndex >= textLink.endIndex + ) { + indicesToFilterOut.add(i) + return@forEachIndexed + } + } + newLinks = + newLinks.filterIndexed { linkIndex, _ -> !indicesToFilterOut.contains(linkIndex) }.toMutableList() + _textLinks.clear() + _textLinks.addAll(newLinks) + } + } + + private fun getUserOrChannelReference( + splitSymbol: String, + referencesObject: Map, + ): UserOfChannelReference { + val copiedObject = referencesObject.toMutableMap() + val previousWordsStartingWithSymbol = previousValue.split(" ").filter { it.startsWith(splitSymbol) } + val currentWordsStartingWithSymbol = value.split(" ").filter { it.startsWith(splitSymbol) } + + var differentReferencePosition = -1 + var differentReference: String? = null + + for (i in currentWordsStartingWithSymbol.indices) { + if (currentWordsStartingWithSymbol[i] != previousWordsStartingWithSymbol.getOrElse(i) { "" }) { + differentReference = currentWordsStartingWithSymbol[i] + differentReferencePosition = i + break + } + } + + if (previousWordsStartingWithSymbol.size > currentWordsStartingWithSymbol.size) { + // a mention was removed + val firstRemovalIndex = + previousWordsStartingWithSymbol.indexOfFirst { e -> !currentWordsStartingWithSymbol.contains(e) } + val lastRemovalIndex = + previousWordsStartingWithSymbol.indexOfLast { e -> !currentWordsStartingWithSymbol.contains(e) } + + if (lastRemovalIndex != -1) { + val reindexedReferences = copiedObject.toMutableMap() + + copiedObject.forEach { (key, _) -> + if (key >= firstRemovalIndex && key <= lastRemovalIndex) { + reindexedReferences.remove(key) + } + if (key > lastRemovalIndex) { + val newValue = copiedObject[key] + reindexedReferences.remove(key) + reindexedReferences[key - lastRemovalIndex + firstRemovalIndex - 1] = newValue!! + } + } + + copiedObject.clear() + copiedObject.putAll(reindexedReferences) + } + } + + copiedObject.forEach { (key, value) -> + val referencedName = when (value) { + is UserJs -> value.name.orEmpty() + is ChannelJs -> value.name.orEmpty() + else -> error("Not going to happen") + } + + if (referencedName.isNotEmpty() && currentWordsStartingWithSymbol.getOrElse(key) { "" }.isEmpty()) { + copiedObject.remove(key) + } + + val splitSymbolRegex = if (splitSymbol == "@") { + """(^|\s)@([^\s@]+(?:\s+[^\s@]+)*)""".toRegex() + } else { + """(^|\s)#([^\s#]+(?:\s+[^\s#]+)*)""".toRegex() + } + + val splitMentionsByAt = + (splitSymbolRegex.findAll(this.value).map { it.value }.toList()).map { it.trim().substring(1) } + + if (referencedName.isNotEmpty() && !splitMentionsByAt.getOrElse(key) { "" }.startsWith(referencedName)) { + copiedObject.remove(key) + } + } + + return UserOfChannelReference( + referencesObject = copiedObject, + differentReference = differentReference, + differentReferencePosition = differentReferencePosition, + ) + } + + private fun parseTextToGetSuggestedUser(): Promise> { + val result = getUserOrChannelReference("@", mentionedUsers) + val differentReference = result.differentReference + val differentReferencePosition = result.differentReferencePosition + val referencesObject = result.referencesObject + + mentionedUsers.clear() + mentionedUsers.putAll(referencesObject.mapValues { it.value as UserJs }) + + if (differentReference == null) { + return Promise.resolve( + mapOf( + "nameOccurrenceIndex" to -1, + "suggestedUsers" to arrayOf() + ).toJsMap() + ) + } + + val limitOption = config.userLimit?.let { userLimit -> createJsObject { limit = userLimit } } + val suggestedUsers = if (config.userSuggestionSource == "channel") { + channel.getUserSuggestions(differentReference, limitOption).then { it.map { it.user }.toTypedArray() } + } else { + chat.getUserSuggestions(differentReference, limitOption) + } + + return suggestedUsers.then { users -> + mapOf( + "nameOccurrenceIndex" to differentReferencePosition, + "suggestedUsers" to users + ).toJsMap() + } + } + + private fun parseTextToGetSuggestedChannels(): Promise> { + val result = getUserOrChannelReference("#", referencedChannels) + val differentReference = result.differentReference + val differentReferencePosition = result.differentReferencePosition + val referencesObject = result.referencesObject + + referencedChannels.clear() + referencedChannels.putAll(referencesObject.mapValues { it.value as ChannelJs }) + + if (differentReference == null) { + return Promise.resolve( + mapOf( + "channelOccurrenceIndex" to -1, + "suggestedChannels" to arrayOf() + ).toJsMap() + ) + } + + val limitOption = config.channelLimit?.let { channelLimit -> createJsObject { limit = channelLimit } } + val suggestedChannels = chat.getChannelSuggestions(differentReference, limitOption) + + return suggestedChannels.then { channels -> + mapOf( + "channelOccurrenceIndex" to differentReferencePosition, + "suggestedChannels" to channels + ).toJsMap() + } + } + + fun onChange(text: String): Promise> { + previousValue = value + value = text + + if (config.isTypingIndicatorTriggered ?: false) { + if (value.isNotEmpty()) { + channel.startTyping() + } else { + channel.stopTyping() + } + } + + reindexTextLinks() + val usersPromise = parseTextToGetSuggestedUser() + val channelsPromise = parseTextToGetSuggestedChannels() + + return Promise.all(arrayOf(usersPromise, channelsPromise)).then { (users, channels) -> + mapOf( + "users" to users, + "channels" to channels + ).toJsMap() + } + } + + fun addMentionedUser(user: UserJs, nameOccurrenceIndex: Int) { + var counter = 0 + var result = "" + var isUserFound = false + + value.split(" ").forEach { word -> + if (!word.startsWith("@")) { + result += "$word " + } else { + if (counter != nameOccurrenceIndex) { + result += "$word " + } else { + val lastCharacter = word.last() + result += "@${user.name}" + if (listOf('!', '?', '.', ',').contains(lastCharacter)) { + result += "$lastCharacter " + } else { + result += " " + } + + mentionedUsers[nameOccurrenceIndex] = user + isUserFound = true + } + counter++ + } + } + + if (!isUserFound) { + throw Exception("This user does not appear in the text") + } + + value = result.trim() + } + + fun addReferencedChannel(channel: ChannelJs, channelNameOccurrenceIndex: Int) { + var counter = 0 + var result = "" + var isChannelFound = false + + value.split(" ").forEach { word -> + if (!word.startsWith("#")) { + result += "$word " + } else { + if (counter != channelNameOccurrenceIndex) { + result += "$word " + } else { + val lastCharacter = word.last() + result += "#${channel.name}" + if (listOf('!', '?', '.', ',').contains(lastCharacter)) { + result += "$lastCharacter " + } else { + result += " " + } + referencedChannels[channelNameOccurrenceIndex] = channel + isChannelFound = true + } + counter++ + } + } + + if (!isChannelFound) { + throw Exception("This channel does not appear in the text") + } + + value = result.trim() + } + + fun removeReferencedChannel(channelNameOccurrenceIndex: Int) { + if (referencedChannels.containsKey(channelNameOccurrenceIndex)) { + referencedChannels.remove(channelNameOccurrenceIndex) + return + } + + println("This is noop. There is no channel reference occurrence at this index.") + } + + fun removeMentionedUser(nameOccurrenceIndex: Int) { + if (mentionedUsers.containsKey(nameOccurrenceIndex)) { + mentionedUsers.remove(nameOccurrenceIndex) + return + } + + println("This is noop. There is no mention occurrence at this index.") + } + + private fun transformMentionedUsersToSend(): JsMap { + return mentionedUsers.entries.associate { (key, value) -> + key.toString() to MessageMentionedUser( + value.id, + value.name.orEmpty() + ) + }.toJsMap() + } + + private fun transformReferencedChannelsToSend(): JsMap { + return referencedChannels.entries.associate { (key, value) -> + key.toString() to MessageReferencedChannel( + value.id, + value.name.orEmpty(), + ) + }.toJsMap() + } + + fun send(options: PubNub.PublishParameters?): Promise { + val sendTextOptions = createJsObject { + this.files = this@MessageDraftV1Js.files + this.mentionedUsers = transformMentionedUsersToSend() + this.referencedChannels = transformReferencedChannelsToSend() + this.textLinks = this@MessageDraftV1Js._textLinks.toTypedArray() + this.quotedMessage = this@MessageDraftV1Js.quotedMessage + }.combine(options?.unsafeCast>()) + + return channel.sendText(value, sendTextOptions) + } + + fun getHighlightedMention(selectionStart: Int): JsMap { + val necessaryText = value.substring(0, selectionStart - 1) + val necessaryTextSplitBySpace = necessaryText.split(" ") + val onlyWordsWithAt = necessaryTextSplitBySpace.filter { it.startsWith("@") } + val lastMentionedUserInTextIndex = necessaryTextSplitBySpace.indexOfLast { it.startsWith("@") } + val lastMentionedUserInText = necessaryTextSplitBySpace.subList( + lastMentionedUserInTextIndex, + necessaryTextSplitBySpace.size + ) + + val lastMentionedUser = mentionedUsers[onlyWordsWithAt.size - 1] + + if (lastMentionedUser?.name == null) { + return mapOf( + "mentionedUser" to null, + "nameOccurrenceIndex" to -1 + ).toJsMap() + } + + if (lastMentionedUserInText.size <= lastMentionedUser.name.orEmpty().split(" ").size) { + return mapOf( + "mentionedUser" to lastMentionedUser, + "nameOccurrenceIndex" to onlyWordsWithAt.size - 1 + ).toJsMap() + } + + return mapOf( + "mentionedUser" to null, + "nameOccurrenceIndex" to -1 + ).toJsMap() + } + + fun addLinkedText(params: AddLinkedTextParams) { + if (!Validator.isUrl(params.link)) { + throw Exception("You need to insert a URL") + } + + val linkRanges = _textLinks.flatMap { textLink -> + range(textLink.startIndex, textLink.endIndex) + } + if (linkRanges.contains(params.positionInInput)) { + throw Exception("You cannot insert a link inside another link") + } + + onChange(value.substring(0, params.positionInInput) + params.text + value.substring(params.positionInInput)) + _textLinks.add(TextLink(params.positionInInput, params.positionInInput + params.text.length, params.link)) + } + + fun removeLinkedText(positionInInput: Int) { + val relevantTextLinkIndex = _textLinks.indexOfFirst { textLink -> + range(textLink.startIndex, textLink.endIndex).contains(positionInInput) + } + + if (relevantTextLinkIndex == -1) { + println("This operation is noop. There is no link at this position.") + return + } + _textLinks.removeAt(relevantTextLinkIndex) + } + + fun getMessagePreview(): Array { // Adjust return type as needed + return MessageElementsUtils.getMessageElements( + text = value, + textLinks = _textLinks, + mentionedUsers = mentionedUsers.mapValues { MessageMentionedUser(it.value.id, it.value.name.orEmpty()) }, + referencedChannels = referencedChannels.mapValues { + MessageReferencedChannel( + it.value.id, + it.value.name.orEmpty() + ) + } + ) + } + + fun addQuote(message: MessageJs) { + if (message.channelId != channel.id) { + throw PubNubException("You cannot quote messages from other channels") + } + + quotedMessage = message + } + + fun removeQuote() { + quotedMessage = undefined + } +} + +private class UserOfChannelReference( + val referencesObject: MutableMap, + val differentReference: String?, + val differentReferencePosition: Int +) diff --git a/pubnub-chat-impl/src/jsMain/kotlin/MessageDraftV2Js.kt b/pubnub-chat-impl/src/jsMain/kotlin/MessageDraftV2Js.kt new file mode 100644 index 00000000..bc15f1ea --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/MessageDraftV2Js.kt @@ -0,0 +1,125 @@ +@file:OptIn(ExperimentalJsExport::class) + +import com.pubnub.chat.MentionTarget +import com.pubnub.chat.MessageElement +import com.pubnub.chat.internal.MessageDraftImpl +import com.pubnub.chat.types.InputFile +import com.pubnub.kmp.JsMap +import com.pubnub.kmp.UploadableImpl +import com.pubnub.kmp.createJsObject +import com.pubnub.kmp.then +import com.pubnub.kmp.toMap +import kotlin.js.Promise + +@JsExport +@JsName("MessageDraftV2") +class MessageDraftV2Js internal constructor( + private val messageDraft: MessageDraftImpl, + val config: MessageDraftConfig?, +) { + val value: String get() = messageDraft.value.toString() + var quotedMessage: MessageJs? = null + var files: Any? = null + + fun addQuote(message: MessageJs) { + quotedMessage = message + } + + fun removeQuote() { + quotedMessage = null + } + + fun addLinkedText(params: AddLinkedTextParams) { + val text: String = params.text + val link: String = params.link + val offset: Int = params.positionInInput + messageDraft.insertText(offset, text) + messageDraft.addMention(offset, text.length, MentionTarget.Url(link)) + } + + fun removeLinkedText(positionOnInput: Int) { + messageDraft.removeMention(positionOnInput) + } + + fun getMessagePreview(): Array { + return messageDraft.getMessageElements().map { element -> + when (element) { + is MessageElement.Link -> when (val target = element.target) { + is MentionTarget.Channel -> createJsObject { + this.type = "channelReference" + this.content = createJsObject { + this.name = element.text.substring(1) + this.id = target.channelId + } + } + is MentionTarget.Url -> createJsObject { + this.type = "textLink" + this.content = createJsObject { + this.text = element.text + this.link = target.url + } + } + is MentionTarget.User -> createJsObject { + this.type = "mention" + this.content = createJsObject { + this.name = element.text.substring(1) + this.id = target.userId + } + } + } + is MessageElement.PlainText -> createJsObject { + this.type = "text" + this.content = createJsObject { + this.text = element.text + } + } + } + }.toTypedArray() + } + + fun send(options: PubNub.PublishParameters?): Promise { + val filesArray = files?.let { + it as? Array<*> ?: arrayOf(it) + } ?: arrayOf() + + filesArray.forEach { file: dynamic -> + val type = file.type ?: file.mimeType + val name = file.name + messageDraft.files.add(InputFile(name ?: "", type ?: "", UploadableImpl(file))) + } + messageDraft.quotedMessage = quotedMessage?.message + + return messageDraft.send( + options?.meta?.unsafeCast>()?.toMap(), + options?.storeInHistory ?: true, + options?.sendByPost ?: false, + options?.ttl?.toInt() + ).then { it.toPublishResponse() }.asPromise() + } +} + +external interface MessageElementJs { + var type: String + var content: MessageElementPayloadJs +} + +external interface MessageElementPayloadJs { + interface Text : MessageElementPayloadJs { + var text: String + } + + interface User : MessageElementPayloadJs { + var name: String + var id: String + } + + interface Link : MessageElementPayloadJs { + var text: String + var link: String + } + + interface Channel : MessageElementPayloadJs { + var name: String + var id: String + } +} diff --git a/pubnub-chat-impl/src/jsMain/kotlin/MessageJs.kt b/pubnub-chat-impl/src/jsMain/kotlin/MessageJs.kt new file mode 100644 index 00000000..f1a8dd20 --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/MessageJs.kt @@ -0,0 +1,165 @@ +@file:OptIn(ExperimentalJsExport::class, ExperimentalJsStatic::class) + +import com.pubnub.api.PubNubError +import com.pubnub.api.adjustCollectionTypes +import com.pubnub.chat.Message +import com.pubnub.chat.internal.message.BaseMessage +import com.pubnub.chat.types.EventContent +import com.pubnub.chat.types.MessageMentionedUser +import com.pubnub.chat.types.MessageReferencedChannel +import com.pubnub.kmp.JsMap +import com.pubnub.kmp.createJsObject +import com.pubnub.kmp.then +import com.pubnub.kmp.toJsMap +import com.pubnub.kmp.toMap +import kotlin.js.Json +import kotlin.js.Promise +import kotlin.js.json + +@JsExport +@JsName("Message") +open class MessageJs internal constructor(internal val message: Message, internal val chatJs: ChatJs) { + val hasThread by message::hasThread + val timetoken: String get() = message.timetoken.toString() + val content get() = (message.content as EventContent).toJsObject() + val channelId by message::channelId + val userId by message::userId + val actions get() = message.actions?.mapValues { + it.value.mapValues { + it.value.map { action -> + createJsObject { + uuid = action.uuid + actionTimetoken = action.actionTimetoken.toString() + } + }.toTypedArray() + }.toJsMap() + }?.toJsMap() + val meta get() = message.meta?.adjustCollectionTypes() + val error: String? get() { + return if (message.error == PubNubError.CRYPTO_IS_CONFIGURED_BUT_MESSAGE_IS_NOT_ENCRYPTED) { + "Error while decrypting message content" + } else { + null + } + } + + val mentionedUsers: JsMap? + get() = message.mentionedUsers?.mapKeys { it.key.toString() }?.toJsMap() + val referencedChannels: JsMap? + get() = message.referencedChannels?.mapKeys { it.key.toString() }?.toJsMap() + val textLinks get() = message.textLinks?.toTypedArray() + val type by message::type + val quotedMessage: QuotedMessageJs? get() = message.quotedMessage?.toJs() + val files get() = message.files.toTypedArray() + val text by message::text + val deleted by message::deleted + val reactions: JsMap> + get() = message.reactions.mapValues { mapEntry -> + mapEntry.value.map { action -> + createJsObject { + uuid = action.uuid + actionTimetoken = action.actionTimetoken.toString() + } + }.toTypedArray() + }.toJsMap() + + fun streamUpdates(callback: (MessageJs?) -> Unit): () -> Unit { + return message.streamUpdates { it.asJs(chatJs) }::close + } + + fun getMessageElements(): Array { + return MessageElementsUtils.getMessageElements( + text, + mentionedUsers?.toMap()?.mapKeys { it.key.toInt() } ?: emptyMap(), + textLinks?.toList() ?: emptyList(), + referencedChannels?.toMap()?.mapKeys { it.key.toInt() } ?: emptyMap(), + ) + } + + fun editText(newText: String): Promise { + return message.editText(newText).then { it.asJs(chatJs) }.asPromise() + } + + fun delete(params: DeleteParameters?): Promise { + return message.delete(params?.soft ?: false, params?.asDynamic()?.preserveFiles ?: false) + .then { + it?.asJs(chatJs) ?: true + }.asPromise() + } + + fun restore(): Promise { + return message.restore().then { it.asJs(chatJs) }.asPromise() + } + + fun hasUserReaction(reaction: String): Boolean { + return message.hasUserReaction(reaction) + } + + fun toggleReaction(reaction: String): Promise { + return message.toggleReaction(reaction).then { it.asJs(chatJs) }.asPromise() + } + + fun forward(channelId: String): Promise { + return message.forward(channelId).then { it.toPublishResponse() }.asPromise() + } + + fun pin(): Promise { + return message.pin().then { it.asJs(chatJs) }.asPromise() + } + + fun report(reason: String): Promise { + return message.report(reason).then { it.toPublishResponse() }.asPromise() + } + + fun getThread(): Promise { + return message.getThread().then { it.asJs(chatJs) }.asPromise() + } + + fun createThread(): Promise { + return message.createThread().then { it.asJs(chatJs) }.asPromise() + } + + fun removeThread(): Promise> { + return message.removeThread().then { + arrayOf( + Any(), + it.second?.asJs(chatJs)?.let { channelJs -> DeleteChannelResult(channelJs) } + ?: DeleteChannelResult(true) + ) + }.asPromise() + } + + fun toJSON(): Json { + return json( + "hasThread" to hasThread, + "timetoken" to timetoken, + "content" to content, + "channelId" to channelId, + "userId" to userId, + "actions" to actions, + "meta" to meta, + "mentionedUsers" to mentionedUsers, + "referencedChannels" to referencedChannels, + "textLinks" to textLinks.contentToString(), + "type" to type, + "quotedMessage" to quotedMessage, + "files" to files.contentToString(), + "text" to text, + "deleted" to deleted, + "reactions" to reactions, + "error" to error + ) + } + + companion object { + @JsStatic + fun streamUpdatesOn(messages: Array, callback: (Array) -> Unit): () -> Unit { + val chatJs = messages.first().chatJs + return BaseMessage.streamUpdatesOn(messages.map { it.message }) { kmpMessages -> + callback(kmpMessages.map { kmpMessage -> kmpMessage.asJs(chatJs) }.toTypedArray()) + }::close + } + } +} + +internal fun Message.asJs(chat: ChatJs) = MessageJs(this, chat) diff --git a/pubnub-chat-impl/src/jsMain/kotlin/ThreadChannelJs.kt b/pubnub-chat-impl/src/jsMain/kotlin/ThreadChannelJs.kt new file mode 100644 index 00000000..51a5ab70 --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/ThreadChannelJs.kt @@ -0,0 +1,63 @@ +@file:OptIn(ExperimentalJsExport::class) + +import com.pubnub.chat.Event +import com.pubnub.chat.ThreadChannel +import com.pubnub.kmp.createJsObject +import com.pubnub.kmp.then +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.serializer +import kotlin.js.Promise + +@JsExport +@JsName("ThreadChannel") +class ThreadChannelJs internal constructor(internal val threadChannel: ThreadChannel, chatJs: ChatJs) : ChannelJs(threadChannel, chatJs) { + val parentChannelId by threadChannel::parentChannelId + + override fun pinMessage(message: MessageJs): Promise { + return channel.pinMessage(message.message).then { it.asJs(chatJs) }.asPromise() + } + + override fun unpinMessage(): Promise { + return channel.unpinMessage().then { it.asJs(chatJs) }.asPromise() + } + + fun pinMessageToParentChannel(message: ThreadMessageJs): Promise { + return threadChannel.pinMessageToParentChannel(message.threadMessage).then { it.asJs(chatJs) }.asPromise() + } + + fun unpinMessageFromParentChannel(): Promise { + return threadChannel.unpinMessageFromParentChannel().then { it.asJs(chatJs) }.asPromise() + } + + override fun getHistory(params: dynamic): Promise { + return threadChannel.getHistory( + params?.startTimetoken?.toString()?.toLong(), + params?.endTimetoken?.toString()?.toLong(), + params?.count?.toString()?.toInt() ?: 25 + ).then { result -> + createJsObject { + this.isMore = result.isMore + this.messages = result.messages.map { it.asJs(chatJs) }.toTypedArray() + } + }.asPromise() + } +} + +external fun delete(p: dynamic): Boolean + +@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class) +internal fun Event<*>.toJs(chatJs: ChatJs): EventJs { + return EventJs( + chatJs, + timetoken.toString(), + payload::class.serializer().descriptor.serialName, + payload.toJsObject().apply { + delete(this.asDynamic()["type"]) + }, + channelId, + userId + ) +} + +internal fun ThreadChannel.asJs(chat: ChatJs) = ThreadChannelJs(this, chat) diff --git a/pubnub-chat-impl/src/jsMain/kotlin/ThreadMessageJs.kt b/pubnub-chat-impl/src/jsMain/kotlin/ThreadMessageJs.kt new file mode 100644 index 00000000..aac5823b --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/ThreadMessageJs.kt @@ -0,0 +1,32 @@ +@file:OptIn(ExperimentalJsExport::class, ExperimentalJsStatic::class) + +import com.pubnub.chat.ThreadMessage +import com.pubnub.chat.internal.message.BaseMessage +import com.pubnub.kmp.then +import kotlin.js.Promise + +@JsExport +@JsName("ThreadMessage") +class ThreadMessageJs internal constructor(internal val threadMessage: ThreadMessage, chatJs: ChatJs) : MessageJs(threadMessage, chatJs) { + val parentChannelId by threadMessage::parentChannelId + + fun pinToParentChannel(): Promise { + return threadMessage.pinToParentChannel().then { it.asJs(chatJs) }.asPromise() + } + + fun unpinFromParentChannel(): Promise { + return threadMessage.unpinFromParentChannel().then { it.asJs(chatJs) }.asPromise() + } + + companion object { + @JsStatic + fun streamUpdatesOn(threadMessages: Array, callback: (Array) -> Unit): () -> Unit { + val chatJs = threadMessages.first().chatJs + return BaseMessage.streamUpdatesOn(threadMessages.map { it.threadMessage }) { messages -> + callback(messages.map { it.asJs(chatJs) }.toTypedArray()) + }::close + } + } +} + +internal fun ThreadMessage.asJs(chat: ChatJs) = ThreadMessageJs(this, chat) diff --git a/pubnub-chat-impl/src/jsMain/kotlin/TimeTokenUtils.kt b/pubnub-chat-impl/src/jsMain/kotlin/TimeTokenUtils.kt new file mode 100644 index 00000000..333873b4 --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/TimeTokenUtils.kt @@ -0,0 +1,35 @@ +@file:OptIn(ExperimentalJsExport::class, ExperimentalJsStatic::class) + +import com.pubnub.api.utils.TimetokenUtil +import kotlin.js.Date + +@JsExport +class TimetokenUtils { + companion object { + @JsStatic + fun unixToTimetoken(unixTime: Any): Double { + val unixTimeNumber = unixTime.tryLong() ?: error("The value passed as unixTime is NaN") + return TimetokenUtil.unixToTimetoken(unixTimeNumber).toDouble() + } + + @JsStatic + fun timetokenToUnix(timetoken: Any): Double { + val timetokenNumber = timetoken.tryLong() ?: error("The value passed as timetoken is NaN") + return TimetokenUtil.timetokenToUnix(timetokenNumber).toDouble() + } + + @JsStatic + fun timetokenToDate(timetoken: Any): Date { + return Date(timetokenToUnix(timetoken).toDouble()) + } + + @JsStatic + fun dateToTimetoken(date: Date): Double { + @Suppress("USELESS_IS_CHECK") + if (date !is Date) { + error("The value passed as date is not an instance of Date") + } + return unixToTimetoken(date.getTime()) + } + } +} diff --git a/pubnub-chat-impl/src/jsMain/kotlin/UserJs.kt b/pubnub-chat-impl/src/jsMain/kotlin/UserJs.kt new file mode 100644 index 00000000..bf125d55 --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/UserJs.kt @@ -0,0 +1,151 @@ +@file:OptIn(ExperimentalJsExport::class, ExperimentalJsStatic::class) + +import com.pubnub.api.models.consumer.objects.PNMembershipKey +import com.pubnub.api.models.consumer.objects.PNSortKey +import com.pubnub.chat.User +import com.pubnub.chat.internal.UserImpl +import com.pubnub.kmp.JsMap +import com.pubnub.kmp.createJsObject +import com.pubnub.kmp.then +import com.pubnub.kmp.toJsMap +import com.pubnub.kmp.toMap +import kotlin.js.Promise +import kotlin.js.json + +@JsExport +@JsName("User") +class UserJs internal constructor(internal val user: User, internal val chatJs: ChatJs) : UserFields { + val active: Boolean get() = user.active + + override val id get() = user.id + override val name get() = user.name + override val externalId get() = user.externalId + override val profileUrl get() = user.profileUrl + override val email get() = user.email + override val custom: Any? get() = user.custom?.toJsMap() + override val status get() = user.status + override val type get() = user.type + val updated get() = user.updated + val lastActiveTimestamp get() = user.lastActiveTimestamp?.toInt() + + fun update(data: UserFields): Promise { + return user.update( + data.name, + data.externalId, + data.profileUrl, + data.email, + convertToCustomObject(data.custom), + data.status, + data.type + ).then { + UserJs(it, chatJs) + }.asPromise() + } + + fun delete(options: DeleteParameters?): Promise { + return user.delete(options?.soft ?: false).then { + if (it != null) { + DeleteUserResult(it.asJs(chatJs)) + } else { + DeleteUserResult(true) + } + }.asPromise() + } + + fun streamUpdates(callback: (user: UserJs?) -> Unit): () -> Unit { + val closeable = user.streamUpdates { + callback(it?.asJs(chatJs)) + } + return closeable::close + } + + fun wherePresent(): Promise> { + return user.wherePresent().then { it.toTypedArray() }.asPromise() + } + + fun isPresentOn(channelId: String): Promise { + return user.isPresentOn(channelId).asPromise() + } + + fun getMemberships(params: PubNub.GetMembershipsParametersv2?): Promise { + val page = params?.page + return user.getMemberships( + params?.limit?.toInt(), + page.toKmp(), + params?.filter, + params?.sort?.unsafeCast>()?.toMap()?.map { + val fieldName = it.key + val direction = it.value + if (direction == "asc") { + PNSortKey.PNAsc(PNMembershipKey.valueOf(fieldName)) + } else { + PNSortKey.PNDesc(PNMembershipKey.valueOf(fieldName)) + } + } ?: listOf() + ).then { + createJsObject { + this.page = MetadataPage(it.next, it.prev) + this.total = it.total + this.memberships = it.memberships.map { it.asJs(chatJs) }.toTypedArray() + this.status = it.status + } + }.asPromise() + } + + fun setRestrictions( + channel: ChannelJs, + params: RestrictionJs, + ): Promise { + return user.setRestrictions( + channel.channel, + params.ban ?: false, + params.mute ?: false, + params.reason?.toString() + ).asPromise() + } + + fun getChannelRestrictions(channel: ChannelJs): Promise { + return user.getChannelRestrictions(channel.channel).then { + it.asJs() + }.asPromise() + } + + fun getChannelsRestrictions(params: PubNub.GetChannelMembersParameters?): Promise { + return user.getChannelsRestrictions( + params?.limit?.toInt(), + params?.page?.toKmp(), + extractSortKeys(params?.sort), + ).then { result -> + result.toJs() + }.asPromise() + } + + fun toJSON(): Any { + return json( + "active" to active, + "id" to id, + "name" to name, + "externalId" to externalId, + "profileUrl" to profileUrl, + "email" to email, + "custom" to custom, + "status" to status, + "type" to type, + "updated" to updated, + "lastActiveTimestamp" to lastActiveTimestamp + ) + } + + companion object { + @JsStatic + fun streamUpdatesOn(users: Array, callback: (users: Array) -> Unit): () -> Unit { + val chatJs = users.first().chatJs + val closeable = UserImpl.streamUpdatesOn(users.map { jsUser -> jsUser.user }) { + callback(it.map { kmpUser -> UserJs(kmpUser, chatJs) }.toTypedArray()) + } + return closeable::close + } + } +} + +internal fun User.asJs(chat: ChatJs) = UserJs(this, chat) diff --git a/pubnub-chat-impl/src/jsMain/kotlin/com/pubnub/chat/ChatJs.kt b/pubnub-chat-impl/src/jsMain/kotlin/com/pubnub/chat/ChatJs.kt deleted file mode 100644 index 0f5a7d79..00000000 --- a/pubnub-chat-impl/src/jsMain/kotlin/com/pubnub/chat/ChatJs.kt +++ /dev/null @@ -1,83 +0,0 @@ -@file:OptIn(ExperimentalJsExport::class) - -package com.pubnub.chat - -import com.pubnub.kmp.PNFuture -import kotlin.js.Promise - -fun PNFuture.asPromise(): Promise = Promise { resolve, reject -> - async { - it.onSuccess { - resolve(it) - }.onFailure { - reject(it) - } - } -} -// -// @JsExport -// @JsName("Chat") -// class ChatJs constructor(private val chatConfig: ChatConfiguration) { -// private val chat: Chat = ChatImpl(chatConfig) - -// fun createUser( -// id: String, -// userInfo: dynamic -// ): Promise { -// return GlobalScope.promise { -// UserJs(chat.createUser(id, -// userInfo.name, -// userInfo.externalId, -// userInfo.profileUrl, -// userInfo.email, -// userInfo.custom, -// userInfo.status, -// userInfo.type)) -// } -// } -// -// fun updateUser( -// id: String, -// userInfo: dynamic -// ): Promise { -// return chat.updateUser( -// id, -// userInfo.name, -// userInfo.externalId, -// userInfo.profileUrl, -// userInfo.email, -// userInfo.custom, -// userInfo.status, -// userInfo.type, -// ).then { UserJs(it) }.asPromise() -// } -// -// fun deleteUser(id: String, params: dynamic = null): Promise { -// return chat.deleteUser( -// id, -// params?.softDelete ?: false -// ).then { UserJs(it) }.asPromise() -// } -// } - -// @JsExport -// @JsName("User") -// class UserJs internal constructor(private val user: User) { -// fun update( -// name: String? = null, -// externalId: String? = null, -// profileUrl: String? = null, -// email: String? = null, -// custom: CustomObject? = null, -// status: String? = null, -// type: String? = null, -// updated: String? = null, // todo do we need this? -// ): Promise { -// return user.update(name, externalId, profileUrl, email, custom, status, type) -// .then { UserJs(it) }.asPromise() -// } -// -// fun delete(softDelete: Boolean): Promise { -// return user.delete(softDelete).then { UserJs(it) }.asPromise() -// } -// } diff --git a/pubnub-chat-impl/src/jsMain/kotlin/com/pubnub/chat/internal/ChatImpl.js.kt b/pubnub-chat-impl/src/jsMain/kotlin/com/pubnub/chat/internal/ChatImpl.js.kt new file mode 100644 index 00000000..e734ab91 --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/com/pubnub/chat/internal/ChatImpl.js.kt @@ -0,0 +1,16 @@ +package com.pubnub.chat.internal + +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid + +external val globalThis: dynamic + +@OptIn(ExperimentalUuidApi::class) +actual fun generateRandomUuid(): String { + val process = js("process") + if (process !== undefined && process.versions && process.versions.node && globalThis.crypto === undefined) { + // Node.js environment detected + globalThis.crypto = js("require('crypto')") + } + return Uuid.random().toString() +} diff --git a/pubnub-chat-impl/src/jsMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.js.kt b/pubnub-chat-impl/src/jsMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.js.kt index 2489c3ba..0eae74fa 100644 --- a/pubnub-chat-impl/src/jsMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.js.kt +++ b/pubnub-chat-impl/src/jsMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.js.kt @@ -1,6 +1,6 @@ package com.pubnub.chat.internal -private val userMentionRegex: Regex = Regex("""(?<=^|\p{space})(@[\p{Alpha}\-]+)""") +private val userMentionRegex: Regex = Regex("""(?<=^|\p{space})(@[\p{Alpha}\-\d]+)""") private val channelReferenceRegex = Regex("""(?<=^|\p{space})(#[\p{Alpha}\-\d]+)""") internal actual fun findUserMentionMatches(input: CharSequence): List { diff --git a/pubnub-chat-impl/src/jsMain/kotlin/converters.kt b/pubnub-chat-impl/src/jsMain/kotlin/converters.kt new file mode 100644 index 00000000..a2593495 --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/converters.kt @@ -0,0 +1,175 @@ +import com.pubnub.api.JsonElement +import com.pubnub.api.createJsonElement +import com.pubnub.api.enums.PNPushEnvironment +import com.pubnub.api.enums.PNPushType +import com.pubnub.api.models.consumer.objects.PNKey +import com.pubnub.api.models.consumer.objects.PNMemberKey +import com.pubnub.api.models.consumer.objects.PNMembershipKey +import com.pubnub.api.models.consumer.objects.PNPage +import com.pubnub.api.models.consumer.objects.PNSortKey +import com.pubnub.api.models.consumer.objects.SortField +import com.pubnub.chat.config.ChatConfiguration +import com.pubnub.chat.config.CustomPayloads +import com.pubnub.chat.config.PushNotificationsConfig +import com.pubnub.chat.config.RateLimitPerChannel +import com.pubnub.chat.internal.serialization.PNDataEncoder +import com.pubnub.chat.restrictions.GetRestrictionsResponse +import com.pubnub.chat.restrictions.Restriction +import com.pubnub.chat.types.ChannelType +import com.pubnub.chat.types.EventContent +import com.pubnub.chat.types.QuotedMessage +import com.pubnub.kmp.JsMap +import com.pubnub.kmp.PNFuture +import com.pubnub.kmp.createCustomObject +import com.pubnub.kmp.createJsObject +import com.pubnub.kmp.toMap +import kotlinx.serialization.Serializable +import kotlin.js.Promise +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds + +internal fun GetRestrictionsResponse.toJs() = + createJsObject { + this.page = MetadataPage(next, prev) + this.restrictions = this@toJs.restrictions.map { it.asJs() }.toTypedArray() + this.status = this@toJs.status + this.total = this@toJs.total + } + +internal inline fun extractSortKeys(sort: Any?): List> = + sort?.unsafeCast>()?.toMap()?.map { + val fieldName = it.key + val direction = it.value + when (T::class) { + PNMembershipKey::class -> getAscOrDesc(direction, PNMembershipKey.valueOf(fieldName)) + PNKey::class -> getAscOrDesc(direction, PNKey.valueOf(fieldName)) + PNMemberKey::class -> getAscOrDesc(direction, PNMemberKey.valueOf(fieldName)) + else -> error("Should never happen") + } as PNSortKey + } ?: listOf() + +internal fun getAscOrDesc(direction: String, field: T): PNSortKey { + return if (direction == "asc") { + PNSortKey.PNAsc(field) + } else { + PNSortKey.PNDesc(field) + } +} + +internal fun QuotedMessage.toJs(): QuotedMessageJs { + return createJsObject { + this.text = this@toJs.text + this.userId = this@toJs.userId + this.timetoken = this@toJs.timetoken.toString() + } +} + +internal inline fun @Serializable T.toJsObject(): JsMap { + return createJsonElement(PNDataEncoder.encode(this) as Map).value.unsafeCast>() +} + +internal inline fun Map.toJsObject(): JsMap { + return createJsonElement(this).value.unsafeCast>() +} + +internal fun CustomPayloadsJs?.toKmp(): CustomPayloads { + if (this == null) { + return CustomPayloads() + } + return CustomPayloads( + getMessagePublishBody?.let { mpb -> + { + m: EventContent.TextMessageContent, + channelId: String, + defaultMessagePublishBody: (m: EventContent.TextMessageContent) -> Map -> + mpb( + (m as EventContent).toJsObject(), + channelId + ).unsafeCast>().toMap() + } + }, + getMessageResponseBody?.let { mrb -> + fun( + m: JsonElement, + channelId: String, + defaultMessageResponseBody: (JsonElement) -> EventContent.TextMessageContent? + ): EventContent.TextMessageContent? { + val jsM = m.value.unsafeCast>() + val messageDTOparams = Any().asDynamic() + messageDTOparams.channel = channelId + messageDTOparams.message = jsM + return PNDataEncoder.decode(createJsonElement(mrb(messageDTOparams))) + } + }, + editMessageActionName = editMessageActionName, + deleteMessageActionName = deleteMessageActionName, + reactionsActionName = reactionsActionName + ) +} + +fun PushNotificationsConfigJs?.toKmp(): PushNotificationsConfig { + if (this == null) { + return PushNotificationsConfig(false, "", PNPushType.FCM) + } + return PushNotificationsConfig( + sendPushes, + deviceToken, + PNPushType.fromParamString(deviceGateway), + apnsTopic, + PNPushEnvironment.fromParamString(apnsEnvironment) + ) +} + +internal fun Restriction.asJs(): RestrictionJs { + val restriction = this + return createJsObject { + this.ban = restriction.ban + this.mute = restriction.mute + restriction.reason?.let { this.reason = it } + this.channelId = restriction.channelId + } +} + +internal fun PubNub.MetadataPage?.toKmp() = + this?.next?.let { PNPage.PNNext(it) } ?: this?.prev?.let { PNPage.PNPrev(it) } + +internal fun MetadataPage(next: PNPage.PNNext?, prev: PNPage.PNPrev?) = createJsObject { + this.next = next?.pageHash ?: undefined + this.prev = prev?.pageHash ?: undefined +} + +internal fun convertToCustomObject(custom: Any?) = custom?.let { + createCustomObject(custom.unsafeCast>().toMap()) +} + +fun PNFuture.asPromise(): Promise = Promise { resolve, reject -> + async { + it.onSuccess { + resolve(it) + }.onFailure { + reject(it) + } + } +} + +internal fun ChatConfig.toChatConfiguration(): ChatConfiguration { + return ChatConfiguration( + typingTimeout = typingTimeout?.milliseconds ?: 5.seconds, + storeUserActivityInterval = storeUserActivityInterval?.milliseconds ?: 600.seconds, + storeUserActivityTimestamps = storeUserActivityTimestamps ?: false, + pushNotifications = pushNotifications.toKmp(), + rateLimitFactor = rateLimitFactor ?: 2, + rateLimitPerChannel = rateLimitPerChannel.toKmp(), + customPayloads = customPayloads.toKmp(), + ) +} + +private fun RateLimitPerChannelJs?.toKmp(): Map { + val resultingMap = RateLimitPerChannel().toMutableMap() + this?.unsafeCast>()?.toMap()?.forEach { + val type = ChannelType.from(it.key) + resultingMap[type] = it.value.milliseconds + } + return resultingMap +} diff --git a/pubnub-chat-impl/src/jsMain/kotlin/types.kt b/pubnub-chat-impl/src/jsMain/kotlin/types.kt new file mode 100644 index 00000000..a218cfb0 --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/kotlin/types.kt @@ -0,0 +1,312 @@ +@file:OptIn(ExperimentalJsExport::class) + +import com.pubnub.api.models.consumer.PNPublishResult +import com.pubnub.chat.types.GetFileItem +import com.pubnub.chat.types.MessageMentionedUser +import com.pubnub.chat.types.MessageReferencedChannel +import com.pubnub.chat.types.TextLink +import com.pubnub.kmp.JsMap +import com.pubnub.kmp.createJsObject + +external interface GetEventsHistoryParams { + val channel: String + val startTimetoken: String? + val endTimetoken: String? + val count: Number? +} + +external interface MarkAllMessageAsReadResponseJs { + var memberships: Array + var page: PubNub.MetadataPage + var total: Int? + var status: Int +} + +external interface GetCurrentUserMentionsResultJs { + var enhancedMentionsData: Array + var isMore: Boolean +} + +external interface UserMentionDataJs { + val event: EventJs + val userId: String + val message: MessageJs +} + +external interface ChannelMentionDataJs : UserMentionDataJs { + override var event: EventJs + override var userId: String + override var message: MessageJs + var channelId: String +} + +external interface ThreadMentionDataJs : UserMentionDataJs { + override var event: EventJs + override var userId: String + override var message: MessageJs + var parentChannelId: String + var threadChannelId: String +} + +external interface GetUnreadMessagesCountsJs { + var channel: ChannelJs + var membership: MembershipJs + var count: Double +} + +external interface QuotedMessageJs { + var timetoken: String + var text: String + var userId: String +} + +external interface CreateGroupConversationResultJs { + var channel: ChannelJs + var hostMembership: MembershipJs + var inviteesMemberships: Array +} + +external interface CreateDirectConversationResultJs { + var channel: ChannelJs + var hostMembership: MembershipJs + var inviteeMembership: MembershipJs +} + +external interface GetChannelsResponseJs { + var users: Array + var page: PubNub.MetadataPage + var total: Int +} + +external interface ChannelFields { + val id: String + val name: String? + val custom: Any? + val description: String? + val updated: String? + val status: String? + val type: String? +} + +external interface GetUsersResponseJs { + var users: Array + var page: PubNub.MetadataPage + var total: Int +} + +external interface GetEventsHistoryResultJs { + var events: Array + var isMore: Boolean +} + +external interface PushNotificationsConfigJs { + val sendPushes: Boolean + val deviceToken: String? + val deviceGateway: String + val apnsTopic: String? + val apnsEnvironment: String +} + +external interface GetFilesResultJs { + var files: Array + var next: String? + var total: Int +} + +external interface MembersResponseJs { + var page: PubNub.MetadataPage + var total: Int? + var status: Int + var members: Array +} + +external interface HistoryResponseJs { + var messages: Array + var isMore: Boolean +} + +external interface GetRestrictionsResponseJs { + var page: PubNub.MetadataPage + var total: Int + var status: Int + var restrictions: Array +} + +external interface RestrictionJs { + var ban: Boolean? + var mute: Boolean? + var reason: Any? + var channelId: String? +} + +external interface MembershipsResponseJs { + var page: PubNub.MetadataPage + var total: Int? + var status: Int + var memberships: Array +} + +external interface PageJs { + val next: String? + val prev: String? +} + +external interface UserFields { + val id: String + val name: String? + val externalId: String? + val profileUrl: String? + val email: String? + val custom: Any? + val status: String? + val type: String? +} + +external interface ChatConstructor : ChatConfig + +external interface ChatConfig { + val saveDebugLog: Boolean? + val typingTimeout: Int? + val storeUserActivityInterval: Int? + val storeUserActivityTimestamps: Boolean? + val pushNotifications: PushNotificationsConfigJs? + val rateLimitFactor: Int? + val rateLimitPerChannel: RateLimitPerChannelJs? + val errorLogger: Any? + val customPayloads: CustomPayloadsJs? +} + +external interface CustomPayloadsJs { + val getMessagePublishBody: ((JsMap, String) -> Any)? + val getMessageResponseBody: ((JsMap) -> Any)? + val editMessageActionName: String? + val deleteMessageActionName: String? + val reactionsActionName: String? +} + +external interface RateLimitPerChannelJs { + val direct: Int + val group: Int + val public: Int + val unknown: Int +} + +external interface DeleteParameters { + val soft: Boolean? +} + +external interface MessageDraftConfig { + var userSuggestionSource: String? + var isTypingIndicatorTriggered: Boolean? + var userLimit: Int? + var channelLimit: Int? +} + +external interface SendTextOptionParams : PubNub.PublishParameters { + var mentionedUsers: JsMap? + var referencedChannels: JsMap? + var textLinks: Array? + var quotedMessage: MessageJs? + var files: Any? +} + +external interface GetHistoryParams { + val startTimetoken: String? + val endTimetoken: String? + val count: Int? +} + +external interface GetSuggestionsParams { + var limit: Int? +} + +external interface DeleteUserResult + +@Suppress("NOTHING_TO_INLINE") +inline fun DeleteUserResult(user: UserJs): DeleteUserResult { + return user.unsafeCast() +} + +@Suppress("NOTHING_TO_INLINE") +inline fun DeleteUserResult(boolean: Boolean): DeleteUserResult { + return boolean.unsafeCast() +} + +external interface DeleteChannelResult + +@Suppress("NOTHING_TO_INLINE") +inline fun DeleteChannelResult(channel: ChannelJs): DeleteChannelResult { + return channel.unsafeCast() +} + +@Suppress("NOTHING_TO_INLINE") +inline fun DeleteChannelResult(boolean: Boolean): DeleteChannelResult { + return boolean.unsafeCast() +} + +external interface JoinResultJs { + var membership: MembershipJs + var disconnect: () -> Unit +} + +external interface DeleteFileParams { + val id: String + val name: String +} + +external interface GetMessageReportsHistoryResult { + var events: Array + var isMore: Boolean +} + +@Suppress("NOTHING_TO_INLINE") +internal inline fun PNPublishResult.toPublishResponse(): PubNub.PublishResponse = + createJsObject { timetoken = this@toPublishResponse.timetoken.toString() } + +external interface CreateChannelParams { + val channelId: String? + val channelData: PubNub.ChannelMetadata? +} + +external interface CreateDirectConversationParams { + val user: UserJs + val channelId: String? + val channelData: PubNub.ChannelMetadata? + val membershipData: SetMembershipsParametersAndCustom? +} + +external interface CreateGroupConversationParams { + val users: Array + val channelId: String? + val channelData: PubNub.ChannelMetadata? + val membershipData: SetMembershipsParametersAndCustom? +} + +external interface SetMembershipsParametersAndCustom : PubNub.SetMembershipsParameters { + val custom: PubNub.CustomObject? +} + +external interface ListenForEventsParams { + val type: String + val channel: String? + val user: String? + val method: String? + val callback: (EventJs) -> Any +} + +external interface GetUnreadMessagesCountResult + +@Suppress("NOTHING_TO_INLINE") +inline fun GetUnreadMessagesCountResult(number: Int): GetUnreadMessagesCountResult { + return number.unsafeCast() +} + +@Suppress("NOTHING_TO_INLINE") +inline fun GetUnreadMessagesCountResult(boolean: Boolean): GetUnreadMessagesCountResult { + return boolean.unsafeCast() +} + +external interface Reaction { + var uuid: String + var actionTimetoken: String +} diff --git a/pubnub-chat-impl/src/jsMain/resources/index.html b/pubnub-chat-impl/src/jsMain/resources/index.html index 33ed41fc..19e8c8ff 100644 --- a/pubnub-chat-impl/src/jsMain/resources/index.html +++ b/pubnub-chat-impl/src/jsMain/resources/index.html @@ -14,8 +14,8 @@ })(); - - + + diff --git a/pubnub-chat-impl/src/jsMain/resources/index.ts b/pubnub-chat-impl/src/jsMain/resources/index.ts new file mode 100644 index 00000000..d237723c --- /dev/null +++ b/pubnub-chat-impl/src/jsMain/resources/index.ts @@ -0,0 +1,2 @@ +import { Chat, INTERNAL_MODERATION_PREFIX, User } from "@pubnub/chat_internal" + diff --git a/pubnub-chat-impl/src/jsTest/kotlin/com/pubnub/integration/MessageIntegrationTest.js.kt b/pubnub-chat-impl/src/jsTest/kotlin/com/pubnub/integration/MessageIntegrationTest.js.kt new file mode 100644 index 00000000..079831bd --- /dev/null +++ b/pubnub-chat-impl/src/jsTest/kotlin/com/pubnub/integration/MessageIntegrationTest.js.kt @@ -0,0 +1,18 @@ +package com.pubnub.integration + +import com.pubnub.kmp.Uploadable +import com.pubnub.kmp.UploadableImpl + +actual fun generateFileContent(): Uploadable { + return UploadableImpl( + js( + """ + { + data: "some text", + name: "name.txt", + mimeType: "text/plain" + } + """ + ) + ) +} diff --git a/pubnub-chat-impl/src/jsTest/kotlin/com/pubnub/kmp/utils/BaseTest.js.kt b/pubnub-chat-impl/src/jsTest/kotlin/com/pubnub/kmp/utils/BaseTest.js.kt new file mode 100644 index 00000000..ab4a2c30 --- /dev/null +++ b/pubnub-chat-impl/src/jsTest/kotlin/com/pubnub/kmp/utils/BaseTest.js.kt @@ -0,0 +1,7 @@ +package com.pubnub.kmp.utils + +import com.pubnub.kmp.CustomObject + +internal actual fun CustomObject.get(key: String): Any? { + return this[key] +} diff --git a/pubnub-chat-impl/src/jvmMain/kotlin/com/pubnub/chat/internal/ChatImpl.jvm.kt b/pubnub-chat-impl/src/jvmMain/kotlin/com/pubnub/chat/internal/ChatImpl.jvm.kt new file mode 100644 index 00000000..e78dc7b4 --- /dev/null +++ b/pubnub-chat-impl/src/jvmMain/kotlin/com/pubnub/chat/internal/ChatImpl.jvm.kt @@ -0,0 +1,7 @@ +package com.pubnub.chat.internal + +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid + +@OptIn(ExperimentalUuidApi::class) +actual fun generateRandomUuid(): String = Uuid.random().toString() diff --git a/pubnub-chat-impl/src/jvmMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.jvm.kt b/pubnub-chat-impl/src/jvmMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.jvm.kt index ec2e97e9..e3ae4960 100644 --- a/pubnub-chat-impl/src/jvmMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.jvm.kt +++ b/pubnub-chat-impl/src/jvmMain/kotlin/com/pubnub/chat/internal/MessageDraftImpl.jvm.kt @@ -1,6 +1,6 @@ package com.pubnub.chat.internal -private val userMentionRegex: Regex = Regex("""(?U)(?<=^|\p{Space})(@[\p{Alpha}\-]+)""") +private val userMentionRegex: Regex = Regex("""(?U)(?<=^|\p{Space})(@[\p{Alpha}\-\d]+)""") private val channelReferenceRegex = Regex("""(?U)(?<=^|\p{Space})(#[\p{Alpha}\-\d]+)""") internal actual fun findUserMentionMatches(input: CharSequence): List { diff --git a/pubnub-chat-impl/src/jvmTest/kotlin/com/pubnub/integration/MessageIntegrationTest.jvm.kt b/pubnub-chat-impl/src/jvmTest/kotlin/com/pubnub/integration/MessageIntegrationTest.jvm.kt new file mode 100644 index 00000000..a8597adc --- /dev/null +++ b/pubnub-chat-impl/src/jvmTest/kotlin/com/pubnub/integration/MessageIntegrationTest.jvm.kt @@ -0,0 +1,7 @@ +package com.pubnub.integration + +import com.pubnub.kmp.Uploadable + +actual fun generateFileContent(): Uploadable { + return "some text".byteInputStream() +} diff --git a/pubnub-chat-impl/src/jvmTest/kotlin/com/pubnub/kmp/utils/BaseTest.jvm.kt b/pubnub-chat-impl/src/jvmTest/kotlin/com/pubnub/kmp/utils/BaseTest.jvm.kt new file mode 100644 index 00000000..35925d44 --- /dev/null +++ b/pubnub-chat-impl/src/jvmTest/kotlin/com/pubnub/kmp/utils/BaseTest.jvm.kt @@ -0,0 +1,7 @@ +package com.pubnub.kmp.utils + +import com.pubnub.kmp.CustomObject + +internal actual fun CustomObject.get(key: String): Any? { + return (this as Map)[key] +} diff --git a/pubnub-chat-impl/webpack.config.d/conf.js b/pubnub-chat-impl/webpack.config.d/conf.js new file mode 100644 index 00000000..8c8b5461 --- /dev/null +++ b/pubnub-chat-impl/webpack.config.d/conf.js @@ -0,0 +1,3 @@ +config.resolve.alias = { + "crypto": false, +} \ No newline at end of file diff --git a/pubnub-kotlin b/pubnub-kotlin index 8fd0a4e3..50a9202c 160000 --- a/pubnub-kotlin +++ b/pubnub-kotlin @@ -1 +1 @@ -Subproject commit 8fd0a4e39f6ab98827710b08eb6db0a4d0393845 +Subproject commit 50a9202c630875c40317d6ead2a42e54b2d06328