From f0293a42bf37436270f378a687e96dcf95f5e1ca Mon Sep 17 00:00:00 2001 From: Michael J Feher Date: Thu, 15 Feb 2024 16:54:25 -0600 Subject: [PATCH 01/21] docs: add webrtc decision --- .decisions/3-Peer-to-Peer-Signaling.md | 60 ++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .decisions/3-Peer-to-Peer-Signaling.md diff --git a/.decisions/3-Peer-to-Peer-Signaling.md b/.decisions/3-Peer-to-Peer-Signaling.md new file mode 100644 index 0000000..380852a --- /dev/null +++ b/.decisions/3-Peer-to-Peer-Signaling.md @@ -0,0 +1,60 @@ +# Overview + +Communicating across platforms in a decentralized manner + +## Decisions + +- Limit dependency on WebSockets to signaling +- Allow bidirectional communication between peers +- Enforce locality of device? + +## Implementation + +A WebSocket Service should establish the SDP handshake and emit ICE candidates for WebRTC clients. + +This implementation should replace Wallet Connect with the following sequence +```mermaid +sequenceDiagram + participant Website + participant Server + participant Wallet + Note over Website, Wallet: Link devices + Website->>Server: GET Challenge Message + Server->>Website: Send Challenge Message + + Website-->>Website: Display QR Connect Nonce + Website->>Server: Subscribe to 'wss:link' + Wallet->>Website: Scan QR Code + Wallet->>Server: POST Nonce + Signature + Answer + Server-->>Server: Validate Signature + Server-->>Website: HTTPOnly Session + Server->>Wallet: Ok Response + HTTPOnly Session + Server->>Website: Emit to `wss:link` client + Note over Website, Wallet: Passkeys/FIDO2 + Website-->>Website: Continue FIDO2 Flow + Wallet-->>Wallet: Continue FIDO2 Flow + Note over Website, Wallet: Signaling Peer Offer/Answer + Website-->>Server: Subscribe to 'wss:answer-${address}' + Wallet-->>Server: Subscribe to 'wss:offer-${address}' + + Website-->>Website: Create Peer Offer & DataChannel + Website-->>Server: POST Offer + Server-->>Wallet: Emit Offer + + Wallet-->>Wallet: Create Peer Answer with Offer & DataChannel + + Wallet-->>Server: POST Answer + Server-->>Website: Emit Answer + Website-->>Website: Set Remote SDP + Website-->>Website: Discover ICE Candidates + Website->>Server: Emit candidates to `wss:offer-${address}` + Server->>Wallet: Emit candidates to `wss:offer-${address}` + Wallet-->>Wallet: Set Candidates + Wallet-->>Wallet: Discover ICE Candidates + Wallet->>Server: Emit candidates to `wss:answer-${address}` + Server->>Website: Emit to `wss:answer` + Website->>Website: Set Candidates + +``` + +*Note: This process may be deprecated in the future in favor of `libp2p` which allows for an agnostic discovery layer and also supports the WebRTC transport From 0c7a93506cc13e2de4388f6af57e519cefd79d62 Mon Sep 17 00:00:00 2001 From: Michael J Feher Date: Thu, 15 Feb 2024 16:55:47 -0600 Subject: [PATCH 02/21] feat: session status and webvitals performance increase --- clients/liquid-auth-client-js/package.json | 12 +- clients/liquid-auth-client-js/src/connect.ts | 25 ++-- clients/liquid-auth-client-js/src/encoding.ts | 32 +++++ .../tests/encoding.test.js | 0 .../liquid-auth-client-js/tests/index.test.js | 2 +- sites/dapp-ui/index.html | 22 +++- sites/dapp-ui/package.json | 13 ++- sites/dapp-ui/public/apple-touch-icon.png | Bin 0 -> 1768 bytes sites/dapp-ui/public/hero-1.webp | Bin 0 -> 98904 bytes sites/dapp-ui/public/hero-2.webp | Bin 0 -> 228658 bytes sites/dapp-ui/public/hero-3.webp | Bin 0 -> 232382 bytes sites/dapp-ui/public/icons/144x144.png | Bin 0 -> 4176 bytes sites/dapp-ui/public/icons/192x192.png | Bin 0 -> 6964 bytes sites/dapp-ui/public/icons/48x48.png | Bin 0 -> 1399 bytes sites/dapp-ui/public/icons/512x512.png | Bin 0 -> 20108 bytes sites/dapp-ui/public/icons/72x72.png | Bin 0 -> 2065 bytes sites/dapp-ui/public/icons/96x96.png | Bin 0 -> 2752 bytes sites/dapp-ui/public/logo-background.svg | 24 ++++ sites/dapp-ui/public/logo.svg | 3 + sites/dapp-ui/public/maskable-icon.png | Bin 0 -> 15922 bytes sites/dapp-ui/public/waves_left.svg | 7 ++ sites/dapp-ui/public/waves_right.svg | 7 ++ sites/dapp-ui/src/App.tsx | 13 ++- sites/dapp-ui/src/Contexts.tsx | 5 + sites/dapp-ui/src/Layout.tsx | 22 +--- sites/dapp-ui/src/components/Snackbar.tsx | 43 +++++++ .../src/components/user/Credential.tsx | 8 ++ .../src/components/user/SessionMenu.tsx | 64 ++++++++++ .../src/components/user/StatusCard.tsx | 64 ++++++++++ sites/dapp-ui/src/components/user/types.ts | 11 ++ .../src/components/user/useUserState.ts | 12 ++ sites/dapp-ui/src/hooks/useAddress.ts | 4 +- sites/dapp-ui/src/hooks/usePeerConnection.ts | 62 ++++++++++ sites/dapp-ui/src/hooks/useSocket.ts | 4 +- .../pages/dashboard/WaitForRegistration.tsx | 2 +- sites/dapp-ui/src/pages/home/ConnectModal.tsx | 29 +++-- sites/dapp-ui/src/pages/home/GetStarted.tsx | 43 ++++++- sites/dapp-ui/vite.config.ts | 109 +++++++++++++++--- 38 files changed, 559 insertions(+), 83 deletions(-) create mode 100644 clients/liquid-auth-client-js/tests/encoding.test.js create mode 100644 sites/dapp-ui/public/apple-touch-icon.png create mode 100644 sites/dapp-ui/public/hero-1.webp create mode 100644 sites/dapp-ui/public/hero-2.webp create mode 100644 sites/dapp-ui/public/hero-3.webp create mode 100644 sites/dapp-ui/public/icons/144x144.png create mode 100644 sites/dapp-ui/public/icons/192x192.png create mode 100644 sites/dapp-ui/public/icons/48x48.png create mode 100644 sites/dapp-ui/public/icons/512x512.png create mode 100644 sites/dapp-ui/public/icons/72x72.png create mode 100644 sites/dapp-ui/public/icons/96x96.png create mode 100644 sites/dapp-ui/public/logo-background.svg create mode 100644 sites/dapp-ui/public/logo.svg create mode 100644 sites/dapp-ui/public/maskable-icon.png create mode 100644 sites/dapp-ui/public/waves_left.svg create mode 100644 sites/dapp-ui/public/waves_right.svg create mode 100644 sites/dapp-ui/src/components/Snackbar.tsx create mode 100644 sites/dapp-ui/src/components/user/Credential.tsx create mode 100644 sites/dapp-ui/src/components/user/SessionMenu.tsx create mode 100644 sites/dapp-ui/src/components/user/StatusCard.tsx create mode 100644 sites/dapp-ui/src/components/user/types.ts create mode 100644 sites/dapp-ui/src/components/user/useUserState.ts create mode 100644 sites/dapp-ui/src/hooks/usePeerConnection.ts diff --git a/clients/liquid-auth-client-js/package.json b/clients/liquid-auth-client-js/package.json index 8601e68..cf17033 100644 --- a/clients/liquid-auth-client-js/package.json +++ b/clients/liquid-auth-client-js/package.json @@ -30,20 +30,20 @@ "dev": "tsc --watch", "build": "tsc", "preinstall": "npm run build", - "test": "tsc && c8 node --test ./tests/*.test.js" + "test": "tsc && c8 node --test ./tests/connect.test.js" }, "author": "", "license": "MIT", "devDependencies": { "@types/qrcode": "^1.5.5", + "algosdk": "^2.7.0", "c8": "^9.1.0", "typescript": "^5.3.3" }, "dependencies": { - "qr-code-styling": "^1.6.0-rc.1" - }, - "peerDependencies": { - "algosdk": "^2.7.0", - "tweetnacl": "^1.0.3" + "hi-base32": "^0.5.1", + "js-sha512": "^0.9.0", + "qr-code-styling": "^1.6.0-rc.1", + "tweetnacl": "github:awesome-algorand/tweetnacl-js" } } diff --git a/clients/liquid-auth-client-js/src/connect.ts b/clients/liquid-auth-client-js/src/connect.ts index 992e3d0..9af9a5a 100644 --- a/clients/liquid-auth-client-js/src/connect.ts +++ b/clients/liquid-auth-client-js/src/connect.ts @@ -1,8 +1,9 @@ import {DEFAULT_FETCH_OPTIONS} from "./constants.js"; import type {Account} from 'algosdk' -import {encodeAddress} from "algosdk"; -import nacl from 'tweetnacl' -import {toBase64URL} from './encoding.js' +import type {SignKeyPair} from 'tweetnacl' +import {sign} from 'tweetnacl' +import {toBase64URL, encodeAddress} from './encoding.js' + export class Message { origin: string; @@ -25,16 +26,16 @@ export class Message { * * @param key */ - sign(key: string | Account | Uint8Array | nacl.SignKeyPair): void{ + sign(key: string | Account | Uint8Array | SignKeyPair): void{ const encoder = new TextEncoder() - let keyPair: nacl.SignKeyPair + let keyPair: SignKeyPair // Seed or Secret Key if(key instanceof Uint8Array){ if(key.length === 32){ - keyPair = nacl.sign.keyPair.fromSeed(key) + keyPair = sign.keyPair.fromSeed(key) } else if(key.length === 64){ - keyPair = nacl.sign.keyPair.fromSecretKey(key) + keyPair = sign.keyPair.fromSecretKey(key) } else { throw new TypeError('Invalid seed or secret key') } @@ -42,15 +43,15 @@ export class Message { // Algorand SDK if(typeof (key as Account).addr !== 'undefined' && typeof (key as Account).addr === 'string'){ - keyPair = nacl.sign.keyPair.fromSecretKey((key as Account).sk) + keyPair = sign.keyPair.fromSecretKey((key as Account).sk) } // NACL - if((key as nacl.SignKeyPair).publicKey instanceof Uint8Array && (key as nacl.SignKeyPair).secretKey instanceof Uint8Array){ + if((key as SignKeyPair).publicKey instanceof Uint8Array && (key as SignKeyPair).secretKey instanceof Uint8Array){ console.log('nacl') - keyPair = key as nacl.SignKeyPair + keyPair = key as SignKeyPair } - this.signature = toBase64URL(nacl.sign.detached(encoder.encode(this.challenge), keyPair.secretKey)); + this.signature = toBase64URL(sign.detached(encoder.encode(this.challenge), keyPair.secretKey)); this.wallet = encodeAddress(keyPair.publicKey) } @@ -90,7 +91,7 @@ export async function fetchConnectResponse(msg: Message){ * @param requestId * @param key */ -export async function connect(origin: string, requestId: number, key: string | Account | Uint8Array | nacl.SignKeyPair){ +export async function connect(origin: string, requestId: number, key: string | Account | Uint8Array | SignKeyPair){ const msg = await Message.fromResponse(await fetchConnectRequest(origin, requestId)) msg.sign(key) return await fetchConnectResponse(msg) diff --git a/clients/liquid-auth-client-js/src/encoding.ts b/clients/liquid-auth-client-js/src/encoding.ts index e8c9091..5204fc7 100644 --- a/clients/liquid-auth-client-js/src/encoding.ts +++ b/clients/liquid-auth-client-js/src/encoding.ts @@ -1,3 +1,7 @@ +import {sign} from "tweetnacl"; +import base32 from "hi-base32"; +import {sha512_256} from "js-sha512"; + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; /** @@ -39,3 +43,31 @@ export function fromBase64Url(base64url: string): Uint8Array { .map((c) => c.charCodeAt(0)), ); } + +function concatArrays(...arrs: ArrayLike[]) { + const size = arrs.reduce((sum, arr) => sum + arr.length, 0); + const c = new Uint8Array(size); + + let offset = 0; + for (let i = 0; i < arrs.length; i++) { + c.set(arrs[i], offset); + offset += arrs[i].length; + } + + return c; +} + +const ALGORAND_CHECKSUM_BYTE_LENGTH = 4; +const ALGORAND_ADDRESS_LENGTH = 58; +export function encodeAddress(address: Uint8Array) { + // compute checksum + const checksum = + sha512_256.array(address) + .slice( + sign.publicKeyLength - ALGORAND_CHECKSUM_BYTE_LENGTH, + sign.publicKeyLength + ); + const addr = base32.encode(concatArrays(address, checksum)); + + return addr.toString().slice(0, ALGORAND_ADDRESS_LENGTH); // removing the extra '====' +} diff --git a/clients/liquid-auth-client-js/tests/encoding.test.js b/clients/liquid-auth-client-js/tests/encoding.test.js new file mode 100644 index 0000000..e69de29 diff --git a/clients/liquid-auth-client-js/tests/index.test.js b/clients/liquid-auth-client-js/tests/index.test.js index f8aeb09..9793b02 100644 --- a/clients/liquid-auth-client-js/tests/index.test.js +++ b/clients/liquid-auth-client-js/tests/index.test.js @@ -1,6 +1,6 @@ import test from 'node:test'; import assert from 'node:assert'; -import * as api from '../lib/index.js'; +// import * as api from '../lib/index.js'; test("smoke test", () => { assert.equal(1, 1); }); diff --git a/sites/dapp-ui/index.html b/sites/dapp-ui/index.html index cc4dc5a..22404e1 100644 --- a/sites/dapp-ui/index.html +++ b/sites/dapp-ui/index.html @@ -2,19 +2,29 @@ - + + - + + + + Liquid dApp +
- + diff --git a/sites/dapp-ui/package.json b/sites/dapp-ui/package.json index 2df94bd..13723b6 100644 --- a/sites/dapp-ui/package.json +++ b/sites/dapp-ui/package.json @@ -8,18 +8,19 @@ "type": "module", "scripts": { "dev": "vite build --watch --emptyOutDir", - "dev:no-api": "vite dev", + "dev:no-api": "vite dev --host 0.0.0.0", "build": "vite build --emptyOutDir", "lint": "eslint \"{src,apps,libs,test}/**/*.{ts,tsx}\" --fix" }, "dependencies": { - "@liquid/auth-client": "^1.0.0", "@emotion/react": "^11.11.3", "@emotion/server": "^11.11.0", "@emotion/styled": "^11.11.0", + "@liquid/auth-client": "^1.0.0", "@mui/icons-material": "^5.15.4", "@mui/material": "^5.15.4", - "@tanstack/react-query": "^5.17.15", + "@tanstack/react-query": "^5.20.5", + "@tanstack/react-query-devtools": "^5.20.5", "qr-code-styling": "^1.6.0-rc.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -37,7 +38,11 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", "mkdirp": "^3.0.1", + "sharp": "^0.33.2", + "svgo": "^3.2.0", "typescript": "^5.2.2", - "vite": "^5.0.8" + "vite": "^5.0.8", + "vite-plugin-image-optimizer": "^1.1.7", + "vite-plugin-pwa": "^0.18.1" } } diff --git a/sites/dapp-ui/public/apple-touch-icon.png b/sites/dapp-ui/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e64df379d43601aefd75459f2d44f292320c10d7 GIT binary patch literal 1768 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 literal 0 HcmV?d00001 diff --git a/sites/dapp-ui/public/hero-2.webp b/sites/dapp-ui/public/hero-2.webp new file mode 100644 index 0000000000000000000000000000000000000000..18258a250c2b3cec2d79934519e7a3495c820ed5 GIT binary patch literal 228658 zcmaI7b9g1e*Dku_iOoqS=ESyb+s?#xc5K_`#I`54F%#Q1_Ra76&U5hGbMNY|)o-nO zyVk0zKf0f;UdmG9;xxVx01Yu=MRi3^O*jAmK>Jld5dS%)L_`!v!M>^hu+jf$NJap_ z*3QLASwfgdQ%jo&W*Y$Zb^MnYnK(QAH~PQoFV^Goe{&b;{;!PxeCk{qVM*r0v`#0+2rFag-VR+xwr1p=Tr3X=my z1AqiT@-tyXh>#EyV?^j@6G1@Mx7s;hfKGQDKkv5zXK^?l~|%u6O~)3iC{w^PssXa0xKj{OxscrMpz(GK4~-)jDn z-hTqSZ>$H6)84ai@r1{o+8;?D!$0tcPhKC+VRqjKA1(KDk3Qm_c#y%Hz{Hj>q2$NW2k5!?PT(Cl3i^`AnNNw&xW~_Dfm~27sO&T9Devja zd|Z1P_G*8Xw=eJjO!*J*ruH590Xpp6f6oHpf{;P>uX&Fk(5EKI9K;23e+OQby#tHm zo`A2wNl+&UbWix2_8Rw|cho!opPL_TKXl&1KI3LU(a0deXFpm2+g=dp^jZI767&LM z*_inxyjXeL0)dpb8bR*@lfdS$yTUFEUJafNJ_z$ZKp$Z5Bkw^^l(zrkK*bO17YB7Jp#>pgs*^*(w8JqRp-iat4%VYOlX0dkYx+@$c2)9r^8Ru|0sjexMJn zH~UwH=dBZ0TAf5$JsC9Q{mJ7Mc}^oU10gE?}b;6 z_r}+&SA#2oBY{(335nT_b{w)efZsOH5OUq!o^2_NChjXq^uSdEaLJN_6`=k9o2BUe9~K`n z{Lqr zvU^%c7O73(c3mBEO%`c!+U)DGo^;!3QiVqLy$-S@$dleb31-mm+E>RDgz_Qcfogti z4NH<5L~r�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 literal 0 HcmV?d00001 diff --git a/sites/dapp-ui/public/hero-3.webp b/sites/dapp-ui/public/hero-3.webp new file mode 100644 index 0000000000000000000000000000000000000000..4dbba3476ab8484069f832975f4f9f68a9314e32 GIT binary patch literal 232382 zcmaHSQ91X>_3;3h=}4S$iFH8Wb{84oCyH1vv+k? zmJlY=)Y2w`+6938EB_@%CN7TujsCCpAM1Jfzqt$a|5wKU-w9)C=3?@Xa{Euood0$H zFDA5q7|r6pG1Y&t@qc69|6mVSN7sKG#s6SuRb`QX*z_N!viM)v_<{~{2!ljCIHYD z1^{5K|BsKX002M?1pr!S{>S$}IdL>{Hu|sEf&a6h=H>vveK`OCuLS_0&H?~GbpEUD zpZR~pM)a=SpSws6Clu} zBMkKV5V~f3lXwwWRWJyc`pVwy{~D=g3#SpxXJ(_cIkf z?%jp98+r#}dvAZh^LaSD$r2IbaA7xSS3ACvgTF3P9i| zKLyY|0Qi3Fe-g0rR`J#R)e1~|K>WJ-(g51tD}41@e;Yja-DiIyek*7I$=@UH1-5}x z0gc~4VAnU)ro>_XHL&VFWSQ_$U?QL$*!D4YFY(~e4@?1qeLH;JyxeRGbOF}_CV^TX z6WH%4YOoD(aK!q>H z4}*_@UIU@+RX>f-lUITJfa|_t|MM?Szw*zUC&I3P{XXDl$4A6l{j0%Az;eJLkQjJz z3;Ry^H1kNf8!!c&0Kxztz9aStulk<8y}jMPb^c95-{Vdqp<&+~um#u!g!)bg{(Bw> z{OH3CCqtn3-i|eeM{w3|>(cvxOY3J#`lCTLR{O1zb zdim~~QK$@PFnARZ_|EGy3b^q1E$N867OY~oxRKDMxQv>iZyARR-hArZ}iU%&

5mv(Ar%8K5 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%0AUO2vuvA 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 z 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

2m9kMiOk5ms(j%&imapC#6~! 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 XS&XY_?J4@-iU44E(?qu#?GpPx?$F*$ literal 0 HcmV?d00001 diff --git a/sites/dapp-ui/public/icons/192x192.png b/sites/dapp-ui/public/icons/192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..b7e54c8eea631004a58ed6c9724aa655967a3c44 GIT binary patch literal 6964 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 literal 0 HcmV?d00001 diff --git a/sites/dapp-ui/public/icons/72x72.png b/sites/dapp-ui/public/icons/72x72.png new file mode 100644 index 0000000000000000000000000000000000000000..37848ba8e816c76c296d61628bd7558124ad648e GIT binary patch literal 2065 zcmV+s2=4cZP)k9;yM8O}TZKw~biESW-2pa`SQGA|n zX76^o-FeB!nT*Y0O2;rnz>Rp9`6csF=BH8(O9?cL`4aOk8dzM(+{S#Fxr}*Aw?PSk3duC>YiRIb3G;I1 z&CH}uL%IT$Fh3$wBrTLiDyh7visE0bq60@;NdVKxbh(AhuY)$CBhanP^~~X%r}BG5L6)aQg4v}Mx1pHRn1u6`+b=Cy&(BHZ~NUjh(M*xCKAHDvKUP$DD8UycDLD6YH0hudJ+WgLI?SDH@^a5D1aZ=o;pSw_td1VsiJHpwRJYr(cd~r2smeC-$+4@c-_g5KxZ-$C?=2q zGF_`NQBAEZNWsX6Ar4$^3CE???@c8^;M~#U&SL)QWd}b3A!D6RLV#-(jEWea+q$+G z;s7msV}^@eSWsfP9=5}8g1DCXBJ*A^8+#S#1`C1Z)~;r$ zu3rJY+6HFAZ9}gDJwk%Wbgj%T9b&}yt}?^{7MIsiZOcp&MvNJC+sKPRXk-Qn!RlHa zhjyi1D-%{%D=LhJ(W~K_qU2&lUIdDf5XO#*q^$z3)s*2{naFgtJspiw38X@dT&&1V zAnYt=Jyp?O*Mc>;b>VQe9qrE>@d~}#;_~`_@k$e}R^%p7OzKP!nBgiw@fNQ*wY{r&O0#!;J9T{`Q6)V%< zn*zoVSW!A)b9;gU0m$=w38Ylj#_Y*8Y3te3(G+-Wg@BFy5r@#8uoa)}w&^L$o54 zj6Dk!kvQV)$#T|XZ6_ov8kg-yJAuZLCagV|g!L$(9gO}Hga%Vv$7VFt9Y(3U? zQ3e5Etpc&gmq1#ux>uV`IYSp6hZ22`C!9E@VJFaGGRG%@fFS_@HxMkk_W=?|40;Pj zPNYeNA_a%T*?x2qs5r<`(5oo4hGabo9o-3|%SWz-xTkqDd@1E?WCGJ?=Ees$*6vA;buel%W1j5R!kI7u(;tN&_Dv8XOo)zr_m91lj zL^E5*8mFwQ(VP-R^wGWrLR?UTNw|PO_nQ?+CzJ~dq!YFcI0>Xw^<9_739GwXQ5KV) zs!6rmTgKHC2-oH%%4kma$xKNou(sfMUKT);Rs?CQyR;2?eww3?fxwYk9>^ zdM-XwOC-cIh;A+8Y6fRqn_~lBM)MC-Kr2G*d9WgpUh9Ktn}%%*0=XO93YMe9foo~T zw!fJVxozY{pnYU6ehk#3HEgY2^B#s_w`~;YQY0002CljZ9Gbvz{1XU zw~f5kgRs0G-O|~9&jMXME(Rj0G6>3~Ac8B1F%>Qvf#ympjZ5z zJgz(ND$q}4ic}ZmY;fVVE#?{@tPjFM1ONh9i)&6m?FWHhs4#|W>GZOppNb+@FJXn~ z?GzAjEfnxE+o~2vxdE1iG(oUp_$Hax;t76Bu2}d8kuIfxfHh(G0UE`977BqE@d-qU z1lz#v;C{j*_V1Z@``N*-Kt0S`n7f(UBSpj~;bnDBK;nz#`#pYk3R1NHj(G=}3(AKQ zZX?rzd?DzpMhls-=oK=bg&53$#bDX*2SIk$nfAfcGv6}fWUh`FjOb=o&;UISMX3W{ z#e9d%B{PEwh?96KsavNJiHSp;09JXgBh$|Z17O{E(ja*x6TpceCSe)d)66L}Fu>4Z z1({Fvh(LPk8Sx0kCs@*xB(%=R1MvL(6U>dwe77sz_L~>hrika-@mdNzh3w_k6m~#~0u#ko`wkW?+sINKb%vd00000NkvXXu0mjfl-;q| literal 0 HcmV?d00001 diff --git a/sites/dapp-ui/public/icons/96x96.png b/sites/dapp-ui/public/icons/96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..9848cb08af19598fa8a55e48ea81db218ed827cb GIT binary patch literal 2752 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 + + + + + + diff --git a/sites/dapp-ui/public/logo.svg b/sites/dapp-ui/public/logo.svg new file mode 100644 index 0000000..38949fc --- /dev/null +++ b/sites/dapp-ui/public/logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/sites/dapp-ui/public/maskable-icon.png b/sites/dapp-ui/public/maskable-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..948a48cd9cc12af05d7897c72a3b44054abccf98 GIT binary patch literal 15922 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 literal 0 HcmV?d00001 diff --git a/sites/dapp-ui/public/waves_left.svg b/sites/dapp-ui/public/waves_left.svg new file mode 100644 index 0000000..3fb7519 --- /dev/null +++ b/sites/dapp-ui/public/waves_left.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/sites/dapp-ui/public/waves_right.svg b/sites/dapp-ui/public/waves_right.svg new file mode 100644 index 0000000..bc27739 --- /dev/null +++ b/sites/dapp-ui/public/waves_right.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/sites/dapp-ui/src/App.tsx b/sites/dapp-ui/src/App.tsx index aa987f9..25964f5 100644 --- a/sites/dapp-ui/src/App.tsx +++ b/sites/dapp-ui/src/App.tsx @@ -1,19 +1,23 @@ -import { useContext, ReactElement, useState, useMemo } from 'react'; +import {useContext, ReactElement, useState, useMemo, useEffect} from 'react'; -import { ColorModeContext, StateContext } from './Contexts'; +import { ColorModeContext, SnackbarContext, StateContext } from './Contexts'; import Layout from './Layout'; import { GetStartedCard } from './pages/home/GetStarted'; import { WaitForRegistrationCard } from './pages/dashboard/WaitForRegistration'; import { RegisteredCard } from './pages/dashboard/Registered'; -import { createTheme, CssBaseline } from '@mui/material'; +import {CircularProgress, createTheme, CssBaseline} from '@mui/material'; import { DEFAULT_THEME } from './theme.tsx'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; import { ThemeProvider } from '@emotion/react'; +import {ReactQueryDevtools} from "@tanstack/react-query-devtools"; + const queryClient = new QueryClient() export default function ProviderApp(){ + const [open, setOpen] = useState(false) + const [message, setMessage] = useState('') const [state, setState] = useState('start') const [mode, setMode] = useState<'light' | 'dark'>(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)') ? 'dark' : 'light'); @@ -39,14 +43,17 @@ export default function ProviderApp(){ ); return ( + + + ) } diff --git a/sites/dapp-ui/src/Contexts.tsx b/sites/dapp-ui/src/Contexts.tsx index d4c64a7..579652b 100644 --- a/sites/dapp-ui/src/Contexts.tsx +++ b/sites/dapp-ui/src/Contexts.tsx @@ -3,3 +3,8 @@ import {createContext} from "react"; export const ColorModeContext = createContext({toggle: () => {}}); export const StateContext = createContext({state: 'start', setState: (_: string) => {}}); + +export const SnackbarContext = createContext({ + open: false, setOpen: (_: boolean) => {}, + message: '', setMessage: (_: string) => {} +}); diff --git a/sites/dapp-ui/src/Layout.tsx b/sites/dapp-ui/src/Layout.tsx index 3157474..e1fdfbf 100644 --- a/sites/dapp-ui/src/Layout.tsx +++ b/sites/dapp-ui/src/Layout.tsx @@ -4,15 +4,15 @@ import AppBar from '@mui/material/AppBar'; import Box from '@mui/material/Box'; import Toolbar from '@mui/material/Toolbar'; import IconButton from '@mui/material/IconButton'; -import {Avatar, Badge, useTheme} from '@mui/material'; +import {useTheme} from '@mui/material'; import {PropsWithChildren, useContext} from 'react'; import Brightness4Icon from '@mui/icons-material/Brightness4'; import Brightness7Icon from '@mui/icons-material/Brightness7'; import { ColorModeContext, StateContext } from './Contexts'; import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore'; import NavigateNextIcon from '@mui/icons-material/NavigateNext'; -import { useSocket } from './hooks/useSocket'; - +import {SessionMenu} from "./components/user/SessionMenu.tsx"; +import {MessageSnackbar} from "./components/Snackbar.tsx"; export default function Layout({children}: PropsWithChildren) { const {state, setState} = useContext(StateContext) const colorMode = useContext(ColorModeContext) @@ -21,22 +21,11 @@ export default function Layout({children}: PropsWithChildren) { const bubbleStyle = { backgroundColor: isDarkMode ? 'white' : 'black' } - const {isConnected} = useSocket() return ( <> - - - - - + Liquid dApp @@ -50,7 +39,7 @@ export default function Layout({children}: PropsWithChildren) { }} aria-label="delete" disabled={state === 'registered'} color="inherit"> - + {theme.palette.mode === 'dark' ? : } @@ -60,6 +49,7 @@ export default function Layout({children}: PropsWithChildren) { sx={{display: "flex", height: "calc(100vh - 64px)", overflow: "auto"}}> {children} +

diff --git a/sites/dapp-ui/src/components/Snackbar.tsx b/sites/dapp-ui/src/components/Snackbar.tsx new file mode 100644 index 0000000..efdf552 --- /dev/null +++ b/sites/dapp-ui/src/components/Snackbar.tsx @@ -0,0 +1,43 @@ +import {Alert, Snackbar, useMediaQuery, useTheme} from "@mui/material"; +import {useContext} from "react"; +import {SnackbarContext} from "../Contexts.tsx"; +import IconButton from "@mui/material/IconButton"; +import CloseIcon from "@mui/icons-material/Close"; + +export function MessageSnackbar(){ + const theme = useTheme(); + const greaterThanMid = useMediaQuery(theme.breakpoints.up("md")); + + const {open, setOpen, message, setMessage} = useContext(SnackbarContext) + console.log('MessageSnackbar', open, setOpen, message, setMessage) + const handleClose = () => { + setOpen(false) + setMessage('') + } + return ( + + + } + > + + {message} + + + ) +} diff --git a/sites/dapp-ui/src/components/user/Credential.tsx b/sites/dapp-ui/src/components/user/Credential.tsx new file mode 100644 index 0000000..ad693d7 --- /dev/null +++ b/sites/dapp-ui/src/components/user/Credential.tsx @@ -0,0 +1,8 @@ +export function Credential(){ +return ( +
+

Credential

+ +
+ ) +} diff --git a/sites/dapp-ui/src/components/user/SessionMenu.tsx b/sites/dapp-ui/src/components/user/SessionMenu.tsx new file mode 100644 index 0000000..2b4a509 --- /dev/null +++ b/sites/dapp-ui/src/components/user/SessionMenu.tsx @@ -0,0 +1,64 @@ +import {Avatar, Badge, CircularProgress, Menu} from "@mui/material"; +import IconButton from "@mui/material/IconButton"; +import React, {useState} from "react"; +import {useSocket} from "../../hooks/useSocket.ts"; +import {useUserState} from "./useUserState.ts"; +import {StatusCard} from "./StatusCard.tsx"; + +export function SessionMenu(){ + const state = useUserState() + const {isConnected} = useSocket() + + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + return ( + <> + + + + + + + {state.isLoading && } + {state.isFetched && } + + + ) +} diff --git a/sites/dapp-ui/src/components/user/StatusCard.tsx b/sites/dapp-ui/src/components/user/StatusCard.tsx new file mode 100644 index 0000000..c2b2d32 --- /dev/null +++ b/sites/dapp-ui/src/components/user/StatusCard.tsx @@ -0,0 +1,64 @@ +import Card from "@mui/material/Card"; +import CardActions from "@mui/material/CardActions"; +import CardContent from "@mui/material/CardContent"; +import IconButton from "@mui/material/IconButton"; +import Typography from "@mui/material/Typography"; +import FavoriteIcon from '@mui/icons-material/Favorite'; +import ShareIcon from '@mui/icons-material/Share'; +import LogoutIcon from '@mui/icons-material/Logout'; +import {User} from "./types.ts" +export type ProfileCardProps = { + socket: { + isConnected: boolean + } + session: { + wallet?: string + connected?: boolean + active?: boolean + cookie:{ + expires: string | null + httpOnly: boolean + originalMaxAge: number | null + path: string + secure: boolean + } + } + user: User | null +} +export function StatusCard({session, user, socket}: ProfileCardProps){ + const {cookie, ...sessionData} = session + return ( + + + + Session + +
+                    {JSON.stringify(sessionData, null, 2)}
+                
+ + Signaling Service + +
+                    {JSON.stringify(socket, null, 2)}
+                
+ + Cookie + +
+                    {JSON.stringify(cookie, null, 2)}
+                
+
+ {user && + + { + fetch('/auth/logout') + }} /> + + + + + } +
+ ) +} diff --git a/sites/dapp-ui/src/components/user/types.ts b/sites/dapp-ui/src/components/user/types.ts new file mode 100644 index 0000000..a6e4ff7 --- /dev/null +++ b/sites/dapp-ui/src/components/user/types.ts @@ -0,0 +1,11 @@ +export type CredentialMetadata = { + device: string + publicKey: string + credId: string + prevCounter: number +} +export type User = { + wallet: string + id: string + credentials: CredentialMetadata[] +} diff --git a/sites/dapp-ui/src/components/user/useUserState.ts b/sites/dapp-ui/src/components/user/useUserState.ts new file mode 100644 index 0000000..9c4d0b4 --- /dev/null +++ b/sites/dapp-ui/src/components/user/useUserState.ts @@ -0,0 +1,12 @@ +import {useQuery} from "@tanstack/react-query"; + +export function useUserState(){ + return useQuery({ + refetchInterval: 3000, + queryKey: ['auth-session'], + queryFn: () => + fetch('/auth/session').then((res) => + res.json(), + ), + }) +} diff --git a/sites/dapp-ui/src/hooks/useAddress.ts b/sites/dapp-ui/src/hooks/useAddress.ts index a498c94..0b843af 100644 --- a/sites/dapp-ui/src/hooks/useAddress.ts +++ b/sites/dapp-ui/src/hooks/useAddress.ts @@ -5,9 +5,9 @@ export function useAddressQuery(address?: string){ const theme = useTheme(); const greaterThanMid = useMediaQuery(theme.breakpoints.up("md")); - return useQuery({ queryKey: [address], queryFn: ()=>{ + return useQuery({ enabled: typeof address === 'string',queryKey: ['nfd-lookup'], queryFn: ()=>{ if(typeof address === 'undefined'){ - return address + return } return fetch(`https://api.nf.domains/nfd/lookup?address=${address}&view=tiny&allowUnverified=true`).then(async (r)=>{ if(r.ok){ diff --git a/sites/dapp-ui/src/hooks/usePeerConnection.ts b/sites/dapp-ui/src/hooks/usePeerConnection.ts new file mode 100644 index 0000000..7227315 --- /dev/null +++ b/sites/dapp-ui/src/hooks/usePeerConnection.ts @@ -0,0 +1,62 @@ +import {useEffect, useMemo} from "react"; + + +const DEFAULT_CONFIG = { + iceServers: [ + { + urls: [ + 'stun:stun.l.google.com:19302', + 'stun:stun1.l.google.com:19302', + 'stun:stun2.l.google.com:19302', + ], + }, + ], + iceCandidatePoolSize: 10, +}; + +type PeerConnectionConfig = { + onMessage: (event: MessageEvent)=>void; + onIceCandidate: (event: RTCPeerConnectionIceEvent)=>void; + channelName?: string; + configuration?: RTCConfiguration; +} +export function usePeerConnection(onIceCandidate: (event: RTCPeerConnectionIceEvent)=>void, channelName = 'data', configuration = DEFAULT_CONFIG) { + console.log(configuration) + const peerConnection = useMemo(()=>new RTCPeerConnection(configuration), [configuration]); + const dataChannel = useMemo(()=>peerConnection.createDataChannel(channelName), [channelName, peerConnection]); + useEffect(() => { + function handleICEGatheringStateChange() { + console.log(`ICE gathering state: ${peerConnection.iceGatheringState}`); + } + function handleConnectionStateChange() { + console.log(`Connection state change: ${peerConnection.connectionState}`); + } + + function handleSignalingStateChange() { + console.log(`Signaling state change: ${peerConnection.signalingState}`); + } + + function handleICEConnectionStateChange() { + console.log(`ICE connection state change: ${peerConnection.iceConnectionState}`); + } + peerConnection.addEventListener('icegatheringstatechange', handleICEGatheringStateChange); + peerConnection.addEventListener('connectionstatechange', handleConnectionStateChange); + peerConnection.addEventListener('signalingstatechange', handleSignalingStateChange); + peerConnection.addEventListener('iceconnectionstatechange ', handleICEConnectionStateChange); + peerConnection.addEventListener('icecandidate', onIceCandidate); + + dataChannel.addEventListener("open", (event) => { + console.log(event) + }); + + return () => { + peerConnection.removeEventListener('icegatheringstatechange', handleICEGatheringStateChange); + peerConnection.removeEventListener('connectionstatechange', handleConnectionStateChange); + peerConnection.removeEventListener('signalingstatechange', handleSignalingStateChange); + peerConnection.removeEventListener('iceconnectionstatechange ', handleICEConnectionStateChange); + peerConnection.removeEventListener('icecandidate', onIceCandidate); + } + }, [peerConnection, onIceCandidate]); + + return peerConnection; +} diff --git a/sites/dapp-ui/src/hooks/useSocket.ts b/sites/dapp-ui/src/hooks/useSocket.ts index 0d6c228..7cceb17 100644 --- a/sites/dapp-ui/src/hooks/useSocket.ts +++ b/sites/dapp-ui/src/hooks/useSocket.ts @@ -2,9 +2,9 @@ import { io } from 'socket.io-client'; import { useEffect, useState } from 'react'; // "undefined" means the URL will be computed from the `window.location` object -const URL = process.env.NODE_ENV === 'production' ? undefined : `${window.location.origin}`; +const URL = `${window.location.origin}`; -export const socket = io(URL); +export const socket = io(URL, {autoConnect: false}); export function useSocket(){ diff --git a/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx b/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx index 6a1bd59..fef8c89 100644 --- a/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx +++ b/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx @@ -27,7 +27,7 @@ export function WaitForRegistrationCard(){ window.localStorage.setItem('credId', credId); setState('registered') }); - }, []) + }) return ( state.addresses); const save = useCredentialStore((state)=> state.update); const {state: step, setState} = useContext(StateContext) const [state] = useState({ requestId: Math.random(), - challenge: toBase64URL(nacl.randomBytes(nacl.sign.seedLength)) + challenge: toBase64URL(randomBytes(sign.seedLength)) }) const qrOpts = { "width": 500, @@ -65,10 +67,16 @@ export function ConnectModal({color}: {color?: 'inherit' | 'primary' | 'secondar } } } - useEffect(() => { - if(step !== 'start'){ - return + + const [open, setOpen] = React.useState(false); + const [barcode, setBarcode] = React.useState("/qr-loading.png") + const handleOpen = () => { + setBarcode("/qr-loading.png") + if(socket.disconnected){ + console.warn('Socket is disconnected, attempting to reconnect') + socket.connect() } + socket.emit('link', { requestId: state.requestId }, async ({data}: {data: {credId?: string, requestId: string|number, wallet: string}}) => { console.log('On Link response'); console.log(data) @@ -82,17 +90,16 @@ export function ConnectModal({color}: {color?: 'inherit' | 'primary' | 'secondar console.log(data.credId) window.localStorage.setItem('credId', data.credId); setState('registered') + snackbar.setMessage('Connected with Credential') + snackbar.setOpen(true) } else { + snackbar.setMessage('Connected Public Key') + snackbar.setOpen(true) setState('connected') } }); - }, []) - const [open, setOpen] = React.useState(false); - const [barcode, setBarcode] = React.useState("/qr-loading.png") - const handleOpen = () => { - setBarcode("/qr-loading.png") const message = new Message(window.location.origin, state.challenge, state.requestId) // JSON encoding diff --git a/sites/dapp-ui/src/pages/home/GetStarted.tsx b/sites/dapp-ui/src/pages/home/GetStarted.tsx index 5c59b2a..62e9e6e 100644 --- a/sites/dapp-ui/src/pages/home/GetStarted.tsx +++ b/sites/dapp-ui/src/pages/home/GetStarted.tsx @@ -4,11 +4,47 @@ import Typography from "@mui/material/Typography"; import CardActions from "@mui/material/CardActions"; import Card from '@mui/material/Card'; import {ConnectModal} from "./ConnectModal"; +import {useEffect} from "react"; +import {useSocket} from "../../hooks/useSocket.ts"; +import {usePeerConnection} from "../../hooks/usePeerConnection.ts"; +import Button from "@mui/material/Button"; export function GetStartedCard(){ + const {socket} = useSocket(); + const peerConnection = usePeerConnection((event)=>{ + if(event.candidate){ + console.log('candidate', event.candidate.toJSON()) + socket.emit('ice-candidate-offer', event.candidate.toJSON(), (data)=>{ + console.log('sent ice-candidate-offer', data) + }) + } else { + console.log('Finished gathering candidates.') + } + }); + useEffect(() => { + + }, []); + useEffect(() => { + console.log('GetStartedCard mounted'); + async function letsGo(){ + const offer = await peerConnection.createOffer(); + await peerConnection.setLocalDescription(offer); + } + letsGo() + const onHello = (data: unknown) => {console.log(data)} + socket.on('hello', onHello) + socket.emit('hello', {hello: 'world'}) + return () => { + console.log('GetStartedCard unmounted'); + socket.off('hello', onHello); + } + },[]) return ( - + @@ -30,6 +66,7 @@ export function GetStartedCard(){ + ) diff --git a/sites/dapp-ui/vite.config.ts b/sites/dapp-ui/vite.config.ts index a7febf9..87ca49a 100644 --- a/sites/dapp-ui/vite.config.ts +++ b/sites/dapp-ui/vite.config.ts @@ -1,29 +1,106 @@ -import { defineConfig } from 'vite' +import {defineConfig, splitVendorChunkPlugin} from 'vite' +import { VitePWA } from 'vite-plugin-pwa' +import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'; import react from '@vitejs/plugin-react-swc' import {rename} from 'node:fs/promises' import {resolve} from 'node:path' -import { mkdirp } from 'mkdirp'; +import {mkdirp} from 'mkdirp'; const API_DIR = resolve(__dirname, '..', '..', 'services', 'liquid-auth-api-js') const PUBLIC_DIR = resolve(API_DIR, 'public') const VIEW_DIR = resolve(API_DIR, 'views') console.log(API_DIR) export default defineConfig({ - build:{ - outDir: PUBLIC_DIR, + server: { + proxy: { + '^/auth/.*': 'http://localhost:3000', + '^/connect/.*': 'http://localhost:3000', + '^/attestation/.*': 'http://localhost:3000', + '^/assertion/.*': 'http://localhost:3000', + '/socket.io': { + target: 'ws://localhost:3000', + ws: true, + }, + } }, - plugins: [react(), - { - name: 'move-index-file', - closeBundle: async () => { - await mkdirp(VIEW_DIR) - try{ - await rename(resolve(PUBLIC_DIR, 'index.html'), resolve(VIEW_DIR, 'index.html')) - } catch (e) { - console.log('Skipping') - } - - } + build: { + // rollupOptions: { + // output: { + // manualChunks: { + // // 'algorand': ['tweetnacl', 'algosdk'], + // 'socket.io': ['socket.io-client'], + // 'react': ['react', 'react-dom', '@tanstack/react-query'], + // 'material': ['@mui/material', '@mui/icons-material'] + // } + // } + // }, + outDir: PUBLIC_DIR, }, + plugins: [ + VitePWA({ + includeAssets: ['logo-background.svg', 'apple-touch-icon.png', 'maskable-icon.png'], + workbox: { + navigateFallback: null, + }, + manifest: { + name: 'Liquid dApp', + short_name: 'Liquid', + description: 'FIDO2/Passkey Authentication', + theme_color: '#121212', + icons: [ + { + src: 'icons/48x48.png', + sizes: '48x48', + type: 'image/png' + }, + { + src: 'icons/72x72.png', + sizes: '72x72', + type: 'image/png' + }, + { + src: 'icons/96x96.png', + sizes: '96x96', + type: 'image/png' + }, + { + src: 'icons/144x144.png', + sizes: '144x144', + type: 'image/png' + }, + { + src: 'icons/192x192.png', + sizes: '192x192', + type: 'image/png' + }, + { + src: 'icons/512x512.png', + sizes: '512x512', + type: 'image/png' + }, + { + src: 'maskable-icon.png', + sizes: '512x512', + type: 'image/png', + purpose: "maskable" + } + ] + } + }), + splitVendorChunkPlugin(), + ViteImageOptimizer(), + react(), + { + name: 'move-index-file', + closeBundle: async () => { + await mkdirp(VIEW_DIR) + try { + await rename(resolve(PUBLIC_DIR, 'index.html'), resolve(VIEW_DIR, 'index.html')) + } catch (e) { + console.log('Skipping') + } + + } + }, ], }) From 77353cd17bb162f8e38c1996add27a9f620a5615 Mon Sep 17 00:00:00 2001 From: Michael J Feher Date: Mon, 25 Mar 2024 11:09:25 -0500 Subject: [PATCH 03/21] feat: WebRTC with websocket signaling --- .docker/templates/default.conf.template | 12 + clients/liquid-auth-client-js/src/connect.ts | 32 +- docker-compose.yml | 26 + index.html | 31 + package-lock.json | 4903 +++++++++++++++-- services/liquid-auth-api-js/package.json | 3 +- .../src/adapters/redis-io.adapter.ts | 2 +- .../src/android/android.controller.spec.ts | 18 + .../src/android/android.controller.ts | 56 + .../liquid-auth-api-js/src/app.controller.ts | 58 +- services/liquid-auth-api-js/src/app.module.ts | 22 +- .../src/assertion/assertion.module.ts | 6 +- .../src/attestation/attestation.module.ts | 6 +- .../src/auth/auth.controller.ts | 49 +- .../liquid-auth-api-js/src/auth/auth.guard.ts | 6 +- .../src/auth/auth.module.ts | 6 +- .../src/auth/auth.service.ts | 44 +- .../src/{connect => auth}/session.schema.ts | 0 .../src/connect/connect.dto.ts | 15 + .../src/connect/connect.gateway.ts | 146 +- .../src/connect/connect.module.ts | 12 +- .../src/connect/session.service.ts | 51 - services/liquid-auth-api-js/src/main.ts | 59 +- .../src/signals/signals.gateway.spec.ts | 18 + .../src/signals/signals.gateway.ts | 65 + .../src/signals/signals.interceptor.spec.ts | 7 + .../src/signals/signals.interceptor.ts | 31 + .../src/signals/signals.module.ts | 23 + sites/dapp-ui/package.json | 1 + sites/dapp-ui/public/logo-inverted.png | Bin 0 -> 15926 bytes sites/dapp-ui/src/App.tsx | 60 +- sites/dapp-ui/src/Contexts.tsx | 5 +- sites/dapp-ui/src/Layout.tsx | 34 +- sites/dapp-ui/src/components/Chat.tsx | 73 + sites/dapp-ui/src/components/Snackbar.tsx | 2 +- .../src/components/user/StatusCard.tsx | 8 +- sites/dapp-ui/src/entry-main.tsx | 4 +- sites/dapp-ui/src/hooks/useDataChannel.ts | 52 + sites/dapp-ui/src/hooks/usePeerConnection.ts | 68 +- sites/dapp-ui/src/hooks/useSocket.ts | 2 +- .../pages/dashboard/WaitForRegistration.tsx | 5 + .../dapp-ui/src/pages/debug/ManualWebRTC.tsx | 253 + sites/dapp-ui/src/pages/debug/WebRTC.tsx | 206 + sites/dapp-ui/src/pages/home/ConnectModal.tsx | 43 +- sites/dapp-ui/src/pages/home/GetStarted.tsx | 32 - .../src/pages/peering/WaitForPeers.tsx | 97 + sites/dapp-ui/src/store.ts | 29 + sites/dapp-ui/vite.config.ts | 23 +- 48 files changed, 5706 insertions(+), 998 deletions(-) create mode 100644 .docker/templates/default.conf.template create mode 100644 index.html create mode 100644 services/liquid-auth-api-js/src/android/android.controller.spec.ts create mode 100644 services/liquid-auth-api-js/src/android/android.controller.ts rename services/liquid-auth-api-js/src/{connect => auth}/session.schema.ts (100%) create mode 100644 services/liquid-auth-api-js/src/connect/connect.dto.ts delete mode 100644 services/liquid-auth-api-js/src/connect/session.service.ts create mode 100644 services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts create mode 100644 services/liquid-auth-api-js/src/signals/signals.gateway.ts create mode 100644 services/liquid-auth-api-js/src/signals/signals.interceptor.spec.ts create mode 100644 services/liquid-auth-api-js/src/signals/signals.interceptor.ts create mode 100644 services/liquid-auth-api-js/src/signals/signals.module.ts create mode 100644 sites/dapp-ui/public/logo-inverted.png create mode 100644 sites/dapp-ui/src/components/Chat.tsx create mode 100644 sites/dapp-ui/src/hooks/useDataChannel.ts create mode 100644 sites/dapp-ui/src/pages/debug/ManualWebRTC.tsx create mode 100644 sites/dapp-ui/src/pages/debug/WebRTC.tsx create mode 100644 sites/dapp-ui/src/pages/peering/WaitForPeers.tsx diff --git a/.docker/templates/default.conf.template b/.docker/templates/default.conf.template new file mode 100644 index 0000000..f7ebc36 --- /dev/null +++ b/.docker/templates/default.conf.template @@ -0,0 +1,12 @@ +proxy_cache_path /data/nginx/cache keys_zone=mycache:10m; +server { + listen 80; + listen [::]:80; + server_name localhost; + gzip on; + proxy_cache mycache; + gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css; + location / { + proxy_pass http://host.docker.internal:3000; + } +} diff --git a/clients/liquid-auth-client-js/src/connect.ts b/clients/liquid-auth-client-js/src/connect.ts index 9af9a5a..c747212 100644 --- a/clients/liquid-auth-client-js/src/connect.ts +++ b/clients/liquid-auth-client-js/src/connect.ts @@ -6,15 +6,35 @@ import {toBase64URL, encodeAddress} from './encoding.js' export class Message { + /** + * Origin of the Request + */ origin: string; + /** + * Challenge to be signed + */ challenge: string; + /** + * Linking Request ID + */ requestId: number; + /** + * Label for the remote Service + */ + label?: string; + /** + * Address that signed the message + */ wallet?: string; + /** + * Signature of the challenge + */ signature?: string; - constructor(origin: string, challenge: string, requestId: number) { + constructor(origin: string, challenge: string, requestId: number, label?: string) { this.origin = origin this.challenge = challenge this.requestId = requestId + this.label = label } static async fromResponse(response: Response|Message){ const msg = response instanceof Response ? await response.json(): response; @@ -26,7 +46,7 @@ export class Message { * * @param key */ - sign(key: string | Account | Uint8Array | SignKeyPair): void{ + sign(key: string | Account | Uint8Array | SignKeyPair): Message{ const encoder = new TextEncoder() let keyPair: SignKeyPair @@ -53,10 +73,11 @@ export class Message { } this.signature = toBase64URL(sign.detached(encoder.encode(this.challenge), keyPair.secretKey)); this.wallet = encodeAddress(keyPair.publicKey) + return this; } toString(){ - let optional: {wallet?: string, signature?: string} = {} + let optional: {wallet?: string, signature?: string, label?: string} = {} if(typeof this.wallet === 'string'){ optional.wallet = this.wallet; @@ -65,6 +86,11 @@ export class Message { if(typeof this.signature === 'string'){ optional.signature = this.signature } + + if(typeof this.label === 'string'){ + optional.label = this.label + } + return JSON.stringify({ origin: this.origin, requestId: this.requestId, challenge: this.challenge, ...optional }) } } diff --git a/docker-compose.yml b/docker-compose.yml index 314f828..b76b7c1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,14 @@ version: '3.4' services: + nginx: + image: nginx + restart: always + ports: + - "8080:80" + volumes: + - nginx:/data/nginx/cache + - ./.docker/templates:/etc/nginx/templates/ redis: image: redis restart: always @@ -24,5 +32,23 @@ services: - "27017:27017" volumes: - mongo:/data/db + turn: + image: coturn/coturn + deploy: + replicas: 0 + depends_on: + - mongo + ports: + - "3478:3478" + - "3478:3478/udp" + - "5349:5349" + - "5349:5349/udp" + command: + - "--no-auth" + - "--mongo-userdb" + - "mongodb://${DB_USERNAME:-algorand}:${DB_PASSWORD:-algorand}@mongo:27017/${DB_NAME:-coturn}?authSource=admin&retryWrites=true&w=majority" + - "--redis-userdb" + - "ip=redis dbname=0" volumes: mongo: + nginx: diff --git a/index.html b/index.html new file mode 100644 index 0000000..3c499bc --- /dev/null +++ b/index.html @@ -0,0 +1,31 @@ + + + + + Title + + + + + + + diff --git a/package-lock.json b/package-lock.json index 481f854..5da4941 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,19 +20,23 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "hi-base32": "^0.5.1", + "js-sha512": "^0.9.0", "qr-code-styling": "^1.6.0-rc.1", - "qrcode": "^1.5.3" + "tweetnacl": "github:awesome-algorand/tweetnacl-js" }, "devDependencies": { "@types/qrcode": "^1.5.5", + "algosdk": "^2.7.0", "c8": "^9.1.0", "typescript": "^5.3.3" - }, - "peerDependencies": { - "algosdk": "^2.7.0", - "tweetnacl": "^1.0.3" } }, + "clients/liquid-auth-client-js/node_modules/js-sha512": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.9.0.tgz", + "integrity": "sha512-mirki9WS/SUahm+1TbAPkqvbCiCfOAAsyXeHxK1UkullnJVVqoJG2pL9ObvT05CN+tM7fxhfYm0NbXn+1hWoZg==" + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "dev": true, @@ -158,6 +162,23 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "dev": true, + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "license": "MIT", @@ -227,9 +248,10 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.9", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -290,14 +312,39 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets": { + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -313,6 +360,80 @@ "semver": "bin/semver.js" } }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.23.10", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz", + "integrity": "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", + "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/helper-environment-visitor": { "version": "7.22.20", "dev": true, @@ -344,6 +465,18 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { "version": "7.22.15", "license": "MIT", @@ -355,15 +488,16 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.17", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.15" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -372,6 +506,18 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-plugin-utils": { "version": "7.22.5", "dev": true, @@ -380,6 +526,40 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/helper-simple-access": { "version": "7.22.5", "dev": true, @@ -391,6 +571,18 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.22.6", "dev": true, @@ -417,9 +609,24 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, - "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + }, "engines": { "node": ">=6.9.0" } @@ -517,6 +724,66 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", + "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", + "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", + "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "dev": true, @@ -550,6 +817,75 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", + "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", + "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "dev": true, @@ -652,6 +988,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "dev": true, @@ -680,9 +1031,929 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", + "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", + "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", + "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", + "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", + "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", + "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", + "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", + "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", + "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", + "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", + "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", + "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", + "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", + "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", + "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", + "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", + "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", + "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", + "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", + "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", + "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", + "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", + "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", + "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", + "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", + "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", + "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", + "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", + "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", + "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", + "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", + "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", + "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", + "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", + "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", + "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", + "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", + "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", + "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.9", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.4", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-classes": "^7.23.8", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.4", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-for-of": "^7.23.6", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.4", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.9", + "@babel/plugin-transform-modules-umd": "^7.23.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-object-rest-spread": "^7.23.4", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.8", + "babel-plugin-polyfill-corejs3": "^0.9.0", + "babel-plugin-polyfill-regenerator": "^0.5.5", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, + "node_modules/@babel/runtime": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -778,6 +2049,16 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@emnapi/runtime": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", + "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.11.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", @@ -943,220 +2224,618 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, - "node_modules/@esbuild/aix-ppc64": { + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", "cpu": [ - "ppc64" + "riscv64" ], "dev": true, "optional": true, "os": [ - "aix" + "linux" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/android-arm": { + "node_modules/@esbuild/linux-s390x": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", "cpu": [ - "arm" + "s390x" ], "dev": true, "optional": true, "os": [ - "android" + "linux" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/android-arm64": { + "node_modules/@esbuild/linux-x64": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", "cpu": [ - "arm64" + "x64" ], "dev": true, "optional": true, "os": [ - "android" + "linux" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/android-x64": { + "node_modules/@esbuild/netbsd-x64": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ - "android" + "netbsd" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/darwin-arm64": { + "node_modules/@esbuild/openbsd-x64": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", "cpu": [ - "arm64" + "x64" ], "dev": true, "optional": true, "os": [ - "darwin" + "openbsd" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/darwin-x64": { + "node_modules/@esbuild/sunos-x64": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ - "darwin" + "sunos" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/freebsd-arm64": { + "node_modules/@esbuild/win32-arm64": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ - "freebsd" + "win32" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/freebsd-x64": { + "node_modules/@esbuild/win32-ia32": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", "cpu": [ - "x64" + "ia32" ], "dev": true, "optional": true, "os": [ - "freebsd" + "win32" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/linux-arm": { + "node_modules/@esbuild/win32-x64": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "cpu": [ - "arm" + "x64" ], "dev": true, "optional": true, "os": [ - "linux" + "win32" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.8.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "dependencies": { + "@floating-ui/utils": "^0.2.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.1.tgz", + "integrity": "sha512-iA8qE43/H5iGozC3W0YSnVSW42Vh522yyM1gj+BqRwVsTNOyr231PsXDaV04yT39PsO0QL2QpbI/M0ZaLUQgRQ==", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.1" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", + "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "dependencies": { + "@floating-ui/dom": "^1.6.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.2.tgz", + "integrity": "sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ - "linux" + "darwin" ], "engines": { - "node": ">=12" + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.1" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.2.tgz", + "integrity": "sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==", "cpu": [ - "ia32" + "x64" ], "dev": true, "optional": true, "os": [ - "linux" + "darwin" ], "engines": { - "node": ">=12" + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.1" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==", "cpu": [ - "loong64" + "arm64" ], "dev": true, "optional": true, "os": [ - "linux" + "darwin" ], "engines": { - "node": ">=12" + "macos": ">=11", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.1.tgz", + "integrity": "sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==", "cpu": [ - "mips64el" + "x64" ], "dev": true, "optional": true, "os": [ - "linux" + "darwin" ], "engines": { - "node": ">=12" + "macos": ">=10.13", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.1.tgz", + "integrity": "sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==", "cpu": [ - "ppc64" + "arm" ], "dev": true, "optional": true, @@ -1164,15 +2843,21 @@ "linux" ], "engines": { - "node": ">=12" + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.1.tgz", + "integrity": "sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==", "cpu": [ - "riscv64" + "arm64" ], "dev": true, "optional": true, @@ -1180,13 +2865,19 @@ "linux" ], "engines": { - "node": ">=12" + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.1.tgz", + "integrity": "sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==", "cpu": [ "s390x" ], @@ -1196,13 +2887,19 @@ "linux" ], "engines": { - "node": ">=12" + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.1.tgz", + "integrity": "sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==", "cpu": [ "x64" ], @@ -1212,250 +2909,286 @@ "linux" ], "engines": { - "node": ">=12" + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.1.tgz", + "integrity": "sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==", "cpu": [ - "x64" + "arm64" ], "dev": true, "optional": true, "os": [ - "netbsd" + "linux" ], "engines": { - "node": ">=12" + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.1.tgz", + "integrity": "sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ - "openbsd" + "linux" ], "engines": { - "node": ">=12" + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.2.tgz", + "integrity": "sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==", "cpu": [ - "x64" + "arm" ], "dev": true, "optional": true, "os": [ - "sunos" + "linux" ], "engines": { - "node": ">=12" + "glibc": ">=2.28", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.1" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.2.tgz", + "integrity": "sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">=12" + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.1" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.2.tgz", + "integrity": "sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==", "cpu": [ - "ia32" + "s390x" ], "dev": true, "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">=12" + "glibc": ">=2.28", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.1" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.2.tgz", + "integrity": "sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "funding": { + "url": "https://opencollective.com/libvips" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.1" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.8.0", + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.2.tgz", + "integrity": "sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.1" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.2.tgz", + "integrity": "sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.1" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@img/sharp-wasm32": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.2.tgz", + "integrity": "sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==", + "cpu": [ + "wasm32" + ], "dev": true, + "optional": true, "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" + "@emnapi/runtime": "^0.45.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", - "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", - "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", - "dependencies": { - "@floating-ui/utils": "^0.2.1" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.1.tgz", - "integrity": "sha512-iA8qE43/H5iGozC3W0YSnVSW42Vh522yyM1gj+BqRwVsTNOyr231PsXDaV04yT39PsO0QL2QpbI/M0ZaLUQgRQ==", - "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.1" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", - "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", - "dependencies": { - "@floating-ui/dom": "^1.6.1" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@floating-ui/utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.2.tgz", + "integrity": "sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=10.10.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.2.tgz", + "integrity": "sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=12.22" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://opencollective.com/libvips" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true - }, "node_modules/@ioredis/commands": { "version": "1.2.0", - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -1997,17 +3730,6 @@ "sparse-bitfield": "^3.0.3" } }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.2", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@mui/base": { "version": "5.0.0-beta.34", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.34.tgz", @@ -2788,6 +4510,14 @@ "@redis/client": "^1.0.0" } }, + "node_modules/@remix-run/router": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", + "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.9.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz", @@ -3128,6 +4858,27 @@ "socket.io-adapter": "^2.4.0" } }, + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "dev": true, + "dependencies": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "node_modules/@surma/rollup-plugin-off-main-thread/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, "node_modules/@swc/cli": { "version": "0.1.62", "dev": true, @@ -3394,26 +5145,51 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.18.0.tgz", - "integrity": "sha512-8c6nxeAnGHxIDZIyDmHdmgFt4D+LprAQaJmjsnM4szcIjsWOyFlzxdqQUuQ/XuQRvUgqYaqlJTtDADlSS7pTPQ==", + "version": "5.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.20.5.tgz", + "integrity": "sha512-T1W28gGgWn0A++tH3lxj3ZuUVZZorsiKcv+R50RwmPYz62YoDEkG4/aXHZELGkRp4DfrW07dyq2K5dvJ4Wl1aA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.20.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.20.2.tgz", + "integrity": "sha512-BZfSjhk/NGPbqte5E3Vc1Zbj28uWt///4I0DgzAdWrOtMVvdl0WlUXK23K2daLsbcyfoDR4jRI4f2Z5z/mMzuw==", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@tanstack/react-query": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.18.0.tgz", - "integrity": "sha512-7FKxNfxxKEL7n3ADpwp81Fy4FX85hNkYVzQQVQsF0JAPl93c3d1gmNZMIbEtOqgYfom1/ontGh3FiZGYj3xyWA==", + "version": "5.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.20.5.tgz", + "integrity": "sha512-6MHwJ8G9cnOC/XKrwt56QMc91vN7hLlAQNUA0ubP7h9Jj3a/CmkUwT6ALdFbnVP+PsYdhW3WONa8WQ4VcTaSLQ==", + "dependencies": { + "@tanstack/query-core": "5.20.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.20.5.tgz", + "integrity": "sha512-Wl7IzNuKCb4h41a5iH/YXNwalHItqJPCAr4r8+0iUYOLHNOf3E9P0G4kzZ9sqDoWKxY04qst6Vrij9bwPzLQRQ==", "dependencies": { - "@tanstack/query-core": "5.18.0" + "@tanstack/query-devtools": "5.20.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { + "@tanstack/react-query": "^5.20.5", "react": "^18.0.0" } }, @@ -3431,6 +5207,15 @@ "node": ">= 10" } }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "dev": true, @@ -3614,14 +5399,6 @@ "@types/node": "*" } }, - "node_modules/@types/hbs": { - "version": "4.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "handlebars": "^4.1.0" - } - }, "node_modules/@types/http-cache-semantics": { "version": "4.0.1", "dev": true, @@ -3634,6 +5411,14 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/http-proxy": { + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "dev": true, @@ -3742,6 +5527,15 @@ "@types/react": "*" } }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/responselike": { "version": "1.0.0", "dev": true, @@ -3822,6 +5616,12 @@ "@types/superagent": "*" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true + }, "node_modules/@types/ua-parser-js": { "version": "0.7.37", "dev": true, @@ -4340,6 +6140,7 @@ }, "node_modules/algo-msgpack-with-bigint": { "version": "2.1.1", + "dev": true, "license": "ISC", "engines": { "node": ">= 10" @@ -4347,6 +6148,7 @@ }, "node_modules/algosdk": { "version": "2.7.0", + "dev": true, "license": "MIT", "dependencies": { "algo-msgpack-with-bigint": "^2.1.1", @@ -4365,6 +6167,7 @@ }, "node_modules/algosdk/node_modules/buffer": { "version": "6.0.3", + "dev": true, "funding": [ { "type": "github", @@ -4513,6 +6316,22 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "license": "MIT" @@ -4531,6 +6350,28 @@ "node": ">=8" } }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/asap": { "version": "2.0.6", "dev": true, @@ -4565,11 +6406,38 @@ "node": ">=12.0.0" } }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, "node_modules/asynckit": { "version": "0.4.0", "dev": true, "license": "MIT" }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", + "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "dev": true, @@ -4656,6 +6524,54 @@ "npm": ">=6" } }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz", + "integrity": "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.5.0", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", + "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.5.0", + "core-js-compat": "^3.34.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", + "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.5.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/babel-preset-current-node-syntax": { "version": "1.0.1", "dev": true, @@ -4989,6 +6905,12 @@ "version": "2.0.0", "license": "MIT" }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, "node_modules/bplist-parser": { "version": "0.2.0", "dev": true, @@ -5010,7 +6932,6 @@ }, "node_modules/braces": { "version": "3.0.2", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.0.1" @@ -5024,7 +6945,9 @@ "license": "MIT" }, "node_modules/browserslist": { - "version": "4.21.10", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, "funding": [ { @@ -5040,12 +6963,11 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -5106,27 +7028,16 @@ "version": "1.1.2", "license": "MIT" }, - "node_modules/bull": { - "version": "4.11.4", - "license": "MIT", - "dependencies": { - "cron-parser": "^4.2.1", - "get-port": "^5.1.1", - "ioredis": "^5.3.2", - "lodash": "^4.17.21", - "msgpackr": "^1.5.2", - "semver": "^7.5.2", - "uuid": "^8.3.0" - }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, "engines": { - "node": ">=12" - } - }, - "node_modules/bull/node_modules/uuid": { - "version": "8.3.2", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bundle-name": { @@ -5304,11 +7215,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "license": "MIT", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "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" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5323,13 +7241,16 @@ }, "node_modules/camelcase": { "version": "5.3.1", + "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001532", + "version": "1.0.30001587", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz", + "integrity": "sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==", "dev": true, "funding": [ { @@ -5344,8 +7265,7 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/cbor": { "version": "5.2.0", @@ -5575,6 +7495,19 @@ "dev": true, "license": "MIT" }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "license": "MIT", @@ -5589,6 +7522,16 @@ "version": "1.1.4", "license": "MIT" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/color-support": { "version": "1.1.3", "license": "ISC", @@ -5630,6 +7573,15 @@ "node": ">= 6" } }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/component-emitter": { "version": "1.3.0", "dev": true, @@ -5773,6 +7725,19 @@ "dev": true, "license": "MIT" }, + "node_modules/core-js-compat": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz", + "integrity": "sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "license": "MIT" @@ -5829,50 +7794,123 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/create-require": { - "version": "1.1.1", + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-env": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, - "license": "MIT" + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } }, - "node_modules/cron-parser": { - "version": "4.9.0", - "license": "MIT", + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, "dependencies": { - "luxon": "^3.2.1" + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">=12.0.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/cross-env": { - "version": "7.0.3", + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.1" + "engines": { + "node": ">= 6" }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "dependencies": { + "css-tree": "~2.2.0" }, "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "license": "MIT", + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">= 8" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -5913,13 +7951,6 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/decompress-response": { "version": "6.0.0", "dev": true, @@ -6130,6 +8161,22 @@ "node": ">=10" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/define-lazy-prop": { "version": "3.0.0", "dev": true, @@ -6141,6 +8188,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties/node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "dev": true, @@ -6156,6 +8229,8 @@ "node_modules/denque": { "version": "2.1.0", "license": "Apache-2.0", + "optional": true, + "peer": true, "engines": { "node": ">=0.10" } @@ -6215,10 +8290,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dijkstrajs": { - "version": "1.0.3", - "license": "MIT" - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -6251,6 +8322,61 @@ "csstype": "^3.0.2" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dotenv": { "version": "16.3.1", "license": "BSD-2-Clause", @@ -6284,10 +8410,26 @@ "version": "1.1.1", "license": "MIT" }, - "node_modules/electron-to-chromium": { - "version": "1.4.513", + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", "dev": true, - "license": "ISC" + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.670", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.670.tgz", + "integrity": "sha512-hcijYOWjOtjKrKPtNA6tuLlA/bTLO3heFG8pQA6mLpq7dRydSWicXova5lyxDzp1iVJaYhK7J2OQlGE52KYn7A==", + "dev": true }, "node_modules/elliptic": { "version": "6.5.4", @@ -6317,10 +8459,6 @@ "version": "8.0.0", "license": "MIT" }, - "node_modules/encode-utf8": { - "version": "1.0.3", - "license": "MIT" - }, "node_modules/encodeurl": { "version": "1.0.2", "license": "MIT", @@ -6412,6 +8550,18 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "2.2.1", "license": "MIT", @@ -6430,11 +8580,125 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-abstract": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz", + "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.7", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.2", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.1", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.0", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.1", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract/node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "1.3.1", "dev": true, "license": "MIT" }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/esbuild": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", @@ -6726,6 +8990,12 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, "node_modules/esutils": { "version": "2.0.3", "dev": true, @@ -6741,6 +9011,11 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/events": { "version": "3.3.0", "dev": true, @@ -7006,9 +9281,10 @@ "license": "Apache-2.0" }, "node_modules/fast-glob": { - "version": "3.3.1", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -7101,6 +9377,36 @@ "url": "https://github.com/sindresorhus/file-type?sponsor=1" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/filename-reserved-regex": { "version": "3.0.0", "dev": true, @@ -7134,7 +9440,6 @@ }, "node_modules/fill-range": { "version": "7.0.1", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -7238,9 +9543,38 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/foreachasync": { "version": "3.0.0", - "license": "Apache2" + "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", + "integrity": "sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==" }, "node_modules/foreground-child": { "version": "3.1.1", @@ -7388,8 +9722,39 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "license": "MIT" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gauge": { "version": "4.0.4", @@ -7425,24 +9790,36 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "license": "MIT", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, "node_modules/get-package-type": { "version": "0.1.0", "dev": true, @@ -7451,25 +9828,32 @@ "node": ">=8.0.0" } }, - "node_modules/get-port": { - "version": "5.1.1", + "node_modules/get-stream": { + "version": "6.0.1", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-stream": { - "version": "6.0.1", + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, - "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/glob": { @@ -7521,6 +9905,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -7541,6 +9940,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/got": { "version": "11.8.6", "dev": true, @@ -7578,7 +9988,8 @@ }, "node_modules/handlebars": { "version": "4.7.7", - "license": "MIT", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.0", @@ -7597,7 +10008,8 @@ }, "node_modules/handlebars/node_modules/source-map": { "version": "0.6.1", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "engines": { "node": ">=0.10.0" } @@ -7612,6 +10024,15 @@ "node": ">= 0.4.0" } }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "license": "MIT", @@ -7627,6 +10048,17 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-proto": { "version": "1.0.1", "license": "MIT", @@ -7647,6 +10079,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-unicode": { "version": "2.0.1", "license": "ISC" @@ -7659,9 +10106,21 @@ "minimalistic-assert": "^1.0.1" } }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hbs": { "version": "4.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/hbs/-/hbs-4.2.0.tgz", + "integrity": "sha512-dQwHnrfWlTk5PvG9+a45GYpg0VpX47ryKF8dULVd6DtwOE6TEcYQXQ5QM6nyOx/h7v3bvEQbdn19EDAcfUAgZg==", "dependencies": { "handlebars": "4.7.7", "walk": "2.3.15" @@ -7681,7 +10140,8 @@ }, "node_modules/hi-base32": { "version": "0.5.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", + "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==" }, "node_modules/hmac-drbg": { "version": "1.0.1", @@ -7769,6 +10229,19 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/http-proxy-agent": { "version": "5.0.0", "license": "MIT", @@ -7781,6 +10254,33 @@ "node": ">= 6" } }, + "node_modules/http-proxy-middleware": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.0.tgz", + "integrity": "sha512-36AV1fIaI2cWRzHo+rbcxhe3M3jUDCNzc4D5zRl57sEWRAxdXYtw7FSQKYY6PDKssiAKjLYypbssHk+xs/kMXw==", + "dependencies": { + "@types/http-proxy": "^1.17.10", + "debug": "^4.3.4", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.5" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/http2-wrapper": { "version": "1.0.3", "dev": true, @@ -7831,6 +10331,12 @@ "node": ">=0.10.0" } }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "dev": true + }, "node_modules/ieee754": { "version": "1.2.1", "funding": [ @@ -7940,6 +10446,20 @@ "node": ">=12.0.0" } }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/interpret": { "version": "1.4.0", "dev": true, @@ -7951,6 +10471,8 @@ "node_modules/ioredis": { "version": "5.3.2", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", @@ -7981,10 +10503,38 @@ "node": ">= 0.10" } }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "license": "MIT" }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "dev": true, @@ -7996,6 +10546,34 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.13.0", "license": "MIT", @@ -8006,6 +10584,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "3.0.0", "dev": true, @@ -8022,7 +10615,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -8045,7 +10637,6 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -8083,14 +10674,55 @@ "version": "1.0.1", "license": "MIT" }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "dev": true, @@ -8109,6 +10741,43 @@ "node": ">=0.10.0" } }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "2.0.1", "dev": true, @@ -8120,15 +10789,72 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-wsl": { @@ -8256,6 +10982,24 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest": { "version": "29.7.0", "dev": true, @@ -8802,14 +11546,17 @@ }, "node_modules/js-sha256": { "version": "0.9.0", + "dev": true, "license": "MIT" }, "node_modules/js-sha3": { "version": "0.8.0", + "dev": true, "license": "MIT" }, "node_modules/js-sha512": { "version": "0.8.0", + "dev": true, "license": "MIT" }, "node_modules/js-tokens": { @@ -8841,6 +11588,7 @@ }, "node_modules/json-bigint": { "version": "1.0.0", + "dev": true, "license": "MIT", "dependencies": { "bignumber.js": "^9.0.0" @@ -8855,6 +11603,12 @@ "version": "2.3.1", "license": "MIT" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "dev": true, @@ -8892,6 +11646,15 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/jsrsasign": { "version": "10.8.6", "license": "MIT", @@ -8991,13 +11754,23 @@ "version": "4.17.21", "license": "MIT" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, "node_modules/lodash.defaults": { "version": "4.2.0", - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/lodash.isarguments": { "version": "3.1.0", - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/lodash.memoize": { "version": "4.1.2", @@ -9009,6 +11782,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true + }, "node_modules/log-symbols": { "version": "4.1.0", "dev": true, @@ -9057,13 +11836,6 @@ "yallist": "^3.0.2" } }, - "node_modules/luxon": { - "version": "3.4.3", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/macos-release": { "version": "2.5.1", "dev": true, @@ -9151,6 +11923,12 @@ "tmpl": "1.0.5" } }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, "node_modules/media-typer": { "version": "0.3.0", "license": "MIT", @@ -9200,7 +11978,6 @@ }, "node_modules/micromatch": { "version": "4.0.5", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.2", @@ -9561,33 +12338,6 @@ "version": "2.1.2", "license": "MIT" }, - "node_modules/msgpackr": { - "version": "1.9.9", - "license": "MIT", - "optionalDependencies": { - "msgpackr-extract": "^3.0.2" - } - }, - "node_modules/msgpackr-extract": { - "version": "3.0.2", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-gyp-build-optional-packages": "5.0.7" - }, - "bin": { - "download-msgpackr-prebuilds": "bin/download-prebuilds.js" - }, - "optionalDependencies": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.2", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.2" - } - }, "node_modules/multer": { "version": "1.4.4-lts.1", "license": "MIT", @@ -9716,16 +12466,6 @@ "node": "^12.13 || ^14.13 || >=16" } }, - "node_modules/node-gyp-build-optional-packages": { - "version": "5.0.7", - "license": "MIT", - "optional": true, - "bin": { - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-optional-packages-optional": "optional.js", - "node-gyp-build-optional-packages-test": "build-test.js" - } - }, "node_modules/node-gyp/node_modules/rimraf": { "version": "3.0.2", "license": "ISC", @@ -9745,9 +12485,10 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.13", - "dev": true, - "license": "MIT" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true }, "node_modules/node-rsa": { "version": "1.1.1", @@ -9825,6 +12566,18 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "license": "MIT", @@ -9840,8 +12593,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "license": "MIT", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9851,6 +12605,33 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", "integrity": "sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==" }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.assign/node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/on-finished": { "version": "2.4.1", "license": "MIT", @@ -10043,6 +12824,7 @@ }, "node_modules/p-try": { "version": "2.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -10083,6 +12865,7 @@ }, "node_modules/path-exists": { "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10145,6 +12928,12 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, "node_modules/peek-readable": { "version": "5.0.0", "dev": true, @@ -10166,7 +12955,6 @@ }, "node_modules/picomatch": { "version": "2.3.1", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -10260,13 +13048,6 @@ "node": ">=4" } }, - "node_modules/pngjs": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/postcss": { "version": "8.4.33", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", @@ -10328,6 +13109,18 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "dev": true, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "dev": true, @@ -10465,115 +13258,11 @@ "qrcode-generator": "^1.4.3" } }, - "node_modules/qrcode": { - "version": "1.5.3", - "license": "MIT", - "dependencies": { - "dijkstrajs": "^1.0.1", - "encode-utf8": "^1.0.3", - "pngjs": "^5.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/qrcode-generator": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz", "integrity": "sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==" }, - "node_modules/qrcode/node_modules/cliui": { - "version": "6.0.0", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/qrcode/node_modules/find-up": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/locate-path": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/p-limit": { - "version": "2.3.0", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/qrcode/node_modules/p-locate": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/y18n": { - "version": "4.0.3", - "license": "ISC" - }, - "node_modules/qrcode/node_modules/yargs": { - "version": "15.4.1", - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/yargs-parser": { - "version": "18.1.3", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/qs": { "version": "6.11.0", "license": "BSD-3-Clause", @@ -10681,6 +13370,36 @@ "version": "18.2.0", "license": "MIT" }, + "node_modules/react-router": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", + "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "dependencies": { + "@remix-run/router": "1.15.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", + "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "dependencies": { + "@remix-run/router": "1.15.3", + "react-router": "6.22.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -10784,6 +13503,8 @@ "node_modules/redis-errors": { "version": "1.2.0", "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -10791,6 +13512,8 @@ "node_modules/redis-parser": { "version": "3.0.0", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "redis-errors": "^1.0.0" }, @@ -10798,14 +13521,97 @@ "node": ">=4" } }, - "node_modules/reflect-metadata": { - "version": "0.1.13", - "license": "Apache-2.0" + "node_modules/reflect-metadata": { + "version": "0.1.13", + "license": "Apache-2.0" + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dev": true, + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } }, "node_modules/repeat-string": { "version": "1.6.1", @@ -10817,6 +13623,7 @@ }, "node_modules/require-directory": { "version": "2.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10830,9 +13637,10 @@ "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "license": "ISC" + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/resolve": { "version": "1.22.4", @@ -11070,6 +13878,30 @@ "tslib": "^2.1.0" } }, + "node_modules/safe-array-concat": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/safe-buffer": { "version": "5.2.1", "funding": [ @@ -11088,6 +13920,23 @@ ], "license": "MIT" }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "license": "MIT" @@ -11263,10 +14112,80 @@ "version": "2.0.0", "license": "ISC" }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "license": "ISC" }, + "node_modules/sharp": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.2.tgz", + "integrity": "sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "semver": "^7.5.4" + }, + "engines": { + "libvips": ">=8.15.1", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.2", + "@img/sharp-darwin-x64": "0.33.2", + "@img/sharp-libvips-darwin-arm64": "1.0.1", + "@img/sharp-libvips-darwin-x64": "1.0.1", + "@img/sharp-libvips-linux-arm": "1.0.1", + "@img/sharp-libvips-linux-arm64": "1.0.1", + "@img/sharp-libvips-linux-s390x": "1.0.1", + "@img/sharp-libvips-linux-x64": "1.0.1", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.1", + "@img/sharp-libvips-linuxmusl-x64": "1.0.1", + "@img/sharp-linux-arm": "0.33.2", + "@img/sharp-linux-arm64": "0.33.2", + "@img/sharp-linux-s390x": "0.33.2", + "@img/sharp-linux-x64": "0.33.2", + "@img/sharp-linuxmusl-arm64": "0.33.2", + "@img/sharp-linuxmusl-x64": "0.33.2", + "@img/sharp-wasm32": "0.33.2", + "@img/sharp-win32-ia32": "0.33.2", + "@img/sharp-win32-x64": "0.33.2" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "license": "MIT", @@ -11329,6 +14248,21 @@ "version": "3.0.7", "license": "ISC" }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, "node_modules/sisteransi": { "version": "1.0.5", "dev": true, @@ -11482,6 +14416,13 @@ "node": ">=0.10.0" } }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, "node_modules/sparse-bitfield": { "version": "3.0.3", "license": "MIT", @@ -11539,7 +14480,9 @@ }, "node_modules/standard-as-callback": { "version": "2.1.0", - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/statuses": { "version": "2.0.1", @@ -11602,6 +14545,85 @@ "node": ">=8" } }, + "node_modules/string.prototype.matchall": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "license": "MIT", @@ -11631,6 +14653,15 @@ "node": ">=8" } }, + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/strip-eof": { "version": "1.0.0", "dev": true, @@ -11759,6 +14790,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svgo": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", + "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "dev": true, @@ -11846,6 +14911,45 @@ "version": "4.0.0", "license": "ISC" }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/terser": { "version": "5.19.4", "dev": true, @@ -12027,7 +15131,6 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -12252,6 +15355,7 @@ }, "node_modules/tweetnacl": { "version": "1.0.3", + "resolved": "git+ssh://git@github.com/awesome-algorand/tweetnacl-js.git#55c193f3ade9213b5af0c9e7956aa66c62cae10b", "license": "Unlicense" }, "node_modules/type-check": { @@ -12296,6 +15400,71 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz", + "integrity": "sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typedarray": { "version": "0.0.6", "license": "MIT" @@ -12335,7 +15504,8 @@ }, "node_modules/uglify-js": { "version": "3.17.4", - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -12371,6 +15541,61 @@ "node": ">= 4.0.0" } }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/unique-filename": { "version": "3.0.0", "license": "ISC", @@ -12391,6 +15616,18 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/universalify": { "version": "2.0.0", "dev": true, @@ -12414,8 +15651,20 @@ "node": ">=8" } }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, "node_modules/update-browserslist-db": { - "version": "1.0.11", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -12431,7 +15680,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -12562,13 +15810,55 @@ } } }, + "node_modules/vite-plugin-image-optimizer": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/vite-plugin-image-optimizer/-/vite-plugin-image-optimizer-1.1.7.tgz", + "integrity": "sha512-KPJWndwqVi7Z2hYCudzKeNDw5U7w1DxAc266bqDBKV8taG8W3EtripFuUM4Y05IlFC19yBQndJCFA8+NJymH+w==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "pathe": "^1.1.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "vite": ">=3" + } + }, + "node_modules/vite-plugin-pwa": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.18.1.tgz", + "integrity": "sha512-2A3BF52l9F8hCkdPy/VP2C+hA+fmBvzJGynCZc9XS6mHTcMvo9046FKc2NqlnkKwTOGtQEwXLEIduML/+eYtdw==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "pretty-bytes": "^6.1.1", + "workbox-build": "^7.0.0", + "workbox-window": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0", + "workbox-build": "^7.0.0", + "workbox-window": "^7.0.0" + } + }, "node_modules/vlq": { "version": "2.0.4", + "dev": true, "license": "MIT" }, "node_modules/walk": { "version": "2.3.15", - "license": "(MIT OR Apache-2.0)", + "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.15.tgz", + "integrity": "sha512-4eRTBZljBfIISK1Vnt69Gvr2w/wc3U6Vtrw7qiN5iqYJPH7LElcYh/iU4XWhdCy2dZqv1ToMyYlybDylfG/5Vg==", "dependencies": { "foreachasync": "^3.0.0" } @@ -12708,9 +15998,40 @@ "node": ">= 8" } }, - "node_modules/which-module": { - "version": "2.0.1", - "license": "ISC" + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", + "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.5", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/wide-align": { "version": "1.1.5", @@ -12779,10 +16100,411 @@ }, "node_modules/wordwrap": { "version": "1.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/workbox-background-sync": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.0.0.tgz", + "integrity": "sha512-S+m1+84gjdueM+jIKZ+I0Lx0BDHkk5Nu6a3kTVxP4fdj3gKouRNmhO8H290ybnJTOPfBDtTMXSQA/QLTvr7PeA==", + "dev": true, + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.0.0" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.0.0.tgz", + "integrity": "sha512-oUuh4jzZrLySOo0tC0WoKiSg90bVAcnE98uW7F8GFiSOXnhogfNDGZelPJa+6KpGBO5+Qelv04Hqx2UD+BJqNQ==", + "dev": true, + "dependencies": { + "workbox-core": "7.0.0" + } + }, + "node_modules/workbox-build": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-7.0.0.tgz", + "integrity": "sha512-CttE7WCYW9sZC+nUYhQg3WzzGPr4IHmrPnjKiu3AMXsiNQKx+l4hHl63WTrnicLmKEKHScWDH8xsGBdrYgtBzg==", + "dev": true, + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "7.0.0", + "workbox-broadcast-update": "7.0.0", + "workbox-cacheable-response": "7.0.0", + "workbox-core": "7.0.0", + "workbox-expiration": "7.0.0", + "workbox-google-analytics": "7.0.0", + "workbox-navigation-preload": "7.0.0", + "workbox-precaching": "7.0.0", + "workbox-range-requests": "7.0.0", + "workbox-recipes": "7.0.0", + "workbox-routing": "7.0.0", + "workbox-strategies": "7.0.0", + "workbox-streams": "7.0.0", + "workbox-sw": "7.0.0", + "workbox-window": "7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/workbox-build/node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/workbox-build/node_modules/@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/workbox-build/node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/workbox-build/node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/workbox-build/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "node_modules/workbox-build/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/workbox-build/node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/workbox-build/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/workbox-build/node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/workbox-build/node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/workbox-build/node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/workbox-build/node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/workbox-build/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dev": true, + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workbox-build/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/workbox-build/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "node_modules/workbox-build/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.0.0.tgz", + "integrity": "sha512-0lrtyGHn/LH8kKAJVOQfSu3/80WDc9Ma8ng0p2i/5HuUndGttH+mGMSvOskjOdFImLs2XZIimErp7tSOPmu/6g==", + "dev": true, + "dependencies": { + "workbox-core": "7.0.0" + } + }, + "node_modules/workbox-core": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-7.0.0.tgz", + "integrity": "sha512-81JkAAZtfVP8darBpfRTovHg8DGAVrKFgHpOArZbdFd78VqHr5Iw65f2guwjE2NlCFbPFDoez3D3/6ZvhI/rwQ==", + "dev": true + }, + "node_modules/workbox-expiration": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-7.0.0.tgz", + "integrity": "sha512-MLK+fogW+pC3IWU9SFE+FRStvDVutwJMR5if1g7oBJx3qwmO69BNoJQVaMXq41R0gg3MzxVfwOGKx3i9P6sOLQ==", + "dev": true, + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.0.0" + } + }, + "node_modules/workbox-google-analytics": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-7.0.0.tgz", + "integrity": "sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==", + "deprecated": "It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained", + "dev": true, + "dependencies": { + "workbox-background-sync": "7.0.0", + "workbox-core": "7.0.0", + "workbox-routing": "7.0.0", + "workbox-strategies": "7.0.0" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-7.0.0.tgz", + "integrity": "sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==", + "dev": true, + "dependencies": { + "workbox-core": "7.0.0" + } + }, + "node_modules/workbox-precaching": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-7.0.0.tgz", + "integrity": "sha512-EC0vol623LJqTJo1mkhD9DZmMP604vHqni3EohhQVwhJlTgyKyOkMrZNy5/QHfOby+39xqC01gv4LjOm4HSfnA==", + "dev": true, + "dependencies": { + "workbox-core": "7.0.0", + "workbox-routing": "7.0.0", + "workbox-strategies": "7.0.0" + } + }, + "node_modules/workbox-range-requests": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-7.0.0.tgz", + "integrity": "sha512-SxAzoVl9j/zRU9OT5+IQs7pbJBOUOlriB8Gn9YMvi38BNZRbM+RvkujHMo8FOe9IWrqqwYgDFBfv6sk76I1yaQ==", + "dev": true, + "dependencies": { + "workbox-core": "7.0.0" + } + }, + "node_modules/workbox-recipes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-7.0.0.tgz", + "integrity": "sha512-DntcK9wuG3rYQOONWC0PejxYYIDHyWWZB/ueTbOUDQgefaeIj1kJ7pdP3LZV2lfrj8XXXBWt+JDRSw1lLLOnww==", + "dev": true, + "dependencies": { + "workbox-cacheable-response": "7.0.0", + "workbox-core": "7.0.0", + "workbox-expiration": "7.0.0", + "workbox-precaching": "7.0.0", + "workbox-routing": "7.0.0", + "workbox-strategies": "7.0.0" + } + }, + "node_modules/workbox-routing": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-7.0.0.tgz", + "integrity": "sha512-8YxLr3xvqidnbVeGyRGkaV4YdlKkn5qZ1LfEePW3dq+ydE73hUUJJuLmGEykW3fMX8x8mNdL0XrWgotcuZjIvA==", + "dev": true, + "dependencies": { + "workbox-core": "7.0.0" + } + }, + "node_modules/workbox-strategies": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-7.0.0.tgz", + "integrity": "sha512-dg3qJU7tR/Gcd/XXOOo7x9QoCI9nk74JopaJaYAQ+ugLi57gPsXycVdBnYbayVj34m6Y8ppPwIuecrzkpBVwbA==", + "dev": true, + "dependencies": { + "workbox-core": "7.0.0" + } + }, + "node_modules/workbox-streams": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-7.0.0.tgz", + "integrity": "sha512-moVsh+5to//l6IERWceYKGiftc+prNnqOp2sgALJJFbnNVpTXzKISlTIsrWY+ogMqt+x1oMazIdHj25kBSq/HQ==", + "dev": true, + "dependencies": { + "workbox-core": "7.0.0", + "workbox-routing": "7.0.0" + } + }, + "node_modules/workbox-sw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.0.0.tgz", + "integrity": "sha512-SWfEouQfjRiZ7GNABzHUKUyj8pCoe+RwjfOIajcx6J5mtgKkN+t8UToHnpaJL5UVVOf5YhJh+OHhbVNIHe+LVA==", + "dev": true + }, + "node_modules/workbox-window": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.0.0.tgz", + "integrity": "sha512-j7P/bsAWE/a7sxqTzXo3P2ALb1reTfZdvVp6OJ/uLr/C2kZAMvjeWGm8V4htQhor7DOvYg0sSbFN2+flT5U0qA==", + "dev": true, + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "7.0.0" + } }, "node_modules/wrap-ansi": { "version": "6.2.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -13086,11 +16808,11 @@ "@simplewebauthn/server": "^0.10.3", "@socket.io/redis-adapter": "^8.2.1", "base64url": "^3.0.1", - "bull": "^4.11.4", "connect-mongo": "^5.0.0", "express-session": "^1.17.3", "express-socket.io-session": "^1.3.5", "hbs": "^4.2.0", + "http-proxy-middleware": "^3.0.0", "mongoose": "^7.6.3", "redis": "^4.6.10", "reflect-metadata": "^0.1.13", @@ -13104,7 +16826,6 @@ "@types/express": "^4.17.18", "@types/express-session": "^1.17.8", "@types/express-socket.io-session": "^1.3.7", - "@types/hbs": "^4.0.2", "@types/jest": "^29.5.5", "@types/node": "^20.7.0", "@types/supertest": "^2.0.13", @@ -13140,10 +16861,12 @@ "@liquid/auth-client": "^1.0.0", "@mui/icons-material": "^5.15.4", "@mui/material": "^5.15.4", - "@tanstack/react-query": "^5.17.15", + "@tanstack/react-query": "^5.20.5", + "@tanstack/react-query-devtools": "^5.20.5", "qr-code-styling": "^1.6.0-rc.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.22.3", "reflect-metadata": "^0.1.13", "socket.io-client": "^4.7.4", "zustand": "^4.4.7" @@ -13158,8 +16881,12 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", "mkdirp": "^3.0.1", + "sharp": "^0.33.2", + "svgo": "^3.2.0", "typescript": "^5.2.2", - "vite": "^5.0.8" + "vite": "^5.0.8", + "vite-plugin-image-optimizer": "^1.1.7", + "vite-plugin-pwa": "^0.18.1" } }, "sites/dapp-ui/node_modules/mkdirp": { diff --git a/services/liquid-auth-api-js/package.json b/services/liquid-auth-api-js/package.json index 2451a52..2862891 100644 --- a/services/liquid-auth-api-js/package.json +++ b/services/liquid-auth-api-js/package.json @@ -36,11 +36,11 @@ "@simplewebauthn/server": "^0.10.3", "@socket.io/redis-adapter": "^8.2.1", "base64url": "^3.0.1", - "bull": "^4.11.4", "connect-mongo": "^5.0.0", "express-session": "^1.17.3", "express-socket.io-session": "^1.3.5", "hbs": "^4.2.0", + "http-proxy-middleware": "^3.0.0", "mongoose": "^7.6.3", "redis": "^4.6.10", "reflect-metadata": "^0.1.13", @@ -54,7 +54,6 @@ "@types/express": "^4.17.18", "@types/express-session": "^1.17.8", "@types/express-socket.io-session": "^1.3.7", - "@types/hbs": "^4.0.2", "@types/jest": "^29.5.5", "@types/node": "^20.7.0", "@types/supertest": "^2.0.13", diff --git a/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts b/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts index 8f4c4f1..801d3bf 100644 --- a/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts +++ b/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts @@ -14,7 +14,7 @@ export class RedisIoAdapter extends IoAdapter { private readonly sessionHandler: RequestHandler; private adapterConstructor: ReturnType; private pubClient: Redis; - private subClient: Redis; + public subClient: Redis; constructor(app: NestExpressApplication, sessionHandler: RequestHandler) { super(app); diff --git a/services/liquid-auth-api-js/src/android/android.controller.spec.ts b/services/liquid-auth-api-js/src/android/android.controller.spec.ts new file mode 100644 index 0000000..0ea9570 --- /dev/null +++ b/services/liquid-auth-api-js/src/android/android.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AndroidController } from './android.controller'; + +describe('AndroidController', () => { + let controller: AndroidController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [AndroidController], + }).compile(); + + controller = module.get(AndroidController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/services/liquid-auth-api-js/src/android/android.controller.ts b/services/liquid-auth-api-js/src/android/android.controller.ts new file mode 100644 index 0000000..99255a8 --- /dev/null +++ b/services/liquid-auth-api-js/src/android/android.controller.ts @@ -0,0 +1,56 @@ +import { Controller, Get, Logger, Req, Res } from '@nestjs/common'; +import type { Response } from 'express'; +import assetLinks from '../../assetlinks.json' assert { type: 'json' }; +@Controller('.well-known') +export class AndroidController { + private readonly logger = new Logger(AndroidController.name); + + /** + * Well-Known Asset Links + * + * @see https://developer.android.com/training/app-links/verify-android-applinks + * @param req + * @param res + * + */ + @Get('/assetlinks.json') + assetLinks(@Req() req: Request, @Res() res: Response) { + this.logger.debug( + `GET /.well-known/assetlinks.json ${req.headers['user-agent']}`, + ); + // In Development, allow for overriding the asset links + if (process.env.NODE_ENV === 'development') { + const relation = [ + 'delegate_permission/common.handle_all_urls', + 'delegate_permission/common.get_login_creds', + ]; + if (!assetLinks.some((al) => al.target.site === process.env.ORIGIN)) { + assetLinks.push({ + relation, + target: { + namespace: 'web', + site: process.env.ORIGIN, + }, + }); + } + + if ( + process.env.ANDROID_PACKAGENAME && + process.env.ANDROID_SHA256HASH && + !assetLinks.some( + (al) => al.target.package_name === process.env.ANDROID_PACKAGENAME, + ) + ) { + assetLinks.push({ + relation, + target: { + namespace: 'android_app', + package_name: process.env.ANDROID_PACKAGENAME, + sha256_cert_fingerprints: [process.env.ANDROID_SHA256HASH], + }, + }); + } + } + res.json(assetLinks); + } +} diff --git a/services/liquid-auth-api-js/src/app.controller.ts b/services/liquid-auth-api-js/src/app.controller.ts index 18c7c52..04cdd79 100644 --- a/services/liquid-auth-api-js/src/app.controller.ts +++ b/services/liquid-auth-api-js/src/app.controller.ts @@ -1,58 +1,10 @@ import { Controller, Get, Logger, Req, Res, Session } from '@nestjs/common'; import type { Response } from 'express'; -import assetLinks from '../assetlinks.json' assert { type: 'json' } +import assetLinks from '../assetlinks.json' assert { type: 'json' }; @Controller() export class AppController { private readonly logger = new Logger(AppController.name); - /** - * Well-Known Asset Links - * - * - * @see https://developer.android.com/training/app-links/verify-android-applinks - * @param req - * @param res - * - */ - @Get('/.well-known/assetlinks.json') - assetLinks(@Req() req: Request, @Res() res: Response) { - this.logger.debug( - `GET /.well-known/assetlinks.json ${req.headers['user-agent']}`, - ); - // In Development, allow for overriding the asset links - if(process.env.NODE_ENV === 'development'){ - const relation = [ - 'delegate_permission/common.handle_all_urls', - 'delegate_permission/common.get_login_creds', - ]; - if(!assetLinks.some((al)=>al.target.site === process.env.ORIGIN)) { - assetLinks.push({ - relation, - target: { - namespace: 'web', - site: process.env.ORIGIN, - }, - }); - } - - if ( - process.env.ANDROID_PACKAGENAME && - process.env.ANDROID_SHA256HASH && - !assetLinks.some((al)=>al.target.package_name === process.env.ANDROID_PACKAGENAME) - ) { - assetLinks.push({ - relation, - target: { - namespace: 'android_app', - package_name: process.env.ANDROID_PACKAGENAME, - sha256_cert_fingerprints: [process.env.ANDROID_SHA256HASH], - }, - }); - } - } - res.json(assetLinks); - } - /** * Serve the SPA * @@ -67,9 +19,9 @@ export class AppController { @Session() session: Record, ) { session.active = true; - this.logger.log( - `GET / Render for Session: ${session.id} UA: ${req.headers['user-agent']}`, - ); - res.render('index'); + this.logger.log( + `GET / Render for Session: ${session.id} UA: ${req.headers['user-agent']}`, + ); + res.render('index'); } } diff --git a/services/liquid-auth-api-js/src/app.module.ts b/services/liquid-auth-api-js/src/app.module.ts index 4d5fb7c..3f498d1 100644 --- a/services/liquid-auth-api-js/src/app.module.ts +++ b/services/liquid-auth-api-js/src/app.module.ts @@ -1,13 +1,22 @@ import { Module } from '@nestjs/common'; -import { AuthModule } from './auth/auth.module.js'; -import { AppController } from './app.controller.js'; + import { MongooseModule } from '@nestjs/mongoose'; import { ConfigModule, ConfigService } from '@nestjs/config'; + +import configuration from './config/configuration.js'; + +// FIDO import { AttestationModule } from './attestation/attestation.module.js'; import { AssertionModule } from './assertion/assertion.module.js'; -import { AppService } from './app.service.js'; + +import { AndroidController } from './android/android.controller.js'; +// User Endpoints +import { AuthModule } from './auth/auth.module.js'; + +// Connect/Signals import { ConnectModule } from './connect/connect.module.js'; -import configuration from './config/configuration.js'; +import { SignalsModule } from './signals/signals.module.js'; +import { AppController } from "./app.controller.js"; @Module({ imports: [ @@ -17,7 +26,6 @@ import configuration from './config/configuration.js'; useFactory: async (configService: ConfigService) => { const database = configService.get('database'); const { host, username, password, name, atlas: isAtlas } = database; - //mongodb+srv://algorand:@fido2.ccg8rav.mongodb.net/?retryWrites=true&w=majority const uri = `mongodb${ isAtlas ? '+srv' : '' }://${username}:${password}@${host}/${name}?authSource=admin&retryWrites=true&w=majority`; @@ -31,8 +39,8 @@ import configuration from './config/configuration.js'; AttestationModule, AssertionModule, ConnectModule, + SignalsModule, ], - controllers: [AppController], - providers: [AppService], + controllers: [AndroidController, AppController], }) export class AppModule {} diff --git a/services/liquid-auth-api-js/src/assertion/assertion.module.ts b/services/liquid-auth-api-js/src/assertion/assertion.module.ts index a4790e9..af4e9cb 100644 --- a/services/liquid-auth-api-js/src/assertion/assertion.module.ts +++ b/services/liquid-auth-api-js/src/assertion/assertion.module.ts @@ -7,11 +7,15 @@ import { MongooseModule } from '@nestjs/mongoose'; import { User, UserSchema } from '../auth/auth.schema.js'; import { AppService } from '../app.service.js'; import { ClientsModule, Transport } from '@nestjs/microservices'; +import { Session, SessionSchema } from '../auth/session.schema.js'; @Module({ imports: [ ConfigModule, - MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), + MongooseModule.forFeature([ + { name: User.name, schema: UserSchema }, + { name: Session.name, schema: SessionSchema }, + ]), ClientsModule.register([ { name: 'ACCOUNT_LINK_SERVICE', diff --git a/services/liquid-auth-api-js/src/attestation/attestation.module.ts b/services/liquid-auth-api-js/src/attestation/attestation.module.ts index 427e368..785c3ad 100644 --- a/services/liquid-auth-api-js/src/attestation/attestation.module.ts +++ b/services/liquid-auth-api-js/src/attestation/attestation.module.ts @@ -7,11 +7,15 @@ import { AppService } from '../app.service.js'; import { MongooseModule } from '@nestjs/mongoose'; import { User, UserSchema } from '../auth/auth.schema.js'; import { ClientsModule, Transport } from '@nestjs/microservices'; +import { Session, SessionSchema } from '../auth/session.schema.js'; @Module({ imports: [ ConfigModule, - MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), + MongooseModule.forFeature([ + { name: User.name, schema: UserSchema }, + { name: Session.name, schema: SessionSchema }, + ]), ClientsModule.register([ { name: 'ACCOUNT_LINK_SERVICE', diff --git a/services/liquid-auth-api-js/src/auth/auth.controller.ts b/services/liquid-auth-api-js/src/auth/auth.controller.ts index d830bde..bd82d26 100644 --- a/services/liquid-auth-api-js/src/auth/auth.controller.ts +++ b/services/liquid-auth-api-js/src/auth/auth.controller.ts @@ -78,41 +78,6 @@ export class AuthController { delete session.requestId; res.redirect(302, '/'); } - /** - * Create Session / Login - * - * @remarks - * Post credentials to the server, creates a new credential if it does not exist. - * If this route has not been called, the application should not allow access to private - * routes - * - * @param session - The session object - * @param userLoginDto - The credentials to post - * @param res - The response object - */ - @Post('/session') - async create( - @Session() session: Record, - @Body() userLoginDto: LoginRequestDTO, - @Res() res: Response, - ) { - if ( - typeof userLoginDto.wallet !== 'string' || - userLoginDto.wallet.length !== 58 - ) { - res - .status(400) - .json({ reason: 'invalid_input', error: 'Invalid wallet' }); - } else { - try { - const user = await this.authService.init(userLoginDto.wallet); - session.wallet = user.wallet; - res.json(user); - } catch (e) { - res.status(500).json({ error: e.message }); - } - } - } /** * Read Session * @@ -120,8 +85,18 @@ export class AuthController { */ @Get('/session') async read(@Session() session: Record) { - session.connected = true; const user = await this.authService.find(session.wallet); - return user || {}; + return ( + { + user: user + ? { + id: user.id, + wallet: user.wallet, + credentials: user.credentials, + } + : null, + session, + } || {} + ); } } diff --git a/services/liquid-auth-api-js/src/auth/auth.guard.ts b/services/liquid-auth-api-js/src/auth/auth.guard.ts index dbf3102..b29a387 100644 --- a/services/liquid-auth-api-js/src/auth/auth.guard.ts +++ b/services/liquid-auth-api-js/src/auth/auth.guard.ts @@ -7,9 +7,11 @@ export class AuthGuard implements CanActivate { context: ExecutionContext, ): boolean | Promise | Observable { const request = context.switchToHttp().getRequest(); + const session = request.session || request.handshake.session; return ( - typeof request.session.wallet === 'string' && - request.session.wallet.length === 58 + typeof session !== 'undefined' && + typeof session.wallet === 'string' && + session.wallet.length === 58 ); } } diff --git a/services/liquid-auth-api-js/src/auth/auth.module.ts b/services/liquid-auth-api-js/src/auth/auth.module.ts index 68730ee..6d389c1 100644 --- a/services/liquid-auth-api-js/src/auth/auth.module.ts +++ b/services/liquid-auth-api-js/src/auth/auth.module.ts @@ -3,10 +3,14 @@ import { AuthController } from './auth.controller.js'; import { AuthService } from './auth.service.js'; import { MongooseModule } from '@nestjs/mongoose'; import { User, UserSchema } from './auth.schema.js'; +import { Session, SessionSchema } from './session.schema.js'; @Module({ imports: [ - MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), + MongooseModule.forFeature([ + { name: User.name, schema: UserSchema }, + { name: Session.name, schema: SessionSchema }, + ]), ], controllers: [AuthController], providers: [AuthService], diff --git a/services/liquid-auth-api-js/src/auth/auth.service.ts b/services/liquid-auth-api-js/src/auth/auth.service.ts index 6545c2c..c598116 100644 --- a/services/liquid-auth-api-js/src/auth/auth.service.ts +++ b/services/liquid-auth-api-js/src/auth/auth.service.ts @@ -1,15 +1,20 @@ import * as crypto from 'node:crypto'; -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import base64url from 'base64url'; import type { FilterQuery } from 'mongoose'; import { Credential, User } from './auth.schema.js'; +import { Session } from './session.schema.js'; @Injectable() export class AuthService { - constructor(@InjectModel(User.name) private userModel: Model) {} + private readonly logger = new Logger(AuthService.name); + constructor( + @InjectModel(User.name) private userModel: Model, + @InjectModel(Session.name) private sessionModel: Model, + ) {} /** * Initialize a User @@ -63,7 +68,9 @@ export class AuthService { return this.userModel.findOneAndUpdate({ id: user.id }, user).exec(); } async findCredential(credId: string) { - const user = await this.userModel.findOne({ 'credentials.credId': credId }).exec(); + const user = await this.userModel + .findOne({ 'credentials.credId': credId }) + .exec(); if (user) { return user.credentials.find((cred) => cred.credId === credId); } @@ -92,4 +99,35 @@ export class AuthService { async all() { return this.userModel.find({}).exec(); } + + /** + * Find a Session by ID + * + * @param sid - Session ID + */ + async findSession(sid: string): Promise { + this.logger.log(`Finding session ${sid}`); + return this.sessionModel.findOne({ _id: sid }).exec(); + } + + /** + * Update Wallet by Session ID + * @param session - The stored Session + * @param wallet - The Wallet Address + */ + async updateSessionWallet( + session: Session, + wallet: string, + ): Promise { + const data = JSON.parse(session.session); + data.wallet = wallet; + return this.sessionModel + .findOneAndUpdate( + { _id: session._id }, + { + session: JSON.stringify(data), + }, + ) + .exec(); + } } diff --git a/services/liquid-auth-api-js/src/connect/session.schema.ts b/services/liquid-auth-api-js/src/auth/session.schema.ts similarity index 100% rename from services/liquid-auth-api-js/src/connect/session.schema.ts rename to services/liquid-auth-api-js/src/auth/session.schema.ts diff --git a/services/liquid-auth-api-js/src/connect/connect.dto.ts b/services/liquid-auth-api-js/src/connect/connect.dto.ts new file mode 100644 index 0000000..0c10c2f --- /dev/null +++ b/services/liquid-auth-api-js/src/connect/connect.dto.ts @@ -0,0 +1,15 @@ +export type RTCIceCandidateDto = { + address: string; + candidate: string; + component: string; + foundation: string; + port: number; + priority: number; + protocol: string; + relatedAddress: string; + relatedPort: number; + sdpMid: string; + sdpMLineIndex: number; + tcpType: string; + usernameFragment?: string; +}; diff --git a/services/liquid-auth-api-js/src/connect/connect.gateway.ts b/services/liquid-auth-api-js/src/connect/connect.gateway.ts index 1277490..eccfc7c 100644 --- a/services/liquid-auth-api-js/src/connect/connect.gateway.ts +++ b/services/liquid-auth-api-js/src/connect/connect.gateway.ts @@ -1,104 +1,58 @@ import type { Handshake, Server, Socket } from 'socket.io'; -import Redis from 'ioredis'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { ConnectedSocket, MessageBody, + OnGatewayConnection, + OnGatewayInit, SubscribeMessage, WebSocketGateway, - WebSocketServer, } from '@nestjs/websockets'; -import { SessionService } from './session.service.js'; -import { Logger, Session, Req, Res } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import {Request, Response} from 'express' +import { Logger } from '@nestjs/common'; +import { AuthService } from '../auth/auth.service.js'; +import { RedisIoAdapter } from '../adapters/redis-io.adapter'; @WebSocketGateway({ cors: { origin: '*', }, }) -export class ConnectGateway { - private subClient: Redis; +export class ConnectGateway implements OnGatewayInit, OnGatewayConnection { + private ioAdapter: RedisIoAdapter; private readonly logger = new Logger(ConnectGateway.name); + constructor(private authService: AuthService) {} - constructor( - private configService: ConfigService, - private sessionService: SessionService, - ) { - this.subClient = new Redis({ - host: configService.get('socket.host'), - port: configService.get('socket.port'), - username: configService.get('socket.username'), - password: configService.get('socket.password'), - lazyConnect: true, - }); - // Connect to the redis feed - if (this.subClient.status !== 'ready') { - this.subClient.connect(); - } + /** + * Initialize the Gateway + * + * Pulls the RedisIoAdapter instance from the server + * + * @param server + */ + afterInit(server: Server) { + this.ioAdapter = server.sockets.adapter as unknown as RedisIoAdapter; } - @WebSocketServer() - server: Server; - handleConnection(@Session() session: Record) { - session.connected = true; - this.logger.debug(`WSS / Client Connected with Session: ${session.id}`); - } - @SubscribeMessage('hello') - async hello(@MessageBody() data: string, @ConnectedSocket() client: Socket) { + /** + * Handle Connection + * + * Automatically join the client to the public key's room + * + * @param client + */ + async handleConnection(client: Socket) { const handshake = client.handshake as Handshake; - const session = await this.sessionService.find(handshake.sessionID); - console.log('Hello triggered', session); - return data; - } - @SubscribeMessage('wait') - async waitOnRegistration( - @Res() res: Response, - @Req() req: Request, - @Session() sessionz: Record, - @ConnectedSocket() client: Socket, - @MessageBody() body: { wallet: string }, - ) { - console.log('waiting', sessionz); - const handshake = (req as any).handshake as Handshake; - console.log(sessionz, ((req as any).handshake as Handshake).sessionID); - // Find the stored session - const sessionRecord = await this.sessionService.find(handshake.sessionID); - - console.log(sessionRecord); - - if(sessionRecord) { - const session = JSON.parse(sessionRecord.session); - console.log(session) - - } - // TODO: restrict to session - this.subClient.subscribe('auth-interaction'); - - // Handle messages - const obs$: Observable = new Observable((observer) => { - this.subClient.on('message', async (channel, eventMessage) => { - console.log(eventMessage); - const { data } = JSON.parse(eventMessage); - console.log(body.wallet, data.wallet); - if (body.wallet === data.wallet) { - observer.next(data); - // this.subClient.disconnect(); - observer.complete(); - } - }); - }); - return obs$.pipe( - map((obs$) => ({ - data: { - device: obs$.credential.device, - credId: obs$.credential.credId, - wallet: obs$.wallet, - }, - })), + const session = handshake.session as Record; + this.logger.debug( + `(*) Client Connected with Session: ${handshake.sessionID}${ + session.wallet ? ` and PublicKey: ${session.wallet}` : '' + }`, ); + if (typeof session.wallet === 'string') { + this.logger.debug(`(*) Client Joining Room ${session.wallet}`); + await client.join(session.wallet); + } } /** * On Link Connection, wait for the wallet to connect @@ -107,36 +61,45 @@ export class ConnectGateway { */ @SubscribeMessage('link') async linkAccount( - @Req() req: Request, - @Session() sessionz: Record, @ConnectedSocket() client: Socket, @MessageBody() body: { requestId: string | number }, ): Promise< Observable<{ data: { requestId: string | number; wallet: string } }> > { - console.log(sessionz, ((req as any).handshake as Handshake).sessionID); const handshake = client.handshake as Handshake; this.logger.debug( - `WSS / Event: link for Session: ${handshake.sessionID} with RequestId: ${body.requestId}`, + `(link): link for Session: ${handshake.sessionID} with RequestId: ${body.requestId}`, ); // Find the stored session - const session = await this.sessionService.find(handshake.sessionID); - if(session){ - this.subClient.subscribe('auth'); + const session = await this.authService.findSession(handshake.sessionID); + console.log('Session', session); + if (session) { + await this.ioAdapter.subClient.subscribe('auth'); // Handle messages const obs$: Observable = new Observable((observer) => { - this.subClient.on('message', async (channel, eventMessage) => { + const handleAuthMessage = async (channel, eventMessage)=> { + console.log('Link->Message', channel, eventMessage); const { data } = JSON.parse(eventMessage); console.log(body.requestId, data.requestId, data, body); if (body.requestId === data.requestId) { - await this.sessionService.updateWallet(session, data.wallet); + this.logger.debug( + `(*) Linking Wallet: ${data.wallet} to Session: ${handshake.sessionID}`, + ); + await this.authService.updateSessionWallet(session, data.wallet); + this.logger.debug(`(*) Joining Room: ${data.wallet}`); + await client.join(data.wallet); observer.next(data); - // this.subClient.disconnect(); + this.ioAdapter.subClient.off('message', handleAuthMessage); observer.complete(); } - }); + } + + this.ioAdapter.subClient.on( + 'message', + handleAuthMessage, + ); }); return obs$.pipe( map((obs$) => ({ @@ -148,6 +111,5 @@ export class ConnectGateway { })), ); } - } } diff --git a/services/liquid-auth-api-js/src/connect/connect.module.ts b/services/liquid-auth-api-js/src/connect/connect.module.ts index 93c195d..97a445c 100644 --- a/services/liquid-auth-api-js/src/connect/connect.module.ts +++ b/services/liquid-auth-api-js/src/connect/connect.module.ts @@ -1,11 +1,13 @@ import { Module } from '@nestjs/common'; -import { ConnectController } from './connect.controller.js'; -import { ConnectGateway } from './connect.gateway.js'; import { ClientsModule, Transport } from '@nestjs/microservices'; import { MongooseModule } from '@nestjs/mongoose'; -import { Session, SessionSchema } from './session.schema.js'; -import { SessionService } from './session.service.js'; + +// Connect +import { ConnectController } from './connect.controller.js'; +import { ConnectGateway } from './connect.gateway.js'; +// Auth import { AuthService } from '../auth/auth.service.js'; +import { Session, SessionSchema } from '../auth/session.schema.js'; import { User, UserSchema } from '../auth/auth.schema.js'; @Module({ @@ -29,6 +31,6 @@ import { User, UserSchema } from '../auth/auth.schema.js'; ]), ], controllers: [ConnectController], - providers: [AuthService, SessionService, ConnectGateway], + providers: [AuthService, ConnectGateway], }) export class ConnectModule {} diff --git a/services/liquid-auth-api-js/src/connect/session.service.ts b/services/liquid-auth-api-js/src/connect/session.service.ts deleted file mode 100644 index 323ed6e..0000000 --- a/services/liquid-auth-api-js/src/connect/session.service.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { InjectModel } from '@nestjs/mongoose'; -import { Model } from 'mongoose'; - -import { Session } from './session.schema.js'; - -/** - * Session Service - * - * Bypass Nest.js/Express opinions and directly modify the SessionStore - */ -@Injectable() -export class SessionService { - private readonly logger = new Logger(SessionService.name); - /** - * Construct SessionService - * @param sessionModel - Session Mongoose Model - */ - constructor( - @InjectModel(Session.name) private sessionModel: Model, - ) {} - - /** - * Find a Session by ID - * - * @param sid - Session ID - */ - async find(sid: string): Promise { - this.logger.log(`Finding session ${sid}`); - return this.sessionModel.findOne({ _id: sid }).exec(); - } - - /** - * Update Wallet by Session ID - * @param session - The stored Session - * @param wallet - The Wallet Address - */ - async updateWallet(session: Session, wallet: string): Promise { - console.log(`!!!!!!!!!!!${session}`) - const data = JSON.parse(session.session); - data.wallet = wallet; - return this.sessionModel - .findOneAndUpdate( - { _id: session._id }, - { - session: JSON.stringify(data), - }, - ) - .exec(); - } -} diff --git a/services/liquid-auth-api-js/src/main.ts b/services/liquid-auth-api-js/src/main.ts index 01ee257..da569f2 100644 --- a/services/liquid-auth-api-js/src/main.ts +++ b/services/liquid-auth-api-js/src/main.ts @@ -1,43 +1,52 @@ import 'dotenv/config'; -import { resolve } from 'node:path'; import { HttpAdapterHost, NestFactory } from '@nestjs/core'; import type { NestExpressApplication } from '@nestjs/platform-express'; -import session from 'express-session'; -import hbs from 'hbs'; -import MongoStore from 'connect-mongo'; +import { ConfigService } from '@nestjs/config'; + +// Application import { AppModule } from './app.module.js'; import { RedisIoAdapter } from './adapters/redis-io.adapter.js'; -import { ConfigService } from '@nestjs/config'; + +// Session +import session from 'express-session'; +import MongoStore from 'connect-mongo'; + +// Sentry import * as Sentry from '@sentry/node'; import { ProfilingIntegration } from '@sentry/profiling-node'; import { SentryFilter } from './sentry.filter.js'; +import { createProxyMiddleware } from 'http-proxy-middleware'; +import { resolve } from 'path'; +import hbs from 'hbs'; async function bootstrap() { const app = await NestFactory.create(AppModule, { logger: ['error', 'warn', 'debug', 'log', 'verbose'], }); - Sentry.init({ - dsn: process.env.SENTRY_DNS, - integrations: [ - // enable HTTP calls tracing - new Sentry.Integrations.Http({ tracing: true }), - new ProfilingIntegration(), - ], - // Performance Monitoring - tracesSampleRate: 1.0, - // Set sampling rate for profiling - this is relative to tracesSampleRate - profilesSampleRate: 1.0, - }); - const { httpAdapter } = app.get(HttpAdapterHost); - app.useGlobalFilters(new SentryFilter(httpAdapter)); - const config = app.get(ConfigService); - const env = config.get('env'); + const isSentryEnabled = + config.get('sentry') || typeof process.env.SENTRY_DNS !== 'undefined'; + if (isSentryEnabled) { + Sentry.init({ + dsn: process.env.SENTRY_DNS, + integrations: [ + // enable HTTP calls tracing + new Sentry.Integrations.Http({ tracing: true }), + new ProfilingIntegration(), + ], + // Performance Monitoring + tracesSampleRate: 1.0, + // Set sampling rate for profiling - this is relative to tracesSampleRate + profilesSampleRate: 1.0, + }); + const { httpAdapter } = app.get(HttpAdapterHost); + app.useGlobalFilters(new SentryFilter(httpAdapter)); + } const username = config.get('database.username'); const host = config.get('database.host'); const password = config.get('database.password'); const name = config.get('database.name'); - const isAtlas = config.get('database.atlas') + const isAtlas = config.get('database.atlas'); const uri = `mongodb${ isAtlas ? '+srv' : '' }://${username}:${password}@${host}/${name}?authSource=admin&retryWrites=true&w=majority`; @@ -47,10 +56,12 @@ async function bootstrap() { ttl: 20000, }); + app.enableCors(); + const sessionHandler = session({ secret: 'my-secret', + saveUninitialized: true, resave: false, - saveUninitialized: false, cookie: { httpOnly: true, secure: false, // TODO: Secure the cookie @@ -70,4 +81,4 @@ async function bootstrap() { await app.listen(process.env.PORT || 3000); } -bootstrap(); +await bootstrap(); diff --git a/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts b/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts new file mode 100644 index 0000000..b65e399 --- /dev/null +++ b/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SignalsGateway } from './signals.gateway'; + +describe('SignalsGateway', () => { + let gateway: SignalsGateway; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [SignalsGateway], + }).compile(); + + gateway = module.get(SignalsGateway); + }); + + it('should be defined', () => { + expect(gateway).toBeDefined(); + }); +}); diff --git a/services/liquid-auth-api-js/src/signals/signals.gateway.ts b/services/liquid-auth-api-js/src/signals/signals.gateway.ts new file mode 100644 index 0000000..346e4a2 --- /dev/null +++ b/services/liquid-auth-api-js/src/signals/signals.gateway.ts @@ -0,0 +1,65 @@ +import { + ConnectedSocket, + MessageBody, + SubscribeMessage, + WebSocketGateway, + WebSocketServer, +} from '@nestjs/websockets'; +import { Logger, UseInterceptors } from '@nestjs/common'; +import type { Handshake, Server, Socket } from 'socket.io'; +import { SignalsInterceptor } from './signals.interceptor.js'; + +@WebSocketGateway() +@UseInterceptors(SignalsInterceptor) +export class SignalsGateway { + @WebSocketServer() + server: Server; + private readonly logger = new Logger(SignalsGateway.name); + + @SubscribeMessage('call-candidate') + onCallCandidate( + @MessageBody() + data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, + @ConnectedSocket() client: Socket, + ) { + this.logger.debug(`(call-candidate): ${data}`); + const handshake = client.handshake as Handshake; + const session = handshake.session as Record; + this.server.in(session.wallet).emit('call-candidate', data); + } + @SubscribeMessage('call-description') + async onCallDescription( + @MessageBody() data: string, + @ConnectedSocket() client: Socket, + ) { + this.logger.log(`(call-description): ${data}`); + + // Session from the initial Handshake + const handshake = client.handshake as Handshake; + const session = handshake.session as Record; + + // Send description to all clients in the public key's room + this.server.in(session.wallet).emit('call-description', data); + } + @SubscribeMessage('answer-description') + async onAnswerDescription( + @MessageBody() data: string, + @ConnectedSocket() client: Socket, + ) { + this.logger.log(`(answer-description): ${data}`); + const handshake = client.handshake as Handshake; + const session = handshake.session as Record; + this.server.in(session.wallet).emit('answer-description', data); + } + @SubscribeMessage('answer-candidate') + onAnswerCandidate( + @MessageBody() + data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, + @ConnectedSocket() client: Socket, + ) { + this.logger.debug(`(answer-candidate): ${data}`); + const handshake = client.handshake as Handshake; + const session = handshake.session as Record; + this.server.in(session.wallet).emit('answer-candidate', data); + } +} diff --git a/services/liquid-auth-api-js/src/signals/signals.interceptor.spec.ts b/services/liquid-auth-api-js/src/signals/signals.interceptor.spec.ts new file mode 100644 index 0000000..a7e0b1e --- /dev/null +++ b/services/liquid-auth-api-js/src/signals/signals.interceptor.spec.ts @@ -0,0 +1,7 @@ +import { SignalsInterceptor } from './signals.interceptor'; + +describe('ConnectInterceptor', () => { + it('should be defined', () => { + expect(new SignalsInterceptor()).toBeDefined(); + }); +}); diff --git a/services/liquid-auth-api-js/src/signals/signals.interceptor.ts b/services/liquid-auth-api-js/src/signals/signals.interceptor.ts new file mode 100644 index 0000000..79d6062 --- /dev/null +++ b/services/liquid-auth-api-js/src/signals/signals.interceptor.ts @@ -0,0 +1,31 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + Logger, + NestInterceptor, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { type Handshake, Socket } from 'socket.io'; + +@Injectable() +export class SignalsInterceptor implements NestInterceptor { + private readonly logger = new Logger(SignalsInterceptor.name); + async intercept( + context: ExecutionContext, + next: CallHandler, + ): Promise> { + const client = context.switchToWs().getClient() as Socket; + const handshake = client.handshake as Handshake; + const session = handshake.session as Record; + + if (typeof session.wallet !== 'string') { + this.logger.error( + `(*) Client ${handshake.sessionID} is not authenticated`, + ); + client.disconnect(); + } else { + return next.handle(); + } + } +} diff --git a/services/liquid-auth-api-js/src/signals/signals.module.ts b/services/liquid-auth-api-js/src/signals/signals.module.ts new file mode 100644 index 0000000..3014b61 --- /dev/null +++ b/services/liquid-auth-api-js/src/signals/signals.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { SignalsGateway } from './signals.gateway.js'; +import { ClientsModule, Transport } from "@nestjs/microservices"; + +@Module({ + imports: [ + // TODO: inject configuration + ClientsModule.register([ + { + name: 'ACCOUNT_LINK_SERVICE', + transport: Transport.REDIS, + options: { + host: process.env.REDIS_HOST || 'localhost', + port: parseInt(process.env.REDIS_PORT, 10) || 6379, + username: process.env.REDIS_USERNAME || 'default', + password: process.env.REDIS_PASSWORD || '', + }, + }, + ]), + ], + providers: [SignalsGateway], +}) +export class SignalsModule {} diff --git a/sites/dapp-ui/package.json b/sites/dapp-ui/package.json index 13723b6..9eb0065 100644 --- a/sites/dapp-ui/package.json +++ b/sites/dapp-ui/package.json @@ -24,6 +24,7 @@ "qr-code-styling": "^1.6.0-rc.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.22.3", "reflect-metadata": "^0.1.13", "socket.io-client": "^4.7.4", "zustand": "^4.4.7" diff --git a/sites/dapp-ui/public/logo-inverted.png b/sites/dapp-ui/public/logo-inverted.png new file mode 100644 index 0000000000000000000000000000000000000000..ec175007b4dfa357b8618fa77fd83ad412dd1bbf GIT binary patch literal 15926 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 EKgobfdjJ3c literal 0 HcmV?d00001 diff --git a/sites/dapp-ui/src/App.tsx b/sites/dapp-ui/src/App.tsx index 25964f5..e33c2ae 100644 --- a/sites/dapp-ui/src/App.tsx +++ b/sites/dapp-ui/src/App.tsx @@ -1,25 +1,51 @@ import {useContext, ReactElement, useState, useMemo, useEffect} from 'react'; -import { ColorModeContext, SnackbarContext, StateContext } from './Contexts'; +import { ColorModeContext, DataChannelContext, PeerConnectionContext, SnackbarContext, StateContext } from './Contexts'; import Layout from './Layout'; import { GetStartedCard } from './pages/home/GetStarted'; import { WaitForRegistrationCard } from './pages/dashboard/WaitForRegistration'; import { RegisteredCard } from './pages/dashboard/Registered'; -import {CircularProgress, createTheme, CssBaseline} from '@mui/material'; +import {createTheme, CssBaseline} from '@mui/material'; import { DEFAULT_THEME } from './theme.tsx'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; import { ThemeProvider } from '@emotion/react'; import {ReactQueryDevtools} from "@tanstack/react-query-devtools"; +import {DebugWebRTC} from "./pages/debug/WebRTC.tsx"; - +import { + createHashRouter, + RouterProvider +} from "react-router-dom"; +import { WaitForPeersCard } from "./pages/peering/WaitForPeers.tsx"; const queryClient = new QueryClient() +const DEFAULT_CONFIG: RTCConfiguration = { + iceServers: [ + { + urls: [ + 'stun:stun.l.google.com:19302', + ], + }, + ], + iceCandidatePoolSize: 10, +}; + +const router = createHashRouter([ + {'path': '/', 'element': }, + {'path': '/peering', 'element': }, + {'path': '/connected', 'element': }, + {'path': '/registered', 'element': }, + {'path': '/debug/webrtc', 'element': }, +]) export default function ProviderApp(){ const [open, setOpen] = useState(false) const [message, setMessage] = useState('') - const [state, setState] = useState('start') + const [state, setState] = useState('peering') + const [dataChannel, setDataChannel] = useState(null) + const peerConnection = useMemo(()=>new RTCPeerConnection(DEFAULT_CONFIG), []); + globalThis.peerConnection = peerConnection; const [mode, setMode] = useState<'light' | 'dark'>(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)') ? 'dark' : 'light'); const colorMode = useMemo( () => ({ @@ -48,7 +74,13 @@ export default function ProviderApp(){ - + + + + + + + @@ -57,21 +89,3 @@ export default function ProviderApp(){ ) } - -export function App() { - const { state } = useContext(StateContext); - - // Authentication Steps - const STATES: { [k: string]: () => ReactElement } = { - 'start': GetStartedCard, - 'connected': WaitForRegistrationCard, - 'registered': RegisteredCard, - }; - const Content = STATES[state]; - - return ( - - - - ); -} diff --git a/sites/dapp-ui/src/Contexts.tsx b/sites/dapp-ui/src/Contexts.tsx index 579652b..79b5d8c 100644 --- a/sites/dapp-ui/src/Contexts.tsx +++ b/sites/dapp-ui/src/Contexts.tsx @@ -2,9 +2,12 @@ import {createContext} from "react"; export const ColorModeContext = createContext({toggle: () => {}}); -export const StateContext = createContext({state: 'start', setState: (_: string) => {}}); +export const StateContext = createContext({state: 'debug', setState: (_: string) => {}}); export const SnackbarContext = createContext({ open: false, setOpen: (_: boolean) => {}, message: '', setMessage: (_: string) => {} }); + +export {DataChannelContext} from './hooks/useDataChannel.ts'; +export {PeerConnectionContext} from './hooks/usePeerConnection.ts'; diff --git a/sites/dapp-ui/src/Layout.tsx b/sites/dapp-ui/src/Layout.tsx index e1fdfbf..6d65a26 100644 --- a/sites/dapp-ui/src/Layout.tsx +++ b/sites/dapp-ui/src/Layout.tsx @@ -21,6 +21,34 @@ export default function Layout({children}: PropsWithChildren) { const bubbleStyle = { backgroundColor: isDarkMode ? 'white' : 'black' } + function prevState(){ + switch(state){ + case 'debug': + return 'debug' + case 'start': + return 'debug' + case 'connected': + return 'registered' + case 'registered': + return 'connected' + default: + return 'debug' + } + } + function nextState() { + switch(state) { + case 'debug': + return 'start' + case 'start': + return 'connected' + case 'connected': + return 'registered' + case 'registered': + return 'connected' + default: + return 'debug' + } + } return ( <> @@ -30,12 +58,12 @@ export default function Layout({children}: PropsWithChildren) { Liquid dApp { - setState(state === 'registered' ? 'connected' : 'start') - }} aria-label="delete" disabled={state === 'start'} color="inherit"> + setState(prevState()) + }} aria-label="delete" disabled={state === 'debug'} color="inherit"> { - setState(state === 'start' ? 'connected' : 'registered') + setState(nextState()) }} aria-label="delete" disabled={state === 'registered'} color="inherit"> diff --git a/sites/dapp-ui/src/components/Chat.tsx b/sites/dapp-ui/src/components/Chat.tsx new file mode 100644 index 0000000..d26fd04 --- /dev/null +++ b/sites/dapp-ui/src/components/Chat.tsx @@ -0,0 +1,73 @@ +import Avatar from "@mui/material/Avatar"; +import Divider from "@mui/material/Divider"; +import List from "@mui/material/List"; +import ListItem from "@mui/material/ListItem"; +import ListItemAvatar from "@mui/material/ListItemAvatar"; +import ListItemText from "@mui/material/ListItemText"; +import {Message, useMessageStore} from "../store.ts"; +import TextField from "@mui/material/TextField"; +import {useContext, useState} from "react"; +import Button from "@mui/material/Button"; +import {DataChannelContext} from "../hooks/useDataChannel.ts"; + + +const MESSAGE_TYPES = { + local: 'Local', + remote: 'Remote' +} +const MESSAGE_AVATARS = { + local: '/maskable-icon.png', + remote: '/logo-inverted.png' +} + +function ChatMessage(message: Message) { + return ( + + + + + + + ) +} + +export function ChatList() { + const {dataChannel} = useContext(DataChannelContext) + + const messages = useMessageStore((state) => state.messages) + const addMessage = useMessageStore((state) => state.addMessage) + const [message, setMessage] = useState('') + + const isReady = dataChannel && dataChannel.readyState === 'open' + return ( + <> + + {messages.map((message, i) => { + return ( +
+ + +
+ ) + })} +
+
+ setMessage(e.target.value)}> + +
+ {/**/} + + ) +} diff --git a/sites/dapp-ui/src/components/Snackbar.tsx b/sites/dapp-ui/src/components/Snackbar.tsx index efdf552..99aa6c2 100644 --- a/sites/dapp-ui/src/components/Snackbar.tsx +++ b/sites/dapp-ui/src/components/Snackbar.tsx @@ -9,7 +9,7 @@ export function MessageSnackbar(){ const greaterThanMid = useMediaQuery(theme.breakpoints.up("md")); const {open, setOpen, message, setMessage} = useContext(SnackbarContext) - console.log('MessageSnackbar', open, setOpen, message, setMessage) + const handleClose = () => { setOpen(false) setMessage('') diff --git a/sites/dapp-ui/src/components/user/StatusCard.tsx b/sites/dapp-ui/src/components/user/StatusCard.tsx index c2b2d32..dd58c6e 100644 --- a/sites/dapp-ui/src/components/user/StatusCard.tsx +++ b/sites/dapp-ui/src/components/user/StatusCard.tsx @@ -50,10 +50,10 @@ export function StatusCard({session, user, socket}: ProfileCardProps){ {user && - - { - fetch('/auth/logout') - }} /> + { + fetch('/auth/logout') + }}> + diff --git a/sites/dapp-ui/src/entry-main.tsx b/sites/dapp-ui/src/entry-main.tsx index 56eeefe..cda24cc 100644 --- a/sites/dapp-ui/src/entry-main.tsx +++ b/sites/dapp-ui/src/entry-main.tsx @@ -5,7 +5,7 @@ import App from './App.tsx' ReactDOM.createRoot(document.getElementById('root')!).render( - + // - , + // , ); diff --git a/sites/dapp-ui/src/hooks/useDataChannel.ts b/sites/dapp-ui/src/hooks/useDataChannel.ts new file mode 100644 index 0000000..9ae13b0 --- /dev/null +++ b/sites/dapp-ui/src/hooks/useDataChannel.ts @@ -0,0 +1,52 @@ +import {createContext, useContext, useEffect} from "react"; + +type DataChannelState = {dataChannel: RTCDataChannel | null, setDataChannel: (_: RTCDataChannel) => void} +export const DataChannelContext = createContext({ + dataChannel: null, setDataChannel: (_: RTCDataChannel) => {} +} as DataChannelState); + +/** + * Hook to use data channel messages + * @param onMessage + */ +export function useDataChannelMessages(onMessage: (event: MessageEvent)=>void){ + const {dataChannel} = useContext(DataChannelContext); + useEffect(() => { + if(!dataChannel) return + dataChannel.addEventListener("message", onMessage); + return () => { + dataChannel.removeEventListener("message", onMessage); + } + }, [dataChannel, onMessage]); +} + +/** + * Hook to create a Data Channel + * + * Creates a RTCDataChannel if type is 'local'. If the type is 'remote', it listens for the 'datachannel' event + * + * @param type + * @param peerConnection + */ +export function useDataChannel(type: 'local' | 'remote', peerConnection: RTCPeerConnection | null){ + const {dataChannel, setDataChannel} = useContext(DataChannelContext) + useEffect(() => { + if(!peerConnection) return + function handleOnDataChannel(event: RTCDataChannelEvent){ + setDataChannel(event.channel) + } + if(type === 'local') { + setDataChannel(peerConnection.createDataChannel('data')) + } else { + peerConnection.addEventListener('datachannel', handleOnDataChannel) + } + + return ()=> { + if(type === 'remote') { + peerConnection.removeEventListener('datachannel', handleOnDataChannel) + } + } + }, [peerConnection, setDataChannel, type]); + + return dataChannel; +} diff --git a/sites/dapp-ui/src/hooks/usePeerConnection.ts b/sites/dapp-ui/src/hooks/usePeerConnection.ts index 7227315..f5ab03e 100644 --- a/sites/dapp-ui/src/hooks/usePeerConnection.ts +++ b/sites/dapp-ui/src/hooks/usePeerConnection.ts @@ -1,43 +1,51 @@ -import {useEffect, useMemo} from "react"; - - -const DEFAULT_CONFIG = { - iceServers: [ - { - urls: [ - 'stun:stun.l.google.com:19302', - 'stun:stun1.l.google.com:19302', - 'stun:stun2.l.google.com:19302', - ], - }, - ], - iceCandidatePoolSize: 10, -}; - -type PeerConnectionConfig = { - onMessage: (event: MessageEvent)=>void; - onIceCandidate: (event: RTCPeerConnectionIceEvent)=>void; - channelName?: string; - configuration?: RTCConfiguration; +import {createContext, useContext, useEffect, useState} from "react"; + +type PeerConnectionState = {peerConnection: RTCPeerConnection | null} +export const PeerConnectionContext = createContext({ + peerConnection: null +} as PeerConnectionState); + + + +export function usePeerConnectionState(){ + const {peerConnection} = useContext(PeerConnectionContext); + const [connectionState, setConnectionState] = useState(peerConnection?.connectionState || null); + + useEffect(() => { + if(!peerConnection) return; + function handleConnectionStateChange() { + if(!peerConnection) return; + console.log(`Connection state change: ${peerConnection.connectionState}`); + setConnectionState(peerConnection.connectionState); + } + + peerConnection.addEventListener('connectionstatechange', handleConnectionStateChange); + return () => { + peerConnection.removeEventListener('connectionstatechange', handleConnectionStateChange); + } + }, [peerConnection]); + + return connectionState; } -export function usePeerConnection(onIceCandidate: (event: RTCPeerConnectionIceEvent)=>void, channelName = 'data', configuration = DEFAULT_CONFIG) { - console.log(configuration) - const peerConnection = useMemo(()=>new RTCPeerConnection(configuration), [configuration]); - const dataChannel = useMemo(()=>peerConnection.createDataChannel(channelName), [channelName, peerConnection]); +export function usePeerConnection( + onIceCandidate: (event: RTCPeerConnectionIceEvent)=>void) { + const {peerConnection} = useContext(PeerConnectionContext); + useEffect(() => { + if(!peerConnection) return; function handleICEGatheringStateChange() { - console.log(`ICE gathering state: ${peerConnection.iceGatheringState}`); + console.log(`ICE gathering state: ${peerConnection?.iceGatheringState}`); } function handleConnectionStateChange() { console.log(`Connection state change: ${peerConnection.connectionState}`); } function handleSignalingStateChange() { - console.log(`Signaling state change: ${peerConnection.signalingState}`); + console.log(`Signaling state change: ${peerConnection?.signalingState}`); } function handleICEConnectionStateChange() { - console.log(`ICE connection state change: ${peerConnection.iceConnectionState}`); + console.log(`ICE connection state change: ${peerConnection?.iceConnectionState}`); } peerConnection.addEventListener('icegatheringstatechange', handleICEGatheringStateChange); peerConnection.addEventListener('connectionstatechange', handleConnectionStateChange); @@ -45,10 +53,6 @@ export function usePeerConnection(onIceCandidate: (event: RTCPeerConnectionIceEv peerConnection.addEventListener('iceconnectionstatechange ', handleICEConnectionStateChange); peerConnection.addEventListener('icecandidate', onIceCandidate); - dataChannel.addEventListener("open", (event) => { - console.log(event) - }); - return () => { peerConnection.removeEventListener('icegatheringstatechange', handleICEGatheringStateChange); peerConnection.removeEventListener('connectionstatechange', handleConnectionStateChange); diff --git a/sites/dapp-ui/src/hooks/useSocket.ts b/sites/dapp-ui/src/hooks/useSocket.ts index 7cceb17..63f8ca4 100644 --- a/sites/dapp-ui/src/hooks/useSocket.ts +++ b/sites/dapp-ui/src/hooks/useSocket.ts @@ -4,7 +4,7 @@ import { useEffect, useState } from 'react'; // "undefined" means the URL will be computed from the `window.location` object const URL = `${window.location.origin}`; -export const socket = io(URL, {autoConnect: false}); +export const socket = io(URL, {autoConnect: true}); export function useSocket(){ diff --git a/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx b/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx index fef8c89..949a292 100644 --- a/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx +++ b/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx @@ -28,6 +28,11 @@ export function WaitForRegistrationCard(){ setState('registered') }); }) + useEffect(()=>{ + socket.on('call-candidate', (data: any) => { + console.log(data) + }) + }, [socket]) return ( +

{type}: Session Description

+
{description.sdp}
+
+ ) +} + +type HandshakeProps = { + local: RTCSessionDescriptionInit | null, + remote: RTCSessionDescriptionInit | null, + candidates: RTCIceCandidateInit[] +} + +/** + * Debugging View for WebRTC Handshake + * + * Handles the SDP negotiation between two peers manually + * + * @param local + * @param remote + * @param candidates + * @constructor + */ +function Handshake({local, remote, candidates}: HandshakeProps) { + return ( + + {local && } + {remote && } + +

Local Candidates

+ {candidates?.map((candidate, i) => { + return ( +
{JSON.stringify(candidate, null, 2)}
+ ) + } + )} +
+
+ ) +} + +/** + * Remote Peer + * + * connect to an existing session + * + * @param peerConnection + * @constructor + */ +function RemoteView({peerConnection}: { peerConnection: RTCPeerConnection }) { + const {socket} = useSocket() + const candidates = usePeerStore((state) => state.candidates) + // Session Descriptions + const [sdp, setSDP] = useState() + const [local, setLocal] = useState(null) + const [remote, setRemote] = useState(null) + + // Handle Messages + const addMessage = useMessageStore((state) => state.addMessage) + useDataChannelMessages((event) => addMessage(JSON.parse(event.data))) + + // Handle the remote SDP + useEffect(() => { + if (!remote) return + peerConnection.createAnswer() + .then(async (answer) => { + await peerConnection.setLocalDescription(answer) + setLocal(answer) + }) + }, [peerConnection, remote]) + + return ( +
+

Remote View

+

Offer from Peer:

+
+ +
+ + +
+ ) +} + + +/** + * Local Peer + * + * create a new session + * + * @param peerConnection + * @constructor + */ +function LocalView({peerConnection}: { peerConnection: RTCPeerConnection }) { + const {socket} = useSocket() + const candidates = usePeerStore((state) => state.candidates) + // Session Description + const [sdp, setSDP] = useState() + const [local, setLocal] = useState(null) + const [remote, setRemote] = useState(null) + + // Handle SDP + async function handleCallButton() { + if (peerConnection.signalingState === 'stable') { + const offer = await peerConnection.createOffer(); + await peerConnection.setLocalDescription(offer); + setLocal(offer) + } + } + + return ( +
+

Local View

+ +

Remote Answer:

+
+ +
+ + +
+ ) +} + + +/** + * What does this thing need to do? + * + * Needs a relay for candidates and offers. + * + * After a connection is established, it should pass the ice-candidate-offer to the server. + * + * @constructor + */ +export function DebugWebRTC() { + const state = useUserState() + + // Remote or Local Session + const [type, setType] = useState<'local' | 'remote'>('local') + + // Store Hooks + const addMessage = useMessageStore((state) => state.addMessage) + const clearMessages = useMessageStore((state) => state.clearMessages) + const addCandidate = usePeerStore((state) => state.addCandidate) + const clearCandidates = usePeerStore((state) => state.clearCandidates) + + // Create a Peer Connection + const peerConnection = usePeerConnection((event) => { + if (event.candidate) { + addCandidate(event.candidate.toJSON()) + } + }); + const connectionState = usePeerConnectionState(); + // Create a Data Channel + const dataChannel = useDataChannel(type, peerConnection); + // Handle Messages + useDataChannelMessages((event) => addMessage(JSON.parse(event.data))) + if (state.isLoading) { + return ( + + ) + } else if (!state.data.user) { + return ( + + ) + } + + if (dataChannel && connectionState === 'connected') { + return ( + + ) + } + + return ( + + + WebRTC Debug + + Connecting is a two step handshake, the first step is to create a local offer. The + local offer is sent to a remote peer. + The remote peer + adds the local offer to their remote session description. + The remote peer then creates an answer and sends it back to the + local peer. + + + + + {type === 'local' && peerConnection && } + {type === 'remote' && peerConnection && } + + + + + ) +} diff --git a/sites/dapp-ui/src/pages/debug/WebRTC.tsx b/sites/dapp-ui/src/pages/debug/WebRTC.tsx new file mode 100644 index 0000000..31ee097 --- /dev/null +++ b/sites/dapp-ui/src/pages/debug/WebRTC.tsx @@ -0,0 +1,206 @@ +import {useEffect, useState} from "react"; +import algosdk from 'algosdk'; +import {fetchConnectResponse} from "@liquid/auth-client"; +import {Message} from "@liquid/auth-client/connect"; +import {useMessageStore, usePeerStore} from "../../store.ts"; +import {ChatList} from "../../components/Chat.tsx"; +import {useDataChannel, useDataChannelMessages} from "../../hooks/useDataChannel.ts"; +import {usePeerConnection, usePeerConnectionState} from "../../hooks/usePeerConnection.ts"; +import Button from "@mui/material/Button"; +import {useUserState} from "../../components/user/useUserState.ts"; +import CircularProgress from "@mui/material/CircularProgress"; +import Typography from "@mui/material/Typography"; +import Card from "@mui/material/Card/Card"; +import CardContent from "@mui/material/CardContent"; +import {useSocket} from "../../hooks/useSocket.ts"; + +const acct = algosdk.mnemonicToSecretKey('lab surge abandon artist moon keen license bronze rebuild wing surge apart basket teach deposit patch snow paper sting rural negative logic cousin above gym') + +/** + * Remote Peer + * + * connect to an existing session + * + * @param peerConnection + * @constructor + */ +function RemoteView({peerConnection}: { peerConnection: RTCPeerConnection }) { + const {socket} = useSocket() + const candidates = usePeerStore((state) => state.candidates) + const [local, setLocal] = useState(null) + const [remote, setRemote] = useState(null) + // Handle Messages + const addMessage = useMessageStore((state) => state.addMessage) + useDataChannelMessages((event) => addMessage(JSON.parse(event.data))) + + // Handle the remote SDP + useEffect(() => { + if (!remote) return + peerConnection.createAnswer() + .then(async (answer) => { + await peerConnection.setLocalDescription(answer) + setLocal(answer) + }) + }, [peerConnection, remote]) + + useEffect(()=>{ + socket.emit('wait-for-offer', ({data})=>{ + peerConnection.setRemoteDescription({type: 'offer', sdp: data.sdp}) + .then(() => setRemote({type: 'offer', sdp: data.sdp})) + }) + }, [socket]) + useEffect(()=>{ + if(candidates.length >= 1 && local){ + socket.emit('ice-candidate-answer', peerConnection.localDescription?.sdp || local.sdp) + } + },[socket, candidates, local, peerConnection]) + + useEffect(()=>{ + if(!socket) return + console.log('Running Ice Candidate effect') + function onIceCandidate(e){ + console.log('!!!!!!!!!!!!!!!!1') + console.log(e) + } + socket.on('lfgz', function(){ + console.log('!!!!!!!!!!!!!!!!1') + + }) + // return ()=>{ + // socket.off('lfgz', onIceCandidate) + // } + }, [socket]) + return ( +
+

Remote View

+

Waiting for Caller

+
+ ) +} + + +/** + * Local Peer + * + * create a new session + * + * @param peerConnection + * @constructor + */ +function LocalView({peerConnection}: { peerConnection: RTCPeerConnection }) { + const {socket} = useSocket() + const candidates = usePeerStore((state) => state.candidates) + // Session Description + const [local, setLocal] = useState(null) + const [remote, setRemote] = useState(null) + + // Emit the SDP Offer + useEffect(()=>{ + if(candidates.length >= 1 && local && !remote){ + socket.emit('ice-candidate-offer', peerConnection.localDescription?.sdp || local.sdp, ({data})=>{ + // Server responds with the peer's answer + peerConnection.setRemoteDescription({type: 'answer', sdp: data.sdp}) + .then(() => setRemote({type: 'answer', sdp: data.sdp})) + }) + } + },[remote, socket, candidates, local, peerConnection]) + + useEffect(() => { + socket.on('lfg', (data)=>{ + console.log('lfg') + console.log(data) + }) + }, [socket]); + + // Create a call Offer + async function handleCallButton() { + socket.emit('lfg') + // if (peerConnection.signalingState === 'stable') { + // const offer = await peerConnection.createOffer(); + // await peerConnection.setLocalDescription(offer); + // setLocal(offer) + // } + } + + return ( +
+

Local View

+ +
+ ) +} + +export function DebugWebRTC() { + const state = useUserState() + + // Remote or Local Session + const [type, setType] = useState<'local' | 'remote'>('local') + + // Store Hooks + const addMessage = useMessageStore((state) => state.addMessage) + const clearMessages = useMessageStore((state) => state.clearMessages) + const addCandidate = usePeerStore((state) => state.addCandidate) + const clearCandidates = usePeerStore((state) => state.clearCandidates) + + // Create a Peer Connection + const peerConnection = usePeerConnection((event) => { + if (event.candidate) { + addCandidate(event.candidate.toJSON()) + } + }); + const connectionState = usePeerConnectionState(); + // Create a Data Channel + const dataChannel = useDataChannel(type, peerConnection); + // Handle Messages + useDataChannelMessages((event) => addMessage(JSON.parse(event.data))) + if (state.isLoading) { + return ( + + ) + } else if (!state.data.user) { + return ( + + ) + } + + if (dataChannel && connectionState === 'connected') { + return ( + + ) + } + + return ( + + + WebRTC Debug + + Connecting is a two step handshake, the first step is to create a local offer. The + local offer is sent to a remote peer. + The remote peer + adds the local offer to their remote session description. + The remote peer then creates an answer and sends it back to the + local peer. + + + + + {type === 'local' && peerConnection && } + {type === 'remote' && peerConnection && } + + + + + ) +} diff --git a/sites/dapp-ui/src/pages/home/ConnectModal.tsx b/sites/dapp-ui/src/pages/home/ConnectModal.tsx index c1a2dae..97fe329 100644 --- a/sites/dapp-ui/src/pages/home/ConnectModal.tsx +++ b/sites/dapp-ui/src/pages/home/ConnectModal.tsx @@ -8,9 +8,10 @@ import QRCodeStyling, {Options} from "qr-code-styling"; import { useContext, useEffect, useState } from 'react'; import {Fade} from "@mui/material"; import {useSocket} from '../../hooks/useSocket'; -import {randomBytes, sign} from 'tweetnacl'; -import {SnackbarContext, StateContext} from '../../Contexts'; +import nacl from 'tweetnacl'; +import { StateContext } from '../../Contexts'; import { useCredentialStore, Credential } from '../../store'; +import { useNavigate } from "react-router-dom"; const style = { position: 'absolute' as const, top: '50%', @@ -22,15 +23,14 @@ const style = { boxShadow: 24, }; export function ConnectModal({color}: {color?: 'inherit' | 'primary' | 'secondary' | 'success' | 'error' | 'info' | 'warning'}) { - const snackbar = useContext(SnackbarContext) + const navigate = useNavigate() const {socket} = useSocket(); - const credentials = useCredentialStore((state)=> state.addresses); const save = useCredentialStore((state)=> state.update); const {state: step, setState} = useContext(StateContext) const [state] = useState({ requestId: Math.random(), - challenge: toBase64URL(randomBytes(sign.seedLength)) + challenge: toBase64URL(nacl.randomBytes(nacl.sign.seedLength)) }) const qrOpts = { "width": 500, @@ -67,19 +67,17 @@ export function ConnectModal({color}: {color?: 'inherit' | 'primary' | 'secondar } } } - + useEffect(()=>{ + socket.on('link', (data)=>{ + console.log(data) + }) + },[socket]) const [open, setOpen] = React.useState(false); - const [barcode, setBarcode] = React.useState("/qr-loading.png") - const handleOpen = () => { - setBarcode("/qr-loading.png") - if(socket.disconnected){ - console.warn('Socket is disconnected, attempting to reconnect') - socket.connect() + useEffect(() => { + if(!open){ + return } - socket.emit('link', { requestId: state.requestId }, async ({data}: {data: {credId?: string, requestId: string|number, wallet: string}}) => { - console.log('On Link response'); - console.log(data) let newCredentials: Credential[] = [] if(typeof credentials[data.wallet] !== 'undefined'){ newCredentials = credentials[data.wallet].credentials @@ -90,16 +88,19 @@ export function ConnectModal({color}: {color?: 'inherit' | 'primary' | 'secondar console.log(data.credId) window.localStorage.setItem('credId', data.credId); setState('registered') - snackbar.setMessage('Connected with Credential') - snackbar.setOpen(true) + navigate('/peering') } else { - snackbar.setMessage('Connected Public Key') - snackbar.setOpen(true) setState('connected') + navigate('/peering') } }); + }, [open]) + + const [barcode, setBarcode] = React.useState("/qr-loading.png") + const handleOpen = () => { + setBarcode("/qr-loading.png") const message = new Message(window.location.origin, state.challenge, state.requestId) // JSON encoding @@ -126,6 +127,7 @@ export function ConnectModal({color}: {color?: 'inherit' | 'primary' | 'secondar aria-labelledby="modal-modal-title" aria-describedby="modal-modal-description" > + + + + ); diff --git a/sites/dapp-ui/src/pages/home/GetStarted.tsx b/sites/dapp-ui/src/pages/home/GetStarted.tsx index 62e9e6e..b1c82ec 100644 --- a/sites/dapp-ui/src/pages/home/GetStarted.tsx +++ b/sites/dapp-ui/src/pages/home/GetStarted.tsx @@ -4,40 +4,8 @@ import Typography from "@mui/material/Typography"; import CardActions from "@mui/material/CardActions"; import Card from '@mui/material/Card'; import {ConnectModal} from "./ConnectModal"; -import {useEffect} from "react"; -import {useSocket} from "../../hooks/useSocket.ts"; -import {usePeerConnection} from "../../hooks/usePeerConnection.ts"; import Button from "@mui/material/Button"; export function GetStartedCard(){ - const {socket} = useSocket(); - const peerConnection = usePeerConnection((event)=>{ - if(event.candidate){ - console.log('candidate', event.candidate.toJSON()) - socket.emit('ice-candidate-offer', event.candidate.toJSON(), (data)=>{ - console.log('sent ice-candidate-offer', data) - }) - } else { - console.log('Finished gathering candidates.') - } - }); - useEffect(() => { - - }, []); - useEffect(() => { - console.log('GetStartedCard mounted'); - async function letsGo(){ - const offer = await peerConnection.createOffer(); - await peerConnection.setLocalDescription(offer); - } - letsGo() - const onHello = (data: unknown) => {console.log(data)} - socket.on('hello', onHello) - socket.emit('hello', {hello: 'world'}) - return () => { - console.log('GetStartedCard unmounted'); - socket.off('hello', onHello); - } - },[]) return ( { + if (event.candidate) { + console.log('Local Candidate', event.candidate.toJSON()) + socket.emit('answer-candidate', event.candidate.toJSON()) + } else { + console.log(event) + } + }); + + useEffect(()=>{ + if(!peerConnection) return + // peerConnection.createDataChannel('1') + function handleOnDataChannel(event: RTCDataChannelEvent){ + console.log('Data Channel Event', event.channel) + event.channel.send('Hello World') + } + peerConnection.addEventListener('datachannel', handleOnDataChannel) + + return ()=> { + peerConnection.removeEventListener('datachannel', handleOnDataChannel) + } + }, [peerConnection]) + + useEffect(()=>{ + if(!peerConnection) return + async function handleDescription(sdp: string){ + console.log('OFFER', sdp) + await peerConnection?.setRemoteDescription({type: 'offer', sdp} as RTCSessionDescriptionInit) + const answer = await peerConnection?.createAnswer() + await peerConnection?.setLocalDescription(answer) + console.log('ANSWER', answer?.sdp) + socket.emit('answer-description', answer?.sdp) + } + socket.on('call-description', handleDescription) + return ()=>{ + socket.off('call-description', handleDescription) + } + },[socket]) + useEffect(()=>{ + if(!peerConnection) return + function handleCallCandidate(data: RTCIceCandidate){ + console.log('Remote Candidate', data) + peerConnection.addIceCandidate(data) + } + socket.on('call-candidate', handleCallCandidate) + return ()=>{ + socket.off('call-candidate', handleCallCandidate) + } + }, [socket]) + return ( + + + + + Waiting for Peer Connection (2 of 3) + + + Waiting for Passkey registration for address: + + {address.isLoading && } + {address.isFetched && {address.data}} + + + ) +} diff --git a/sites/dapp-ui/src/store.ts b/sites/dapp-ui/src/store.ts index 7a9a08c..0a4d78c 100644 --- a/sites/dapp-ui/src/store.ts +++ b/sites/dapp-ui/src/store.ts @@ -39,3 +39,32 @@ export const useCredentialStore = create( { name: 'avicennia-credential-store', storage: createJSONStorage(() => localStorage) } ), ); + +interface PeerStore { + candidates: RTCIceCandidateInit[]; + addCandidate: (candidate: RTCIceCandidateInit) => void; + clearCandidates: () => void; +} +export const usePeerStore = create((set) => ({ + candidates: [], + addCandidate: (candidate: RTCIceCandidateInit) => set((state)=>({candidates: [...state.candidates, candidate]})), + clearCandidates: () => set({candidates: []}), +})); + +export type Message = { + text: string; + type: 'local' | 'remote'; + timestamp: number; +} + +interface MessageStore { + messages: Message[]; + addMessage: (message: Message) => void; + clearMessages: () => void; + +} +export const useMessageStore = create((set) => ({ + messages: [], + addMessage: (message: Message) => set((state)=>({messages: [...state.messages, message]})), +clearMessages: () => set({messages: []}), +})) diff --git a/sites/dapp-ui/vite.config.ts b/sites/dapp-ui/vite.config.ts index 87ca49a..7bef127 100644 --- a/sites/dapp-ui/vite.config.ts +++ b/sites/dapp-ui/vite.config.ts @@ -11,17 +11,22 @@ const PUBLIC_DIR = resolve(API_DIR, 'public') const VIEW_DIR = resolve(API_DIR, 'views') console.log(API_DIR) export default defineConfig({ + // base: '/app', server: { - proxy: { - '^/auth/.*': 'http://localhost:3000', - '^/connect/.*': 'http://localhost:3000', - '^/attestation/.*': 'http://localhost:3000', - '^/assertion/.*': 'http://localhost:3000', - '/socket.io': { - target: 'ws://localhost:3000', - ws: true, - }, + hmr: { + port: 8000, + host: 'localhost' } + // proxy: { + // '^/auth/.*': 'http://localhost:3000', + // '^/connect/.*': 'http://localhost:3000', + // '^/attestation/.*': 'http://localhost:3000', + // '^/assertion/.*': 'http://localhost:3000', + // '/socket.io': { + // target: 'ws://localhost:3000', + // ws: true, + // }, + // } }, build: { // rollupOptions: { From 96834782281eab45a4949d42b42bdd28d7626a68 Mon Sep 17 00:00:00 2001 From: Michael Feher Date: Tue, 9 Apr 2024 12:49:23 -0400 Subject: [PATCH 04/21] fix: session adapter --- .../src/adapters/redis-io.adapter.ts | 8 +-- .../src/connect/connect.gateway.ts | 51 +++++++++++++++---- .../src/signals/signals.gateway.ts | 20 ++++---- .../src/signals/signals.interceptor.ts | 6 +-- sites/dapp-ui/src/App.tsx | 2 + sites/dapp-ui/src/hooks/usePeerConnection.ts | 8 ++- .../src/pages/peering/WaitForPeers.tsx | 10 ++-- 7 files changed, 69 insertions(+), 36 deletions(-) diff --git a/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts b/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts index 801d3bf..8464e68 100644 --- a/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts +++ b/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts @@ -35,13 +35,7 @@ export class RedisIoAdapter extends IoAdapter { createIOServer(port: number, options?: ServerOptions): any { const server = super.createIOServer(port, options); - const wrap = - (middleware: (request: any, options: any, next: any) => any) => - (socket: Socket, next: (err?: Error) => void) => { - return middleware(socket.request, {}, next); - }; - server.use(wrap(this.sessionHandler)); - server.use(socketSessions(this.sessionHandler, { autoSave: true })); + server.engine.use(this.sessionHandler); server.adapter(this.adapterConstructor); return server; } diff --git a/services/liquid-auth-api-js/src/connect/connect.gateway.ts b/services/liquid-auth-api-js/src/connect/connect.gateway.ts index eccfc7c..85a05e3 100644 --- a/services/liquid-auth-api-js/src/connect/connect.gateway.ts +++ b/services/liquid-auth-api-js/src/connect/connect.gateway.ts @@ -4,7 +4,7 @@ import { map } from 'rxjs/operators'; import { ConnectedSocket, MessageBody, - OnGatewayConnection, + OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, SubscribeMessage, WebSocketGateway, @@ -18,7 +18,8 @@ import { RedisIoAdapter } from '../adapters/redis-io.adapter'; origin: '*', }, }) -export class ConnectGateway implements OnGatewayInit, OnGatewayConnection { +export class ConnectGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { + private timers = new Map(); private ioAdapter: RedisIoAdapter; private readonly logger = new Logger(ConnectGateway.name); constructor(private authService: AuthService) {} @@ -41,17 +42,44 @@ export class ConnectGateway implements OnGatewayInit, OnGatewayConnection { * * @param client */ - async handleConnection(client: Socket) { - const handshake = client.handshake as Handshake; - const session = handshake.session as Record; + async handleConnection(socket: Socket) { + const request = socket.request as Record; + const session = request.session as Record; + + const timer = setInterval(() => { + session.reload((err) => { + // console.log('Reloaded session') + if (err) { + // forces the client to reconnect + socket.conn.close(); + // you can also use socket.disconnect(), but in that case the client + // will not try to reconnect + } + }); + }, 200); + + if(this.timers.has(request.sessionID)) { + clearInterval(this.timers.get(request.sessionID)); + } + + this.timers.set(request.sessionID, timer); + this.logger.debug( - `(*) Client Connected with Session: ${handshake.sessionID}${ + `(*) Client Connected with Session: ${request.sessionID}${ session.wallet ? ` and PublicKey: ${session.wallet}` : '' }`, ); if (typeof session.wallet === 'string') { this.logger.debug(`(*) Client Joining Room ${session.wallet}`); - await client.join(session.wallet); + await socket.join(session.wallet); + } + } + + handleDisconnect(socket: Socket) { + const request = socket.request as Record; + this.logger.debug(`(*) Client Disconnected with Session: ${request.sessionID}`); + if(this.timers.has(request.sessionID)) { + clearInterval(this.timers.get(request.sessionID)); } } /** @@ -66,15 +94,16 @@ export class ConnectGateway implements OnGatewayInit, OnGatewayConnection { ): Promise< Observable<{ data: { requestId: string | number; wallet: string } }> > { - const handshake = client.handshake as Handshake; + const request = client.request as Record; this.logger.debug( - `(link): link for Session: ${handshake.sessionID} with RequestId: ${body.requestId}`, + `(link): link for Session: ${request.sessionID} with RequestId: ${body.requestId}`, ); // Find the stored session - const session = await this.authService.findSession(handshake.sessionID); + const session = await this.authService.findSession(request.sessionID); console.log('Session', session); if (session) { + console.log('Listening to auth messages') await this.ioAdapter.subClient.subscribe('auth'); // Handle messages @@ -85,7 +114,7 @@ export class ConnectGateway implements OnGatewayInit, OnGatewayConnection { console.log(body.requestId, data.requestId, data, body); if (body.requestId === data.requestId) { this.logger.debug( - `(*) Linking Wallet: ${data.wallet} to Session: ${handshake.sessionID}`, + `(*) Linking Wallet: ${data.wallet} to Session: ${request.sessionID}`, ); await this.authService.updateSessionWallet(session, data.wallet); this.logger.debug(`(*) Joining Room: ${data.wallet}`); diff --git a/services/liquid-auth-api-js/src/signals/signals.gateway.ts b/services/liquid-auth-api-js/src/signals/signals.gateway.ts index 346e4a2..d3ddcdf 100644 --- a/services/liquid-auth-api-js/src/signals/signals.gateway.ts +++ b/services/liquid-auth-api-js/src/signals/signals.gateway.ts @@ -22,9 +22,9 @@ export class SignalsGateway { data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, @ConnectedSocket() client: Socket, ) { - this.logger.debug(`(call-candidate): ${data}`); - const handshake = client.handshake as Handshake; - const session = handshake.session as Record; + this.logger.debug(`(call-candidate): ${JSON.stringify(data)}`); + const request = client.request as Record; + const session = request.session as Record; this.server.in(session.wallet).emit('call-candidate', data); } @SubscribeMessage('call-description') @@ -35,8 +35,8 @@ export class SignalsGateway { this.logger.log(`(call-description): ${data}`); // Session from the initial Handshake - const handshake = client.handshake as Handshake; - const session = handshake.session as Record; + const request = client.request as Record; + const session = request.session as Record; // Send description to all clients in the public key's room this.server.in(session.wallet).emit('call-description', data); @@ -47,8 +47,8 @@ export class SignalsGateway { @ConnectedSocket() client: Socket, ) { this.logger.log(`(answer-description): ${data}`); - const handshake = client.handshake as Handshake; - const session = handshake.session as Record; + const request = client.request as Record; + const session = request.session as Record; this.server.in(session.wallet).emit('answer-description', data); } @SubscribeMessage('answer-candidate') @@ -57,9 +57,9 @@ export class SignalsGateway { data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, @ConnectedSocket() client: Socket, ) { - this.logger.debug(`(answer-candidate): ${data}`); - const handshake = client.handshake as Handshake; - const session = handshake.session as Record; + this.logger.debug(`(answer-candidate): ${JSON.stringify(data)}`); + const request = client.request as Record; + const session = request.session as Record; this.server.in(session.wallet).emit('answer-candidate', data); } } diff --git a/services/liquid-auth-api-js/src/signals/signals.interceptor.ts b/services/liquid-auth-api-js/src/signals/signals.interceptor.ts index 79d6062..f1e2027 100644 --- a/services/liquid-auth-api-js/src/signals/signals.interceptor.ts +++ b/services/liquid-auth-api-js/src/signals/signals.interceptor.ts @@ -16,12 +16,12 @@ export class SignalsInterceptor implements NestInterceptor { next: CallHandler, ): Promise> { const client = context.switchToWs().getClient() as Socket; - const handshake = client.handshake as Handshake; - const session = handshake.session as Record; + const request = client.request as Record; + const session = request.session as Record; if (typeof session.wallet !== 'string') { this.logger.error( - `(*) Client ${handshake.sessionID} is not authenticated`, + `(*) Client ${request.sessionID} is not authenticated`, ); client.disconnect(); } else { diff --git a/sites/dapp-ui/src/App.tsx b/sites/dapp-ui/src/App.tsx index e33c2ae..1d7385d 100644 --- a/sites/dapp-ui/src/App.tsx +++ b/sites/dapp-ui/src/App.tsx @@ -25,6 +25,8 @@ const DEFAULT_CONFIG: RTCConfiguration = { { urls: [ 'stun:stun.l.google.com:19302', + 'stun:stun1.l.google.com:19302', + 'stun:stun2.l.google.com:19302', ], }, ], diff --git a/sites/dapp-ui/src/hooks/usePeerConnection.ts b/sites/dapp-ui/src/hooks/usePeerConnection.ts index f5ab03e..6682acc 100644 --- a/sites/dapp-ui/src/hooks/usePeerConnection.ts +++ b/sites/dapp-ui/src/hooks/usePeerConnection.ts @@ -30,7 +30,7 @@ export function usePeerConnectionState(){ export function usePeerConnection( onIceCandidate: (event: RTCPeerConnectionIceEvent)=>void) { const {peerConnection} = useContext(PeerConnectionContext); - + window.peerConnection = peerConnection; useEffect(() => { if(!peerConnection) return; function handleICEGatheringStateChange() { @@ -47,12 +47,16 @@ export function usePeerConnection( function handleICEConnectionStateChange() { console.log(`ICE connection state change: ${peerConnection?.iceConnectionState}`); } + + function handleICECandidateError(event){ + console.error('ICE Candidate Error', event) + } peerConnection.addEventListener('icegatheringstatechange', handleICEGatheringStateChange); peerConnection.addEventListener('connectionstatechange', handleConnectionStateChange); peerConnection.addEventListener('signalingstatechange', handleSignalingStateChange); peerConnection.addEventListener('iceconnectionstatechange ', handleICEConnectionStateChange); peerConnection.addEventListener('icecandidate', onIceCandidate); - + peerConnection.addEventListener('icecandidateerror', handleICECandidateError) return () => { peerConnection.removeEventListener('icegatheringstatechange', handleICEGatheringStateChange); peerConnection.removeEventListener('connectionstatechange', handleConnectionStateChange); diff --git a/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx b/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx index a87f36c..0ae7def 100644 --- a/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx +++ b/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx @@ -22,7 +22,9 @@ export function WaitForPeersCard(){ const peerConnection = usePeerConnection((event) => { if (event.candidate) { console.log('Local Candidate', event.candidate.toJSON()) - socket.emit('answer-candidate', event.candidate.toJSON()) + const data = event.candidate.toJSON() + // data.type = 'offerCandidate' + socket.emit('answer-candidate', data) } else { console.log(event) } @@ -59,9 +61,11 @@ export function WaitForPeersCard(){ },[socket]) useEffect(()=>{ if(!peerConnection) return - function handleCallCandidate(data: RTCIceCandidate){ + async function handleCallCandidate(data: RTCIceCandidate){ console.log('Remote Candidate', data) - peerConnection.addIceCandidate(data) + // data.type = 'answerCandidate' + console.log('Remote Candidate ICE', new RTCIceCandidate(data)) + await peerConnection!!.addIceCandidate( new RTCIceCandidate(data)) } socket.on('call-candidate', handleCallCandidate) return ()=>{ From 6396c367e7a4af67b5bf3ead9a776a2374cd733d Mon Sep 17 00:00:00 2001 From: Michael J Feher Date: Tue, 9 Apr 2024 22:29:43 -0500 Subject: [PATCH 05/21] wip: adds basic message passing demo --- sites/dapp-ui/src/App.tsx | 13 +++-- sites/dapp-ui/src/Layout.tsx | 44 ++++----------- sites/dapp-ui/src/pages/connected.tsx | 54 +++++++++++++++++++ .../src/pages/peering/WaitForPeers.tsx | 31 +++++------ 4 files changed, 82 insertions(+), 60 deletions(-) create mode 100644 sites/dapp-ui/src/pages/connected.tsx diff --git a/sites/dapp-ui/src/App.tsx b/sites/dapp-ui/src/App.tsx index 1d7385d..8250f7c 100644 --- a/sites/dapp-ui/src/App.tsx +++ b/sites/dapp-ui/src/App.tsx @@ -18,6 +18,7 @@ import { RouterProvider } from "react-router-dom"; import { WaitForPeersCard } from "./pages/peering/WaitForPeers.tsx"; +import ConnectedPage from "./pages/connected.tsx"; const queryClient = new QueryClient() const DEFAULT_CONFIG: RTCConfiguration = { @@ -34,11 +35,11 @@ const DEFAULT_CONFIG: RTCConfiguration = { }; const router = createHashRouter([ - {'path': '/', 'element': }, - {'path': '/peering', 'element': }, - {'path': '/connected', 'element': }, - {'path': '/registered', 'element': }, - {'path': '/debug/webrtc', 'element': }, + {'path': '/', 'element': }, + {'path': '/peering', 'element': }, + {'path': '/connected', 'element': }, + {'path': '/registered', 'element': }, + {'path': '/debug/webrtc', 'element': }, ]) export default function ProviderApp(){ const [open, setOpen] = useState(false) @@ -78,9 +79,7 @@ export default function ProviderApp(){ - - diff --git a/sites/dapp-ui/src/Layout.tsx b/sites/dapp-ui/src/Layout.tsx index 6d65a26..ce27197 100644 --- a/sites/dapp-ui/src/Layout.tsx +++ b/sites/dapp-ui/src/Layout.tsx @@ -8,47 +8,23 @@ import {useTheme} from '@mui/material'; import {PropsWithChildren, useContext} from 'react'; import Brightness4Icon from '@mui/icons-material/Brightness4'; import Brightness7Icon from '@mui/icons-material/Brightness7'; -import { ColorModeContext, StateContext } from './Contexts'; +import { ColorModeContext } from './Contexts'; import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore'; import NavigateNextIcon from '@mui/icons-material/NavigateNext'; import {SessionMenu} from "./components/user/SessionMenu.tsx"; import {MessageSnackbar} from "./components/Snackbar.tsx"; +import { useLocation, useNavigate } from "react-router-dom"; export default function Layout({children}: PropsWithChildren) { - const {state, setState} = useContext(StateContext) + const location = useLocation(); + const navigate = useNavigate() const colorMode = useContext(ColorModeContext) const theme = useTheme() const isDarkMode = theme.palette.mode === 'dark' const bubbleStyle = { backgroundColor: isDarkMode ? 'white' : 'black' } - function prevState(){ - switch(state){ - case 'debug': - return 'debug' - case 'start': - return 'debug' - case 'connected': - return 'registered' - case 'registered': - return 'connected' - default: - return 'debug' - } - } - function nextState() { - switch(state) { - case 'debug': - return 'start' - case 'start': - return 'connected' - case 'connected': - return 'registered' - case 'registered': - return 'connected' - default: - return 'debug' - } - } + const breadcrumbs = ["/", "/peering", "/connected", "/registered"] + const index = breadcrumbs.indexOf(location.pathname) return ( <> @@ -58,13 +34,13 @@ export default function Layout({children}: PropsWithChildren) { Liquid dApp { - setState(prevState()) - }} aria-label="delete" disabled={state === 'debug'} color="inherit"> + index > 0 && navigate(breadcrumbs[index - 1]) + }} aria-label="delete" disabled={index === 0} color="inherit"> { - setState(nextState()) - }} aria-label="delete" disabled={state === 'registered'} color="inherit"> + index < breadcrumbs.length - 1 && navigate(breadcrumbs[index + 1]) + }} aria-label="delete" disabled={index === breadcrumbs.length - 1} color="inherit"> diff --git a/sites/dapp-ui/src/pages/connected.tsx b/sites/dapp-ui/src/pages/connected.tsx new file mode 100644 index 0000000..cc7dfda --- /dev/null +++ b/sites/dapp-ui/src/pages/connected.tsx @@ -0,0 +1,54 @@ +import { useDataChannel, useDataChannelMessages } from "../hooks/useDataChannel.ts"; +import { PeerConnectionContext } from "../hooks/usePeerConnection.ts"; +import { useContext, useEffect, useState } from "react"; +import Button from "@mui/material/Button"; +import algosdk from 'algosdk' +import { toBase64URL, fromBase64Url } from "@liquid/auth-client/encoding"; + +const algodClient = new algosdk.Algodv2('','https://testnet-api.algonode.cloud',443); + +export default function ConnectedPage(){ + const walletStr = window.localStorage.getItem('wallet'); + const wallet = walletStr ? JSON.parse(walletStr) : null; + const [txn, setTxn] = useState(null) + const {peerConnection} = useContext(PeerConnectionContext) + const datachannel = useDataChannel("remote", peerConnection) + + // Receive response + useDataChannelMessages((event) => { + if(!txn) return + async function handleMessage(){ + if(!txn) return + console.log(event) + const sig = fromBase64Url(event.data) + const signedTxn = txn.attachSignature(wallet, sig) + + const { txId } = await algodClient.sendRawTransaction(signedTxn).do(); + const result = await algosdk.waitForConfirmation(algodClient, txId, 4); + console.log(result) + } + handleMessage() + }) + + // Send Transaction + useEffect(()=>{ + if(!txn || !datachannel) return + datachannel?.send(toBase64URL(txn.bytesToSign())) + },[txn, datachannel]) + return ( +
+ Connected + +
+ ) +} diff --git a/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx b/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx index 0ae7def..d90f0db 100644 --- a/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx +++ b/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx @@ -5,20 +5,17 @@ import Card from "@mui/material/Card"; import { CircularProgress } from '@mui/material'; import { useEffect } from 'react'; import {useSocket} from '../../hooks/useSocket'; -import { useCredentialStore } from '../../store'; -import { useAddressQuery } from '../../hooks/useAddress'; import { useNavigate } from "react-router-dom"; import { usePeerConnection } from "../../hooks/usePeerConnection.ts"; -import { useDataChannel } from "../../hooks/useDataChannel.ts"; +import { useDataChannel, useDataChannelMessages } from "../../hooks/useDataChannel.ts"; export function WaitForPeersCard(){ const navigate = useNavigate() - const walletStr = window.localStorage.getItem('wallet'); const wallet = walletStr ? JSON.parse(walletStr) : null; const {socket} = useSocket(); - const address = useAddressQuery(wallet); + // const address = useAddressQuery(wallet); const peerConnection = usePeerConnection((event) => { if (event.candidate) { console.log('Local Candidate', event.candidate.toJSON()) @@ -29,20 +26,17 @@ export function WaitForPeersCard(){ console.log(event) } }); - + const datachannel = useDataChannel("remote", peerConnection) + useDataChannelMessages((event) => { + console.log(event) + }) useEffect(()=>{ - if(!peerConnection) return - // peerConnection.createDataChannel('1') - function handleOnDataChannel(event: RTCDataChannelEvent){ - console.log('Data Channel Event', event.channel) - event.channel.send('Hello World') - } - peerConnection.addEventListener('datachannel', handleOnDataChannel) + if(!datachannel) return - return ()=> { - peerConnection.removeEventListener('datachannel', handleOnDataChannel) - } - }, [peerConnection]) + + // datachannel.send('Hello World') + navigate('/connected') + }, [datachannel]) useEffect(()=>{ if(!peerConnection) return @@ -93,8 +87,7 @@ export function WaitForPeersCard(){ Waiting for Passkey registration for address: - {address.isLoading && } - {address.isFetched && {address.data}} + {wallet}
) From da647ae510dcf93a6d3e8d51320642b016b3565f Mon Sep 17 00:00:00 2001 From: Michael J Feher Date: Wed, 10 Apr 2024 16:03:00 -0400 Subject: [PATCH 06/21] fix: test stubs --- package-lock.json | 97 ++++--------------- .../src/android/android.controller.ts | 1 + 2 files changed, 20 insertions(+), 78 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5da4941..8189f8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5399,6 +5399,15 @@ "@types/node": "*" } }, + "node_modules/@types/hbs": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/hbs/-/hbs-4.0.4.tgz", + "integrity": "sha512-GH3SIb2tzDBnTByUSOIVcD6AcLufnydBllTuFAIAGMhqPNbz8GL4tLryVdNqhq0NQEb5mVpu2FJOrUeqwJrPtg==", + "dev": true, + "dependencies": { + "handlebars": "^4.1.0" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.1", "dev": true, @@ -5411,14 +5420,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "dev": true, @@ -6932,6 +6933,7 @@ }, "node_modules/braces": { "version": "3.0.2", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.0.1" @@ -9011,11 +9013,6 @@ "node": ">= 0.6" } }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, "node_modules/events": { "version": "3.3.0", "dev": true, @@ -9440,6 +9437,7 @@ }, "node_modules/fill-range": { "version": "7.0.1", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -9543,25 +9541,6 @@ "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -10229,19 +10208,6 @@ "node": ">= 0.8" } }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/http-proxy-agent": { "version": "5.0.0", "license": "MIT", @@ -10254,33 +10220,6 @@ "node": ">= 6" } }, - "node_modules/http-proxy-middleware": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.0.tgz", - "integrity": "sha512-36AV1fIaI2cWRzHo+rbcxhe3M3jUDCNzc4D5zRl57sEWRAxdXYtw7FSQKYY6PDKssiAKjLYypbssHk+xs/kMXw==", - "dependencies": { - "@types/http-proxy": "^1.17.10", - "debug": "^4.3.4", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.5" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/http2-wrapper": { "version": "1.0.3", "dev": true, @@ -10615,6 +10554,7 @@ }, "node_modules/is-extglob": { "version": "2.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10637,6 +10577,7 @@ }, "node_modules/is-glob": { "version": "4.0.3", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -10694,6 +10635,7 @@ }, "node_modules/is-number": { "version": "7.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -11978,6 +11920,7 @@ }, "node_modules/micromatch": { "version": "4.0.5", + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.2", @@ -12955,6 +12898,7 @@ }, "node_modules/picomatch": { "version": "2.3.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -13637,11 +13581,6 @@ "node": ">=0.10.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, "node_modules/resolve": { "version": "1.22.4", "license": "MIT", @@ -15131,6 +15070,7 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -16812,7 +16752,7 @@ "express-session": "^1.17.3", "express-socket.io-session": "^1.3.5", "hbs": "^4.2.0", - "http-proxy-middleware": "^3.0.0", + "hi-base32": "^0.5.1", "mongoose": "^7.6.3", "redis": "^4.6.10", "reflect-metadata": "^0.1.13", @@ -16826,6 +16766,7 @@ "@types/express": "^4.17.18", "@types/express-session": "^1.17.8", "@types/express-socket.io-session": "^1.3.7", + "@types/hbs": "^4.0.2", "@types/jest": "^29.5.5", "@types/node": "^20.7.0", "@types/supertest": "^2.0.13", diff --git a/services/liquid-auth-api-js/src/android/android.controller.ts b/services/liquid-auth-api-js/src/android/android.controller.ts index 99255a8..172350a 100644 --- a/services/liquid-auth-api-js/src/android/android.controller.ts +++ b/services/liquid-auth-api-js/src/android/android.controller.ts @@ -1,5 +1,6 @@ import { Controller, Get, Logger, Req, Res } from '@nestjs/common'; import type { Response } from 'express'; +//@ts-ignore, required for jest import assetLinks from '../../assetlinks.json' assert { type: 'json' }; @Controller('.well-known') export class AndroidController { From fb021a4df9330c57b5124fa3d9747b1928578b72 Mon Sep 17 00:00:00 2001 From: Michael J Feher Date: Wed, 10 Apr 2024 17:42:29 -0400 Subject: [PATCH 07/21] test: add signals unit tests --- .../__fixtures__/candidate.fixture.json | 5 ++ .../signals/__fixtures__/sdp.fixtures.json | 4 + .../src/signals/signals.gateway.spec.ts | 68 +++++++++++++++- .../src/signals/signals.gateway.ts | 6 +- .../src/signals/signals.interceptor.spec.ts | 80 ++++++++++++++++++- .../src/signals/signals.interceptor.ts | 12 +-- .../src/signals/signals.module.spec.ts | 14 ++++ .../src/signals/signals.module.ts | 16 ---- 8 files changed, 173 insertions(+), 32 deletions(-) create mode 100644 services/liquid-auth-api-js/src/signals/__fixtures__/candidate.fixture.json create mode 100644 services/liquid-auth-api-js/src/signals/__fixtures__/sdp.fixtures.json create mode 100644 services/liquid-auth-api-js/src/signals/signals.module.spec.ts diff --git a/services/liquid-auth-api-js/src/signals/__fixtures__/candidate.fixture.json b/services/liquid-auth-api-js/src/signals/__fixtures__/candidate.fixture.json new file mode 100644 index 0000000..9380cad --- /dev/null +++ b/services/liquid-auth-api-js/src/signals/__fixtures__/candidate.fixture.json @@ -0,0 +1,5 @@ +{ + "candidate": "candidate:344579997 1 tcp 1517952767 127.0.0.1 52619 typ host tcptype passive generation 0 ufrag NXxR network-id 1", + "sdpMid": "data", + "sdpMLineIndex": 0 +} diff --git a/services/liquid-auth-api-js/src/signals/__fixtures__/sdp.fixtures.json b/services/liquid-auth-api-js/src/signals/__fixtures__/sdp.fixtures.json new file mode 100644 index 0000000..7f39639 --- /dev/null +++ b/services/liquid-auth-api-js/src/signals/__fixtures__/sdp.fixtures.json @@ -0,0 +1,4 @@ +{ + "call": "v=0\no=- 872275877217328554 2 IN IP4 127.0.0.1\ns=-\nt=0 0\na=group:BUNDLE data\na=msid-semantic: WMS\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\nc=IN IP4 0.0.0.0\na=ice-ufrag:NXxR\na=ice-pwd:PGHIKpCaFejy4Sadz+snc5UN\na=ice-options:trickle renomination\na=fingerprint:sha-256 9D:60:CD:39:4A:DC:B5:01:B3:F1:08:84:B9:4E:B5:6B:98:61:57:15:8B:1B:64:D6:C6:5B:AF:3D:5B:EF:48:4B\na=setup:actpass\na=mid:data\na=sctp-port:5000\na=max-message-size:262144", + "answer": "v=0\no=- 1147071363730077050 2 IN IP4 127.0.0.1\ns=-\nt=0 0\na=group:BUNDLE data\na=msid-semantic: WMS\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\nc=IN IP4 0.0.0.0\na=ice-ufrag:v0OA\na=ice-pwd:DzuJ/M1IneOLTOKOMC3XPWWF\na=ice-options:trickle\na=fingerprint:sha-256 25:0A:8E:3E:EB:31:86:0E:EA:2E:32:A1:2D:BE:C4:73:C7:A0:D4:43:D0:A7:B6:BB:C6:D3:68:23:85:C0:18:3C\na=setup:active\na=mid:data\na=sctp-port:5000\na=max-message-size:262144" +} diff --git a/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts b/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts index b65e399..b1a38b2 100644 --- a/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts +++ b/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts @@ -1,5 +1,30 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SignalsGateway } from './signals.gateway'; +import { Server, Socket } from "socket.io"; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const candidateFixture = require('./__fixtures__/candidate.fixture.json'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const sdpFixtures = require('./__fixtures__/sdp.fixtures.json'); + +const clientFixture = { + request: { + session: { + wallet: 'AVALIDWALLETADDRESS', + }, + }, +} as unknown as Socket; + +jest.mock('socket.io', () => { + return { + Server: jest.fn().mockImplementation(() => { + return { + emit: jest.fn(), + in: jest.fn().mockReturnThis(), + }; + }), + }; +}); describe('SignalsGateway', () => { let gateway: SignalsGateway; @@ -10,9 +35,46 @@ describe('SignalsGateway', () => { }).compile(); gateway = module.get(SignalsGateway); + gateway.server = new Server(); }); - - it('should be defined', () => { - expect(gateway).toBeDefined(); + it('should signal a call-description', () => { + gateway.onCallDescription(sdpFixtures.call, clientFixture); + expect(gateway.server.in).toHaveBeenCalledWith( + (clientFixture.request as any).session.wallet, + ); + expect(gateway.server.emit).toHaveBeenCalledWith( + 'call-description', + sdpFixtures.call, + ); + }); + it('should signal a call-candidate', () => { + gateway.onCallCandidate(candidateFixture, clientFixture); + expect(gateway.server.in).toHaveBeenCalledWith( + (clientFixture.request as any).session.wallet, + ); + expect(gateway.server.emit).toHaveBeenCalledWith( + 'call-candidate', + candidateFixture, + ); + }); + it('should signal a answer-description', () => { + gateway.onAnswerDescription(sdpFixtures.answer, clientFixture); + expect(gateway.server.in).toHaveBeenCalledWith( + (clientFixture.request as any).session.wallet, + ); + expect(gateway.server.emit).toHaveBeenCalledWith( + 'answer-description', + sdpFixtures.answer, + ); + }); + it('should signal a answer-candidate', () => { + gateway.onAnswerCandidate(candidateFixture, clientFixture); + expect(gateway.server.in).toHaveBeenCalledWith( + (clientFixture.request as any).session.wallet, + ); + expect(gateway.server.emit).toHaveBeenCalledWith( + 'answer-candidate', + candidateFixture, + ); }); }); diff --git a/services/liquid-auth-api-js/src/signals/signals.gateway.ts b/services/liquid-auth-api-js/src/signals/signals.gateway.ts index d3ddcdf..61b685c 100644 --- a/services/liquid-auth-api-js/src/signals/signals.gateway.ts +++ b/services/liquid-auth-api-js/src/signals/signals.gateway.ts @@ -6,7 +6,7 @@ import { WebSocketServer, } from '@nestjs/websockets'; import { Logger, UseInterceptors } from '@nestjs/common'; -import type { Handshake, Server, Socket } from 'socket.io'; +import type { Server, Socket } from 'socket.io'; import { SignalsInterceptor } from './signals.interceptor.js'; @WebSocketGateway() @@ -28,7 +28,7 @@ export class SignalsGateway { this.server.in(session.wallet).emit('call-candidate', data); } @SubscribeMessage('call-description') - async onCallDescription( + onCallDescription( @MessageBody() data: string, @ConnectedSocket() client: Socket, ) { @@ -42,7 +42,7 @@ export class SignalsGateway { this.server.in(session.wallet).emit('call-description', data); } @SubscribeMessage('answer-description') - async onAnswerDescription( + onAnswerDescription( @MessageBody() data: string, @ConnectedSocket() client: Socket, ) { diff --git a/services/liquid-auth-api-js/src/signals/signals.interceptor.spec.ts b/services/liquid-auth-api-js/src/signals/signals.interceptor.spec.ts index a7e0b1e..96a904c 100644 --- a/services/liquid-auth-api-js/src/signals/signals.interceptor.spec.ts +++ b/services/liquid-auth-api-js/src/signals/signals.interceptor.spec.ts @@ -1,7 +1,83 @@ import { SignalsInterceptor } from './signals.interceptor'; +import { ExecutionContext } from '@nestjs/common'; +import { of } from 'rxjs'; +function createExecutionContextMock(sessionFixture = {}): { + executionContext: ExecutionContext; + disconnect: jest.Mock; +} { + const disconnect = jest.fn(); + return { + executionContext: { + getArgByIndex: jest.fn().mockReturnThis(), + getArgs: jest.fn().mockReturnThis(), + getType: jest.fn().mockReturnThis(), + switchToWs: jest.fn().mockReturnValue({ + getClient: jest.fn().mockReturnValue({ + disconnect, + request: { session: sessionFixture }, + }), + }), + switchToHttp: jest.fn().mockReturnThis(), + switchToRpc: jest.fn().mockReturnThis(), + getClass: () => + ({ + name: 'something', + }) as any, + getHandler: () => + ({ + name: 'something', + }) as any, + }, + disconnect, + }; +} + +const next = { + handle: jest.fn(() => of()), +}; describe('ConnectInterceptor', () => { - it('should be defined', () => { - expect(new SignalsInterceptor()).toBeDefined(); + let interceptor: SignalsInterceptor; + + beforeEach(() => { + interceptor = new SignalsInterceptor(); + }); + it('should disconnect a invalid session', (done) => { + expect(interceptor).toBeDefined(); + const { executionContext, disconnect } = createExecutionContextMock({}); + const response = interceptor.intercept(executionContext, next); + + response.subscribe({ + next: () => { + expect(disconnect).toHaveBeenCalled(); + }, + error: (error) => { + throw error; + }, + complete: () => { + expect(disconnect).toBeCalledTimes(1); + done(); + }, + }); + }); + it('should continue with a valid session', (done) => { + expect(interceptor).toBeDefined(); + const { executionContext, disconnect } = createExecutionContextMock({ + wallet: 'AVALIDWALLETADDRESS', + }); + const response = interceptor.intercept(executionContext, next); + + response.subscribe({ + next: () => { + expect(disconnect).not.toHaveBeenCalled(); + }, + error: (error) => { + throw error; + }, + complete: () => { + expect(disconnect).toBeCalledTimes(0); + done(); + }, + }); }); }); diff --git a/services/liquid-auth-api-js/src/signals/signals.interceptor.ts b/services/liquid-auth-api-js/src/signals/signals.interceptor.ts index f1e2027..38141a1 100644 --- a/services/liquid-auth-api-js/src/signals/signals.interceptor.ts +++ b/services/liquid-auth-api-js/src/signals/signals.interceptor.ts @@ -6,24 +6,20 @@ import { NestInterceptor, } from '@nestjs/common'; import { Observable } from 'rxjs'; -import { type Handshake, Socket } from 'socket.io'; +import { Socket } from 'socket.io'; @Injectable() export class SignalsInterceptor implements NestInterceptor { private readonly logger = new Logger(SignalsInterceptor.name); - async intercept( - context: ExecutionContext, - next: CallHandler, - ): Promise> { + intercept(context: ExecutionContext, next: CallHandler): Observable { const client = context.switchToWs().getClient() as Socket; const request = client.request as Record; const session = request.session as Record; if (typeof session.wallet !== 'string') { - this.logger.error( - `(*) Client ${request.sessionID} is not authenticated`, - ); + this.logger.error(`(*) Client ${request.sessionID} is not authenticated`); client.disconnect(); + return next.handle(); } else { return next.handle(); } diff --git a/services/liquid-auth-api-js/src/signals/signals.module.spec.ts b/services/liquid-auth-api-js/src/signals/signals.module.spec.ts new file mode 100644 index 0000000..5309d9b --- /dev/null +++ b/services/liquid-auth-api-js/src/signals/signals.module.spec.ts @@ -0,0 +1,14 @@ +import { SignalsModule } from './signals.module'; +import { SignalsGateway } from './signals.gateway'; +import { Test } from '@nestjs/testing'; + +describe('SignalsModule', () => { + it('should create the module', async () => { + const module = await Test.createTestingModule({ + imports: [SignalsModule], + }).compile(); + + expect(module).toBeDefined(); + expect(module.get(SignalsGateway)).toBeInstanceOf(SignalsGateway); + }); +}); diff --git a/services/liquid-auth-api-js/src/signals/signals.module.ts b/services/liquid-auth-api-js/src/signals/signals.module.ts index 3014b61..36d094e 100644 --- a/services/liquid-auth-api-js/src/signals/signals.module.ts +++ b/services/liquid-auth-api-js/src/signals/signals.module.ts @@ -1,23 +1,7 @@ import { Module } from '@nestjs/common'; import { SignalsGateway } from './signals.gateway.js'; -import { ClientsModule, Transport } from "@nestjs/microservices"; @Module({ - imports: [ - // TODO: inject configuration - ClientsModule.register([ - { - name: 'ACCOUNT_LINK_SERVICE', - transport: Transport.REDIS, - options: { - host: process.env.REDIS_HOST || 'localhost', - port: parseInt(process.env.REDIS_PORT, 10) || 6379, - username: process.env.REDIS_USERNAME || 'default', - password: process.env.REDIS_PASSWORD || '', - }, - }, - ]), - ], providers: [SignalsGateway], }) export class SignalsModule {} From 1d88a242c551a8e32b1ca733231f0da64d88ba38 Mon Sep 17 00:00:00 2001 From: Michael J Feher Date: Thu, 11 Apr 2024 15:29:15 -0400 Subject: [PATCH 08/21] refactor: add core module and enforce style guides --- clients/liquid-auth-client-js/.eslintrc.json | 20 + clients/liquid-auth-client-js/.prettierrc | 4 + clients/liquid-auth-client-js/package.json | 14 +- .../liquid-auth-client-js/src/assertion.ts | 157 +- .../liquid-auth-client-js/src/attestation.ts | 160 +- clients/liquid-auth-client-js/src/connect.ts | 226 +-- .../liquid-auth-client-js/src/constants.ts | 10 +- clients/liquid-auth-client-js/src/encoding.ts | 73 - clients/liquid-auth-client-js/src/index.ts | 7 +- clients/liquid-auth-client-js/tsconfig.json | 2 +- clients/liquid-auth-core/.eslintrc.json | 20 + clients/liquid-auth-core/.gitignore | 132 ++ clients/liquid-auth-core/package.json | 45 + clients/liquid-auth-core/src/encoding.ts | 135 ++ clients/liquid-auth-core/src/hi-base32.ts | 544 +++++++ clients/liquid-auth-core/src/index.ts | 1 + clients/liquid-auth-core/src/sha512.ts | 1328 +++++++++++++++++ clients/liquid-auth-core/tsconfig.json | 11 + package-lock.json | 1290 ++++++++++------ package.json | 1 + services/liquid-auth-api-js/package.json | 1 + .../src/__mocks__/assertion.service.mock.ts | 2 +- .../src/__mocks__/attestation.service.mock.ts | 2 +- .../src/__mocks__/auth.service.mock.ts | 2 +- .../src/adapters/redis-io.adapter.ts | 3 +- .../src/android/android.controller.spec.ts | 2 +- .../liquid-auth-api-js/src/app.service.ts | 6 +- .../src/assertion/assertion.service.ts | 2 +- .../src/attestation/attestation.service.ts | 3 +- .../src/auth/auth.service.ts | 5 +- .../src/connect/AlgoEncoder.ts | 60 - .../src/connect/connect.controller.ts | 14 +- .../src/connect/connect.gateway.ts | 2 +- services/liquid-auth-api-js/src/main.ts | 2 +- .../liquid-auth-api-js/tests/constants.ts | 4 +- services/liquid-auth-api-js/tsconfig.json | 6 +- sites/dapp-ui/.eslintrc.cjs | 1 + sites/dapp-ui/.prettierrc | 4 + sites/dapp-ui/package.json | 2 + sites/dapp-ui/src/App.tsx | 186 ++- sites/dapp-ui/src/Contexts.tsx | 19 +- sites/dapp-ui/src/Layout.tsx | 142 +- sites/dapp-ui/src/components/Chat.tsx | 140 +- sites/dapp-ui/src/components/Snackbar.tsx | 80 +- .../src/components/user/Credential.tsx | 13 +- .../src/components/user/SessionMenu.tsx | 126 +- .../src/components/user/StatusCard.tsx | 118 +- sites/dapp-ui/src/components/user/types.ts | 18 +- .../src/components/user/useUserState.ts | 17 +- sites/dapp-ui/src/entry-main.tsx | 13 +- sites/dapp-ui/src/hooks/useAddress.ts | 42 +- sites/dapp-ui/src/hooks/useDataChannel.ts | 71 +- sites/dapp-ui/src/hooks/usePeerConnection.ts | 148 +- sites/dapp-ui/src/hooks/useSocket.ts | 11 +- sites/dapp-ui/src/pages/connected.tsx | 84 +- .../src/pages/dashboard/Registered.tsx | 130 +- .../pages/dashboard/WaitForRegistration.tsx | 117 +- .../dapp-ui/src/pages/debug/ManualWebRTC.tsx | 253 ---- sites/dapp-ui/src/pages/debug/WebRTC.tsx | 206 --- sites/dapp-ui/src/pages/home/ConnectModal.tsx | 335 +++-- sites/dapp-ui/src/pages/home/GetStarted.tsx | 79 +- .../src/pages/peering/WaitForPeers.tsx | 160 +- sites/dapp-ui/src/store.ts | 86 +- sites/dapp-ui/src/theme.tsx | 16 +- 64 files changed, 4581 insertions(+), 2332 deletions(-) create mode 100644 clients/liquid-auth-client-js/.eslintrc.json create mode 100644 clients/liquid-auth-client-js/.prettierrc delete mode 100644 clients/liquid-auth-client-js/src/encoding.ts create mode 100644 clients/liquid-auth-core/.eslintrc.json create mode 100644 clients/liquid-auth-core/.gitignore create mode 100644 clients/liquid-auth-core/package.json create mode 100644 clients/liquid-auth-core/src/encoding.ts create mode 100644 clients/liquid-auth-core/src/hi-base32.ts create mode 100644 clients/liquid-auth-core/src/index.ts create mode 100644 clients/liquid-auth-core/src/sha512.ts create mode 100644 clients/liquid-auth-core/tsconfig.json delete mode 100644 services/liquid-auth-api-js/src/connect/AlgoEncoder.ts create mode 100644 sites/dapp-ui/.prettierrc delete mode 100644 sites/dapp-ui/src/pages/debug/ManualWebRTC.tsx delete mode 100644 sites/dapp-ui/src/pages/debug/WebRTC.tsx diff --git a/clients/liquid-auth-client-js/.eslintrc.json b/clients/liquid-auth-client-js/.eslintrc.json new file mode 100644 index 0000000..be26503 --- /dev/null +++ b/clients/liquid-auth-client-js/.eslintrc.json @@ -0,0 +1,20 @@ +{ + "env": { + "browser": true, + "es2021": true, + "node": true + }, + "parserOptions": { + "project": "tsconfig.json", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint/eslint-plugin" + ], + "extends": [ + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "rules": { + } +} diff --git a/clients/liquid-auth-client-js/.prettierrc b/clients/liquid-auth-client-js/.prettierrc new file mode 100644 index 0000000..dcb7279 --- /dev/null +++ b/clients/liquid-auth-client-js/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/clients/liquid-auth-client-js/package.json b/clients/liquid-auth-client-js/package.json index cf17033..d31fe48 100644 --- a/clients/liquid-auth-client-js/package.json +++ b/clients/liquid-auth-client-js/package.json @@ -20,10 +20,6 @@ "./connect": { "default": "./lib/connect.js", "types": "./lib/connect.d.ts" - }, - "./encoding": { - "default": "./lib/encoding.js", - "types": "./lib/encoding.d.ts" } }, "scripts": { @@ -36,13 +32,17 @@ "license": "MIT", "devDependencies": { "@types/qrcode": "^1.5.5", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^7.6.0", "algosdk": "^2.7.0", "c8": "^9.1.0", - "typescript": "^5.3.3" + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "typescript": "^5.4.5" }, "dependencies": { - "hi-base32": "^0.5.1", - "js-sha512": "^0.9.0", + "@liquid/core": "^1.0.0", "qr-code-styling": "^1.6.0-rc.1", "tweetnacl": "github:awesome-algorand/tweetnacl-js" } diff --git a/clients/liquid-auth-client-js/src/assertion.ts b/clients/liquid-auth-client-js/src/assertion.ts index d58771d..0254a56 100644 --- a/clients/liquid-auth-client-js/src/assertion.ts +++ b/clients/liquid-auth-client-js/src/assertion.ts @@ -1,88 +1,95 @@ -import {fromBase64Url, toBase64URL} from "./encoding.js"; -import {DEFAULT_FETCH_OPTIONS} from './constants.js' +import { fromBase64Url, toBase64URL } from '@liquid/core/encoding'; +import { DEFAULT_FETCH_OPTIONS } from './constants.js'; -type SerializedAuthenticatorAssertionResponse = { - [k: string]: string - clientDataJSON: string, - authenticatorData: string, - signature: string, - userHandle: string, +interface SerializedAuthenticatorAssertionResponse { + [k: string]: string; + clientDataJSON: string; + authenticatorData: string; + signature: string; + userHandle: string; } -type SerializedCredential = { - [k: string]: string | SerializedAuthenticatorAssertionResponse - id: string - type: string - response: SerializedAuthenticatorAssertionResponse - rawId: string +interface SerializedCredential { + [k: string]: string | SerializedAuthenticatorAssertionResponse; + id: string; + type: string; + response: SerializedAuthenticatorAssertionResponse; + rawId: string; } export async function assertion(credId: string) { - console.log( - `%cFETCHING: %c/assertion/request/${credId}`, - 'color: yellow', - 'color: cyan', - ); - const options = await fetch(`/assertion/request/${credId}`, { - ...DEFAULT_FETCH_OPTIONS, - }).then((r) => r.json()); + console.log( + `%cFETCHING: %c/assertion/request/${credId}`, + 'color: yellow', + 'color: cyan', + ); + const options = await fetch(`/assertion/request/${credId}`, { + ...DEFAULT_FETCH_OPTIONS, + }).then(async (r) => await r.json()); - if (options.allowCredentials.length === 0) { - console.info('No registered credentials found.'); - return Promise.resolve(null); - } + if (options.allowCredentials.length === 0) { + console.info('No registered credentials found.'); + return await Promise.resolve(null); + } - options.challenge = fromBase64Url(options.challenge); + options.challenge = fromBase64Url(options.challenge as string); - for (const cred of options.allowCredentials) { - cred.id = fromBase64Url(cred.id); - } + for (const cred of options.allowCredentials) { + cred.id = fromBase64Url(cred.id as string); + } - console.log( - '%cGET_CREDENTIAL:%c navigator.credentials.get', - 'color: yellow', - 'color: cyan', - options, - ); - const cred = (await navigator.credentials.get({ - publicKey: options, - })) as PublicKeyCredential; + console.log( + '%cGET_CREDENTIAL:%c navigator.credentials.get', + 'color: yellow', + 'color: cyan', + options, + ); + const cred = (await navigator.credentials.get({ + publicKey: options, + })) as PublicKeyCredential; - if(!cred) throw new Error('Could not get credential') - const response = cred.response as AuthenticatorAssertionResponse & {[k: string]: ArrayBuffer} - const credential: SerializedCredential = { - id: cred.id, - type: cred.type, - rawId: toBase64URL(cred.rawId), - response: Object.keys(response).reduce((prev, curr)=>{ - prev[curr] = toBase64URL(response[curr]) - return prev - }, {} as SerializedAuthenticatorAssertionResponse) - }; - credential.id = cred.id; - credential.type = cred.type; - credential.rawId = toBase64URL(cred.rawId); + if (!cred) throw new Error('Could not get credential'); + const response = cred.response as AuthenticatorAssertionResponse & + Record; + const credential: SerializedCredential = { + id: cred.id, + type: cred.type, + rawId: toBase64URL(cred.rawId), + response: Object.keys( + response, + ).reduce((prev, curr) => { + prev[curr] = toBase64URL(response[curr]); + return prev; + }, {} as SerializedAuthenticatorAssertionResponse), + }; + credential.id = cred.id; + credential.type = cred.type; + credential.rawId = toBase64URL(cred.rawId); - if (cred.response) { - - const clientDataJSON = toBase64URL(response.clientDataJSON); - const authenticatorData = toBase64URL(response.authenticatorData); - const signature = toBase64URL(response.signature); - const userHandle = toBase64URL(response.userHandle || new Uint8Array()); - credential.response = { - clientDataJSON, - authenticatorData, - signature, - userHandle, - }; - } - console.log( - '%cPOSTING: %c/assertion/response', - 'color: yellow', - 'color: cyan', - credential, + if (cred.response) { + const clientDataJSON = toBase64URL(response.clientDataJSON); + const authenticatorData = toBase64URL(response.authenticatorData); + const signature = toBase64URL(response.signature); + const userHandle = toBase64URL( + typeof response?.userHandle !== 'undefined' && + response.userHandle !== null + ? response.userHandle + : new Uint8Array(), ); - return await fetch(`/assertion/response`, { - ...DEFAULT_FETCH_OPTIONS, - body: JSON.stringify(credential), - }); + credential.response = { + clientDataJSON, + authenticatorData, + signature, + userHandle, + }; + } + console.log( + '%cPOSTING: %c/assertion/response', + 'color: yellow', + 'color: cyan', + credential, + ); + return await fetch('/assertion/response', { + ...DEFAULT_FETCH_OPTIONS, + body: JSON.stringify(credential), + }); } diff --git a/clients/liquid-auth-client-js/src/attestation.ts b/clients/liquid-auth-client-js/src/attestation.ts index f51030e..a6f8286 100644 --- a/clients/liquid-auth-client-js/src/attestation.ts +++ b/clients/liquid-auth-client-js/src/attestation.ts @@ -1,27 +1,27 @@ -import {fromBase64Url, toBase64URL} from "./encoding.js"; -import {DEFAULT_FETCH_OPTIONS} from './constants.js' +import { fromBase64Url, toBase64URL } from '@liquid/core/encoding'; +import { DEFAULT_FETCH_OPTIONS } from './constants.js'; const DEFAULT_ATTESTATION_OPTIONS = { - attestationType: 'none', - authenticatorSelection: { - authenticatorAttachment: 'platform', - userVerification: 'required', - requireResidentKey: false, - }, -} -export type EncodedAuthenticatorAttestationResponse = { - [k: string]: string - clientDataJSON: string, - attestationObject: string, - signature?: string, - userHandle?: string, + attestationType: 'none', + authenticatorSelection: { + authenticatorAttachment: 'platform', + userVerification: 'required', + requireResidentKey: false, + }, +}; +export interface EncodedAuthenticatorAttestationResponse { + [k: string]: string | undefined; + clientDataJSON: string; + attestationObject: string; + signature?: string; + userHandle?: string; } -export type EncodedAttestationCredential = { - [k: string]: string | EncodedAuthenticatorAttestationResponse - id: string - type: string - response: EncodedAuthenticatorAttestationResponse - rawId: string +export interface EncodedAttestationCredential { + [k: string]: string | EncodedAuthenticatorAttestationResponse; + id: string; + type: string; + response: EncodedAuthenticatorAttestationResponse; + rawId: string; } /** @@ -29,49 +29,49 @@ export type EncodedAttestationCredential = { * * @param credential - PublicKeyCredential from navigator.credentials.create */ -function encodeAttestationCredential(credential: PublicKeyCredential): EncodedAttestationCredential { - const response = credential.response as AuthenticatorAttestationResponse - return { - id: credential.id, - rawId: toBase64URL(credential.rawId), - type: credential.type, - response: { - clientDataJSON: toBase64URL(response.clientDataJSON), - attestationObject: toBase64URL(response.attestationObject) - } - }; +function encodeAttestationCredential( + credential: PublicKeyCredential, +): EncodedAttestationCredential { + const response = credential.response as AuthenticatorAttestationResponse; + return { + id: credential.id, + rawId: toBase64URL(credential.rawId), + type: credential.type, + response: { + clientDataJSON: toBase64URL(response.clientDataJSON), + attestationObject: toBase64URL(response.attestationObject), + }, + }; } /** * Decoding an Encoded Attestation Credential * @param credential - Encoded Attestation Credential */ -function decodeAttestationCredential(credential: EncodedAttestationCredential){ - return { - id: credential.id, - rawId: fromBase64Url(credential.rawId), - type: credential.type, - response: { - clientDataJSON: fromBase64Url(credential.response.clientDataJSON), - attestationObject: fromBase64Url(credential.response.attestationObject) - } - } +function decodeAttestationCredential(credential: EncodedAttestationCredential) { + return { + id: credential.id, + rawId: fromBase64Url(credential.rawId), + type: credential.type, + response: { + clientDataJSON: fromBase64Url(credential.response.clientDataJSON), + attestationObject: fromBase64Url(credential.response.attestationObject), + }, + }; } -function decodeAttestationOptions(options){ - const attestationOptions = {...options} - attestationOptions.user.id = fromBase64Url(options.user.id); - attestationOptions.challenge = fromBase64Url( - options.challenge, - ); +function decodeAttestationOptions(options) { + const attestationOptions = { ...options }; + attestationOptions.user.id = fromBase64Url(options.user.id); + attestationOptions.challenge = fromBase64Url(options.challenge); - if (attestationOptions.excludeCredentials) { - for (let cred of attestationOptions.excludeCredentials) { - cred.id = fromBase64Url(cred.id); - } + if (attestationOptions.excludeCredentials) { + for (const cred of attestationOptions.excludeCredentials) { + cred.id = fromBase64Url(cred.id); } + } - return attestationOptions + return attestationOptions; } /** @@ -80,11 +80,14 @@ function decodeAttestationOptions(options){ * @param origin * @param options */ -export async function fetchAttestationRequest(origin: string, options = DEFAULT_ATTESTATION_OPTIONS){ - return fetch(`${origin}/attestation/request`, { - ...DEFAULT_FETCH_OPTIONS, - body: JSON.stringify(options), - }); +export async function fetchAttestationRequest( + origin: string, + options = DEFAULT_ATTESTATION_OPTIONS, +) { + return await fetch(`${origin}/attestation/request`, { + ...DEFAULT_FETCH_OPTIONS, + body: JSON.stringify(options), + }); } /** @@ -93,11 +96,14 @@ export async function fetchAttestationRequest(origin: string, options = DEFAULT_ * @param origin * @param credential */ -export async function fetchAttestationResponse(origin: string, credential: EncodedAttestationCredential){ - return fetch(`${origin}/attestation/response`, { - ...DEFAULT_FETCH_OPTIONS, - body: JSON.stringify(credential), - }); +export async function fetchAttestationResponse( + origin: string, + credential: EncodedAttestationCredential, +) { + return await fetch(`${origin}/attestation/response`, { + ...DEFAULT_FETCH_OPTIONS, + body: JSON.stringify(credential), + }); } /** @@ -111,21 +117,23 @@ export async function fetchAttestationResponse(origin: string, credential: Encod * @return {Promise} */ export async function attestation( - origin: string, - options = DEFAULT_ATTESTATION_OPTIONS, + origin: string, + options = DEFAULT_ATTESTATION_OPTIONS, ) { - const encodedAttestationOptions = await fetchAttestationRequest(origin, options) - .then((r) => r.json()); + const encodedAttestationOptions = await fetchAttestationRequest( + origin, + options, + ).then(async (r) => await r.json()); - if (typeof encodedAttestationOptions.error !== 'undefined') { - throw new Error(encodedAttestationOptions.error); - } + if (typeof encodedAttestationOptions.error !== 'undefined') { + throw new Error(encodedAttestationOptions.error); + } - const credential = encodeAttestationCredential( - (await navigator.credentials.create({ - publicKey: decodeAttestationOptions(encodedAttestationOptions), - }) as PublicKeyCredential) - ); + const credential = encodeAttestationCredential( + (await navigator.credentials.create({ + publicKey: decodeAttestationOptions(encodedAttestationOptions), + })) as PublicKeyCredential, + ); - return fetchAttestationResponse(origin, credential); + return await fetchAttestationResponse(origin, credential); } diff --git a/clients/liquid-auth-client-js/src/connect.ts b/clients/liquid-auth-client-js/src/connect.ts index c747212..b680b21 100644 --- a/clients/liquid-auth-client-js/src/connect.ts +++ b/clients/liquid-auth-client-js/src/connect.ts @@ -1,114 +1,136 @@ -import {DEFAULT_FETCH_OPTIONS} from "./constants.js"; -import type {Account} from 'algosdk' -import type {SignKeyPair} from 'tweetnacl' -import {sign} from 'tweetnacl' -import {toBase64URL, encodeAddress} from './encoding.js' - +import { DEFAULT_FETCH_OPTIONS } from './constants.js'; +import type { Account } from 'algosdk'; +import type { SignKeyPair } from 'tweetnacl'; +import { sign } from 'tweetnacl'; +import { toBase64URL, encodeAddress } from '@liquid/core/encoding'; export class Message { - /** - * Origin of the Request - */ - origin: string; - /** - * Challenge to be signed - */ - challenge: string; - /** - * Linking Request ID - */ - requestId: number; - /** - * Label for the remote Service - */ - label?: string; - /** - * Address that signed the message - */ - wallet?: string; - /** - * Signature of the challenge - */ - signature?: string; - constructor(origin: string, challenge: string, requestId: number, label?: string) { - this.origin = origin - this.challenge = challenge - this.requestId = requestId - this.label = label - } - static async fromResponse(response: Response|Message){ - const msg = response instanceof Response ? await response.json(): response; - return new Message(msg.origin, msg.challenge, msg.requestId) - } + /** + * Origin of the Request + */ + origin: string; + /** + * Challenge to be signed + */ + challenge: string; + /** + * Linking Request ID + */ + requestId: number; + /** + * Label for the remote Service + */ + label?: string; + /** + * Address that signed the message + */ + wallet?: string; + /** + * Signature of the challenge + */ + signature?: string; + constructor( + origin: string, + challenge: string, + requestId: number, + label?: string, + ) { + this.origin = origin; + this.challenge = challenge; + this.requestId = requestId; + this.label = label; + } - /** - * Sign Message with Wallet Key - * - * @param key - */ - sign(key: string | Account | Uint8Array | SignKeyPair): Message{ - const encoder = new TextEncoder() - let keyPair: SignKeyPair + static async fromResponse(response: Response | Message) { + const msg = response instanceof Response ? await response.json() : response; + return new Message(msg.origin, msg.challenge, msg.requestId); + } - // Seed or Secret Key - if(key instanceof Uint8Array){ - if(key.length === 32){ - keyPair = sign.keyPair.fromSeed(key) - } else if(key.length === 64){ - keyPair = sign.keyPair.fromSecretKey(key) - } else { - throw new TypeError('Invalid seed or secret key') - } - } + /** + * Sign Message with Wallet Key + * + * @param key + */ + sign(key: string | Account | Uint8Array | SignKeyPair): this { + const encoder = new TextEncoder(); + let keyPair: SignKeyPair | null = null; - // Algorand SDK - if(typeof (key as Account).addr !== 'undefined' && typeof (key as Account).addr === 'string'){ - keyPair = sign.keyPair.fromSecretKey((key as Account).sk) - } + // Seed or Secret Key + if (key instanceof Uint8Array) { + if (key.length === 32) { + keyPair = sign.keyPair.fromSeed(key); + } else if (key.length === 64) { + keyPair = sign.keyPair.fromSecretKey(key); + } else { + throw new TypeError('Invalid seed or secret key'); + } + } - // NACL - if((key as SignKeyPair).publicKey instanceof Uint8Array && (key as SignKeyPair).secretKey instanceof Uint8Array){ - console.log('nacl') - keyPair = key as SignKeyPair - } - this.signature = toBase64URL(sign.detached(encoder.encode(this.challenge), keyPair.secretKey)); - this.wallet = encodeAddress(keyPair.publicKey) - return this; + // Algorand SDK + if ( + typeof (key as Account).addr !== 'undefined' && + typeof (key as Account).addr === 'string' + ) { + keyPair = sign.keyPair.fromSecretKey((key as Account).sk); } - toString(){ - let optional: {wallet?: string, signature?: string, label?: string} = {} + // NACL + if ( + (key as SignKeyPair).publicKey instanceof Uint8Array && + (key as SignKeyPair).secretKey instanceof Uint8Array + ) { + console.log('nacl'); + keyPair = key as SignKeyPair; + } + if (keyPair === null) { + throw new TypeError('Invalid key'); + } + this.signature = toBase64URL( + sign.detached(encoder.encode(this.challenge), keyPair.secretKey), + ); + this.wallet = encodeAddress(keyPair.publicKey); + return this; + } - if(typeof this.wallet === 'string'){ - optional.wallet = this.wallet; - } + toString(): string { + const optional: { wallet?: string; signature?: string; label?: string } = + {}; - if(typeof this.signature === 'string'){ - optional.signature = this.signature - } + if (typeof this.wallet === 'string') { + optional.wallet = this.wallet; + } - if(typeof this.label === 'string'){ - optional.label = this.label - } + if (typeof this.signature === 'string') { + optional.signature = this.signature; + } - return JSON.stringify({ origin: this.origin, requestId: this.requestId, challenge: this.challenge, ...optional }) + if (typeof this.label === 'string') { + optional.label = this.label; } + + return JSON.stringify({ + origin: this.origin, + requestId: this.requestId, + challenge: this.challenge, + ...optional, + }); + } } -export async function fetchConnectRequest(origin: string, requestId: number){ - return fetch(`${origin}/connect/request`, { - ...DEFAULT_FETCH_OPTIONS, - body: JSON.stringify({ requestId }), - }) +export async function fetchConnectRequest(origin: string, requestId: number) { + return await fetch(`${origin}/connect/request`, { + ...DEFAULT_FETCH_OPTIONS, + body: JSON.stringify({ requestId }), + }); } -export async function fetchConnectResponse(msg: Message){ - if(typeof msg.signature === 'undefined'){ - throw new TypeError('Message must be signed!') - } - return fetch('/connect/response', { - ...DEFAULT_FETCH_OPTIONS, - body: JSON.stringify(msg), - }) +export async function fetchConnectResponse(msg: Message) { + if (typeof msg.signature === 'undefined') { + throw new TypeError('Message must be signed!'); + } + return await fetch('/connect/response', { + ...DEFAULT_FETCH_OPTIONS, + body: JSON.stringify(msg), + }); } /** @@ -117,8 +139,14 @@ export async function fetchConnectResponse(msg: Message){ * @param requestId * @param key */ -export async function connect(origin: string, requestId: number, key: string | Account | Uint8Array | SignKeyPair){ - const msg = await Message.fromResponse(await fetchConnectRequest(origin, requestId)) - msg.sign(key) - return await fetchConnectResponse(msg) +export async function connect( + origin: string, + requestId: number, + key: string | Account | Uint8Array | SignKeyPair, +) { + const msg = await Message.fromResponse( + await fetchConnectRequest(origin, requestId), + ); + msg.sign(key); + return await fetchConnectResponse(msg); } diff --git a/clients/liquid-auth-client-js/src/constants.ts b/clients/liquid-auth-client-js/src/constants.ts index 40fe56a..c1327e5 100644 --- a/clients/liquid-auth-client-js/src/constants.ts +++ b/clients/liquid-auth-client-js/src/constants.ts @@ -1,6 +1,6 @@ export const DEFAULT_FETCH_OPTIONS = { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, -} + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, +}; diff --git a/clients/liquid-auth-client-js/src/encoding.ts b/clients/liquid-auth-client-js/src/encoding.ts deleted file mode 100644 index 5204fc7..0000000 --- a/clients/liquid-auth-client-js/src/encoding.ts +++ /dev/null @@ -1,73 +0,0 @@ -import {sign} from "tweetnacl"; -import base32 from "hi-base32"; -import {sha512_256} from "js-sha512"; - -const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; - -/** - * Bytes to Base64URL - * @param {Uint8Array| ArrayBuffer} arr Bytes to convert to URL safe Base64 - */ -export function toBase64URL(arr: Uint8Array | ArrayBuffer): string { - let bytes = arr instanceof Uint8Array ? arr : new Uint8Array(arr); - let len = bytes.length - let base64 = ""; - - for (let i = 0; i < len; i+=3) { - base64 += chars[bytes[i] >> 2]; - base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; - base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; - base64 += chars[bytes[i + 2] & 63]; - } - - if ((len % 3) === 2) { - base64 = base64.substring(0, base64.length - 1); - } else if (len % 3 === 1) { - base64 = base64.substring(0, base64.length - 2); - } - - return base64; -} - -/** - * Base64URL to Bytes - * @param {string} base64url URL safe Base64 string - */ -export function fromBase64Url(base64url: string): Uint8Array { - if(typeof base64url !== 'string'){ - throw new TypeError('Must be string!') - } - return new Uint8Array( - atob(base64url.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, '')) - .split('') - .map((c) => c.charCodeAt(0)), - ); -} - -function concatArrays(...arrs: ArrayLike[]) { - const size = arrs.reduce((sum, arr) => sum + arr.length, 0); - const c = new Uint8Array(size); - - let offset = 0; - for (let i = 0; i < arrs.length; i++) { - c.set(arrs[i], offset); - offset += arrs[i].length; - } - - return c; -} - -const ALGORAND_CHECKSUM_BYTE_LENGTH = 4; -const ALGORAND_ADDRESS_LENGTH = 58; -export function encodeAddress(address: Uint8Array) { - // compute checksum - const checksum = - sha512_256.array(address) - .slice( - sign.publicKeyLength - ALGORAND_CHECKSUM_BYTE_LENGTH, - sign.publicKeyLength - ); - const addr = base32.encode(concatArrays(address, checksum)); - - return addr.toString().slice(0, ALGORAND_ADDRESS_LENGTH); // removing the extra '====' -} diff --git a/clients/liquid-auth-client-js/src/index.ts b/clients/liquid-auth-client-js/src/index.ts index c49e070..619109e 100644 --- a/clients/liquid-auth-client-js/src/index.ts +++ b/clients/liquid-auth-client-js/src/index.ts @@ -1,4 +1,3 @@ -export * from './assertion.js' -export * from './attestation.js' -export * from './encoding.js' -export * from './connect.js' +export * from './assertion.js'; +export * from './attestation.js'; +export * from './connect.js'; diff --git a/clients/liquid-auth-client-js/tsconfig.json b/clients/liquid-auth-client-js/tsconfig.json index f587ad2..02cf9d0 100644 --- a/clients/liquid-auth-client-js/tsconfig.json +++ b/clients/liquid-auth-client-js/tsconfig.json @@ -5,6 +5,6 @@ "declaration": true, "module": "NodeNext", "moduleResolution": "NodeNext", - "skipLibCheck": true + "skipLibCheck": true, } } diff --git a/clients/liquid-auth-core/.eslintrc.json b/clients/liquid-auth-core/.eslintrc.json new file mode 100644 index 0000000..be26503 --- /dev/null +++ b/clients/liquid-auth-core/.eslintrc.json @@ -0,0 +1,20 @@ +{ + "env": { + "browser": true, + "es2021": true, + "node": true + }, + "parserOptions": { + "project": "tsconfig.json", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint/eslint-plugin" + ], + "extends": [ + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "rules": { + } +} diff --git a/clients/liquid-auth-core/.gitignore b/clients/liquid-auth-core/.gitignore new file mode 100644 index 0000000..8813e9f --- /dev/null +++ b/clients/liquid-auth-core/.gitignore @@ -0,0 +1,132 @@ +lib + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/clients/liquid-auth-core/package.json b/clients/liquid-auth-core/package.json new file mode 100644 index 0000000..4d97a06 --- /dev/null +++ b/clients/liquid-auth-core/package.json @@ -0,0 +1,45 @@ +{ + "name": "@liquid/core", + "version": "1.0.0", + "description": "Utilities for Liquid Auth JS", + "main": "./lib/index.js", + "type": "module", + "exports": { + ".": { + "default": "./lib/index.js", + "types": "./lib/index.d.ts" + }, + "./hi-base32": { + "default": "./lib/hi-base32.js", + "types": "./lib/hi-base32.d.ts" + }, + "./sha512": { + "default": "./lib/sha512.js", + "types": "./lib/sha512.d.ts" + }, + "./encoding": { + "default": "./lib/encoding.js", + "types": "./lib/encoding.d.ts" + } + }, + "scripts": { + "dev": "tsc --watch", + "build": "tsc", + "preinstall": "npm run build", + "test": "tsc && c8 node --test ./tests/connect.test.js" + }, + "author": "", + "license": "MIT", + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^7.6.0", + "c8": "^9.1.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "typescript": "^5.4.5" + }, + "dependencies": { + "tweetnacl": "github:awesome-algorand/tweetnacl-js" + } +} diff --git a/clients/liquid-auth-core/src/encoding.ts b/clients/liquid-auth-core/src/encoding.ts new file mode 100644 index 0000000..acb8761 --- /dev/null +++ b/clients/liquid-auth-core/src/encoding.ts @@ -0,0 +1,135 @@ +import * as nacl from "tweetnacl"; +import { decodeAsBytes, encode } from "./hi-base32.js"; +import { createMethod } from "./sha512.js"; +const sha512_256 = createMethod(256); +const chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; +const ALGORAND_PUBLIC_KEY_BYTE_LENGTH = 32; +const ALGORAND_ADDRESS_BYTE_LENGTH = 36; +const ALGORAND_CHECKSUM_BYTE_LENGTH = 4; +const ALGORAND_ADDRESS_LENGTH = 58; +const HASH_BYTES_LENGTH = 32; +export const MALFORMED_ADDRESS_ERROR_MSG = "Malformed address"; +export const ALGORAND_ADDRESS_BAD_CHECKSUM_ERROR_MSG = "Bad checksum"; +/** + * Bytes to Base64URL + * @param {Uint8Array| ArrayBuffer} arr Bytes to convert to URL safe Base64 + */ +export function toBase64URL(arr: Uint8Array | ArrayBuffer): string { + const bytes = arr instanceof Uint8Array ? arr : new Uint8Array(arr); + const len = bytes.length; + let base64 = ""; + + for (let i = 0; i < len; i += 3) { + base64 += chars[bytes[i] >> 2]; + base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; + base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; + base64 += chars[bytes[i + 2] & 63]; + } + + if (len % 3 === 2) { + base64 = base64.substring(0, base64.length - 1); + } else if (len % 3 === 1) { + base64 = base64.substring(0, base64.length - 2); + } + + return base64; +} + +/** + * Base64URL to Bytes + * @param {string} base64url URL safe Base64 string + */ +export function fromBase64Url(base64url: string): Uint8Array { + if (typeof base64url !== "string") { + throw new TypeError("Must be string!"); + } + return new Uint8Array( + atob(base64url.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, "")) + .split("") + .map((c) => c.charCodeAt(0)), + ); +} + +function concatArrays(...arrs: Array>) { + const size = arrs.reduce((sum, arr) => sum + arr.length, 0); + const c = new Uint8Array(size); + + let offset = 0; + for (let i = 0; i < arrs.length; i++) { + c.set(arrs[i], offset); + offset += arrs[i].length; + } + + return c; +} + +export function encodeAddress(address: Uint8Array) { + // compute checksum + const checksum = sha512_256 + .array(address) + .slice( + nacl.sign.publicKeyLength - ALGORAND_CHECKSUM_BYTE_LENGTH, + nacl.sign.publicKeyLength, + ); + const addr = encode(concatArrays(address, checksum)); + + return addr.toString().slice(0, ALGORAND_ADDRESS_LENGTH); // removing the extra '====' +} +/** + * decodeAddress takes an Algorand address in string form and decodes it into a Uint8Array. + * @param address - an Algorand address with checksum. + * @returns the decoded form of the address's public key and checksum + */ +export function decodeAddress(address: string): Uint8Array { + if ( + typeof address !== "string" || + address.length !== ALGORAND_ADDRESS_LENGTH + ) { + throw new Error(MALFORMED_ADDRESS_ERROR_MSG); + } + + // try to decode + const decoded = decodeAsBytes(address.toString()); + + // Find publickey and checksum + const pk = new Uint8Array( + decoded.slice( + 0, + ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH, + ), + ); + const cs = new Uint8Array( + decoded.slice( + ALGORAND_PUBLIC_KEY_BYTE_LENGTH, + ALGORAND_ADDRESS_BYTE_LENGTH, + ), + ); + + // Compute checksum + const checksum = sha512_256 + .array(pk) + .slice( + HASH_BYTES_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH, + HASH_BYTES_LENGTH, + ); + + // Check if the checksum and the address are equal + if ( + checksum.length !== cs.length || + !Array.from(checksum).every((val, i) => val === cs[i]) + ) { + throw new Error(ALGORAND_ADDRESS_BAD_CHECKSUM_ERROR_MSG); + } + + return pk; +} + +export function base64ToUint8Array(encoded) { + return new Uint8Array( + // TODO: Cross-platform solution since atob is deprecated in Node + atob(encoded) + .split("") + .map((c) => c.charCodeAt(0)), + ); +} diff --git a/clients/liquid-auth-core/src/hi-base32.ts b/clients/liquid-auth-core/src/hi-base32.ts new file mode 100644 index 0000000..4864374 --- /dev/null +++ b/clients/liquid-auth-core/src/hi-base32.ts @@ -0,0 +1,544 @@ +/* + * [hi-base32]{@link https://github.com/emn178/hi-base32} + * + * @version 0.5.1 + * @author Chen, Yi-Cyuan [emn178@gmail.com] + * @copyright Chen, Yi-Cyuan 2015-2021 + * @license MIT + */ +const BASE32_ENCODE_CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".split(""); +const BASE32_DECODE_CHAR: Record = { + A: 0, + B: 1, + C: 2, + D: 3, + E: 4, + F: 5, + G: 6, + H: 7, + I: 8, + J: 9, + K: 10, + L: 11, + M: 12, + N: 13, + O: 14, + P: 15, + Q: 16, + R: 17, + S: 18, + T: 19, + U: 20, + V: 21, + W: 22, + X: 23, + Y: 24, + Z: 25, + 2: 26, + 3: 27, + 4: 28, + 5: 29, + 6: 30, + 7: 31, +}; + +const blocks = [0, 0, 0, 0, 0, 0, 0, 0]; + +const throwInvalidUtf8 = function (position: number, partial: string) { + if (partial.length > 10) { + partial = "..." + partial.substr(-10); + } + const err: Error & { position?: any } = new Error( + "Decoded data is not valid UTF-8." + + " Maybe try base32.decode.asBytes()?" + + " Partial data after reading " + + position + + " bytes: " + + partial + + " <-", + ); + err.position = position; + throw err; +}; + +const toUtf8String = function (bytes: number[]) { + let str = ""; + const length = bytes.length; + let i = 0; + let followingChars = 0; + let b; + let c; + while (i < length) { + b = bytes[i++]; + if (b <= 0x7f) { + str += String.fromCharCode(b); + continue; + } else if (b > 0xbf && b <= 0xdf) { + c = b & 0x1f; + followingChars = 1; + } else if (b <= 0xef) { + c = b & 0x0f; + followingChars = 2; + } else if (b <= 0xf7) { + c = b & 0x07; + followingChars = 3; + } else { + throwInvalidUtf8(i, str); + } + + if (typeof c === "undefined") throw new Error("c is undefined"); + + for (let j = 0; j < followingChars; ++j) { + b = bytes[i++]; + if (b < 0x80 || b > 0xbf) { + throwInvalidUtf8(i, str); + } + c <<= 6; + c += b & 0x3f; + } + if (c >= 0xd800 && c <= 0xdfff) { + throwInvalidUtf8(i, str); + } + if (c > 0x10ffff) { + throwInvalidUtf8(i, str); + } + + if (c <= 0xffff) { + str += String.fromCharCode(c); + } else { + c -= 0x10000; + str += String.fromCharCode((c >> 10) + 0xd800); + str += String.fromCharCode((c & 0x3ff) + 0xdc00); + } + } + return str; +}; + +export const decodeAsBytes = function (base32Str: string): number[] { + if (base32Str === "") { + return []; + } else if (!/^[A-Z2-7=]+$/.test(base32Str)) { + throw new Error("Invalid base32 characters"); + } + base32Str = base32Str.replace(/=/g, ""); + let v1; + let v2; + let v3; + let v4; + let v5; + let v6; + let v7; + let v8; + const bytes: number[] = []; + let index = 0; + const length = base32Str.length; + + // 4 char to 3 bytes + for (var i = 0, count = (length >> 3) << 3; i < count; ) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v8 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = ((v1 << 3) | (v2 >>> 2)) & 255; + bytes[index++] = ((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255; + bytes[index++] = ((v4 << 4) | (v5 >>> 1)) & 255; + bytes[index++] = ((v5 << 7) | (v6 << 2) | (v7 >>> 3)) & 255; + bytes[index++] = ((v7 << 5) | v8) & 255; + } + + // remain bytes + const remain = length - count; + if (remain === 2) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = ((v1 << 3) | (v2 >>> 2)) & 255; + } else if (remain === 4) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = ((v1 << 3) | (v2 >>> 2)) & 255; + bytes[index++] = ((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255; + } else if (remain === 5) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = ((v1 << 3) | (v2 >>> 2)) & 255; + bytes[index++] = ((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255; + bytes[index++] = ((v4 << 4) | (v5 >>> 1)) & 255; + } else if (remain === 7) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = ((v1 << 3) | (v2 >>> 2)) & 255; + bytes[index++] = ((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255; + bytes[index++] = ((v4 << 4) | (v5 >>> 1)) & 255; + bytes[index++] = ((v5 << 7) | (v6 << 2) | (v7 >>> 3)) & 255; + } + return bytes; +}; + +const encodeAscii = function (str: string) { + let v1; + let v2; + let v3; + let v4; + let v5; + let base32Str = ""; + const length = str.length; + for ( + var i = 0, count = parseInt((length / 5) as unknown as string) * 5; + i < count; + + ) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i++); + v3 = str.charCodeAt(i++); + v4 = str.charCodeAt(i++); + v5 = str.charCodeAt(i++); + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + + BASE32_ENCODE_CHAR[((v3 << 1) | (v4 >>> 7)) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[((v4 << 3) | (v5 >>> 5)) & 31] + + BASE32_ENCODE_CHAR[v5 & 31]; + } + + // remain char + const remain = length - count; + if (remain === 1) { + v1 = str.charCodeAt(i); + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2) & 31] + + "======"; + } else if (remain === 2) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i); + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4) & 31] + + "===="; + } else if (remain === 3) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i++); + v3 = str.charCodeAt(i); + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1) & 31] + + "==="; + } else if (remain === 4) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i++); + v3 = str.charCodeAt(i++); + v4 = str.charCodeAt(i); + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + + BASE32_ENCODE_CHAR[((v3 << 1) | (v4 >>> 7)) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3) & 31] + + "="; + } + return base32Str; +}; + +const encodeUtf8 = function (str: string) { + let v1; + let v2; + let v3; + let v4; + let v5; + let code; + let end = false; + let base32Str = ""; + let index = 0; + let i; + let start = 0; + let bytes = 0; + const length = str.length; + if (str === "") { + return base32Str; + } + do { + blocks[0] = blocks[5]; + blocks[1] = blocks[6]; + blocks[2] = blocks[7]; + for (i = start; index < length && i < 5; ++index) { + code = str.charCodeAt(index); + if (code < 0x80) { + blocks[i++] = code; + } else if (code < 0x800) { + blocks[i++] = 0xc0 | (code >> 6); + blocks[i++] = 0x80 | (code & 0x3f); + } else if (code < 0xd800 || code >= 0xe000) { + blocks[i++] = 0xe0 | (code >> 12); + blocks[i++] = 0x80 | ((code >> 6) & 0x3f); + blocks[i++] = 0x80 | (code & 0x3f); + } else { + code = + 0x10000 + + (((code & 0x3ff) << 10) | (str.charCodeAt(++index) & 0x3ff)); + blocks[i++] = 0xf0 | (code >> 18); + blocks[i++] = 0x80 | ((code >> 12) & 0x3f); + blocks[i++] = 0x80 | ((code >> 6) & 0x3f); + blocks[i++] = 0x80 | (code & 0x3f); + } + } + bytes += i - start; + start = i - 5; + if (index === length) { + ++index; + } + if (index > length && i < 6) { + end = true; + } + v1 = blocks[0]; + if (i > 4) { + v2 = blocks[1]; + v3 = blocks[2]; + v4 = blocks[3]; + v5 = blocks[4]; + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + + BASE32_ENCODE_CHAR[((v3 << 1) | (v4 >>> 7)) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[((v4 << 3) | (v5 >>> 5)) & 31] + + BASE32_ENCODE_CHAR[v5 & 31]; + } else if (i === 1) { + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2) & 31] + + "======"; + } else if (i === 2) { + v2 = blocks[1]; + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4) & 31] + + "===="; + } else if (i === 3) { + v2 = blocks[1]; + v3 = blocks[2]; + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1) & 31] + + "==="; + } else { + v2 = blocks[1]; + v3 = blocks[2]; + v4 = blocks[3]; + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + + BASE32_ENCODE_CHAR[((v3 << 1) | (v4 >>> 7)) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3) & 31] + + "="; + } + } while (!end); + return base32Str; +}; + +const encodeBytes = function (bytes: Uint8Array) { + let v1; + let v2; + let v3; + let v4; + let v5; + let base32Str = ""; + const length = bytes.length; + for ( + var i = 0, count = parseInt((length / 5) as unknown as string) * 5; + i < count; + + ) { + v1 = bytes[i++]; + v2 = bytes[i++]; + v3 = bytes[i++]; + v4 = bytes[i++]; + v5 = bytes[i++]; + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + + BASE32_ENCODE_CHAR[((v3 << 1) | (v4 >>> 7)) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[((v4 << 3) | (v5 >>> 5)) & 31] + + BASE32_ENCODE_CHAR[v5 & 31]; + } + + // remain char + const remain = length - count; + if (remain === 1) { + v1 = bytes[i]; + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2) & 31] + + "======"; + } else if (remain === 2) { + v1 = bytes[i++]; + v2 = bytes[i]; + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4) & 31] + + "===="; + } else if (remain === 3) { + v1 = bytes[i++]; + v2 = bytes[i++]; + v3 = bytes[i]; + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1) & 31] + + "==="; + } else if (remain === 4) { + v1 = bytes[i++]; + v2 = bytes[i++]; + v3 = bytes[i++]; + v4 = bytes[i]; + base32Str += + BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + + BASE32_ENCODE_CHAR[((v3 << 1) | (v4 >>> 7)) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3) & 31] + + "="; + } + return base32Str; +}; + +export const encode = function ( + input: string | ArrayBuffer | Uint8Array | number[], + asciiOnly?: boolean, +) { + const notString = typeof input !== "string"; + if (notString && input.constructor === ArrayBuffer) { + input = new Uint8Array(input); + } + if (notString) { + return encodeBytes(input as Uint8Array); + } else if (asciiOnly) { + return encodeAscii(input as string); + } else { + return encodeUtf8(input as string); + } +}; + +export const decode = function (base32Str: string, asciiOnly?: boolean) { + if (!asciiOnly) { + return toUtf8String(decodeAsBytes(base32Str)); + } + if (base32Str === "") { + return ""; + } else if (!/^[A-Z2-7=]+$/.test(base32Str)) { + throw new Error("Invalid base32 characters"); + } + let v1; + let v2; + let v3; + let v4; + let v5; + let v6; + let v7; + let v8; + let str = ""; + let length = base32Str.indexOf("="); + if (length === -1) { + length = base32Str.length; + } + + // 8 char to 5 bytes + for (var i = 0, count = (length >> 3) << 3; i < count; ) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v8 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += + String.fromCharCode(((v1 << 3) | (v2 >>> 2)) & 255) + + String.fromCharCode(((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255) + + String.fromCharCode(((v4 << 4) | (v5 >>> 1)) & 255) + + String.fromCharCode(((v5 << 7) | (v6 << 2) | (v7 >>> 3)) & 255) + + String.fromCharCode(((v7 << 5) | v8) & 255); + } + + // remain bytes + const remain = length - count; + if (remain === 2) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += String.fromCharCode(((v1 << 3) | (v2 >>> 2)) & 255); + } else if (remain === 4) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += + String.fromCharCode(((v1 << 3) | (v2 >>> 2)) & 255) + + String.fromCharCode(((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255); + } else if (remain === 5) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += + String.fromCharCode(((v1 << 3) | (v2 >>> 2)) & 255) + + String.fromCharCode(((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255) + + String.fromCharCode(((v4 << 4) | (v5 >>> 1)) & 255); + } else if (remain === 7) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += + String.fromCharCode(((v1 << 3) | (v2 >>> 2)) & 255) + + String.fromCharCode(((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255) + + String.fromCharCode(((v4 << 4) | (v5 >>> 1)) & 255) + + String.fromCharCode(((v5 << 7) | (v6 << 2) | (v7 >>> 3)) & 255); + } + return str; +}; diff --git a/clients/liquid-auth-core/src/index.ts b/clients/liquid-auth-core/src/index.ts new file mode 100644 index 0000000..a443d34 --- /dev/null +++ b/clients/liquid-auth-core/src/index.ts @@ -0,0 +1 @@ +export * from "./encoding.js"; diff --git a/clients/liquid-auth-core/src/sha512.ts b/clients/liquid-auth-core/src/sha512.ts new file mode 100644 index 0000000..8339a82 --- /dev/null +++ b/clients/liquid-auth-core/src/sha512.ts @@ -0,0 +1,1328 @@ +// @ts-nocheck +/* + * [js-sha512]{@link https://github.com/emn178/js-sha512} + * + * @version 0.9.0 + * @author Chen, Yi-Cyuan [emn178@gmail.com] + * @copyright Chen, Yi-Cyuan 2014-2024 + * @license MIT + */ +const INPUT_ERROR = "input is invalid type"; +const FINALIZE_ERROR = "finalize already called"; + +const ARRAY_BUFFER = typeof ArrayBuffer !== "undefined"; +const HEX_CHARS = "0123456789abcdef".split(""); +const EXTRA = [-2147483648, 8388608, 32768, 128]; +const SHIFT = [24, 16, 8, 0]; +const K = [ + 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, 0xb5c0fbcf, 0xec4d3b2f, + 0xe9b5dba5, 0x8189dbbc, 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, + 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, 0xd807aa98, 0xa3030242, + 0x12835b01, 0x45706fbe, 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, + 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, 0x9bdc06a7, 0x25c71235, + 0xc19bf174, 0xcf692694, 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, + 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, 0x2de92c6f, 0x592b0275, + 0x4a7484aa, 0x6ea6e483, 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, + 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, 0xb00327c8, 0x98fb213f, + 0xbf597fc7, 0xbeef0ee4, 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, + 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, 0x27b70a85, 0x46d22ffc, + 0x2e1b2138, 0x5c26c926, 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, + 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, 0x81c2c92e, 0x47edaee6, + 0x92722c85, 0x1482353b, 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, + 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, 0xd192e819, 0xd6ef5218, + 0xd6990624, 0x5565a910, 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, + 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, 0x2748774c, 0xdf8eeb99, + 0x34b0bcb5, 0xe19b48a8, 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, + 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, 0x748f82ee, 0x5defb2fc, + 0x78a5636f, 0x43172f60, 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, + 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, 0xbef9a3f7, 0xb2c67915, + 0xc67178f2, 0xe372532b, 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, + 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, 0x06f067aa, 0x72176fba, + 0x0a637dc5, 0xa2c898a6, 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, + 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, 0x3c9ebe0a, 0x15c9bebc, + 0x431d67c4, 0x9c100d4c, 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, + 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817, +]; + +const OUTPUT_TYPES = ["hex", "array", "digest", "arrayBuffer"]; + +const blocks = []; + +const isArray = Array.isArray; +const isView = ArrayBuffer.isView; + +// [message: string, isString: bool] +const formatMessage = function ( + message: string | ArrayBuffer | ArrayBufferView, +) { + const type = typeof message; + if (type === "string") { + return [message, true]; + } + if (type !== "object" || message === null) { + throw new Error(INPUT_ERROR); + } + if (ARRAY_BUFFER && message.constructor === ArrayBuffer) { + return [new Uint8Array(message), false]; + } + if (!isArray(message) && !isView(message)) { + throw new Error(INPUT_ERROR); + } + return [message, false]; +}; + +const createOutputMethod = function (outputType, bits) { + return function (message) { + return new (Sha512 as any)(bits, true).update(message)[outputType](); + }; +}; + +export const createMethod = function (bits): { + array: (d: Uint8Array) => Uint8Array; +} { + const method = createOutputMethod("hex", bits); + method.create = function () { + return new (Sha512 as any)(bits); + }; + method.update = function (message) { + return method.create().update(message); + }; + for (let i = 0; i < OUTPUT_TYPES.length; ++i) { + const type = OUTPUT_TYPES[i]; + method[type] = createOutputMethod(type, bits); + } + return method; +}; + +const createHmacOutputMethod = function (outputType, bits) { + return function (key, message) { + return new (HmacSha512 as any)(key, bits, true) + .update(message) + [outputType](); + }; +}; + +const createHmacMethod = function (bits) { + const method = createHmacOutputMethod("hex", bits); + method.create = function (key) { + return new (HmacSha512 as any)(key, bits); + }; + method.update = function (key, message) { + return method.create(key).update(message); + }; + for (let i = 0; i < OUTPUT_TYPES.length; ++i) { + const type = OUTPUT_TYPES[i]; + method[type] = createHmacOutputMethod(type, bits); + } + return method; +}; + +/** + * @class + * @param bits + * @param sharedMemory + * @constructor + */ +function Sha512(bits, sharedMemory) { + if (sharedMemory) { + blocks[0] = + blocks[1] = + blocks[2] = + blocks[3] = + blocks[4] = + blocks[5] = + blocks[6] = + blocks[7] = + blocks[8] = + blocks[9] = + blocks[10] = + blocks[11] = + blocks[12] = + blocks[13] = + blocks[14] = + blocks[15] = + blocks[16] = + blocks[17] = + blocks[18] = + blocks[19] = + blocks[20] = + blocks[21] = + blocks[22] = + blocks[23] = + blocks[24] = + blocks[25] = + blocks[26] = + blocks[27] = + blocks[28] = + blocks[29] = + blocks[30] = + blocks[31] = + blocks[32] = + 0; + this.blocks = blocks; + } else { + this.blocks = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + } + + if (bits == 384) { + this.h0h = 0xcbbb9d5d; + this.h0l = 0xc1059ed8; + this.h1h = 0x629a292a; + this.h1l = 0x367cd507; + this.h2h = 0x9159015a; + this.h2l = 0x3070dd17; + this.h3h = 0x152fecd8; + this.h3l = 0xf70e5939; + this.h4h = 0x67332667; + this.h4l = 0xffc00b31; + this.h5h = 0x8eb44a87; + this.h5l = 0x68581511; + this.h6h = 0xdb0c2e0d; + this.h6l = 0x64f98fa7; + this.h7h = 0x47b5481d; + this.h7l = 0xbefa4fa4; + } else if (bits == 256) { + this.h0h = 0x22312194; + this.h0l = 0xfc2bf72c; + this.h1h = 0x9f555fa3; + this.h1l = 0xc84c64c2; + this.h2h = 0x2393b86b; + this.h2l = 0x6f53b151; + this.h3h = 0x96387719; + this.h3l = 0x5940eabd; + this.h4h = 0x96283ee2; + this.h4l = 0xa88effe3; + this.h5h = 0xbe5e1e25; + this.h5l = 0x53863992; + this.h6h = 0x2b0199fc; + this.h6l = 0x2c85b8aa; + this.h7h = 0x0eb72ddc; + this.h7l = 0x81c52ca2; + } else if (bits == 224) { + this.h0h = 0x8c3d37c8; + this.h0l = 0x19544da2; + this.h1h = 0x73e19966; + this.h1l = 0x89dcd4d6; + this.h2h = 0x1dfab7ae; + this.h2l = 0x32ff9c82; + this.h3h = 0x679dd514; + this.h3l = 0x582f9fcf; + this.h4h = 0x0f6d2b69; + this.h4l = 0x7bd44da8; + this.h5h = 0x77e36f73; + this.h5l = 0x04c48942; + this.h6h = 0x3f9d85a8; + this.h6l = 0x6a1d36c8; + this.h7h = 0x1112e6ad; + this.h7l = 0x91d692a1; + } else { + // 512 + this.h0h = 0x6a09e667; + this.h0l = 0xf3bcc908; + this.h1h = 0xbb67ae85; + this.h1l = 0x84caa73b; + this.h2h = 0x3c6ef372; + this.h2l = 0xfe94f82b; + this.h3h = 0xa54ff53a; + this.h3l = 0x5f1d36f1; + this.h4h = 0x510e527f; + this.h4l = 0xade682d1; + this.h5h = 0x9b05688c; + this.h5l = 0x2b3e6c1f; + this.h6h = 0x1f83d9ab; + this.h6l = 0xfb41bd6b; + this.h7h = 0x5be0cd19; + this.h7l = 0x137e2179; + } + this.bits = bits; + + this.block = this.start = this.bytes = this.hBytes = 0; + this.finalized = this.hashed = false; +} + +Sha512.prototype.update = function (message) { + if (this.finalized) { + throw new Error(FINALIZE_ERROR); + } + const result = formatMessage(message); + message = result[0]; + const isString = result[1]; + let code; + let index = 0; + let i; + const length = message.length; + const blocks = this.blocks; + + while (index < length) { + if (this.hashed) { + this.hashed = false; + blocks[0] = this.block; + this.block = + blocks[1] = + blocks[2] = + blocks[3] = + blocks[4] = + blocks[5] = + blocks[6] = + blocks[7] = + blocks[8] = + blocks[9] = + blocks[10] = + blocks[11] = + blocks[12] = + blocks[13] = + blocks[14] = + blocks[15] = + blocks[16] = + blocks[17] = + blocks[18] = + blocks[19] = + blocks[20] = + blocks[21] = + blocks[22] = + blocks[23] = + blocks[24] = + blocks[25] = + blocks[26] = + blocks[27] = + blocks[28] = + blocks[29] = + blocks[30] = + blocks[31] = + blocks[32] = + 0; + } + + if (isString) { + for (i = this.start; index < length && i < 128; ++index) { + code = message.charCodeAt(index); + if (code < 0x80) { + blocks[i >>> 2] |= code << SHIFT[i++ & 3]; + } else if (code < 0x800) { + blocks[i >>> 2] |= (0xc0 | (code >>> 6)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } else if (code < 0xd800 || code >= 0xe000) { + blocks[i >>> 2] |= (0xe0 | (code >>> 12)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | ((code >>> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } else { + code = + 0x10000 + + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); + blocks[i >>> 2] |= (0xf0 | (code >>> 18)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | ((code >>> 12) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | ((code >>> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } + } + } else { + for (i = this.start; index < length && i < 128; ++index) { + blocks[i >>> 2] |= message[index] << SHIFT[i++ & 3]; + } + } + + this.lastByteIndex = i; + this.bytes += i - this.start; + if (i >= 128) { + this.block = blocks[32]; + this.start = i - 128; + this.hash(); + this.hashed = true; + } else { + this.start = i; + } + } + if (this.bytes > 4294967295) { + this.hBytes += (this.bytes / 4294967296) << 0; + this.bytes = this.bytes % 4294967296; + } + return this; +}; + +Sha512.prototype.finalize = function () { + if (this.finalized) { + return; + } + this.finalized = true; + const blocks = this.blocks; + const i = this.lastByteIndex; + blocks[32] = this.block; + blocks[i >>> 2] |= EXTRA[i & 3]; + this.block = blocks[32]; + if (i >= 112) { + if (!this.hashed) { + this.hash(); + } + blocks[0] = this.block; + blocks[1] = + blocks[2] = + blocks[3] = + blocks[4] = + blocks[5] = + blocks[6] = + blocks[7] = + blocks[8] = + blocks[9] = + blocks[10] = + blocks[11] = + blocks[12] = + blocks[13] = + blocks[14] = + blocks[15] = + blocks[16] = + blocks[17] = + blocks[18] = + blocks[19] = + blocks[20] = + blocks[21] = + blocks[22] = + blocks[23] = + blocks[24] = + blocks[25] = + blocks[26] = + blocks[27] = + blocks[28] = + blocks[29] = + blocks[30] = + blocks[31] = + blocks[32] = + 0; + } + blocks[30] = (this.hBytes << 3) | (this.bytes >>> 29); + blocks[31] = this.bytes << 3; + this.hash(); +}; + +Sha512.prototype.hash = function () { + const h0h = this.h0h; + const h0l = this.h0l; + const h1h = this.h1h; + const h1l = this.h1l; + const h2h = this.h2h; + const h2l = this.h2l; + const h3h = this.h3h; + const h3l = this.h3l; + const h4h = this.h4h; + const h4l = this.h4l; + const h5h = this.h5h; + const h5l = this.h5l; + const h6h = this.h6h; + const h6l = this.h6l; + const h7h = this.h7h; + const h7l = this.h7l; + const blocks = this.blocks; + let j; + let s0h; + let s0l; + let s1h; + let s1l; + let c1; + let c2; + let c3; + let c4; + let abh; + let abl; + let dah; + let dal; + let cdh; + let cdl; + let bch; + let bcl; + let majh; + let majl; + let t1h; + let t1l; + let t2h; + let t2l; + let chh; + let chl; + + for (j = 32; j < 160; j += 2) { + t1h = blocks[j - 30]; + t1l = blocks[j - 29]; + s0h = + ((t1h >>> 1) | (t1l << 31)) ^ ((t1h >>> 8) | (t1l << 24)) ^ (t1h >>> 7); + s0l = + ((t1l >>> 1) | (t1h << 31)) ^ + ((t1l >>> 8) | (t1h << 24)) ^ + ((t1l >>> 7) | (t1h << 25)); + + t1h = blocks[j - 4]; + t1l = blocks[j - 3]; + s1h = + ((t1h >>> 19) | (t1l << 13)) ^ ((t1l >>> 29) | (t1h << 3)) ^ (t1h >>> 6); + s1l = + ((t1l >>> 19) | (t1h << 13)) ^ + ((t1h >>> 29) | (t1l << 3)) ^ + ((t1l >>> 6) | (t1h << 26)); + + t1h = blocks[j - 32]; + t1l = blocks[j - 31]; + t2h = blocks[j - 14]; + t2l = blocks[j - 13]; + + c1 = (t2l & 0xffff) + (t1l & 0xffff) + (s0l & 0xffff) + (s1l & 0xffff); + c2 = + (t2l >>> 16) + (t1l >>> 16) + (s0l >>> 16) + (s1l >>> 16) + (c1 >>> 16); + c3 = + (t2h & 0xffff) + + (t1h & 0xffff) + + (s0h & 0xffff) + + (s1h & 0xffff) + + (c2 >>> 16); + c4 = + (t2h >>> 16) + (t1h >>> 16) + (s0h >>> 16) + (s1h >>> 16) + (c3 >>> 16); + + blocks[j] = (c4 << 16) | (c3 & 0xffff); + blocks[j + 1] = (c2 << 16) | (c1 & 0xffff); + } + + let ah = h0h; + let al = h0l; + let bh = h1h; + let bl = h1l; + let ch = h2h; + let cl = h2l; + let dh = h3h; + let dl = h3l; + let eh = h4h; + let el = h4l; + let fh = h5h; + let fl = h5l; + let gh = h6h; + let gl = h6l; + let hh = h7h; + let hl = h7l; + bch = bh & ch; + bcl = bl & cl; + for (j = 0; j < 160; j += 8) { + s0h = + ((ah >>> 28) | (al << 4)) ^ + ((al >>> 2) | (ah << 30)) ^ + ((al >>> 7) | (ah << 25)); + s0l = + ((al >>> 28) | (ah << 4)) ^ + ((ah >>> 2) | (al << 30)) ^ + ((ah >>> 7) | (al << 25)); + + s1h = + ((eh >>> 14) | (el << 18)) ^ + ((eh >>> 18) | (el << 14)) ^ + ((el >>> 9) | (eh << 23)); + s1l = + ((el >>> 14) | (eh << 18)) ^ + ((el >>> 18) | (eh << 14)) ^ + ((eh >>> 9) | (el << 23)); + + abh = ah & bh; + abl = al & bl; + majh = abh ^ (ah & ch) ^ bch; + majl = abl ^ (al & cl) ^ bcl; + + chh = (eh & fh) ^ (~eh & gh); + chl = (el & fl) ^ (~el & gl); + + t1h = blocks[j]; + t1l = blocks[j + 1]; + t2h = K[j]; + t2l = K[j + 1]; + + c1 = + (t2l & 0xffff) + + (t1l & 0xffff) + + (chl & 0xffff) + + (s1l & 0xffff) + + (hl & 0xffff); + c2 = + (t2l >>> 16) + + (t1l >>> 16) + + (chl >>> 16) + + (s1l >>> 16) + + (hl >>> 16) + + (c1 >>> 16); + c3 = + (t2h & 0xffff) + + (t1h & 0xffff) + + (chh & 0xffff) + + (s1h & 0xffff) + + (hh & 0xffff) + + (c2 >>> 16); + c4 = + (t2h >>> 16) + + (t1h >>> 16) + + (chh >>> 16) + + (s1h >>> 16) + + (hh >>> 16) + + (c3 >>> 16); + + t1h = (c4 << 16) | (c3 & 0xffff); + t1l = (c2 << 16) | (c1 & 0xffff); + + c1 = (majl & 0xffff) + (s0l & 0xffff); + c2 = (majl >>> 16) + (s0l >>> 16) + (c1 >>> 16); + c3 = (majh & 0xffff) + (s0h & 0xffff) + (c2 >>> 16); + c4 = (majh >>> 16) + (s0h >>> 16) + (c3 >>> 16); + + t2h = (c4 << 16) | (c3 & 0xffff); + t2l = (c2 << 16) | (c1 & 0xffff); + + c1 = (dl & 0xffff) + (t1l & 0xffff); + c2 = (dl >>> 16) + (t1l >>> 16) + (c1 >>> 16); + c3 = (dh & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); + c4 = (dh >>> 16) + (t1h >>> 16) + (c3 >>> 16); + + hh = (c4 << 16) | (c3 & 0xffff); + hl = (c2 << 16) | (c1 & 0xffff); + + c1 = (t2l & 0xffff) + (t1l & 0xffff); + c2 = (t2l >>> 16) + (t1l >>> 16) + (c1 >>> 16); + c3 = (t2h & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); + c4 = (t2h >>> 16) + (t1h >>> 16) + (c3 >>> 16); + + dh = (c4 << 16) | (c3 & 0xffff); + dl = (c2 << 16) | (c1 & 0xffff); + + s0h = + ((dh >>> 28) | (dl << 4)) ^ + ((dl >>> 2) | (dh << 30)) ^ + ((dl >>> 7) | (dh << 25)); + s0l = + ((dl >>> 28) | (dh << 4)) ^ + ((dh >>> 2) | (dl << 30)) ^ + ((dh >>> 7) | (dl << 25)); + + s1h = + ((hh >>> 14) | (hl << 18)) ^ + ((hh >>> 18) | (hl << 14)) ^ + ((hl >>> 9) | (hh << 23)); + s1l = + ((hl >>> 14) | (hh << 18)) ^ + ((hl >>> 18) | (hh << 14)) ^ + ((hh >>> 9) | (hl << 23)); + + dah = dh & ah; + dal = dl & al; + majh = dah ^ (dh & bh) ^ abh; + majl = dal ^ (dl & bl) ^ abl; + + chh = (hh & eh) ^ (~hh & fh); + chl = (hl & el) ^ (~hl & fl); + + t1h = blocks[j + 2]; + t1l = blocks[j + 3]; + t2h = K[j + 2]; + t2l = K[j + 3]; + + c1 = + (t2l & 0xffff) + + (t1l & 0xffff) + + (chl & 0xffff) + + (s1l & 0xffff) + + (gl & 0xffff); + c2 = + (t2l >>> 16) + + (t1l >>> 16) + + (chl >>> 16) + + (s1l >>> 16) + + (gl >>> 16) + + (c1 >>> 16); + c3 = + (t2h & 0xffff) + + (t1h & 0xffff) + + (chh & 0xffff) + + (s1h & 0xffff) + + (gh & 0xffff) + + (c2 >>> 16); + c4 = + (t2h >>> 16) + + (t1h >>> 16) + + (chh >>> 16) + + (s1h >>> 16) + + (gh >>> 16) + + (c3 >>> 16); + + t1h = (c4 << 16) | (c3 & 0xffff); + t1l = (c2 << 16) | (c1 & 0xffff); + + c1 = (majl & 0xffff) + (s0l & 0xffff); + c2 = (majl >>> 16) + (s0l >>> 16) + (c1 >>> 16); + c3 = (majh & 0xffff) + (s0h & 0xffff) + (c2 >>> 16); + c4 = (majh >>> 16) + (s0h >>> 16) + (c3 >>> 16); + + t2h = (c4 << 16) | (c3 & 0xffff); + t2l = (c2 << 16) | (c1 & 0xffff); + + c1 = (cl & 0xffff) + (t1l & 0xffff); + c2 = (cl >>> 16) + (t1l >>> 16) + (c1 >>> 16); + c3 = (ch & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); + c4 = (ch >>> 16) + (t1h >>> 16) + (c3 >>> 16); + + gh = (c4 << 16) | (c3 & 0xffff); + gl = (c2 << 16) | (c1 & 0xffff); + + c1 = (t2l & 0xffff) + (t1l & 0xffff); + c2 = (t2l >>> 16) + (t1l >>> 16) + (c1 >>> 16); + c3 = (t2h & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); + c4 = (t2h >>> 16) + (t1h >>> 16) + (c3 >>> 16); + + ch = (c4 << 16) | (c3 & 0xffff); + cl = (c2 << 16) | (c1 & 0xffff); + + s0h = + ((ch >>> 28) | (cl << 4)) ^ + ((cl >>> 2) | (ch << 30)) ^ + ((cl >>> 7) | (ch << 25)); + s0l = + ((cl >>> 28) | (ch << 4)) ^ + ((ch >>> 2) | (cl << 30)) ^ + ((ch >>> 7) | (cl << 25)); + + s1h = + ((gh >>> 14) | (gl << 18)) ^ + ((gh >>> 18) | (gl << 14)) ^ + ((gl >>> 9) | (gh << 23)); + s1l = + ((gl >>> 14) | (gh << 18)) ^ + ((gl >>> 18) | (gh << 14)) ^ + ((gh >>> 9) | (gl << 23)); + + cdh = ch & dh; + cdl = cl & dl; + majh = cdh ^ (ch & ah) ^ dah; + majl = cdl ^ (cl & al) ^ dal; + + chh = (gh & hh) ^ (~gh & eh); + chl = (gl & hl) ^ (~gl & el); + + t1h = blocks[j + 4]; + t1l = blocks[j + 5]; + t2h = K[j + 4]; + t2l = K[j + 5]; + + c1 = + (t2l & 0xffff) + + (t1l & 0xffff) + + (chl & 0xffff) + + (s1l & 0xffff) + + (fl & 0xffff); + c2 = + (t2l >>> 16) + + (t1l >>> 16) + + (chl >>> 16) + + (s1l >>> 16) + + (fl >>> 16) + + (c1 >>> 16); + c3 = + (t2h & 0xffff) + + (t1h & 0xffff) + + (chh & 0xffff) + + (s1h & 0xffff) + + (fh & 0xffff) + + (c2 >>> 16); + c4 = + (t2h >>> 16) + + (t1h >>> 16) + + (chh >>> 16) + + (s1h >>> 16) + + (fh >>> 16) + + (c3 >>> 16); + + t1h = (c4 << 16) | (c3 & 0xffff); + t1l = (c2 << 16) | (c1 & 0xffff); + + c1 = (majl & 0xffff) + (s0l & 0xffff); + c2 = (majl >>> 16) + (s0l >>> 16) + (c1 >>> 16); + c3 = (majh & 0xffff) + (s0h & 0xffff) + (c2 >>> 16); + c4 = (majh >>> 16) + (s0h >>> 16) + (c3 >>> 16); + + t2h = (c4 << 16) | (c3 & 0xffff); + t2l = (c2 << 16) | (c1 & 0xffff); + + c1 = (bl & 0xffff) + (t1l & 0xffff); + c2 = (bl >>> 16) + (t1l >>> 16) + (c1 >>> 16); + c3 = (bh & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); + c4 = (bh >>> 16) + (t1h >>> 16) + (c3 >>> 16); + + fh = (c4 << 16) | (c3 & 0xffff); + fl = (c2 << 16) | (c1 & 0xffff); + + c1 = (t2l & 0xffff) + (t1l & 0xffff); + c2 = (t2l >>> 16) + (t1l >>> 16) + (c1 >>> 16); + c3 = (t2h & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); + c4 = (t2h >>> 16) + (t1h >>> 16) + (c3 >>> 16); + + bh = (c4 << 16) | (c3 & 0xffff); + bl = (c2 << 16) | (c1 & 0xffff); + + s0h = + ((bh >>> 28) | (bl << 4)) ^ + ((bl >>> 2) | (bh << 30)) ^ + ((bl >>> 7) | (bh << 25)); + s0l = + ((bl >>> 28) | (bh << 4)) ^ + ((bh >>> 2) | (bl << 30)) ^ + ((bh >>> 7) | (bl << 25)); + + s1h = + ((fh >>> 14) | (fl << 18)) ^ + ((fh >>> 18) | (fl << 14)) ^ + ((fl >>> 9) | (fh << 23)); + s1l = + ((fl >>> 14) | (fh << 18)) ^ + ((fl >>> 18) | (fh << 14)) ^ + ((fh >>> 9) | (fl << 23)); + + bch = bh & ch; + bcl = bl & cl; + majh = bch ^ (bh & dh) ^ cdh; + majl = bcl ^ (bl & dl) ^ cdl; + + chh = (fh & gh) ^ (~fh & hh); + chl = (fl & gl) ^ (~fl & hl); + + t1h = blocks[j + 6]; + t1l = blocks[j + 7]; + t2h = K[j + 6]; + t2l = K[j + 7]; + + c1 = + (t2l & 0xffff) + + (t1l & 0xffff) + + (chl & 0xffff) + + (s1l & 0xffff) + + (el & 0xffff); + c2 = + (t2l >>> 16) + + (t1l >>> 16) + + (chl >>> 16) + + (s1l >>> 16) + + (el >>> 16) + + (c1 >>> 16); + c3 = + (t2h & 0xffff) + + (t1h & 0xffff) + + (chh & 0xffff) + + (s1h & 0xffff) + + (eh & 0xffff) + + (c2 >>> 16); + c4 = + (t2h >>> 16) + + (t1h >>> 16) + + (chh >>> 16) + + (s1h >>> 16) + + (eh >>> 16) + + (c3 >>> 16); + + t1h = (c4 << 16) | (c3 & 0xffff); + t1l = (c2 << 16) | (c1 & 0xffff); + + c1 = (majl & 0xffff) + (s0l & 0xffff); + c2 = (majl >>> 16) + (s0l >>> 16) + (c1 >>> 16); + c3 = (majh & 0xffff) + (s0h & 0xffff) + (c2 >>> 16); + c4 = (majh >>> 16) + (s0h >>> 16) + (c3 >>> 16); + + t2h = (c4 << 16) | (c3 & 0xffff); + t2l = (c2 << 16) | (c1 & 0xffff); + + c1 = (al & 0xffff) + (t1l & 0xffff); + c2 = (al >>> 16) + (t1l >>> 16) + (c1 >>> 16); + c3 = (ah & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); + c4 = (ah >>> 16) + (t1h >>> 16) + (c3 >>> 16); + + eh = (c4 << 16) | (c3 & 0xffff); + el = (c2 << 16) | (c1 & 0xffff); + + c1 = (t2l & 0xffff) + (t1l & 0xffff); + c2 = (t2l >>> 16) + (t1l >>> 16) + (c1 >>> 16); + c3 = (t2h & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); + c4 = (t2h >>> 16) + (t1h >>> 16) + (c3 >>> 16); + + ah = (c4 << 16) | (c3 & 0xffff); + al = (c2 << 16) | (c1 & 0xffff); + } + + c1 = (h0l & 0xffff) + (al & 0xffff); + c2 = (h0l >>> 16) + (al >>> 16) + (c1 >>> 16); + c3 = (h0h & 0xffff) + (ah & 0xffff) + (c2 >>> 16); + c4 = (h0h >>> 16) + (ah >>> 16) + (c3 >>> 16); + + this.h0h = (c4 << 16) | (c3 & 0xffff); + this.h0l = (c2 << 16) | (c1 & 0xffff); + + c1 = (h1l & 0xffff) + (bl & 0xffff); + c2 = (h1l >>> 16) + (bl >>> 16) + (c1 >>> 16); + c3 = (h1h & 0xffff) + (bh & 0xffff) + (c2 >>> 16); + c4 = (h1h >>> 16) + (bh >>> 16) + (c3 >>> 16); + + this.h1h = (c4 << 16) | (c3 & 0xffff); + this.h1l = (c2 << 16) | (c1 & 0xffff); + + c1 = (h2l & 0xffff) + (cl & 0xffff); + c2 = (h2l >>> 16) + (cl >>> 16) + (c1 >>> 16); + c3 = (h2h & 0xffff) + (ch & 0xffff) + (c2 >>> 16); + c4 = (h2h >>> 16) + (ch >>> 16) + (c3 >>> 16); + + this.h2h = (c4 << 16) | (c3 & 0xffff); + this.h2l = (c2 << 16) | (c1 & 0xffff); + + c1 = (h3l & 0xffff) + (dl & 0xffff); + c2 = (h3l >>> 16) + (dl >>> 16) + (c1 >>> 16); + c3 = (h3h & 0xffff) + (dh & 0xffff) + (c2 >>> 16); + c4 = (h3h >>> 16) + (dh >>> 16) + (c3 >>> 16); + + this.h3h = (c4 << 16) | (c3 & 0xffff); + this.h3l = (c2 << 16) | (c1 & 0xffff); + + c1 = (h4l & 0xffff) + (el & 0xffff); + c2 = (h4l >>> 16) + (el >>> 16) + (c1 >>> 16); + c3 = (h4h & 0xffff) + (eh & 0xffff) + (c2 >>> 16); + c4 = (h4h >>> 16) + (eh >>> 16) + (c3 >>> 16); + + this.h4h = (c4 << 16) | (c3 & 0xffff); + this.h4l = (c2 << 16) | (c1 & 0xffff); + + c1 = (h5l & 0xffff) + (fl & 0xffff); + c2 = (h5l >>> 16) + (fl >>> 16) + (c1 >>> 16); + c3 = (h5h & 0xffff) + (fh & 0xffff) + (c2 >>> 16); + c4 = (h5h >>> 16) + (fh >>> 16) + (c3 >>> 16); + + this.h5h = (c4 << 16) | (c3 & 0xffff); + this.h5l = (c2 << 16) | (c1 & 0xffff); + + c1 = (h6l & 0xffff) + (gl & 0xffff); + c2 = (h6l >>> 16) + (gl >>> 16) + (c1 >>> 16); + c3 = (h6h & 0xffff) + (gh & 0xffff) + (c2 >>> 16); + c4 = (h6h >>> 16) + (gh >>> 16) + (c3 >>> 16); + + this.h6h = (c4 << 16) | (c3 & 0xffff); + this.h6l = (c2 << 16) | (c1 & 0xffff); + + c1 = (h7l & 0xffff) + (hl & 0xffff); + c2 = (h7l >>> 16) + (hl >>> 16) + (c1 >>> 16); + c3 = (h7h & 0xffff) + (hh & 0xffff) + (c2 >>> 16); + c4 = (h7h >>> 16) + (hh >>> 16) + (c3 >>> 16); + + this.h7h = (c4 << 16) | (c3 & 0xffff); + this.h7l = (c2 << 16) | (c1 & 0xffff); +}; + +Sha512.prototype.hex = function () { + this.finalize(); + + const h0h = this.h0h; + const h0l = this.h0l; + const h1h = this.h1h; + const h1l = this.h1l; + const h2h = this.h2h; + const h2l = this.h2l; + const h3h = this.h3h; + const h3l = this.h3l; + const h4h = this.h4h; + const h4l = this.h4l; + const h5h = this.h5h; + const h5l = this.h5l; + const h6h = this.h6h; + const h6l = this.h6l; + const h7h = this.h7h; + const h7l = this.h7l; + const bits = this.bits; + + let hex = + HEX_CHARS[(h0h >>> 28) & 0x0f] + + HEX_CHARS[(h0h >>> 24) & 0x0f] + + HEX_CHARS[(h0h >>> 20) & 0x0f] + + HEX_CHARS[(h0h >>> 16) & 0x0f] + + HEX_CHARS[(h0h >>> 12) & 0x0f] + + HEX_CHARS[(h0h >>> 8) & 0x0f] + + HEX_CHARS[(h0h >>> 4) & 0x0f] + + HEX_CHARS[h0h & 0x0f] + + HEX_CHARS[(h0l >>> 28) & 0x0f] + + HEX_CHARS[(h0l >>> 24) & 0x0f] + + HEX_CHARS[(h0l >>> 20) & 0x0f] + + HEX_CHARS[(h0l >>> 16) & 0x0f] + + HEX_CHARS[(h0l >>> 12) & 0x0f] + + HEX_CHARS[(h0l >>> 8) & 0x0f] + + HEX_CHARS[(h0l >>> 4) & 0x0f] + + HEX_CHARS[h0l & 0x0f] + + HEX_CHARS[(h1h >>> 28) & 0x0f] + + HEX_CHARS[(h1h >>> 24) & 0x0f] + + HEX_CHARS[(h1h >>> 20) & 0x0f] + + HEX_CHARS[(h1h >>> 16) & 0x0f] + + HEX_CHARS[(h1h >>> 12) & 0x0f] + + HEX_CHARS[(h1h >>> 8) & 0x0f] + + HEX_CHARS[(h1h >>> 4) & 0x0f] + + HEX_CHARS[h1h & 0x0f] + + HEX_CHARS[(h1l >>> 28) & 0x0f] + + HEX_CHARS[(h1l >>> 24) & 0x0f] + + HEX_CHARS[(h1l >>> 20) & 0x0f] + + HEX_CHARS[(h1l >>> 16) & 0x0f] + + HEX_CHARS[(h1l >>> 12) & 0x0f] + + HEX_CHARS[(h1l >>> 8) & 0x0f] + + HEX_CHARS[(h1l >>> 4) & 0x0f] + + HEX_CHARS[h1l & 0x0f] + + HEX_CHARS[(h2h >>> 28) & 0x0f] + + HEX_CHARS[(h2h >>> 24) & 0x0f] + + HEX_CHARS[(h2h >>> 20) & 0x0f] + + HEX_CHARS[(h2h >>> 16) & 0x0f] + + HEX_CHARS[(h2h >>> 12) & 0x0f] + + HEX_CHARS[(h2h >>> 8) & 0x0f] + + HEX_CHARS[(h2h >>> 4) & 0x0f] + + HEX_CHARS[h2h & 0x0f] + + HEX_CHARS[(h2l >>> 28) & 0x0f] + + HEX_CHARS[(h2l >>> 24) & 0x0f] + + HEX_CHARS[(h2l >>> 20) & 0x0f] + + HEX_CHARS[(h2l >>> 16) & 0x0f] + + HEX_CHARS[(h2l >>> 12) & 0x0f] + + HEX_CHARS[(h2l >>> 8) & 0x0f] + + HEX_CHARS[(h2l >>> 4) & 0x0f] + + HEX_CHARS[h2l & 0x0f] + + HEX_CHARS[(h3h >>> 28) & 0x0f] + + HEX_CHARS[(h3h >>> 24) & 0x0f] + + HEX_CHARS[(h3h >>> 20) & 0x0f] + + HEX_CHARS[(h3h >>> 16) & 0x0f] + + HEX_CHARS[(h3h >>> 12) & 0x0f] + + HEX_CHARS[(h3h >>> 8) & 0x0f] + + HEX_CHARS[(h3h >>> 4) & 0x0f] + + HEX_CHARS[h3h & 0x0f]; + if (bits >= 256) { + hex += + HEX_CHARS[(h3l >>> 28) & 0x0f] + + HEX_CHARS[(h3l >>> 24) & 0x0f] + + HEX_CHARS[(h3l >>> 20) & 0x0f] + + HEX_CHARS[(h3l >>> 16) & 0x0f] + + HEX_CHARS[(h3l >>> 12) & 0x0f] + + HEX_CHARS[(h3l >>> 8) & 0x0f] + + HEX_CHARS[(h3l >>> 4) & 0x0f] + + HEX_CHARS[h3l & 0x0f]; + } + if (bits >= 384) { + hex += + HEX_CHARS[(h4h >>> 28) & 0x0f] + + HEX_CHARS[(h4h >>> 24) & 0x0f] + + HEX_CHARS[(h4h >>> 20) & 0x0f] + + HEX_CHARS[(h4h >>> 16) & 0x0f] + + HEX_CHARS[(h4h >>> 12) & 0x0f] + + HEX_CHARS[(h4h >>> 8) & 0x0f] + + HEX_CHARS[(h4h >>> 4) & 0x0f] + + HEX_CHARS[h4h & 0x0f] + + HEX_CHARS[(h4l >>> 28) & 0x0f] + + HEX_CHARS[(h4l >>> 24) & 0x0f] + + HEX_CHARS[(h4l >>> 20) & 0x0f] + + HEX_CHARS[(h4l >>> 16) & 0x0f] + + HEX_CHARS[(h4l >>> 12) & 0x0f] + + HEX_CHARS[(h4l >>> 8) & 0x0f] + + HEX_CHARS[(h4l >>> 4) & 0x0f] + + HEX_CHARS[h4l & 0x0f] + + HEX_CHARS[(h5h >>> 28) & 0x0f] + + HEX_CHARS[(h5h >>> 24) & 0x0f] + + HEX_CHARS[(h5h >>> 20) & 0x0f] + + HEX_CHARS[(h5h >>> 16) & 0x0f] + + HEX_CHARS[(h5h >>> 12) & 0x0f] + + HEX_CHARS[(h5h >>> 8) & 0x0f] + + HEX_CHARS[(h5h >>> 4) & 0x0f] + + HEX_CHARS[h5h & 0x0f] + + HEX_CHARS[(h5l >>> 28) & 0x0f] + + HEX_CHARS[(h5l >>> 24) & 0x0f] + + HEX_CHARS[(h5l >>> 20) & 0x0f] + + HEX_CHARS[(h5l >>> 16) & 0x0f] + + HEX_CHARS[(h5l >>> 12) & 0x0f] + + HEX_CHARS[(h5l >>> 8) & 0x0f] + + HEX_CHARS[(h5l >>> 4) & 0x0f] + + HEX_CHARS[h5l & 0x0f]; + } + if (bits == 512) { + hex += + HEX_CHARS[(h6h >>> 28) & 0x0f] + + HEX_CHARS[(h6h >>> 24) & 0x0f] + + HEX_CHARS[(h6h >>> 20) & 0x0f] + + HEX_CHARS[(h6h >>> 16) & 0x0f] + + HEX_CHARS[(h6h >>> 12) & 0x0f] + + HEX_CHARS[(h6h >>> 8) & 0x0f] + + HEX_CHARS[(h6h >>> 4) & 0x0f] + + HEX_CHARS[h6h & 0x0f] + + HEX_CHARS[(h6l >>> 28) & 0x0f] + + HEX_CHARS[(h6l >>> 24) & 0x0f] + + HEX_CHARS[(h6l >>> 20) & 0x0f] + + HEX_CHARS[(h6l >>> 16) & 0x0f] + + HEX_CHARS[(h6l >>> 12) & 0x0f] + + HEX_CHARS[(h6l >>> 8) & 0x0f] + + HEX_CHARS[(h6l >>> 4) & 0x0f] + + HEX_CHARS[h6l & 0x0f] + + HEX_CHARS[(h7h >>> 28) & 0x0f] + + HEX_CHARS[(h7h >>> 24) & 0x0f] + + HEX_CHARS[(h7h >>> 20) & 0x0f] + + HEX_CHARS[(h7h >>> 16) & 0x0f] + + HEX_CHARS[(h7h >>> 12) & 0x0f] + + HEX_CHARS[(h7h >>> 8) & 0x0f] + + HEX_CHARS[(h7h >>> 4) & 0x0f] + + HEX_CHARS[h7h & 0x0f] + + HEX_CHARS[(h7l >>> 28) & 0x0f] + + HEX_CHARS[(h7l >>> 24) & 0x0f] + + HEX_CHARS[(h7l >>> 20) & 0x0f] + + HEX_CHARS[(h7l >>> 16) & 0x0f] + + HEX_CHARS[(h7l >>> 12) & 0x0f] + + HEX_CHARS[(h7l >>> 8) & 0x0f] + + HEX_CHARS[(h7l >>> 4) & 0x0f] + + HEX_CHARS[h7l & 0x0f]; + } + return hex; +}; + +Sha512.prototype.toString = Sha512.prototype.hex; + +Sha512.prototype.digest = function () { + this.finalize(); + + const h0h = this.h0h; + const h0l = this.h0l; + const h1h = this.h1h; + const h1l = this.h1l; + const h2h = this.h2h; + const h2l = this.h2l; + const h3h = this.h3h; + const h3l = this.h3l; + const h4h = this.h4h; + const h4l = this.h4l; + const h5h = this.h5h; + const h5l = this.h5l; + const h6h = this.h6h; + const h6l = this.h6l; + const h7h = this.h7h; + const h7l = this.h7l; + const bits = this.bits; + + const arr = [ + (h0h >>> 24) & 0xff, + (h0h >>> 16) & 0xff, + (h0h >>> 8) & 0xff, + h0h & 0xff, + (h0l >>> 24) & 0xff, + (h0l >>> 16) & 0xff, + (h0l >>> 8) & 0xff, + h0l & 0xff, + (h1h >>> 24) & 0xff, + (h1h >>> 16) & 0xff, + (h1h >>> 8) & 0xff, + h1h & 0xff, + (h1l >>> 24) & 0xff, + (h1l >>> 16) & 0xff, + (h1l >>> 8) & 0xff, + h1l & 0xff, + (h2h >>> 24) & 0xff, + (h2h >>> 16) & 0xff, + (h2h >>> 8) & 0xff, + h2h & 0xff, + (h2l >>> 24) & 0xff, + (h2l >>> 16) & 0xff, + (h2l >>> 8) & 0xff, + h2l & 0xff, + (h3h >>> 24) & 0xff, + (h3h >>> 16) & 0xff, + (h3h >>> 8) & 0xff, + h3h & 0xff, + ]; + + if (bits >= 256) { + arr.push( + (h3l >>> 24) & 0xff, + (h3l >>> 16) & 0xff, + (h3l >>> 8) & 0xff, + h3l & 0xff, + ); + } + if (bits >= 384) { + arr.push( + (h4h >>> 24) & 0xff, + (h4h >>> 16) & 0xff, + (h4h >>> 8) & 0xff, + h4h & 0xff, + (h4l >>> 24) & 0xff, + (h4l >>> 16) & 0xff, + (h4l >>> 8) & 0xff, + h4l & 0xff, + (h5h >>> 24) & 0xff, + (h5h >>> 16) & 0xff, + (h5h >>> 8) & 0xff, + h5h & 0xff, + (h5l >>> 24) & 0xff, + (h5l >>> 16) & 0xff, + (h5l >>> 8) & 0xff, + h5l & 0xff, + ); + } + if (bits == 512) { + arr.push( + (h6h >>> 24) & 0xff, + (h6h >>> 16) & 0xff, + (h6h >>> 8) & 0xff, + h6h & 0xff, + (h6l >>> 24) & 0xff, + (h6l >>> 16) & 0xff, + (h6l >>> 8) & 0xff, + h6l & 0xff, + (h7h >>> 24) & 0xff, + (h7h >>> 16) & 0xff, + (h7h >>> 8) & 0xff, + h7h & 0xff, + (h7l >>> 24) & 0xff, + (h7l >>> 16) & 0xff, + (h7l >>> 8) & 0xff, + h7l & 0xff, + ); + } + return arr; +}; + +Sha512.prototype.array = Sha512.prototype.digest; + +Sha512.prototype.arrayBuffer = function () { + this.finalize(); + + const bits = this.bits; + const buffer = new ArrayBuffer(bits / 8); + const dataView = new DataView(buffer); + dataView.setUint32(0, this.h0h); + dataView.setUint32(4, this.h0l); + dataView.setUint32(8, this.h1h); + dataView.setUint32(12, this.h1l); + dataView.setUint32(16, this.h2h); + dataView.setUint32(20, this.h2l); + dataView.setUint32(24, this.h3h); + + if (bits >= 256) { + dataView.setUint32(28, this.h3l); + } + if (bits >= 384) { + dataView.setUint32(32, this.h4h); + dataView.setUint32(36, this.h4l); + dataView.setUint32(40, this.h5h); + dataView.setUint32(44, this.h5l); + } + if (bits == 512) { + dataView.setUint32(48, this.h6h); + dataView.setUint32(52, this.h6l); + dataView.setUint32(56, this.h7h); + dataView.setUint32(60, this.h7l); + } + return buffer; +}; + +Sha512.prototype.clone = function () { + const hash = new Sha512(this.bits, false); + this.copyTo(hash); + return hash; +}; + +Sha512.prototype.copyTo = function (hash) { + let i = 0; + const attrs = [ + "h0h", + "h0l", + "h1h", + "h1l", + "h2h", + "h2l", + "h3h", + "h3l", + "h4h", + "h4l", + "h5h", + "h5l", + "h6h", + "h6l", + "h7h", + "h7l", + "start", + "bytes", + "hBytes", + "finalized", + "hashed", + "lastByteIndex", + ]; + for (i = 0; i < attrs.length; ++i) { + hash[attrs[i]] = this[attrs[i]]; + } + for (i = 0; i < this.blocks.length; ++i) { + hash.blocks[i] = this.blocks[i]; + } +}; + +function HmacSha512(key, bits, sharedMemory) { + var i; + const result = formatMessage(key); + key = result[0]; + if (result[1]) { + const bytes = []; + const length = key.length; + let index = 0; + let code; + for (var i = 0; i < length; ++i) { + code = key.charCodeAt(i); + if (code < 0x80) { + bytes[index++] = code; + } else if (code < 0x800) { + bytes[index++] = 0xc0 | (code >>> 6); + bytes[index++] = 0x80 | (code & 0x3f); + } else if (code < 0xd800 || code >= 0xe000) { + bytes[index++] = 0xe0 | (code >>> 12); + bytes[index++] = 0x80 | ((code >>> 6) & 0x3f); + bytes[index++] = 0x80 | (code & 0x3f); + } else { + code = + 0x10000 + (((code & 0x3ff) << 10) | (key.charCodeAt(++i) & 0x3ff)); + bytes[index++] = 0xf0 | (code >>> 18); + bytes[index++] = 0x80 | ((code >>> 12) & 0x3f); + bytes[index++] = 0x80 | ((code >>> 6) & 0x3f); + bytes[index++] = 0x80 | (code & 0x3f); + } + } + key = bytes; + } + + if (key.length > 128) { + key = new Sha512(bits, true).update(key).array(); + } + + const oKeyPad = []; + const iKeyPad = []; + for (var i = 0; i < 128; ++i) { + const b = key[i] || 0; + oKeyPad[i] = 0x5c ^ b; + iKeyPad[i] = 0x36 ^ b; + } + + Sha512.call(this, bits, sharedMemory); + + this.update(iKeyPad); + this.oKeyPad = oKeyPad; + this.inner = true; + this.sharedMemory = sharedMemory; +} +HmacSha512.prototype = new Sha512(); + +HmacSha512.prototype.finalize = function () { + Sha512.prototype.finalize.call(this); + if (this.inner) { + this.inner = false; + const innerHash = this.array(); + Sha512.call(this, this.bits, this.sharedMemory); + this.update(this.oKeyPad); + this.update(innerHash); + Sha512.prototype.finalize.call(this); + } +}; + +HmacSha512.prototype.clone = function () { + const hash = new HmacSha512([], this.bits, false); + this.copyTo(hash); + hash.inner = this.inner; + for (let i = 0; i < this.oKeyPad.length; ++i) { + hash.oKeyPad[i] = this.oKeyPad[i]; + } + return hash; +}; + +export const sha512_256 = createMethod(256); diff --git a/clients/liquid-auth-core/tsconfig.json b/clients/liquid-auth-core/tsconfig.json new file mode 100644 index 0000000..3380b91 --- /dev/null +++ b/clients/liquid-auth-core/tsconfig.json @@ -0,0 +1,11 @@ +{ + "include": ["./src/**/*.ts"], + "compilerOptions": { + "outDir": "lib", + "declaration": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", + "skipLibCheck": true, + "strictNullChecks": true + } +} diff --git a/package-lock.json b/package-lock.json index 8189f8e..87d749f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,22 +20,293 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "hi-base32": "^0.5.1", - "js-sha512": "^0.9.0", + "@liquid/core": "^1.0.0", "qr-code-styling": "^1.6.0-rc.1", "tweetnacl": "github:awesome-algorand/tweetnacl-js" }, "devDependencies": { "@types/qrcode": "^1.5.5", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^7.6.0", "algosdk": "^2.7.0", "c8": "^9.1.0", - "typescript": "^5.3.3" + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "typescript": "^5.4.5" } }, - "clients/liquid-auth-client-js/node_modules/js-sha512": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.9.0.tgz", - "integrity": "sha512-mirki9WS/SUahm+1TbAPkqvbCiCfOAAsyXeHxK1UkullnJVVqoJG2pL9ObvT05CN+tM7fxhfYm0NbXn+1hWoZg==" + "clients/liquid-auth-client-js/node_modules/@typescript-eslint/parser": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "clients/liquid-auth-client-js/node_modules/@typescript-eslint/scope-manager": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", + "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "clients/liquid-auth-client-js/node_modules/@typescript-eslint/types": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", + "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "clients/liquid-auth-client-js/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", + "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "clients/liquid-auth-client-js/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", + "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.6.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "clients/liquid-auth-client-js/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "clients/liquid-auth-client-js/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "clients/liquid-auth-core": { + "name": "@liquid/core", + "version": "1.0.0", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "tweetnacl": "github:awesome-algorand/tweetnacl-js" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^7.6.0", + "c8": "^9.1.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "typescript": "^5.4.5" + } + }, + "clients/liquid-auth-core/node_modules/@typescript-eslint/parser": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "clients/liquid-auth-core/node_modules/@typescript-eslint/scope-manager": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", + "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "clients/liquid-auth-core/node_modules/@typescript-eslint/types": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", + "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "clients/liquid-auth-core/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", + "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "clients/liquid-auth-core/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", + "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.6.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "clients/liquid-auth-core/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "clients/liquid-auth-core/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", @@ -2660,9 +2931,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3695,6 +3966,10 @@ "resolved": "clients/liquid-auth-client-js", "link": true }, + "node_modules/@liquid/core": { + "resolved": "clients/liquid-auth-core", + "link": true + }, "node_modules/@lukeed/csprng": { "version": "1.1.0", "license": "MIT", @@ -4431,18 +4706,11 @@ "node": ">=14" } }, - "node_modules/@pkgr/utils": { - "version": "2.4.2", + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" - }, "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, @@ -5553,9 +5821,9 @@ "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" }, "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/send": { @@ -5654,16 +5922,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.20.0.tgz", - "integrity": "sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.20.0", - "@typescript-eslint/type-utils": "6.20.0", - "@typescript-eslint/utils": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -5688,6 +5956,53 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/parser": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz", @@ -5734,13 +6049,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.20.0.tgz", - "integrity": "sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.20.0", - "@typescript-eslint/utils": "6.20.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -5760,10 +6075,10 @@ } } }, - "node_modules/@typescript-eslint/types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", - "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -5773,14 +6088,14 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", - "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -5801,7 +6116,24 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", @@ -5810,7 +6142,7 @@ "balanced-match": "^1.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", @@ -5825,18 +6157,83 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/utils": { + "node_modules/@typescript-eslint/types": { "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", - "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.20.0", + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "dev": true, + "dependencies": { "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { @@ -5850,6 +6247,105 @@ "eslint": "^7.0.0 || ^8.0.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", @@ -6428,10 +6924,13 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", - "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -6646,14 +7145,6 @@ "node": ">=6.0.0" } }, - "node_modules/big-integer": { - "version": "1.6.51", - "dev": true, - "license": "Unlicense", - "engines": { - "node": ">=0.6" - } - }, "node_modules/bignumber.js": { "version": "9.1.2", "license": "MIT", @@ -6912,17 +7403,6 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "big-integer": "^1.6.44" - }, - "engines": { - "node": ">= 5.10.0" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "license": "MIT", @@ -7042,20 +7522,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bundle-name": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "run-applescript": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/busboy": { "version": "1.6.0", "dependencies": { @@ -7922,224 +8388,141 @@ "resolved": "sites/dapp-ui", "link": true }, - "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.21.0" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": ">=0.11" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/decompress-response": { - "version": "6.0.0", + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "dev": true, - "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dedent": { - "version": "1.5.1", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/default-browser": { - "version": "4.0.0", + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "dev": true, - "license": "MIT", "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": ">=14.16" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/default-browser-id": { - "version": "3.0.0", + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", "dev": true, - "license": "MIT", "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" + "@babel/runtime": "^7.21.0" }, "engines": { - "node": ">=12" + "node": ">=0.11" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/date-fns" } }, - "node_modules/default-browser/node_modules/execa": { - "version": "7.2.0", - "dev": true, + "node_modules/debug": { + "version": "4.3.4", "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/default-browser/node_modules/human-signals": { - "version": "4.3.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/default-browser/node_modules/is-stream": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "ms": "2.1.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/mimic-fn": { - "version": "4.0.0", - "dev": true, - "license": "MIT", "engines": { - "node": ">=12" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/default-browser/node_modules/npm-run-path": { - "version": "5.1.0", + "node_modules/decompress-response": { + "version": "6.0.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "path-key": "^4.0.0" + "mimic-response": "^3.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/default-browser/node_modules/onetime": { - "version": "6.0.0", + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", "dev": true, "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, + "optional": true, + "peer": true, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/default-browser/node_modules/path-key": { - "version": "4.0.0", + "node_modules/dedent": { + "version": "1.5.1", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } } }, - "node_modules/default-browser/node_modules/strip-final-newline": { - "version": "3.0.0", + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/defaults": { @@ -8179,17 +8562,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -8583,18 +8955,22 @@ } }, "node_modules/es-abstract": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz", - "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.6", + "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.2", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", "get-intrinsic": "^1.2.4", @@ -8602,15 +8978,16 @@ "globalthis": "^1.0.3", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.1", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.1", + "hasown": "^2.0.2", "internal-slot": "^1.0.7", "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", @@ -8618,17 +8995,17 @@ "object-keys": "^1.1.1", "object.assign": "^4.1.5", "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", + "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.1", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -8670,15 +9047,27 @@ "dev": true, "license": "MIT" }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -8762,16 +9151,16 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -8817,9 +9206,10 @@ } }, "node_modules/eslint-config-prettier": { - "version": "9.0.0", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -8828,22 +9218,24 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.0.0", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", "dev": true, - "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" + "synckit": "^0.8.6" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/prettier" + "url": "https://opencollective.com/eslint-plugin-prettier" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", + "eslint-config-prettier": "*", "prettier": ">=3.0.0" }, "peerDependenciesMeta": { @@ -9993,16 +10385,6 @@ "node": ">=0.10.0" } }, - "node_modules/has": { - "version": "1.0.3", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -10039,8 +10421,9 @@ } }, "node_modules/has-proto": { - "version": "1.0.1", - "license": "MIT", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "engines": { "node": ">= 0.4" }, @@ -10086,9 +10469,9 @@ } }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, @@ -10514,22 +10897,23 @@ } }, "node_modules/is-core-module": { - "version": "2.13.0", - "license": "MIT", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -10538,18 +10922,19 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "3.0.0", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" + "dependencies": { + "has-tostringtag": "^1.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-extglob": { @@ -10586,23 +10971,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-interactive": { "version": "1.0.0", "dev": true, @@ -10622,9 +10990,9 @@ "dev": true }, "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "engines": { "node": ">= 0.4" @@ -10709,12 +11077,15 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10799,31 +11170,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl/node_modules/is-docker": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isarray": { "version": "1.0.0", "license": "MIT" @@ -12613,23 +12959,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "9.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.3", "dev": true, @@ -12992,6 +13321,15 @@ "node": ">=4" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.4.33", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", @@ -13766,20 +14104,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/run-applescript": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/run-async": { "version": "2.4.1", "dev": true, @@ -13818,13 +14142,13 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -13934,8 +14258,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.5.4", - "license": "ISC", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -14505,14 +14830,15 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -14522,28 +14848,31 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14772,12 +15101,13 @@ } }, "node_modules/synckit": { - "version": "0.8.5", + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", "dev": true, - "license": "MIT", "dependencies": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -15034,17 +15364,6 @@ "node": ">=0.4" } }, - "node_modules/titleize": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/tmp": { "version": "0.0.33", "dev": true, @@ -15143,11 +15462,12 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.3", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -15341,12 +15661,12 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz", - "integrity": "sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "es-errors": "^1.3.0", "is-typed-array": "^1.1.13" }, @@ -15355,15 +15675,16 @@ } }, "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -15373,16 +15694,17 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -15392,14 +15714,20 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -15410,9 +15738,10 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.3.3", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15583,14 +15912,6 @@ "node": ">= 0.8" } }, - "node_modules/untildify": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", @@ -15955,16 +16276,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.5", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.1" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -16735,6 +17056,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "@liquid/core": "^1.0.0", "@nestjs/common": "^10.2.6", "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.2.6", @@ -16819,6 +17141,8 @@ "@typescript-eslint/parser": "^6.14.0", "@vitejs/plugin-react-swc": "^3.5.0", "eslint": "^8.55.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", "mkdirp": "^3.0.1", diff --git a/package.json b/package.json index ff93347..82baed9 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "scripts": { "dev": "concurrently \"npm:dev:*\"", "dev:api": "npm run dev --if-present --workspace @liquid/auth-api", + "dev:core": "npm run dev --if-present --workspace @liquid/core", "dev:sdk": "npm run dev --if-present --workspace @liquid/auth-client", "dev:ui": "npm run dev --if-present --workspace dapp-ui", "build": "npm run build --workspace @liquid/auth-client && npm run build --workspace @liquid/auth-api && npm run build --workspace dapp-ui", diff --git a/services/liquid-auth-api-js/package.json b/services/liquid-auth-api-js/package.json index 1cec58e..c45ade5 100644 --- a/services/liquid-auth-api-js/package.json +++ b/services/liquid-auth-api-js/package.json @@ -23,6 +23,7 @@ "sentry:sourcemaps": "sentry-cli sourcemaps inject --org phearzero --project node-express dist && sentry-cli sourcemaps upload --org phearzero --project node-express dist" }, "dependencies": { + "@liquid/core": "^1.0.0", "@nestjs/common": "^10.2.6", "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.2.6", diff --git a/services/liquid-auth-api-js/src/__mocks__/assertion.service.mock.ts b/services/liquid-auth-api-js/src/__mocks__/assertion.service.mock.ts index ae31d76..d59ef3e 100644 --- a/services/liquid-auth-api-js/src/__mocks__/assertion.service.mock.ts +++ b/services/liquid-auth-api-js/src/__mocks__/assertion.service.mock.ts @@ -1,4 +1,4 @@ -import { dummyUsers, dummyOptions } from '../../tests/constants'; +import { dummyUsers, dummyOptions } from '../../tests/constants.js'; export const mockAssertionService = { request: jest.fn().mockReturnValue(dummyOptions), diff --git a/services/liquid-auth-api-js/src/__mocks__/attestation.service.mock.ts b/services/liquid-auth-api-js/src/__mocks__/attestation.service.mock.ts index 9867b75..ab062ee 100644 --- a/services/liquid-auth-api-js/src/__mocks__/attestation.service.mock.ts +++ b/services/liquid-auth-api-js/src/__mocks__/attestation.service.mock.ts @@ -1,4 +1,4 @@ -import { dummyAttestationOptions } from '../../tests/constants'; +import { dummyAttestationOptions } from '../../tests/constants.js'; export const mockAttestationService = { request: jest.fn().mockReturnValue(dummyAttestationOptions), diff --git a/services/liquid-auth-api-js/src/__mocks__/auth.service.mock.ts b/services/liquid-auth-api-js/src/__mocks__/auth.service.mock.ts index f74cd1c..3cfcb99 100644 --- a/services/liquid-auth-api-js/src/__mocks__/auth.service.mock.ts +++ b/services/liquid-auth-api-js/src/__mocks__/auth.service.mock.ts @@ -1,4 +1,4 @@ -import { dummyUsers } from '../../tests/constants'; +import { dummyUsers } from '../../tests/constants.js'; const dummyUser = dummyUsers[0]; diff --git a/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts b/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts index 8464e68..21e84f9 100644 --- a/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts +++ b/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts @@ -1,8 +1,7 @@ import { IoAdapter } from '@nestjs/platform-socket.io'; import { ServerOptions, Socket } from 'socket.io'; import { createAdapter } from '@socket.io/redis-adapter'; -import Redis from 'ioredis'; -import socketSessions from 'express-socket.io-session'; +import { Redis } from 'ioredis'; import { NestExpressApplication } from '@nestjs/platform-express'; import { RequestHandler } from 'express'; import { ConfigService } from '@nestjs/config'; diff --git a/services/liquid-auth-api-js/src/android/android.controller.spec.ts b/services/liquid-auth-api-js/src/android/android.controller.spec.ts index 0ea9570..88a47f5 100644 --- a/services/liquid-auth-api-js/src/android/android.controller.spec.ts +++ b/services/liquid-auth-api-js/src/android/android.controller.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { AndroidController } from './android.controller'; +import { AndroidController } from './android.controller.js'; describe('AndroidController', () => { let controller: AndroidController; diff --git a/services/liquid-auth-api-js/src/app.service.ts b/services/liquid-auth-api-js/src/app.service.ts index 2d2e705..4675d28 100644 --- a/services/liquid-auth-api-js/src/app.service.ts +++ b/services/liquid-auth-api-js/src/app.service.ts @@ -5,6 +5,7 @@ import UAParser from 'ua-parser-js'; // ignore due to jest // @ts-ignore import assetLinks from '../assetlinks.json' with { type: 'json' }; +import { toBase64URL } from "@liquid/core"; @Injectable() export class AppService { @@ -19,13 +20,14 @@ export class AppService { typeof parser.getBrowser().name !== 'string' ) { const pkgName = ua.split('/')[0] + console.log(pkgName) const statement = assetLinks.filter( (al) => al?.target?.package_name === pkgName, ); // TODO: better lookup for fingerprints using Headers - const octArray: unknown = statement[0].target.sha256_cert_fingerprints[0].split(':') + const octArray: number[] = statement[0].target.sha256_cert_fingerprints[0].split(':') .map((h) => parseInt(h, 16)); - const androidHash = (octArray as Buffer).toString('base64url'); + const androidHash = toBase64URL(new Uint8Array(octArray)); origin = `android:apk-key-hash:${androidHash}`; } // Web Origin diff --git a/services/liquid-auth-api-js/src/assertion/assertion.service.ts b/services/liquid-auth-api-js/src/assertion/assertion.service.ts index 7549fe6..9ff0930 100644 --- a/services/liquid-auth-api-js/src/assertion/assertion.service.ts +++ b/services/liquid-auth-api-js/src/assertion/assertion.service.ts @@ -38,7 +38,7 @@ export class AssertionService { rpID: this.configService.get('hostname'), allowCredentials, /** - * This optional value controls whether or not the authenticator needs be able to uniquely + * This optional value controls whether the authenticator needs to be able to uniquely * identify the user interacting with it (via built-in PIN pad, fingerprint scanner, etc...) */ userVerification, diff --git a/services/liquid-auth-api-js/src/attestation/attestation.service.ts b/services/liquid-auth-api-js/src/attestation/attestation.service.ts index c8bce80..328daa5 100644 --- a/services/liquid-auth-api-js/src/attestation/attestation.service.ts +++ b/services/liquid-auth-api-js/src/attestation/attestation.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { AppService } from '../app.service.js'; import fido2 from '@simplewebauthn/server'; -import { AttestationSelectorDto } from './attestation.dto'; +import { AttestationSelectorDto } from './attestation.dto.js'; import { User } from '../auth/auth.schema.js'; import type { AttestationCredentialJSON } from '@simplewebauthn/typescript-types'; @Injectable() @@ -63,6 +63,7 @@ export class AttestationService { credential: AttestationCredentialJSON & { device?: string }, ) { const expectedOrigin = this.appService.getOrigin(ua); + console.log(expectedOrigin) const expectedRPID = this.configService.get('hostname'); const verifiedAttestation = await fido2.verifyAttestationResponse({ credential, diff --git a/services/liquid-auth-api-js/src/auth/auth.service.ts b/services/liquid-auth-api-js/src/auth/auth.service.ts index c598116..fafccad 100644 --- a/services/liquid-auth-api-js/src/auth/auth.service.ts +++ b/services/liquid-auth-api-js/src/auth/auth.service.ts @@ -2,8 +2,7 @@ import * as crypto from 'node:crypto'; import { Injectable, Logger } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; -import base64url from 'base64url'; - +import { toBase64URL } from '@liquid/core/encoding'; import type { FilterQuery } from 'mongoose'; import { Credential, User } from './auth.schema.js'; import { Session } from './session.schema.js'; @@ -41,7 +40,7 @@ export class AuthService { */ async create(wallet: string): Promise { const createdUser = new this.userModel({ - id: base64url.encode(crypto.randomBytes(32)), + id: toBase64URL(crypto.randomBytes(32)), wallet, credentials: [], }); diff --git a/services/liquid-auth-api-js/src/connect/AlgoEncoder.ts b/services/liquid-auth-api-js/src/connect/AlgoEncoder.ts deleted file mode 100644 index 0436e56..0000000 --- a/services/liquid-auth-api-js/src/connect/AlgoEncoder.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { sha512_256 } from 'js-sha512'; -import * as base32 from 'hi-base32'; - -const ALGORAND_PUBLIC_KEY_BYTE_LENGTH = 32; -const ALGORAND_ADDRESS_BYTE_LENGTH = 36; -const ALGORAND_CHECKSUM_BYTE_LENGTH = 4; -const ALGORAND_ADDRESS_LENGTH = 58; -const HASH_BYTES_LENGTH = 32; -export const MALFORMED_ADDRESS_ERROR_MSG = 'Malformed address'; -export const ALGORAND_ADDRESS_BAD_CHECKSUM_ERROR_MSG = 'Bad checksum'; - -export class AlgorandEncoder { - /** - * decodeAddress takes an Algorand address in string form and decodes it into a Uint8Array. - * @param address - an Algorand address with checksum. - * @returns the decoded form of the address's public key and checksum - */ - decodeAddress(address: string): Uint8Array { - if ( - typeof address !== 'string' || - address.length !== ALGORAND_ADDRESS_LENGTH - ) - throw new Error(MALFORMED_ADDRESS_ERROR_MSG); - - // try to decode - const decoded = base32.decode.asBytes(address.toString()); - - // Find publickey and checksum - const pk = new Uint8Array( - decoded.slice( - 0, - ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH, - ), - ); - const cs = new Uint8Array( - decoded.slice( - ALGORAND_PUBLIC_KEY_BYTE_LENGTH, - ALGORAND_ADDRESS_BYTE_LENGTH, - ), - ); - - // Compute checksum - const checksum = sha512_256 - .array(pk) - .slice( - HASH_BYTES_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH, - HASH_BYTES_LENGTH, - ); - - // Check if the checksum and the address are equal - if ( - checksum.length !== cs.length || - !Array.from(checksum).every((val, i) => val === cs[i]) - ) { - throw new Error(ALGORAND_ADDRESS_BAD_CHECKSUM_ERROR_MSG); - } - - return pk; - } -} diff --git a/services/liquid-auth-api-js/src/connect/connect.controller.ts b/services/liquid-auth-api-js/src/connect/connect.controller.ts index 1c597ce..ed4d076 100644 --- a/services/liquid-auth-api-js/src/connect/connect.controller.ts +++ b/services/liquid-auth-api-js/src/connect/connect.controller.ts @@ -10,18 +10,8 @@ import { import { ClientProxy } from '@nestjs/microservices'; import { Response } from 'express'; import { AuthService } from '../auth/auth.service.js'; -import { AlgorandEncoder } from './AlgoEncoder.js'; - -const algoEncoder = new AlgorandEncoder(); - -const base64ToUint8Array = (encoded) => { - return new Uint8Array( - atob(encoded) - .split('') - .map((c) => c.charCodeAt(0)), - ); -}; import nacl from 'tweetnacl'; +import { decodeAddress, base64ToUint8Array } from '@liquid/core/encoding'; type LinkResponseDTO = { credId?: string; @@ -61,7 +51,7 @@ export class ConnectController { `POST /connect/response for RequestId: ${requestId} Session: ${session.id} with Wallet: ${wallet}`, ); // Decode Address - const publicKey = algoEncoder.decodeAddress(wallet); + const publicKey = decodeAddress(wallet); // Decode signature const uint8Signature = base64ToUint8Array( diff --git a/services/liquid-auth-api-js/src/connect/connect.gateway.ts b/services/liquid-auth-api-js/src/connect/connect.gateway.ts index 85a05e3..600b2cd 100644 --- a/services/liquid-auth-api-js/src/connect/connect.gateway.ts +++ b/services/liquid-auth-api-js/src/connect/connect.gateway.ts @@ -12,7 +12,7 @@ import { import { Logger } from '@nestjs/common'; import { AuthService } from '../auth/auth.service.js'; -import { RedisIoAdapter } from '../adapters/redis-io.adapter'; +import { RedisIoAdapter } from '../adapters/redis-io.adapter.js'; @WebSocketGateway({ cors: { origin: '*', diff --git a/services/liquid-auth-api-js/src/main.ts b/services/liquid-auth-api-js/src/main.ts index da569f2..e53dcb4 100644 --- a/services/liquid-auth-api-js/src/main.ts +++ b/services/liquid-auth-api-js/src/main.ts @@ -15,7 +15,7 @@ import MongoStore from 'connect-mongo'; import * as Sentry from '@sentry/node'; import { ProfilingIntegration } from '@sentry/profiling-node'; import { SentryFilter } from './sentry.filter.js'; -import { createProxyMiddleware } from 'http-proxy-middleware'; + import { resolve } from 'path'; import hbs from 'hbs'; async function bootstrap() { diff --git a/services/liquid-auth-api-js/tests/constants.ts b/services/liquid-auth-api-js/tests/constants.ts index 315e917..5c86529 100644 --- a/services/liquid-auth-api-js/tests/constants.ts +++ b/services/liquid-auth-api-js/tests/constants.ts @@ -1,4 +1,4 @@ -import { AlgorandEncoder } from "../src/connect/AlgoEncoder"; +import { decodeAddress } from '@liquid/core/encoding'; export const accFixture = { challenge: '1234', @@ -28,7 +28,7 @@ export const dummyUsers = [ credentials: [ { credId: 0, - publicKey: new AlgorandEncoder().decodeAddress(accFixture.accs[0].addr), + publicKey: decodeAddress(accFixture.accs[0].addr), }, ], }, diff --git a/services/liquid-auth-api-js/tsconfig.json b/services/liquid-auth-api-js/tsconfig.json index 3bb2f4d..587d901 100644 --- a/services/liquid-auth-api-js/tsconfig.json +++ b/services/liquid-auth-api-js/tsconfig.json @@ -1,13 +1,13 @@ { "compilerOptions": { - "module": "ESNext", + "module": "NodeNext", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "allowSyntheticDefaultImports": true, - "target": "ES2022", - "moduleResolution": "node", + "target": "ESNext", + "moduleResolution": "NodeNext", "sourceMap": true, "outDir": "./dist", "baseUrl": "./", diff --git a/sites/dapp-ui/.eslintrc.cjs b/sites/dapp-ui/.eslintrc.cjs index d6c9537..137bb37 100644 --- a/sites/dapp-ui/.eslintrc.cjs +++ b/sites/dapp-ui/.eslintrc.cjs @@ -5,6 +5,7 @@ module.exports = { 'eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', + 'plugin:prettier/recommended' ], ignorePatterns: ['dist', '.eslintrc.cjs'], parser: '@typescript-eslint/parser', diff --git a/sites/dapp-ui/.prettierrc b/sites/dapp-ui/.prettierrc new file mode 100644 index 0000000..dcb7279 --- /dev/null +++ b/sites/dapp-ui/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/sites/dapp-ui/package.json b/sites/dapp-ui/package.json index 9eb0065..a406486 100644 --- a/sites/dapp-ui/package.json +++ b/sites/dapp-ui/package.json @@ -36,6 +36,8 @@ "@typescript-eslint/parser": "^6.14.0", "@vitejs/plugin-react-swc": "^3.5.0", "eslint": "^8.55.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", "mkdirp": "^3.0.1", diff --git a/sites/dapp-ui/src/App.tsx b/sites/dapp-ui/src/App.tsx index 8250f7c..a40c4ae 100644 --- a/sites/dapp-ui/src/App.tsx +++ b/sites/dapp-ui/src/App.tsx @@ -1,92 +1,128 @@ -import {useContext, ReactElement, useState, useMemo, useEffect} from 'react'; +import { useState, useMemo } from 'react'; -import { ColorModeContext, DataChannelContext, PeerConnectionContext, SnackbarContext, StateContext } from './Contexts'; +import { + ColorModeContext, + DataChannelContext, + PeerConnectionContext, + SnackbarContext, + StateContext, +} from './Contexts'; import Layout from './Layout'; import { GetStartedCard } from './pages/home/GetStarted'; -import { WaitForRegistrationCard } from './pages/dashboard/WaitForRegistration'; import { RegisteredCard } from './pages/dashboard/Registered'; -import {createTheme, CssBaseline} from '@mui/material'; +import { createTheme, CssBaseline } from '@mui/material'; import { DEFAULT_THEME } from './theme.tsx'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; import { ThemeProvider } from '@emotion/react'; -import {ReactQueryDevtools} from "@tanstack/react-query-devtools"; -import {DebugWebRTC} from "./pages/debug/WebRTC.tsx"; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; -import { - createHashRouter, - RouterProvider -} from "react-router-dom"; -import { WaitForPeersCard } from "./pages/peering/WaitForPeers.tsx"; -import ConnectedPage from "./pages/connected.tsx"; -const queryClient = new QueryClient() +import { createHashRouter, RouterProvider } from 'react-router-dom'; +import { WaitForPeersCard } from './pages/peering/WaitForPeers.tsx'; +import ConnectedPage from './pages/connected.tsx'; +const queryClient = new QueryClient(); const DEFAULT_CONFIG: RTCConfiguration = { - iceServers: [ - { - urls: [ - 'stun:stun.l.google.com:19302', - 'stun:stun1.l.google.com:19302', - 'stun:stun2.l.google.com:19302', - ], - }, - ], - iceCandidatePoolSize: 10, + iceServers: [ + { + urls: [ + 'stun:stun.l.google.com:19302', + 'stun:stun1.l.google.com:19302', + 'stun:stun2.l.google.com:19302', + ], + }, + ], + iceCandidatePoolSize: 10, }; const router = createHashRouter([ - {'path': '/', 'element': }, - {'path': '/peering', 'element': }, - {'path': '/connected', 'element': }, - {'path': '/registered', 'element': }, - {'path': '/debug/webrtc', 'element': }, -]) -export default function ProviderApp(){ - const [open, setOpen] = useState(false) - const [message, setMessage] = useState('') - const [state, setState] = useState('peering') - const [dataChannel, setDataChannel] = useState(null) + { + path: '/', + element: ( + + + + ), + }, + { + path: '/peering', + element: ( + + + + ), + }, + { + path: '/connected', + element: ( + + + + ), + }, + { + path: '/registered', + element: ( + + + + ), + }, +]); +export default function ProviderApp() { + const [open, setOpen] = useState(false); + const [message, setMessage] = useState(''); + const [state, setState] = useState('peering'); + const [dataChannel, setDataChannel] = useState(null); - const peerConnection = useMemo(()=>new RTCPeerConnection(DEFAULT_CONFIG), []); - globalThis.peerConnection = peerConnection; - const [mode, setMode] = useState<'light' | 'dark'>(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)') ? 'dark' : 'light'); - const colorMode = useMemo( - () => ({ - toggle: () => { - setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); - }, - }), - [], - ); + const peerConnection = useMemo( + () => new RTCPeerConnection(DEFAULT_CONFIG), + [], + ); + const [mode, setMode] = useState<'light' | 'dark'>( + window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)') + ? 'dark' + : 'light', + ); + const colorMode = useMemo( + () => ({ + toggle: () => { + setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); + }, + }), + [], + ); - const theme = useMemo( - () => - createTheme({ - ...DEFAULT_THEME, - palette: { - ...DEFAULT_THEME.palette, - mode, - }, - }), - [mode], - ); - return ( - - - - - - - - - - - - - - - - - - ) + const theme = useMemo( + () => + createTheme({ + ...DEFAULT_THEME, + palette: { + ...DEFAULT_THEME.palette, + mode, + }, + }), + [mode], + ); + return ( + + + + + + + + + + + + + + + + + + ); } diff --git a/sites/dapp-ui/src/Contexts.tsx b/sites/dapp-ui/src/Contexts.tsx index 79b5d8c..27973eb 100644 --- a/sites/dapp-ui/src/Contexts.tsx +++ b/sites/dapp-ui/src/Contexts.tsx @@ -1,13 +1,18 @@ -import {createContext} from "react"; +import { createContext } from 'react'; -export const ColorModeContext = createContext({toggle: () => {}}); +export const ColorModeContext = createContext({ toggle: () => {} }); -export const StateContext = createContext({state: 'debug', setState: (_: string) => {}}); +export const StateContext = createContext({ + state: 'debug', + setState: (_: string) => {}, +}); export const SnackbarContext = createContext({ - open: false, setOpen: (_: boolean) => {}, - message: '', setMessage: (_: string) => {} + open: false, + setOpen: (_: boolean) => {}, + message: '', + setMessage: (_: string) => {}, }); -export {DataChannelContext} from './hooks/useDataChannel.ts'; -export {PeerConnectionContext} from './hooks/usePeerConnection.ts'; +export { DataChannelContext } from './hooks/useDataChannel.ts'; +export { PeerConnectionContext } from './hooks/usePeerConnection.ts'; diff --git a/sites/dapp-ui/src/Layout.tsx b/sites/dapp-ui/src/Layout.tsx index ce27197..af20a32 100644 --- a/sites/dapp-ui/src/Layout.tsx +++ b/sites/dapp-ui/src/Layout.tsx @@ -4,72 +4,98 @@ import AppBar from '@mui/material/AppBar'; import Box from '@mui/material/Box'; import Toolbar from '@mui/material/Toolbar'; import IconButton from '@mui/material/IconButton'; -import {useTheme} from '@mui/material'; -import {PropsWithChildren, useContext} from 'react'; +import { useTheme } from '@mui/material'; +import { PropsWithChildren, useContext } from 'react'; import Brightness4Icon from '@mui/icons-material/Brightness4'; import Brightness7Icon from '@mui/icons-material/Brightness7'; import { ColorModeContext } from './Contexts'; import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore'; import NavigateNextIcon from '@mui/icons-material/NavigateNext'; -import {SessionMenu} from "./components/user/SessionMenu.tsx"; -import {MessageSnackbar} from "./components/Snackbar.tsx"; -import { useLocation, useNavigate } from "react-router-dom"; -export default function Layout({children}: PropsWithChildren) { +import { SessionMenu } from './components/user/SessionMenu.tsx'; +import { MessageSnackbar } from './components/Snackbar.tsx'; +import { useLocation, useNavigate } from 'react-router-dom'; +export default function Layout({ children }: PropsWithChildren) { const location = useLocation(); - const navigate = useNavigate() - const colorMode = useContext(ColorModeContext) - const theme = useTheme() - const isDarkMode = theme.palette.mode === 'dark' + const navigate = useNavigate(); + const colorMode = useContext(ColorModeContext); + const theme = useTheme(); + const isDarkMode = theme.palette.mode === 'dark'; const bubbleStyle = { - backgroundColor: isDarkMode ? 'white' : 'black' - } - const breadcrumbs = ["/", "/peering", "/connected", "/registered"] - const index = breadcrumbs.indexOf(location.pathname) + backgroundColor: isDarkMode ? 'white' : 'black', + }; + const breadcrumbs = ['/', '/peering', '/connected', '/registered']; + const index = breadcrumbs.indexOf(location.pathname); return ( - <> - - - - - Liquid dApp - - { - index > 0 && navigate(breadcrumbs[index - 1]) - }} aria-label="delete" disabled={index === 0} color="inherit"> - - - { - index < breadcrumbs.length - 1 && navigate(breadcrumbs[index + 1]) - }} aria-label="delete" disabled={index === breadcrumbs.length - 1} color="inherit"> - - - - {theme.palette.mode === 'dark' ? : } - - - + <> + + + + + Liquid dApp + + { + index > 0 && navigate(breadcrumbs[index - 1]); + }} + aria-label="delete" + disabled={index === 0} + color="inherit" + > + + + { + index < breadcrumbs.length - 1 && + navigate(breadcrumbs[index + 1]); + }} + aria-label="delete" + disabled={index === breadcrumbs.length - 1} + color="inherit" + > + + + + {theme.palette.mode === 'dark' ? ( + + ) : ( + + )} + + + - - - {children} - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
- + + + {children} + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ); } diff --git a/sites/dapp-ui/src/components/Chat.tsx b/sites/dapp-ui/src/components/Chat.tsx index d26fd04..fecc1b7 100644 --- a/sites/dapp-ui/src/components/Chat.tsx +++ b/sites/dapp-ui/src/components/Chat.tsx @@ -1,73 +1,89 @@ -import Avatar from "@mui/material/Avatar"; -import Divider from "@mui/material/Divider"; -import List from "@mui/material/List"; -import ListItem from "@mui/material/ListItem"; -import ListItemAvatar from "@mui/material/ListItemAvatar"; -import ListItemText from "@mui/material/ListItemText"; -import {Message, useMessageStore} from "../store.ts"; -import TextField from "@mui/material/TextField"; -import {useContext, useState} from "react"; -import Button from "@mui/material/Button"; -import {DataChannelContext} from "../hooks/useDataChannel.ts"; - +import Avatar from '@mui/material/Avatar'; +import Divider from '@mui/material/Divider'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemAvatar from '@mui/material/ListItemAvatar'; +import ListItemText from '@mui/material/ListItemText'; +import { Message, useMessageStore } from '../store.ts'; +import TextField from '@mui/material/TextField'; +import { useContext, useState } from 'react'; +import Button from '@mui/material/Button'; +import { DataChannelContext } from '../hooks/useDataChannel.ts'; const MESSAGE_TYPES = { - local: 'Local', - remote: 'Remote' -} + local: 'Local', + remote: 'Remote', +}; const MESSAGE_AVATARS = { - local: '/maskable-icon.png', - remote: '/logo-inverted.png' -} + local: '/maskable-icon.png', + remote: '/logo-inverted.png', +}; function ChatMessage(message: Message) { - return ( - - - - - - - ) + return ( + + + + + + + ); } export function ChatList() { - const {dataChannel} = useContext(DataChannelContext) + const { dataChannel } = useContext(DataChannelContext); - const messages = useMessageStore((state) => state.messages) - const addMessage = useMessageStore((state) => state.addMessage) - const [message, setMessage] = useState('') + const messages = useMessageStore((state) => state.messages); + const addMessage = useMessageStore((state) => state.addMessage); + const [message, setMessage] = useState(''); - const isReady = dataChannel && dataChannel.readyState === 'open' - return ( - <> - - {messages.map((message, i) => { - return ( -
- - -
- ) - })} -
-
- setMessage(e.target.value)}> - -
- {/**/} - - ) + const isReady = dataChannel && dataChannel.readyState === 'open'; + return ( + <> + + {messages.map((message, i) => { + return ( +
+ + +
+ ); + })} +
+
+ setMessage(e.target.value)} + > + +
+ {/**/} + + ); } diff --git a/sites/dapp-ui/src/components/Snackbar.tsx b/sites/dapp-ui/src/components/Snackbar.tsx index 99aa6c2..91118d0 100644 --- a/sites/dapp-ui/src/components/Snackbar.tsx +++ b/sites/dapp-ui/src/components/Snackbar.tsx @@ -1,43 +1,47 @@ -import {Alert, Snackbar, useMediaQuery, useTheme} from "@mui/material"; -import {useContext} from "react"; -import {SnackbarContext} from "../Contexts.tsx"; -import IconButton from "@mui/material/IconButton"; -import CloseIcon from "@mui/icons-material/Close"; +import { Alert, Snackbar, useMediaQuery, useTheme } from '@mui/material'; +import { useContext } from 'react'; +import { SnackbarContext } from '../Contexts.tsx'; +import IconButton from '@mui/material/IconButton'; +import CloseIcon from '@mui/icons-material/Close'; -export function MessageSnackbar(){ - const theme = useTheme(); - const greaterThanMid = useMediaQuery(theme.breakpoints.up("md")); +export function MessageSnackbar() { + const theme = useTheme(); + const greaterThanMid = useMediaQuery(theme.breakpoints.up('md')); - const {open, setOpen, message, setMessage} = useContext(SnackbarContext) + const { open, setOpen, message, setMessage } = useContext(SnackbarContext); - const handleClose = () => { - setOpen(false) - setMessage('') - } - return ( - - - } + const handleClose = () => { + setOpen(false); + setMessage(''); + }; + return ( + - - {message} - - - ) + + + } + > + + {message} + + + ); } diff --git a/sites/dapp-ui/src/components/user/Credential.tsx b/sites/dapp-ui/src/components/user/Credential.tsx index ad693d7..96559e4 100644 --- a/sites/dapp-ui/src/components/user/Credential.tsx +++ b/sites/dapp-ui/src/components/user/Credential.tsx @@ -1,8 +1,7 @@ -export function Credential(){ -return ( -
-

Credential

- -
- ) +export function Credential() { + return ( +
+

Credential

+
+ ); } diff --git a/sites/dapp-ui/src/components/user/SessionMenu.tsx b/sites/dapp-ui/src/components/user/SessionMenu.tsx index 2b4a509..0c0c06f 100644 --- a/sites/dapp-ui/src/components/user/SessionMenu.tsx +++ b/sites/dapp-ui/src/components/user/SessionMenu.tsx @@ -1,64 +1,70 @@ -import {Avatar, Badge, CircularProgress, Menu} from "@mui/material"; -import IconButton from "@mui/material/IconButton"; -import React, {useState} from "react"; -import {useSocket} from "../../hooks/useSocket.ts"; -import {useUserState} from "./useUserState.ts"; -import {StatusCard} from "./StatusCard.tsx"; +import { Avatar, Badge, CircularProgress, Menu } from '@mui/material'; +import IconButton from '@mui/material/IconButton'; +import React, { useState } from 'react'; +import { useSocket } from '../../hooks/useSocket.ts'; +import { useUserState } from './useUserState.ts'; +import { StatusCard } from './StatusCard.tsx'; -export function SessionMenu(){ - const state = useUserState() - const {isConnected} = useSocket() +export function SessionMenu() { + const state = useUserState(); + const { isConnected } = useSocket(); - const [anchorEl, setAnchorEl] = useState(null); - const open = Boolean(anchorEl); - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; - return ( - <> - - - - - - - {state.isLoading && } - {state.isFetched && } - - - ) + return ( + <> + + + + + + + {state.isLoading && } + {state.isFetched && ( + + )} + + + ); } diff --git a/sites/dapp-ui/src/components/user/StatusCard.tsx b/sites/dapp-ui/src/components/user/StatusCard.tsx index dd58c6e..1f30b1a 100644 --- a/sites/dapp-ui/src/components/user/StatusCard.tsx +++ b/sites/dapp-ui/src/components/user/StatusCard.tsx @@ -1,64 +1,62 @@ -import Card from "@mui/material/Card"; -import CardActions from "@mui/material/CardActions"; -import CardContent from "@mui/material/CardContent"; -import IconButton from "@mui/material/IconButton"; -import Typography from "@mui/material/Typography"; -import FavoriteIcon from '@mui/icons-material/Favorite'; +import Card from '@mui/material/Card'; +import CardActions from '@mui/material/CardActions'; +import CardContent from '@mui/material/CardContent'; +import IconButton from '@mui/material/IconButton'; +import Typography from '@mui/material/Typography'; import ShareIcon from '@mui/icons-material/Share'; import LogoutIcon from '@mui/icons-material/Logout'; -import {User} from "./types.ts" +import { User } from './types.ts'; export type ProfileCardProps = { - socket: { - isConnected: boolean - } - session: { - wallet?: string - connected?: boolean - active?: boolean - cookie:{ - expires: string | null - httpOnly: boolean - originalMaxAge: number | null - path: string - secure: boolean - } - } - user: User | null -} -export function StatusCard({session, user, socket}: ProfileCardProps){ - const {cookie, ...sessionData} = session - return ( - - - - Session - -
-                    {JSON.stringify(sessionData, null, 2)}
-                
- - Signaling Service - -
-                    {JSON.stringify(socket, null, 2)}
-                
- - Cookie - -
-                    {JSON.stringify(cookie, null, 2)}
-                
-
- {user && - { - fetch('/auth/logout') - }}> - - - - - - } -
- ) + socket: { + isConnected: boolean; + }; + session: { + wallet?: string; + connected?: boolean; + active?: boolean; + cookie: { + expires: string | null; + httpOnly: boolean; + originalMaxAge: number | null; + path: string; + secure: boolean; + }; + }; + user: User | null; +}; +export function StatusCard({ session, user, socket }: ProfileCardProps) { + const { cookie, ...sessionData } = session; + return ( + + + + Session + +
{JSON.stringify(sessionData, null, 2)}
+ + Signaling Service + +
{JSON.stringify(socket, null, 2)}
+ + Cookie + +
{JSON.stringify(cookie, null, 2)}
+
+ {user && ( + + { + fetch('/auth/logout'); + }} + > + + + + + + + )} +
+ ); } diff --git a/sites/dapp-ui/src/components/user/types.ts b/sites/dapp-ui/src/components/user/types.ts index a6e4ff7..953d284 100644 --- a/sites/dapp-ui/src/components/user/types.ts +++ b/sites/dapp-ui/src/components/user/types.ts @@ -1,11 +1,11 @@ export type CredentialMetadata = { - device: string - publicKey: string - credId: string - prevCounter: number -} + device: string; + publicKey: string; + credId: string; + prevCounter: number; +}; export type User = { - wallet: string - id: string - credentials: CredentialMetadata[] -} + wallet: string; + id: string; + credentials: CredentialMetadata[]; +}; diff --git a/sites/dapp-ui/src/components/user/useUserState.ts b/sites/dapp-ui/src/components/user/useUserState.ts index 9c4d0b4..987bb1a 100644 --- a/sites/dapp-ui/src/components/user/useUserState.ts +++ b/sites/dapp-ui/src/components/user/useUserState.ts @@ -1,12 +1,9 @@ -import {useQuery} from "@tanstack/react-query"; +import { useQuery } from '@tanstack/react-query'; -export function useUserState(){ - return useQuery({ - refetchInterval: 3000, - queryKey: ['auth-session'], - queryFn: () => - fetch('/auth/session').then((res) => - res.json(), - ), - }) +export function useUserState() { + return useQuery({ + refetchInterval: 3000, + queryKey: ['auth-session'], + queryFn: () => fetch('/auth/session').then((res) => res.json()), + }); } diff --git a/sites/dapp-ui/src/entry-main.tsx b/sites/dapp-ui/src/entry-main.tsx index cda24cc..0565258 100644 --- a/sites/dapp-ui/src/entry-main.tsx +++ b/sites/dapp-ui/src/entry-main.tsx @@ -1,11 +1,10 @@ -import './index.css' +import './index.css'; import * as ReactDOM from 'react-dom/client'; -import {StrictMode} from 'react'; -import App from './App.tsx' - +import { StrictMode } from 'react'; +import App from './App.tsx'; ReactDOM.createRoot(document.getElementById('root')!).render( - // - - // , + // + , + // , ); diff --git a/sites/dapp-ui/src/hooks/useAddress.ts b/sites/dapp-ui/src/hooks/useAddress.ts index 0b843af..24ba4f0 100644 --- a/sites/dapp-ui/src/hooks/useAddress.ts +++ b/sites/dapp-ui/src/hooks/useAddress.ts @@ -1,24 +1,32 @@ import { useMediaQuery, useTheme } from '@mui/material'; import { useQuery } from '@tanstack/react-query'; -export function useAddressQuery(address?: string){ +export function useAddressQuery(address?: string) { const theme = useTheme(); - const greaterThanMid = useMediaQuery(theme.breakpoints.up("md")); + const greaterThanMid = useMediaQuery(theme.breakpoints.up('md')); - return useQuery({ enabled: typeof address === 'string',queryKey: ['nfd-lookup'], queryFn: ()=>{ - if(typeof address === 'undefined'){ - return - } - return fetch(`https://api.nf.domains/nfd/lookup?address=${address}&view=tiny&allowUnverified=true`).then(async (r)=>{ - if(r.ok){ - const data= await r.json() - return data[address].name - }else{ - if(r.status === 404){ - return greaterThanMid ? address : `${address.slice(0, 5)}...${address.slice(-5)}` - } - throw new Error('Failed to fetch address') + return useQuery({ + enabled: typeof address === 'string', + queryKey: ['nfd-lookup'], + queryFn: () => { + if (typeof address === 'undefined') { + return; } - }) - }}) + return fetch( + `https://api.nf.domains/nfd/lookup?address=${address}&view=tiny&allowUnverified=true`, + ).then(async (r) => { + if (r.ok) { + const data = await r.json(); + return data[address].name; + } else { + if (r.status === 404) { + return greaterThanMid + ? address + : `${address.slice(0, 5)}...${address.slice(-5)}`; + } + throw new Error('Failed to fetch address'); + } + }); + }, + }); } diff --git a/sites/dapp-ui/src/hooks/useDataChannel.ts b/sites/dapp-ui/src/hooks/useDataChannel.ts index 9ae13b0..41e15e0 100644 --- a/sites/dapp-ui/src/hooks/useDataChannel.ts +++ b/sites/dapp-ui/src/hooks/useDataChannel.ts @@ -1,23 +1,29 @@ -import {createContext, useContext, useEffect} from "react"; +import { createContext, useContext, useEffect } from 'react'; -type DataChannelState = {dataChannel: RTCDataChannel | null, setDataChannel: (_: RTCDataChannel) => void} +type DataChannelState = { + dataChannel: RTCDataChannel | null; + setDataChannel: (_: RTCDataChannel) => void; +}; export const DataChannelContext = createContext({ - dataChannel: null, setDataChannel: (_: RTCDataChannel) => {} + dataChannel: null, + setDataChannel: (_: RTCDataChannel) => {}, } as DataChannelState); /** * Hook to use data channel messages * @param onMessage */ -export function useDataChannelMessages(onMessage: (event: MessageEvent)=>void){ - const {dataChannel} = useContext(DataChannelContext); - useEffect(() => { - if(!dataChannel) return - dataChannel.addEventListener("message", onMessage); - return () => { - dataChannel.removeEventListener("message", onMessage); - } - }, [dataChannel, onMessage]); +export function useDataChannelMessages( + onMessage: (event: MessageEvent) => void, +) { + const { dataChannel } = useContext(DataChannelContext); + useEffect(() => { + if (!dataChannel) return; + dataChannel.addEventListener('message', onMessage); + return () => { + dataChannel.removeEventListener('message', onMessage); + }; + }, [dataChannel, onMessage]); } /** @@ -28,25 +34,28 @@ export function useDataChannelMessages(onMessage: (event: MessageEvent)=>void){ * @param type * @param peerConnection */ -export function useDataChannel(type: 'local' | 'remote', peerConnection: RTCPeerConnection | null){ - const {dataChannel, setDataChannel} = useContext(DataChannelContext) - useEffect(() => { - if(!peerConnection) return - function handleOnDataChannel(event: RTCDataChannelEvent){ - setDataChannel(event.channel) - } - if(type === 'local') { - setDataChannel(peerConnection.createDataChannel('data')) - } else { - peerConnection.addEventListener('datachannel', handleOnDataChannel) - } +export function useDataChannel( + type: 'local' | 'remote', + peerConnection: RTCPeerConnection | null, +) { + const { dataChannel, setDataChannel } = useContext(DataChannelContext); + useEffect(() => { + if (!peerConnection) return; + function handleOnDataChannel(event: RTCDataChannelEvent) { + setDataChannel(event.channel); + } + if (type === 'local') { + setDataChannel(peerConnection.createDataChannel('data')); + } else { + peerConnection.addEventListener('datachannel', handleOnDataChannel); + } - return ()=> { - if(type === 'remote') { - peerConnection.removeEventListener('datachannel', handleOnDataChannel) - } - } - }, [peerConnection, setDataChannel, type]); + return () => { + if (type === 'remote') { + peerConnection.removeEventListener('datachannel', handleOnDataChannel); + } + }; + }, [peerConnection, setDataChannel, type]); - return dataChannel; + return dataChannel; } diff --git a/sites/dapp-ui/src/hooks/usePeerConnection.ts b/sites/dapp-ui/src/hooks/usePeerConnection.ts index 6682acc..f0d6590 100644 --- a/sites/dapp-ui/src/hooks/usePeerConnection.ts +++ b/sites/dapp-ui/src/hooks/usePeerConnection.ts @@ -1,70 +1,106 @@ -import {createContext, useContext, useEffect, useState} from "react"; +import { createContext, useContext, useEffect, useState } from 'react'; -type PeerConnectionState = {peerConnection: RTCPeerConnection | null} +type PeerConnectionState = { peerConnection: RTCPeerConnection | null }; export const PeerConnectionContext = createContext({ - peerConnection: null + peerConnection: null, } as PeerConnectionState); +export function usePeerConnectionState() { + const { peerConnection } = useContext(PeerConnectionContext); + const [connectionState, setConnectionState] = useState( + peerConnection?.connectionState || null, + ); + useEffect(() => { + if (!peerConnection) return; + function handleConnectionStateChange() { + if (!peerConnection) return; + console.log(`Connection state change: ${peerConnection.connectionState}`); + setConnectionState(peerConnection.connectionState); + } -export function usePeerConnectionState(){ - const {peerConnection} = useContext(PeerConnectionContext); - const [connectionState, setConnectionState] = useState(peerConnection?.connectionState || null); + peerConnection.addEventListener( + 'connectionstatechange', + handleConnectionStateChange, + ); + return () => { + peerConnection.removeEventListener( + 'connectionstatechange', + handleConnectionStateChange, + ); + }; + }, [peerConnection]); - useEffect(() => { - if(!peerConnection) return; - function handleConnectionStateChange() { - if(!peerConnection) return; - console.log(`Connection state change: ${peerConnection.connectionState}`); - setConnectionState(peerConnection.connectionState); - } - - peerConnection.addEventListener('connectionstatechange', handleConnectionStateChange); - return () => { - peerConnection.removeEventListener('connectionstatechange', handleConnectionStateChange); - } - }, [peerConnection]); - - return connectionState; + return connectionState; } export function usePeerConnection( - onIceCandidate: (event: RTCPeerConnectionIceEvent)=>void) { - const {peerConnection} = useContext(PeerConnectionContext); - window.peerConnection = peerConnection; - useEffect(() => { - if(!peerConnection) return; - function handleICEGatheringStateChange() { - console.log(`ICE gathering state: ${peerConnection?.iceGatheringState}`); - } - function handleConnectionStateChange() { - console.log(`Connection state change: ${peerConnection.connectionState}`); - } + onIceCandidate: (event: RTCPeerConnectionIceEvent) => void, +) { + const { peerConnection } = useContext(PeerConnectionContext); + window.peerConnection = peerConnection; + useEffect(() => { + if (!peerConnection) return; + function handleICEGatheringStateChange() { + console.log(`ICE gathering state: ${peerConnection?.iceGatheringState}`); + } + function handleConnectionStateChange() { + console.log(`Connection state change: ${peerConnection.connectionState}`); + } - function handleSignalingStateChange() { - console.log(`Signaling state change: ${peerConnection?.signalingState}`); - } + function handleSignalingStateChange() { + console.log(`Signaling state change: ${peerConnection?.signalingState}`); + } - function handleICEConnectionStateChange() { - console.log(`ICE connection state change: ${peerConnection?.iceConnectionState}`); - } + function handleICEConnectionStateChange() { + console.log( + `ICE connection state change: ${peerConnection?.iceConnectionState}`, + ); + } - function handleICECandidateError(event){ - console.error('ICE Candidate Error', event) - } - peerConnection.addEventListener('icegatheringstatechange', handleICEGatheringStateChange); - peerConnection.addEventListener('connectionstatechange', handleConnectionStateChange); - peerConnection.addEventListener('signalingstatechange', handleSignalingStateChange); - peerConnection.addEventListener('iceconnectionstatechange ', handleICEConnectionStateChange); - peerConnection.addEventListener('icecandidate', onIceCandidate); - peerConnection.addEventListener('icecandidateerror', handleICECandidateError) - return () => { - peerConnection.removeEventListener('icegatheringstatechange', handleICEGatheringStateChange); - peerConnection.removeEventListener('connectionstatechange', handleConnectionStateChange); - peerConnection.removeEventListener('signalingstatechange', handleSignalingStateChange); - peerConnection.removeEventListener('iceconnectionstatechange ', handleICEConnectionStateChange); - peerConnection.removeEventListener('icecandidate', onIceCandidate); - } - }, [peerConnection, onIceCandidate]); + function handleICECandidateError(event) { + console.error('ICE Candidate Error', event); + } + peerConnection.addEventListener( + 'icegatheringstatechange', + handleICEGatheringStateChange, + ); + peerConnection.addEventListener( + 'connectionstatechange', + handleConnectionStateChange, + ); + peerConnection.addEventListener( + 'signalingstatechange', + handleSignalingStateChange, + ); + peerConnection.addEventListener( + 'iceconnectionstatechange ', + handleICEConnectionStateChange, + ); + peerConnection.addEventListener('icecandidate', onIceCandidate); + peerConnection.addEventListener( + 'icecandidateerror', + handleICECandidateError, + ); + return () => { + peerConnection.removeEventListener( + 'icegatheringstatechange', + handleICEGatheringStateChange, + ); + peerConnection.removeEventListener( + 'connectionstatechange', + handleConnectionStateChange, + ); + peerConnection.removeEventListener( + 'signalingstatechange', + handleSignalingStateChange, + ); + peerConnection.removeEventListener( + 'iceconnectionstatechange ', + handleICEConnectionStateChange, + ); + peerConnection.removeEventListener('icecandidate', onIceCandidate); + }; + }, [peerConnection, onIceCandidate]); - return peerConnection; + return peerConnection; } diff --git a/sites/dapp-ui/src/hooks/useSocket.ts b/sites/dapp-ui/src/hooks/useSocket.ts index 63f8ca4..db93f37 100644 --- a/sites/dapp-ui/src/hooks/useSocket.ts +++ b/sites/dapp-ui/src/hooks/useSocket.ts @@ -4,20 +4,19 @@ import { useEffect, useState } from 'react'; // "undefined" means the URL will be computed from the `window.location` object const URL = `${window.location.origin}`; -export const socket = io(URL, {autoConnect: true}); - -export function useSocket(){ +export const socket = io(URL, { autoConnect: true }); +export function useSocket() { const [isConnected, setIsConnected] = useState(socket.connected); useEffect(() => { function onConnect() { - console.log('Connected') + console.log('Connected'); setIsConnected(true); } function onDisconnect() { - console.log('Disconnected') + console.log('Disconnected'); setIsConnected(false); } socket.on('connect', onConnect); @@ -29,5 +28,5 @@ export function useSocket(){ }; }, []); - return { isConnected, socket} + return { isConnected, socket }; } diff --git a/sites/dapp-ui/src/pages/connected.tsx b/sites/dapp-ui/src/pages/connected.tsx index cc7dfda..0881371 100644 --- a/sites/dapp-ui/src/pages/connected.tsx +++ b/sites/dapp-ui/src/pages/connected.tsx @@ -1,54 +1,68 @@ -import { useDataChannel, useDataChannelMessages } from "../hooks/useDataChannel.ts"; -import { PeerConnectionContext } from "../hooks/usePeerConnection.ts"; -import { useContext, useEffect, useState } from "react"; -import Button from "@mui/material/Button"; -import algosdk from 'algosdk' -import { toBase64URL, fromBase64Url } from "@liquid/auth-client/encoding"; +import { + useDataChannel, + useDataChannelMessages, +} from '../hooks/useDataChannel.ts'; +import { PeerConnectionContext } from '../hooks/usePeerConnection.ts'; +import { useContext, useEffect, useState } from 'react'; +import Button from '@mui/material/Button'; +import algosdk from 'algosdk'; +import { toBase64URL, fromBase64Url } from '@liquid/core/encoding'; -const algodClient = new algosdk.Algodv2('','https://testnet-api.algonode.cloud',443); +const algodClient = new algosdk.Algodv2( + '', + 'https://testnet-api.algonode.cloud', + 443, +); -export default function ConnectedPage(){ +export default function ConnectedPage() { const walletStr = window.localStorage.getItem('wallet'); const wallet = walletStr ? JSON.parse(walletStr) : null; - const [txn, setTxn] = useState(null) - const {peerConnection} = useContext(PeerConnectionContext) - const datachannel = useDataChannel("remote", peerConnection) + const [txn, setTxn] = useState(null); + const { peerConnection } = useContext(PeerConnectionContext); + const datachannel = useDataChannel('remote', peerConnection); // Receive response useDataChannelMessages((event) => { - if(!txn) return - async function handleMessage(){ - if(!txn) return - console.log(event) - const sig = fromBase64Url(event.data) - const signedTxn = txn.attachSignature(wallet, sig) + if (!txn) return; + async function handleMessage() { + if (!txn) return; + console.log(event); + const sig = fromBase64Url(event.data); + const signedTxn = txn.attachSignature(wallet, sig); const { txId } = await algodClient.sendRawTransaction(signedTxn).do(); const result = await algosdk.waitForConfirmation(algodClient, txId, 4); - console.log(result) + console.log(result); } - handleMessage() - }) + handleMessage(); + }); // Send Transaction - useEffect(()=>{ - if(!txn || !datachannel) return - datachannel?.send(toBase64URL(txn.bytesToSign())) - },[txn, datachannel]) + useEffect(() => { + if (!txn || !datachannel) return; + datachannel?.send(toBase64URL(txn.bytesToSign())); + }, [txn, datachannel]); return (
Connected - + const suggestedParams = await algodClient.getTransactionParams().do(); + setTxn( + algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: wallet, + suggestedParams, + to: wallet, + amount: 0, + }), + ); + }} + > + Send Transaction +
- ) + ); } diff --git a/sites/dapp-ui/src/pages/dashboard/Registered.tsx b/sites/dapp-ui/src/pages/dashboard/Registered.tsx index 68ffab5..a82d63b 100644 --- a/sites/dapp-ui/src/pages/dashboard/Registered.tsx +++ b/sites/dapp-ui/src/pages/dashboard/Registered.tsx @@ -1,67 +1,77 @@ -import CardMedia from "@mui/material/CardMedia"; -import CardContent from "@mui/material/CardContent"; -import Typography from "@mui/material/Typography"; -import CardActions from "@mui/material/CardActions"; -import Button from "@mui/material/Button"; -import Card from "@mui/material/Card"; +import CardMedia from '@mui/material/CardMedia'; +import CardContent from '@mui/material/CardContent'; +import Typography from '@mui/material/Typography'; +import CardActions from '@mui/material/CardActions'; +import Button from '@mui/material/Button'; +import Card from '@mui/material/Card'; import { assertion } from '@liquid/auth-client/assertion'; -import { Table, TableBody, TableCell, TableHead, TableRow } from '@mui/material'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableRow, +} from '@mui/material'; import { useCredentialStore } from '../../store'; - -export function RegisteredCard(){ - const credentials = useCredentialStore((state)=> state.addresses); +export function RegisteredCard() { + const credentials = useCredentialStore((state) => state.addresses); const handleTestCredentialClick = () => { const credId = window.localStorage.getItem('credId'); - if(!credId) return + if (!credId) return; assertion(credId); - } - return ( - - - - - Registered (3 of 3) - - - Mobile Device is Connected and Registered - - - - - Address - Actions - - - - {Object.keys(credentials).map((address) => ( - - - {address.slice(0, 5)}...{address.slice(-5)} - - - - ))} - -
-
- - - -
- ) + }; + return ( + + + + + Registered (3 of 3) + + + Mobile Device is Connected and Registered + + + + + Address + Actions + + + + {Object.keys(credentials).map((address) => ( + + + {address.slice(0, 5)}...{address.slice(-5)} + + + + + + ))} + +
+
+ + + +
+ ); } diff --git a/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx b/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx index 949a292..e0f7817 100644 --- a/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx +++ b/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx @@ -1,62 +1,83 @@ -import CardMedia from "@mui/material/CardMedia"; -import CardContent from "@mui/material/CardContent"; -import Typography from "@mui/material/Typography"; -import Card from "@mui/material/Card"; +import CardMedia from '@mui/material/CardMedia'; +import CardContent from '@mui/material/CardContent'; +import Typography from '@mui/material/Typography'; +import Card from '@mui/material/Card'; import { CircularProgress } from '@mui/material'; import { useContext, useEffect } from 'react'; -import {useSocket} from '../../hooks/useSocket'; +import { useSocket } from '../../hooks/useSocket'; import { StateContext } from '../../Contexts'; import { useCredentialStore } from '../../store'; import { useAddressQuery } from '../../hooks/useAddress'; -export function WaitForRegistrationCard(){ - const credentials = useCredentialStore((state)=> state.addresses); - const save = useCredentialStore((state)=> state.update); - const {state: step, setState} = useContext(StateContext) +export function WaitForRegistrationCard() { + const credentials = useCredentialStore((state) => state.addresses); + const save = useCredentialStore((state) => state.update); + const { state: step, setState } = useContext(StateContext); const walletStr = window.localStorage.getItem('wallet'); const wallet = walletStr ? JSON.parse(walletStr) : null; - const {socket} = useSocket(); + const { socket } = useSocket(); const address = useAddressQuery(wallet); useEffect(() => { - if(step !== 'connected'){ - return + if (step !== 'connected') { + return; } - socket.emit('wait', { wallet }, async ({data: {credId, device}}: {data: {credId: string, device: string}}) => { - save({name: wallet, credentials: [...credentials[wallet].credentials, {id: credId, device}]}); - window.localStorage.setItem('credId', credId); - setState('registered') - }); - }) - useEffect(()=>{ + socket.emit( + 'wait', + { wallet }, + async ({ + data: { credId, device }, + }: { + data: { credId: string; device: string }; + }) => { + save({ + name: wallet, + credentials: [ + ...credentials[wallet].credentials, + { id: credId, device }, + ], + }); + window.localStorage.setItem('credId', credId); + setState('registered'); + }, + ); + }); + useEffect(() => { socket.on('call-candidate', (data: any) => { - console.log(data) - }) - }, [socket]) - return ( - - - - - Connected (2 of 3) - - - Waiting for Passkey registration for address: - - {address.isLoading && } - {address.isFetched && {address.data}} - - - ) + console.log(data); + }); + }, [socket]); + return ( + + + + + Connected (2 of 3) + + + Waiting for Passkey registration for + address: + + {address.isLoading && } + {address.isFetched && ( + + {' '} + {address.data} + + )} + + + ); } diff --git a/sites/dapp-ui/src/pages/debug/ManualWebRTC.tsx b/sites/dapp-ui/src/pages/debug/ManualWebRTC.tsx deleted file mode 100644 index 619baa8..0000000 --- a/sites/dapp-ui/src/pages/debug/ManualWebRTC.tsx +++ /dev/null @@ -1,253 +0,0 @@ -import {useEffect, useState} from "react"; -import algosdk from 'algosdk'; -import {fetchConnectResponse} from "@liquid/auth-client"; -import {Message} from "@liquid/auth-client/connect"; -import {useMessageStore, usePeerStore} from "../../store.ts"; -import {ChatList} from "../../components/Chat.tsx"; -import {useDataChannel, useDataChannelMessages} from "../../hooks/useDataChannel.ts"; -import {usePeerConnection, usePeerConnectionState} from "../../hooks/usePeerConnection.ts"; -import Button from "@mui/material/Button"; -import {useUserState} from "../../components/user/useUserState.ts"; -import CircularProgress from "@mui/material/CircularProgress"; -import Typography from "@mui/material/Typography"; -import Card from "@mui/material/Card/Card"; -import CardContent from "@mui/material/CardContent"; -import Box from "@mui/material/Box"; -import {useSocket} from "../../hooks/useSocket.ts"; - -const acct = algosdk.mnemonicToSecretKey('lab surge abandon artist moon keen license bronze rebuild wing surge apart basket teach deposit patch snow paper sting rural negative logic cousin above gym') - -type SessionDescriptionProps = { - type: string, - description: RTCSessionDescriptionInit -} - -/** - * Session Description Component - * - * Display the state of a RTCSessionDescriptionInit - * - * @param type - * @param description - * @constructor - */ -function SessionDescription({type, description}: SessionDescriptionProps) { - return ( -
-

{type}: Session Description

-
{description.sdp}
-
- ) -} - -type HandshakeProps = { - local: RTCSessionDescriptionInit | null, - remote: RTCSessionDescriptionInit | null, - candidates: RTCIceCandidateInit[] -} - -/** - * Debugging View for WebRTC Handshake - * - * Handles the SDP negotiation between two peers manually - * - * @param local - * @param remote - * @param candidates - * @constructor - */ -function Handshake({local, remote, candidates}: HandshakeProps) { - return ( - - {local && } - {remote && } - -

Local Candidates

- {candidates?.map((candidate, i) => { - return ( -
{JSON.stringify(candidate, null, 2)}
- ) - } - )} -
-
- ) -} - -/** - * Remote Peer - * - * connect to an existing session - * - * @param peerConnection - * @constructor - */ -function RemoteView({peerConnection}: { peerConnection: RTCPeerConnection }) { - const {socket} = useSocket() - const candidates = usePeerStore((state) => state.candidates) - // Session Descriptions - const [sdp, setSDP] = useState() - const [local, setLocal] = useState(null) - const [remote, setRemote] = useState(null) - - // Handle Messages - const addMessage = useMessageStore((state) => state.addMessage) - useDataChannelMessages((event) => addMessage(JSON.parse(event.data))) - - // Handle the remote SDP - useEffect(() => { - if (!remote) return - peerConnection.createAnswer() - .then(async (answer) => { - await peerConnection.setLocalDescription(answer) - setLocal(answer) - }) - }, [peerConnection, remote]) - - return ( -
-

Remote View

-

Offer from Peer:

-
- -
- - -
- ) -} - - -/** - * Local Peer - * - * create a new session - * - * @param peerConnection - * @constructor - */ -function LocalView({peerConnection}: { peerConnection: RTCPeerConnection }) { - const {socket} = useSocket() - const candidates = usePeerStore((state) => state.candidates) - // Session Description - const [sdp, setSDP] = useState() - const [local, setLocal] = useState(null) - const [remote, setRemote] = useState(null) - - // Handle SDP - async function handleCallButton() { - if (peerConnection.signalingState === 'stable') { - const offer = await peerConnection.createOffer(); - await peerConnection.setLocalDescription(offer); - setLocal(offer) - } - } - - return ( -
-

Local View

- -

Remote Answer:

-
- -
- - -
- ) -} - - -/** - * What does this thing need to do? - * - * Needs a relay for candidates and offers. - * - * After a connection is established, it should pass the ice-candidate-offer to the server. - * - * @constructor - */ -export function DebugWebRTC() { - const state = useUserState() - - // Remote or Local Session - const [type, setType] = useState<'local' | 'remote'>('local') - - // Store Hooks - const addMessage = useMessageStore((state) => state.addMessage) - const clearMessages = useMessageStore((state) => state.clearMessages) - const addCandidate = usePeerStore((state) => state.addCandidate) - const clearCandidates = usePeerStore((state) => state.clearCandidates) - - // Create a Peer Connection - const peerConnection = usePeerConnection((event) => { - if (event.candidate) { - addCandidate(event.candidate.toJSON()) - } - }); - const connectionState = usePeerConnectionState(); - // Create a Data Channel - const dataChannel = useDataChannel(type, peerConnection); - // Handle Messages - useDataChannelMessages((event) => addMessage(JSON.parse(event.data))) - if (state.isLoading) { - return ( - - ) - } else if (!state.data.user) { - return ( - - ) - } - - if (dataChannel && connectionState === 'connected') { - return ( - - ) - } - - return ( - - - WebRTC Debug - - Connecting is a two step handshake, the first step is to create a local offer. The - local offer is sent to a remote peer. - The remote peer - adds the local offer to their remote session description. - The remote peer then creates an answer and sends it back to the - local peer. - - - - - {type === 'local' && peerConnection && } - {type === 'remote' && peerConnection && } - - - - - ) -} diff --git a/sites/dapp-ui/src/pages/debug/WebRTC.tsx b/sites/dapp-ui/src/pages/debug/WebRTC.tsx deleted file mode 100644 index 31ee097..0000000 --- a/sites/dapp-ui/src/pages/debug/WebRTC.tsx +++ /dev/null @@ -1,206 +0,0 @@ -import {useEffect, useState} from "react"; -import algosdk from 'algosdk'; -import {fetchConnectResponse} from "@liquid/auth-client"; -import {Message} from "@liquid/auth-client/connect"; -import {useMessageStore, usePeerStore} from "../../store.ts"; -import {ChatList} from "../../components/Chat.tsx"; -import {useDataChannel, useDataChannelMessages} from "../../hooks/useDataChannel.ts"; -import {usePeerConnection, usePeerConnectionState} from "../../hooks/usePeerConnection.ts"; -import Button from "@mui/material/Button"; -import {useUserState} from "../../components/user/useUserState.ts"; -import CircularProgress from "@mui/material/CircularProgress"; -import Typography from "@mui/material/Typography"; -import Card from "@mui/material/Card/Card"; -import CardContent from "@mui/material/CardContent"; -import {useSocket} from "../../hooks/useSocket.ts"; - -const acct = algosdk.mnemonicToSecretKey('lab surge abandon artist moon keen license bronze rebuild wing surge apart basket teach deposit patch snow paper sting rural negative logic cousin above gym') - -/** - * Remote Peer - * - * connect to an existing session - * - * @param peerConnection - * @constructor - */ -function RemoteView({peerConnection}: { peerConnection: RTCPeerConnection }) { - const {socket} = useSocket() - const candidates = usePeerStore((state) => state.candidates) - const [local, setLocal] = useState(null) - const [remote, setRemote] = useState(null) - // Handle Messages - const addMessage = useMessageStore((state) => state.addMessage) - useDataChannelMessages((event) => addMessage(JSON.parse(event.data))) - - // Handle the remote SDP - useEffect(() => { - if (!remote) return - peerConnection.createAnswer() - .then(async (answer) => { - await peerConnection.setLocalDescription(answer) - setLocal(answer) - }) - }, [peerConnection, remote]) - - useEffect(()=>{ - socket.emit('wait-for-offer', ({data})=>{ - peerConnection.setRemoteDescription({type: 'offer', sdp: data.sdp}) - .then(() => setRemote({type: 'offer', sdp: data.sdp})) - }) - }, [socket]) - useEffect(()=>{ - if(candidates.length >= 1 && local){ - socket.emit('ice-candidate-answer', peerConnection.localDescription?.sdp || local.sdp) - } - },[socket, candidates, local, peerConnection]) - - useEffect(()=>{ - if(!socket) return - console.log('Running Ice Candidate effect') - function onIceCandidate(e){ - console.log('!!!!!!!!!!!!!!!!1') - console.log(e) - } - socket.on('lfgz', function(){ - console.log('!!!!!!!!!!!!!!!!1') - - }) - // return ()=>{ - // socket.off('lfgz', onIceCandidate) - // } - }, [socket]) - return ( -
-

Remote View

-

Waiting for Caller

-
- ) -} - - -/** - * Local Peer - * - * create a new session - * - * @param peerConnection - * @constructor - */ -function LocalView({peerConnection}: { peerConnection: RTCPeerConnection }) { - const {socket} = useSocket() - const candidates = usePeerStore((state) => state.candidates) - // Session Description - const [local, setLocal] = useState(null) - const [remote, setRemote] = useState(null) - - // Emit the SDP Offer - useEffect(()=>{ - if(candidates.length >= 1 && local && !remote){ - socket.emit('ice-candidate-offer', peerConnection.localDescription?.sdp || local.sdp, ({data})=>{ - // Server responds with the peer's answer - peerConnection.setRemoteDescription({type: 'answer', sdp: data.sdp}) - .then(() => setRemote({type: 'answer', sdp: data.sdp})) - }) - } - },[remote, socket, candidates, local, peerConnection]) - - useEffect(() => { - socket.on('lfg', (data)=>{ - console.log('lfg') - console.log(data) - }) - }, [socket]); - - // Create a call Offer - async function handleCallButton() { - socket.emit('lfg') - // if (peerConnection.signalingState === 'stable') { - // const offer = await peerConnection.createOffer(); - // await peerConnection.setLocalDescription(offer); - // setLocal(offer) - // } - } - - return ( -
-

Local View

- -
- ) -} - -export function DebugWebRTC() { - const state = useUserState() - - // Remote or Local Session - const [type, setType] = useState<'local' | 'remote'>('local') - - // Store Hooks - const addMessage = useMessageStore((state) => state.addMessage) - const clearMessages = useMessageStore((state) => state.clearMessages) - const addCandidate = usePeerStore((state) => state.addCandidate) - const clearCandidates = usePeerStore((state) => state.clearCandidates) - - // Create a Peer Connection - const peerConnection = usePeerConnection((event) => { - if (event.candidate) { - addCandidate(event.candidate.toJSON()) - } - }); - const connectionState = usePeerConnectionState(); - // Create a Data Channel - const dataChannel = useDataChannel(type, peerConnection); - // Handle Messages - useDataChannelMessages((event) => addMessage(JSON.parse(event.data))) - if (state.isLoading) { - return ( - - ) - } else if (!state.data.user) { - return ( - - ) - } - - if (dataChannel && connectionState === 'connected') { - return ( - - ) - } - - return ( - - - WebRTC Debug - - Connecting is a two step handshake, the first step is to create a local offer. The - local offer is sent to a remote peer. - The remote peer - adds the local offer to their remote session description. - The remote peer then creates an answer and sends it back to the - local peer. - - - - - {type === 'local' && peerConnection && } - {type === 'remote' && peerConnection && } - - - - - ) -} diff --git a/sites/dapp-ui/src/pages/home/ConnectModal.tsx b/sites/dapp-ui/src/pages/home/ConnectModal.tsx index 97fe329..8192f5c 100644 --- a/sites/dapp-ui/src/pages/home/ConnectModal.tsx +++ b/sites/dapp-ui/src/pages/home/ConnectModal.tsx @@ -2,165 +2,198 @@ import * as React from 'react'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Modal from '@mui/material/Modal'; -import {toBase64URL} from "@liquid/auth-client/encoding"; +import { toBase64URL } from '@liquid/core/encoding'; import { Message } from '@liquid/auth-client/connect'; -import QRCodeStyling, {Options} from "qr-code-styling"; -import { useContext, useEffect, useState } from 'react'; -import {Fade} from "@mui/material"; -import {useSocket} from '../../hooks/useSocket'; +import QRCodeStyling, { Options } from 'qr-code-styling'; +import { useEffect, useState } from 'react'; +import { Fade } from '@mui/material'; +import { useSocket } from '../../hooks/useSocket'; import nacl from 'tweetnacl'; -import { StateContext } from '../../Contexts'; import { useCredentialStore, Credential } from '../../store'; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from 'react-router-dom'; const style = { - position: 'absolute' as const, - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - bgcolor: 'background.paper', - background: "linear-gradient(to right, rgba(248,80,50,1) 0%, rgba(241,111,92,1) 50%, rgba(246,41,12,1) 51%, rgba(240,47,23,1) 71%, rgba(231,56,39,1) 100%)", - border: '2px solid #000', - boxShadow: 24, + position: 'absolute' as const, + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + bgcolor: 'background.paper', + background: + 'linear-gradient(to right, rgba(248,80,50,1) 0%, rgba(241,111,92,1) 50%, rgba(246,41,12,1) 51%, rgba(240,47,23,1) 71%, rgba(231,56,39,1) 100%)', + border: '2px solid #000', + boxShadow: 24, }; -export function ConnectModal({color}: {color?: 'inherit' | 'primary' | 'secondary' | 'success' | 'error' | 'info' | 'warning'}) { - const navigate = useNavigate() - const {socket} = useSocket(); - const credentials = useCredentialStore((state)=> state.addresses); - const save = useCredentialStore((state)=> state.update); - const {state: step, setState} = useContext(StateContext) - const [state] = useState({ - requestId: Math.random(), - challenge: toBase64URL(nacl.randomBytes(nacl.sign.seedLength)) - }) - const qrOpts = { - "width": 500, - "height": 500, - "data": "algorand://", - "margin": 25, - "imageOptions": {"hideBackgroundDots": true, "imageSize": 0.4, "margin": 15}, - "dotsOptions": { - "type": "extra-rounded", - "gradient": { - "type": "radial", - "rotation": 0, - "colorStops": [{"offset": 0, "color": "#9966ff"}, {"offset": 1, "color": "#332257"}] - } - }, - "backgroundOptions": {"color": "#ffffff", "gradient": null}, - "image": "/logo.png", - "cornersSquareOptions": { - "type": "", - "color": "#000000", - "gradient": { - "type": "linear", - "rotation": 0, - "colorStops": [{"offset": 0, "color": "#332257"}, {"offset": 1, "color": "#040908"}] - } - }, - "cornersDotOptions": { - "type": "dot", - "color": "#000000", - "gradient": { - "type": "linear", - "rotation": 0, - "colorStops": [{"offset": 0, "color": "#000000"}, {"offset": 1, "color": "#000000"}] - } - } +export function ConnectModal({ + color, +}: { + color?: + | 'inherit' + | 'primary' + | 'secondary' + | 'success' + | 'error' + | 'info' + | 'warning'; +}) { + const navigate = useNavigate(); + const { socket } = useSocket(); + const credentials = useCredentialStore((state) => state.addresses); + const save = useCredentialStore((state) => state.update); + const [state] = useState({ + requestId: Math.random(), + challenge: toBase64URL(nacl.randomBytes(nacl.sign.seedLength)), + }); + const qrOpts = { + width: 500, + height: 500, + data: 'algorand://', + margin: 25, + imageOptions: { hideBackgroundDots: true, imageSize: 0.4, margin: 15 }, + dotsOptions: { + type: 'extra-rounded', + gradient: { + type: 'radial', + rotation: 0, + colorStops: [ + { offset: 0, color: '#9966ff' }, + { offset: 1, color: '#332257' }, + ], + }, + }, + backgroundOptions: { color: '#ffffff', gradient: null }, + image: '/logo.png', + cornersSquareOptions: { + type: '', + color: '#000000', + gradient: { + type: 'linear', + rotation: 0, + colorStops: [ + { offset: 0, color: '#332257' }, + { offset: 1, color: '#040908' }, + ], + }, + }, + cornersDotOptions: { + type: 'dot', + color: '#000000', + gradient: { + type: 'linear', + rotation: 0, + colorStops: [ + { offset: 0, color: '#000000' }, + { offset: 1, color: '#000000' }, + ], + }, + }, + }; + useEffect(() => { + socket.on('link', (data) => { + console.log(data); + }); + }, [socket]); + const [open, setOpen] = React.useState(false); + useEffect(() => { + if (!open) { + return; } - useEffect(()=>{ - socket.on('link', (data)=>{ - console.log(data) - }) - },[socket]) - const [open, setOpen] = React.useState(false); - useEffect(() => { - if(!open){ - return + socket.emit( + 'link', + { requestId: state.requestId }, + async ({ + data, + }: { + data: { credId?: string; requestId: string | number; wallet: string }; + }) => { + let newCredentials: Credential[] = []; + if (typeof credentials[data.wallet] !== 'undefined') { + newCredentials = credentials[data.wallet].credentials; } - socket.emit('link', { requestId: state.requestId }, async ({data}: {data: {credId?: string, requestId: string|number, wallet: string}}) => { - let newCredentials: Credential[] = [] - if(typeof credentials[data.wallet] !== 'undefined'){ - newCredentials = credentials[data.wallet].credentials - } - save({name: data.wallet, credentials: [...newCredentials]}) - window.localStorage.setItem('wallet', JSON.stringify(data.wallet)); - if(typeof data.credId !== 'undefined'){ - console.log(data.credId) - window.localStorage.setItem('credId', data.credId); - setState('registered') - navigate('/peering') - } else { - setState('connected') - navigate('/peering') - } - - }); - }, [open]) - - - const [barcode, setBarcode] = React.useState("/qr-loading.png") - const handleOpen = () => { - setBarcode("/qr-loading.png") - const message = new Message(window.location.origin, state.challenge, state.requestId) - - // JSON encoding - qrOpts.data = `${message}` - console.log(qrOpts.data) - const qrCode = new QRCodeStyling(qrOpts as unknown as Options) - qrCode.getRawData("png").then((blob) => { - if(!blob) throw TypeError('Could not get qrcode blob') - setBarcode(URL.createObjectURL(blob)) - setOpen(true) - }) - // message.toBarcode({color: {light: "#00000000"}}).then(setBarcode) - setOpen(true) - }; - const handleClose = () => setOpen(false); + save({ name: data.wallet, credentials: [...newCredentials] }); + window.localStorage.setItem('wallet', JSON.stringify(data.wallet)); + if (typeof data.credId !== 'undefined') { + console.log(data.credId); + window.localStorage.setItem('credId', data.credId); + navigate('/peering'); + } else { + navigate('/peering'); + } + }, + ); + }, [open]); - return ( -
- - - - - - - + const [barcode, setBarcode] = React.useState('/qr-loading.png'); + const handleOpen = () => { + setBarcode('/qr-loading.png'); + const message = new Message( + window.location.origin, + state.challenge, + state.requestId, + ); - + // JSON encoding + qrOpts.data = `${message}`; + console.log(qrOpts.data); + const qrCode = new QRCodeStyling(qrOpts as unknown as Options); + qrCode.getRawData('png').then((blob) => { + if (!blob) throw TypeError('Could not get qrcode blob'); + setBarcode(URL.createObjectURL(blob)); + setOpen(true); + }); + // message.toBarcode({color: {light: "#00000000"}}).then(setBarcode) + setOpen(true); + }; + const handleClose = () => setOpen(false); - - - - -
- ); + return ( +
+ + + + + + + + + + + + +
+ ); } diff --git a/sites/dapp-ui/src/pages/home/GetStarted.tsx b/sites/dapp-ui/src/pages/home/GetStarted.tsx index b1c82ec..2018215 100644 --- a/sites/dapp-ui/src/pages/home/GetStarted.tsx +++ b/sites/dapp-ui/src/pages/home/GetStarted.tsx @@ -1,41 +1,42 @@ -import CardMedia from "@mui/material/CardMedia"; -import CardContent from "@mui/material/CardContent"; -import Typography from "@mui/material/Typography"; -import CardActions from "@mui/material/CardActions"; +import CardMedia from '@mui/material/CardMedia'; +import CardContent from '@mui/material/CardContent'; +import Typography from '@mui/material/Typography'; +import CardActions from '@mui/material/CardActions'; import Card from '@mui/material/Card'; -import {ConnectModal} from "./ConnectModal"; -import Button from "@mui/material/Button"; -export function GetStartedCard(){ - return ( - - - - - Get Started (1 of 3) - - - Start by connecting a valid wallet, this is the first step in a three step process. The connecting - wallet receives the current website URL from the QR Code and submits a verification request to the service. - - - - - - - - ) +import { ConnectModal } from './ConnectModal'; +import Button from '@mui/material/Button'; +export function GetStartedCard() { + return ( + + + + + Get Started (1 of 3) + + + Start by connecting a valid wallet, this is the first step in a three + step process. The connecting wallet receives the current website URL + from the QR Code and submits a verification request to the service. + + + + + + + + ); } diff --git a/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx b/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx index d90f0db..a829285 100644 --- a/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx +++ b/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx @@ -1,94 +1,104 @@ -import CardMedia from "@mui/material/CardMedia"; -import CardContent from "@mui/material/CardContent"; -import Typography from "@mui/material/Typography"; -import Card from "@mui/material/Card"; +import CardMedia from '@mui/material/CardMedia'; +import CardContent from '@mui/material/CardContent'; +import Typography from '@mui/material/Typography'; +import Card from '@mui/material/Card'; import { CircularProgress } from '@mui/material'; import { useEffect } from 'react'; -import {useSocket} from '../../hooks/useSocket'; -import { useNavigate } from "react-router-dom"; -import { usePeerConnection } from "../../hooks/usePeerConnection.ts"; -import { useDataChannel, useDataChannelMessages } from "../../hooks/useDataChannel.ts"; +import { useSocket } from '../../hooks/useSocket'; +import { useNavigate } from 'react-router-dom'; +import { usePeerConnection } from '../../hooks/usePeerConnection.ts'; +import { + useDataChannel, + useDataChannelMessages, +} from '../../hooks/useDataChannel.ts'; -export function WaitForPeersCard(){ - const navigate = useNavigate() +export function WaitForPeersCard() { + const navigate = useNavigate(); const walletStr = window.localStorage.getItem('wallet'); const wallet = walletStr ? JSON.parse(walletStr) : null; - const {socket} = useSocket(); + const { socket } = useSocket(); // const address = useAddressQuery(wallet); const peerConnection = usePeerConnection((event) => { if (event.candidate) { - console.log('Local Candidate', event.candidate.toJSON()) - const data = event.candidate.toJSON() - // data.type = 'offerCandidate' - socket.emit('answer-candidate', data) + console.log('Local Candidate', event.candidate.toJSON()); + const data = event.candidate.toJSON(); + // data.type = 'offerCandidate' + socket.emit('answer-candidate', data); } else { - console.log(event) + console.log(event); } }); - const datachannel = useDataChannel("remote", peerConnection) + const datachannel = useDataChannel('remote', peerConnection); useDataChannelMessages((event) => { - console.log(event) - }) - useEffect(()=>{ - if(!datachannel) return - + console.log(event); + }); + useEffect(() => { + if (!datachannel) return; // datachannel.send('Hello World') - navigate('/connected') - }, [datachannel]) + navigate('/connected'); + }, [datachannel]); - useEffect(()=>{ - if(!peerConnection) return - async function handleDescription(sdp: string){ - console.log('OFFER', sdp) - await peerConnection?.setRemoteDescription({type: 'offer', sdp} as RTCSessionDescriptionInit) - const answer = await peerConnection?.createAnswer() - await peerConnection?.setLocalDescription(answer) - console.log('ANSWER', answer?.sdp) - socket.emit('answer-description', answer?.sdp) - } - socket.on('call-description', handleDescription) - return ()=>{ - socket.off('call-description', handleDescription) - } - },[socket]) - useEffect(()=>{ - if(!peerConnection) return - async function handleCallCandidate(data: RTCIceCandidate){ - console.log('Remote Candidate', data) - // data.type = 'answerCandidate' - console.log('Remote Candidate ICE', new RTCIceCandidate(data)) - await peerConnection!!.addIceCandidate( new RTCIceCandidate(data)) + useEffect(() => { + if (!peerConnection) return; + async function handleDescription(sdp: string) { + console.log('OFFER', sdp); + await peerConnection?.setRemoteDescription({ + type: 'offer', + sdp, + } as RTCSessionDescriptionInit); + const answer = await peerConnection?.createAnswer(); + await peerConnection?.setLocalDescription(answer); + console.log('ANSWER', answer?.sdp); + socket.emit('answer-description', answer?.sdp); } - socket.on('call-candidate', handleCallCandidate) - return ()=>{ - socket.off('call-candidate', handleCallCandidate) + socket.on('call-description', handleDescription); + return () => { + socket.off('call-description', handleDescription); + }; + }, [socket]); + useEffect(() => { + if (!peerConnection) return; + async function handleCallCandidate(data: RTCIceCandidate) { + console.log('Remote Candidate', data); + // data.type = 'answerCandidate' + console.log('Remote Candidate ICE', new RTCIceCandidate(data)); + await peerConnection!.addIceCandidate(new RTCIceCandidate(data)); } - }, [socket]) - return ( - - - - - Waiting for Peer Connection (2 of 3) - - - Waiting for Passkey registration for address: - - {wallet} - - - ) + socket.on('call-candidate', handleCallCandidate); + return () => { + socket.off('call-candidate', handleCallCandidate); + }; + }, [socket]); + return ( + + + + + Waiting for Peer Connection (2 of 3) + + + Waiting for Passkey registration for + address: + + + {' '} + {wallet} + + + + ); } diff --git a/sites/dapp-ui/src/store.ts b/sites/dapp-ui/src/store.ts index 0a4d78c..68b1ef6 100644 --- a/sites/dapp-ui/src/store.ts +++ b/sites/dapp-ui/src/store.ts @@ -2,69 +2,77 @@ import { create, StateCreator } from 'zustand'; import { createJSONStorage, persist, PersistOptions } from 'zustand/middleware'; export type Credential = { - id: string, - device: string, -} + id: string; + device: string; +}; export type Address = { - name: string, - credentials: Credential[] -} + name: string; + credentials: Credential[]; +}; export type Addresses = { - [k: string]: Address -} + [k: string]: Address; +}; export type CredentialStore = { - addresses: Addresses, - update: (address: Address) => void, - remove: (address: Address) => void, -} + addresses: Addresses; + update: (address: Address) => void; + remove: (address: Address) => void; +}; type PersistCredentials = ( config: StateCreator, - options: PersistOptions -) => StateCreator + options: PersistOptions, +) => StateCreator; export const useCredentialStore = create( (persist as PersistCredentials)( (set, get) => ({ addresses: {}, - update: (address) => set(() => ({ addresses: { ...get().addresses, [address.name]: address } })), - remove: (address) => set(() => { - const addresses = { ...get().addresses }; - delete addresses[address.name]; - return { addresses }; - }), + update: (address) => + set(() => ({ + addresses: { ...get().addresses, [address.name]: address }, + })), + remove: (address) => + set(() => { + const addresses = { ...get().addresses }; + delete addresses[address.name]; + return { addresses }; + }), }), - { name: 'avicennia-credential-store', storage: createJSONStorage(() => localStorage) } + { + name: 'avicennia-credential-store', + storage: createJSONStorage(() => localStorage), + }, ), ); interface PeerStore { - candidates: RTCIceCandidateInit[]; - addCandidate: (candidate: RTCIceCandidateInit) => void; - clearCandidates: () => void; + candidates: RTCIceCandidateInit[]; + addCandidate: (candidate: RTCIceCandidateInit) => void; + clearCandidates: () => void; } export const usePeerStore = create((set) => ({ - candidates: [], - addCandidate: (candidate: RTCIceCandidateInit) => set((state)=>({candidates: [...state.candidates, candidate]})), - clearCandidates: () => set({candidates: []}), + candidates: [], + addCandidate: (candidate: RTCIceCandidateInit) => + set((state) => ({ candidates: [...state.candidates, candidate] })), + clearCandidates: () => set({ candidates: [] }), })); export type Message = { - text: string; - type: 'local' | 'remote'; - timestamp: number; -} + text: string; + type: 'local' | 'remote'; + timestamp: number; +}; interface MessageStore { - messages: Message[]; - addMessage: (message: Message) => void; - clearMessages: () => void; - + messages: Message[]; + addMessage: (message: Message) => void; + clearMessages: () => void; } export const useMessageStore = create((set) => ({ - messages: [], - addMessage: (message: Message) => set((state)=>({messages: [...state.messages, message]})), -clearMessages: () => set({messages: []}), -})) + messages: [], + addMessage: (message: Message) => + set((state) => ({ messages: [...state.messages, message] })), + clearMessages: () => set({ messages: [] }), +})); diff --git a/sites/dapp-ui/src/theme.tsx b/sites/dapp-ui/src/theme.tsx index 351b42a..654557d 100644 --- a/sites/dapp-ui/src/theme.tsx +++ b/sites/dapp-ui/src/theme.tsx @@ -1,10 +1,10 @@ export const DEFAULT_THEME = { - palette: { - primary: { - main: '#000000', - }, - secondary: { - main: '#9966ff', - }, + palette: { + primary: { + main: '#000000', }, -} + secondary: { + main: '#9966ff', + }, + }, +}; From d1a61e02533f38c7bbfae48bf9057c5345c1192d Mon Sep 17 00:00:00 2001 From: Michael J Feher Date: Thu, 11 Apr 2024 15:31:43 -0400 Subject: [PATCH 09/21] chore: cleanup docker files --- .docker/templates/default.conf.template | 12 ------------ docker-compose.yml | 15 --------------- 2 files changed, 27 deletions(-) delete mode 100644 .docker/templates/default.conf.template diff --git a/.docker/templates/default.conf.template b/.docker/templates/default.conf.template deleted file mode 100644 index f7ebc36..0000000 --- a/.docker/templates/default.conf.template +++ /dev/null @@ -1,12 +0,0 @@ -proxy_cache_path /data/nginx/cache keys_zone=mycache:10m; -server { - listen 80; - listen [::]:80; - server_name localhost; - gzip on; - proxy_cache mycache; - gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css; - location / { - proxy_pass http://host.docker.internal:3000; - } -} diff --git a/docker-compose.yml b/docker-compose.yml index b76b7c1..c70f651 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,26 +1,11 @@ version: '3.4' services: - nginx: - image: nginx - restart: always - ports: - - "8080:80" - volumes: - - nginx:/data/nginx/cache - - ./.docker/templates:/etc/nginx/templates/ redis: image: redis restart: always ports: - "6379:6379" - redis-commander: - image: rediscommander/redis-commander:latest - restart: always - environment: - - REDIS_HOSTS=local:redis:6379 - ports: - - "8081:8081" mongo: image: mongo:7.0 restart: always From 92039108cd24956270071fe72f50eb91fd27c2ef Mon Sep 17 00:00:00 2001 From: Michael J Feher Date: Fri, 12 Apr 2024 08:18:35 -0400 Subject: [PATCH 10/21] chore: cleanup demo application --- index.html | 31 --- .../src/connect/connect.gateway.ts | 40 +-- sites/dapp-ui/src/App.tsx | 78 +++--- sites/dapp-ui/src/components/Chat.tsx | 89 ------- .../home => components}/ConnectModal.tsx | 4 +- .../src/components/user/Credential.tsx | 7 - .../src/components/user/SessionMenu.tsx | 4 +- sites/dapp-ui/src/hooks/index.ts | 7 + sites/dapp-ui/src/hooks/useAccountInfo.ts | 18 ++ sites/dapp-ui/src/hooks/useAlgod.ts | 16 ++ sites/dapp-ui/src/hooks/usePeerConnection.ts | 14 +- .../user => hooks}/useUserState.ts | 0 sites/dapp-ui/src/pages/connected.tsx | 152 ++++++++++-- .../src/pages/dashboard/Registered.tsx | 77 ------ .../pages/dashboard/WaitForRegistration.tsx | 83 ------- .../pages/{home/GetStarted.tsx => home.tsx} | 21 +- sites/dapp-ui/src/pages/index.ts | 3 + .../{peering/WaitForPeers.tsx => peering.tsx} | 38 +-- sites/dapp-ui/src/store.ts | 2 +- sites/dapp-ui/tsconfig.json | 6 +- sites/dapp-ui/vite.config.ts | 232 ++++++++++-------- 21 files changed, 419 insertions(+), 503 deletions(-) delete mode 100644 index.html delete mode 100644 sites/dapp-ui/src/components/Chat.tsx rename sites/dapp-ui/src/{pages/home => components}/ConnectModal.tsx (97%) delete mode 100644 sites/dapp-ui/src/components/user/Credential.tsx create mode 100644 sites/dapp-ui/src/hooks/index.ts create mode 100644 sites/dapp-ui/src/hooks/useAccountInfo.ts create mode 100644 sites/dapp-ui/src/hooks/useAlgod.ts rename sites/dapp-ui/src/{components/user => hooks}/useUserState.ts (100%) delete mode 100644 sites/dapp-ui/src/pages/dashboard/Registered.tsx delete mode 100644 sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx rename sites/dapp-ui/src/pages/{home/GetStarted.tsx => home.tsx} (68%) create mode 100644 sites/dapp-ui/src/pages/index.ts rename sites/dapp-ui/src/pages/{peering/WaitForPeers.tsx => peering.tsx} (74%) diff --git a/index.html b/index.html deleted file mode 100644 index 3c499bc..0000000 --- a/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Title - - - - - - - diff --git a/services/liquid-auth-api-js/src/connect/connect.gateway.ts b/services/liquid-auth-api-js/src/connect/connect.gateway.ts index 600b2cd..bb69a89 100644 --- a/services/liquid-auth-api-js/src/connect/connect.gateway.ts +++ b/services/liquid-auth-api-js/src/connect/connect.gateway.ts @@ -1,10 +1,11 @@ -import type { Handshake, Server, Socket } from 'socket.io'; +import type { Server, Socket } from 'socket.io'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { ConnectedSocket, MessageBody, - OnGatewayConnection, OnGatewayDisconnect, + OnGatewayConnection, + OnGatewayDisconnect, OnGatewayInit, SubscribeMessage, WebSocketGateway, @@ -18,7 +19,9 @@ import { RedisIoAdapter } from '../adapters/redis-io.adapter.js'; origin: '*', }, }) -export class ConnectGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { +export class ConnectGateway + implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect +{ private timers = new Map(); private ioAdapter: RedisIoAdapter; private readonly logger = new Logger(ConnectGateway.name); @@ -54,12 +57,20 @@ export class ConnectGateway implements OnGatewayInit, OnGatewayConnection, OnGat socket.conn.close(); // you can also use socket.disconnect(), but in that case the client // will not try to reconnect + } else { + if ( + typeof session.wallet === 'string' && + socket.rooms.has(session.wallet) === false + ) { + this.logger.debug(`(*) Client Joining Room ${session.wallet}`); + socket.join(session.wallet); + } } }); }, 200); - if(this.timers.has(request.sessionID)) { - clearInterval(this.timers.get(request.sessionID)); + if (this.timers.has(request.sessionID)) { + clearInterval(this.timers.get(request.sessionID)); } this.timers.set(request.sessionID, timer); @@ -77,9 +88,11 @@ export class ConnectGateway implements OnGatewayInit, OnGatewayConnection, OnGat handleDisconnect(socket: Socket) { const request = socket.request as Record; - this.logger.debug(`(*) Client Disconnected with Session: ${request.sessionID}`); - if(this.timers.has(request.sessionID)) { - clearInterval(this.timers.get(request.sessionID)); + this.logger.debug( + `(*) Client Disconnected with Session: ${request.sessionID}`, + ); + if (this.timers.has(request.sessionID)) { + clearInterval(this.timers.get(request.sessionID)); } } /** @@ -103,12 +116,12 @@ export class ConnectGateway implements OnGatewayInit, OnGatewayConnection, OnGat const session = await this.authService.findSession(request.sessionID); console.log('Session', session); if (session) { - console.log('Listening to auth messages') + console.log('Listening to auth messages'); await this.ioAdapter.subClient.subscribe('auth'); // Handle messages const obs$: Observable = new Observable((observer) => { - const handleAuthMessage = async (channel, eventMessage)=> { + const handleAuthMessage = async (channel, eventMessage) => { console.log('Link->Message', channel, eventMessage); const { data } = JSON.parse(eventMessage); console.log(body.requestId, data.requestId, data, body); @@ -123,12 +136,9 @@ export class ConnectGateway implements OnGatewayInit, OnGatewayConnection, OnGat this.ioAdapter.subClient.off('message', handleAuthMessage); observer.complete(); } - } + }; - this.ioAdapter.subClient.on( - 'message', - handleAuthMessage, - ); + this.ioAdapter.subClient.on('message', handleAuthMessage); }); return obs$.pipe( map((obs$) => ({ diff --git a/sites/dapp-ui/src/App.tsx b/sites/dapp-ui/src/App.tsx index a40c4ae..b3a20dd 100644 --- a/sites/dapp-ui/src/App.tsx +++ b/sites/dapp-ui/src/App.tsx @@ -9,8 +9,7 @@ import { } from './Contexts'; import Layout from './Layout'; -import { GetStartedCard } from './pages/home/GetStarted'; -import { RegisteredCard } from './pages/dashboard/Registered'; +import { HomePage } from './pages/home.tsx'; import { createTheme, CssBaseline } from '@mui/material'; import { DEFAULT_THEME } from './theme.tsx'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; @@ -18,10 +17,17 @@ import { ThemeProvider } from '@emotion/react'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { createHashRouter, RouterProvider } from 'react-router-dom'; -import { WaitForPeersCard } from './pages/peering/WaitForPeers.tsx'; +import { PeeringPage } from './pages/peering.tsx'; import ConnectedPage from './pages/connected.tsx'; +import { Algodv2 } from 'algosdk'; +import { AlgodContext } from './hooks/useAlgod.ts'; const queryClient = new QueryClient(); +const algod = new Algodv2( + process.env.VITE_ALGOD_TOKEN || '', + process.env.VITE_ALGOD_SERVER || 'https://testnet-api.algonode.cloud', + process.env.VITE_ALGOD_PORT || 443, +); const DEFAULT_CONFIG: RTCConfiguration = { iceServers: [ { @@ -40,7 +46,7 @@ const router = createHashRouter([ path: '/', element: ( - + ), }, @@ -48,7 +54,7 @@ const router = createHashRouter([ path: '/peering', element: ( - + ), }, @@ -60,14 +66,6 @@ const router = createHashRouter([ ), }, - { - path: '/registered', - element: ( - - - - ), - }, ]); export default function ProviderApp() { const [open, setOpen] = useState(false); @@ -97,32 +95,40 @@ export default function ProviderApp() { () => createTheme({ ...DEFAULT_THEME, - palette: { - ...DEFAULT_THEME.palette, - mode, - }, + palette: + mode === 'dark' + ? { + primary: { main: '#9966ff' }, + mode: 'dark', + } + : { ...DEFAULT_THEME.palette }, }), [mode], ); + console.log(theme, DEFAULT_THEME.palette); return ( - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + ); } diff --git a/sites/dapp-ui/src/components/Chat.tsx b/sites/dapp-ui/src/components/Chat.tsx deleted file mode 100644 index fecc1b7..0000000 --- a/sites/dapp-ui/src/components/Chat.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import Avatar from '@mui/material/Avatar'; -import Divider from '@mui/material/Divider'; -import List from '@mui/material/List'; -import ListItem from '@mui/material/ListItem'; -import ListItemAvatar from '@mui/material/ListItemAvatar'; -import ListItemText from '@mui/material/ListItemText'; -import { Message, useMessageStore } from '../store.ts'; -import TextField from '@mui/material/TextField'; -import { useContext, useState } from 'react'; -import Button from '@mui/material/Button'; -import { DataChannelContext } from '../hooks/useDataChannel.ts'; - -const MESSAGE_TYPES = { - local: 'Local', - remote: 'Remote', -}; -const MESSAGE_AVATARS = { - local: '/maskable-icon.png', - remote: '/logo-inverted.png', -}; - -function ChatMessage(message: Message) { - return ( - - - - - - - ); -} - -export function ChatList() { - const { dataChannel } = useContext(DataChannelContext); - - const messages = useMessageStore((state) => state.messages); - const addMessage = useMessageStore((state) => state.addMessage); - const [message, setMessage] = useState(''); - - const isReady = dataChannel && dataChannel.readyState === 'open'; - return ( - <> - - {messages.map((message, i) => { - return ( -
- - -
- ); - })} -
-
- setMessage(e.target.value)} - > - -
- {/**/} - - ); -} diff --git a/sites/dapp-ui/src/pages/home/ConnectModal.tsx b/sites/dapp-ui/src/components/ConnectModal.tsx similarity index 97% rename from sites/dapp-ui/src/pages/home/ConnectModal.tsx rename to sites/dapp-ui/src/components/ConnectModal.tsx index 8192f5c..3be09b3 100644 --- a/sites/dapp-ui/src/pages/home/ConnectModal.tsx +++ b/sites/dapp-ui/src/components/ConnectModal.tsx @@ -7,9 +7,9 @@ import { Message } from '@liquid/auth-client/connect'; import QRCodeStyling, { Options } from 'qr-code-styling'; import { useEffect, useState } from 'react'; import { Fade } from '@mui/material'; -import { useSocket } from '../../hooks/useSocket'; +import { useSocket } from '../hooks/useSocket'; import nacl from 'tweetnacl'; -import { useCredentialStore, Credential } from '../../store'; +import { useCredentialStore, Credential } from '../store'; import { useNavigate } from 'react-router-dom'; const style = { position: 'absolute' as const, diff --git a/sites/dapp-ui/src/components/user/Credential.tsx b/sites/dapp-ui/src/components/user/Credential.tsx deleted file mode 100644 index 96559e4..0000000 --- a/sites/dapp-ui/src/components/user/Credential.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export function Credential() { - return ( -
-

Credential

-
- ); -} diff --git a/sites/dapp-ui/src/components/user/SessionMenu.tsx b/sites/dapp-ui/src/components/user/SessionMenu.tsx index 0c0c06f..09e5ec9 100644 --- a/sites/dapp-ui/src/components/user/SessionMenu.tsx +++ b/sites/dapp-ui/src/components/user/SessionMenu.tsx @@ -1,8 +1,8 @@ import { Avatar, Badge, CircularProgress, Menu } from '@mui/material'; import IconButton from '@mui/material/IconButton'; import React, { useState } from 'react'; -import { useSocket } from '../../hooks/useSocket.ts'; -import { useUserState } from './useUserState.ts'; +import { useSocket } from '@/hooks/useSocket.ts'; +import { useUserState } from '@/hooks/useUserState.ts'; import { StatusCard } from './StatusCard.tsx'; export function SessionMenu() { diff --git a/sites/dapp-ui/src/hooks/index.ts b/sites/dapp-ui/src/hooks/index.ts new file mode 100644 index 0000000..fbbe4b9 --- /dev/null +++ b/sites/dapp-ui/src/hooks/index.ts @@ -0,0 +1,7 @@ +export * from './useAccountInfo'; +export * from './useAddress'; +export * from './useAlgod'; +export * from './useDataChannel'; +export * from './usePeerConnection'; +export * from './useSocket'; +export * from './useUserState'; diff --git a/sites/dapp-ui/src/hooks/useAccountInfo.ts b/sites/dapp-ui/src/hooks/useAccountInfo.ts new file mode 100644 index 0000000..43242d5 --- /dev/null +++ b/sites/dapp-ui/src/hooks/useAccountInfo.ts @@ -0,0 +1,18 @@ +import { useQuery } from '@tanstack/react-query'; +import { useAlgod } from './useAlgod.ts'; + +export function useAccountInfo( + address: string | null, + refetchInterval?: number, +) { + const algod = useAlgod(); + return useQuery({ + refetchInterval, + queryKey: ['accountInfo', address], + queryFn: async () => { + if (!algod || !address) return; + return await algod.accountInformation(address).do(); + }, + enabled: !!algod && !!address, + }); +} diff --git a/sites/dapp-ui/src/hooks/useAlgod.ts b/sites/dapp-ui/src/hooks/useAlgod.ts new file mode 100644 index 0000000..ea66f1a --- /dev/null +++ b/sites/dapp-ui/src/hooks/useAlgod.ts @@ -0,0 +1,16 @@ +import { createContext, useContext } from 'react'; +import { Algodv2 } from 'algosdk'; + +type AlgodState = { algod: Algodv2 | null }; +export const AlgodContext = createContext({ + algod: null, +} as AlgodState); + +export function useAlgod() { + const { algod } = useContext(AlgodContext); + if (!algod) + throw new Error( + 'Algod not found, make sure it is provided in the context.', + ); + return algod; +} diff --git a/sites/dapp-ui/src/hooks/usePeerConnection.ts b/sites/dapp-ui/src/hooks/usePeerConnection.ts index f0d6590..fb4b3a8 100644 --- a/sites/dapp-ui/src/hooks/usePeerConnection.ts +++ b/sites/dapp-ui/src/hooks/usePeerConnection.ts @@ -37,14 +37,16 @@ export function usePeerConnection( onIceCandidate: (event: RTCPeerConnectionIceEvent) => void, ) { const { peerConnection } = useContext(PeerConnectionContext); - window.peerConnection = peerConnection; + useEffect(() => { if (!peerConnection) return; function handleICEGatheringStateChange() { console.log(`ICE gathering state: ${peerConnection?.iceGatheringState}`); } function handleConnectionStateChange() { - console.log(`Connection state change: ${peerConnection.connectionState}`); + console.log( + `Connection state change: ${peerConnection?.connectionState}`, + ); } function handleSignalingStateChange() { @@ -57,9 +59,6 @@ export function usePeerConnection( ); } - function handleICECandidateError(event) { - console.error('ICE Candidate Error', event); - } peerConnection.addEventListener( 'icegatheringstatechange', handleICEGatheringStateChange, @@ -77,10 +76,7 @@ export function usePeerConnection( handleICEConnectionStateChange, ); peerConnection.addEventListener('icecandidate', onIceCandidate); - peerConnection.addEventListener( - 'icecandidateerror', - handleICECandidateError, - ); + return () => { peerConnection.removeEventListener( 'icegatheringstatechange', diff --git a/sites/dapp-ui/src/components/user/useUserState.ts b/sites/dapp-ui/src/hooks/useUserState.ts similarity index 100% rename from sites/dapp-ui/src/components/user/useUserState.ts rename to sites/dapp-ui/src/hooks/useUserState.ts diff --git a/sites/dapp-ui/src/pages/connected.tsx b/sites/dapp-ui/src/pages/connected.tsx index 0881371..53104f6 100644 --- a/sites/dapp-ui/src/pages/connected.tsx +++ b/sites/dapp-ui/src/pages/connected.tsx @@ -5,64 +5,164 @@ import { import { PeerConnectionContext } from '../hooks/usePeerConnection.ts'; import { useContext, useEffect, useState } from 'react'; import Button from '@mui/material/Button'; -import algosdk from 'algosdk'; +import { + Transaction, + encodeUnsignedTransaction, + waitForConfirmation, + makePaymentTxnWithSuggestedParamsFromObject, +} from 'algosdk'; import { toBase64URL, fromBase64Url } from '@liquid/core/encoding'; - -const algodClient = new algosdk.Algodv2( - '', - 'https://testnet-api.algonode.cloud', - 443, -); +import { useAlgod } from '../hooks/useAlgod.ts'; +import { useAccountInfo } from '../hooks/useAccountInfo.ts'; +import FormControl from '@mui/material/FormControl'; +import { Box, CircularProgress, Input, Slider } from '@mui/material'; +import Typography from '@mui/material/Typography'; +import { useMessageStore } from '../store.ts'; export default function ConnectedPage() { + const algod = useAlgod(); const walletStr = window.localStorage.getItem('wallet'); const wallet = walletStr ? JSON.parse(walletStr) : null; - const [txn, setTxn] = useState(null); + const [txn, setTxn] = useState(null); const { peerConnection } = useContext(PeerConnectionContext); const datachannel = useDataChannel('remote', peerConnection); + const accountInfo = useAccountInfo(wallet, 3000); + const [from, setFrom] = useState(wallet); + const [to, setTo] = useState(wallet); + const [amount, setAmount] = useState(0); + const [isWaitingForSignature, setIsWaitingForSignature] = useState(false); + const [isWaitingForConfirmation, setIsWaitingForConfirmation] = + useState(false); + const addMessage = useMessageStore((state) => state.addMessage); + const messages = useMessageStore((state) => state.messages); // Receive response useDataChannelMessages((event) => { + addMessage({ + type: 'remote', + data: JSON.parse(event.data), + timestamp: Date.now(), + }); if (!txn) return; async function handleMessage() { if (!txn) return; - console.log(event); - const sig = fromBase64Url(event.data); + const message = JSON.parse(event.data); + if (message.type !== 'transaction-signature') return; + + if (txn.txID() !== message.txId) throw new Error('Invalid txId'); + + const sig = fromBase64Url(message.sig); const signedTxn = txn.attachSignature(wallet, sig); - const { txId } = await algodClient.sendRawTransaction(signedTxn).do(); - const result = await algosdk.waitForConfirmation(algodClient, txId, 4); - console.log(result); + setIsWaitingForSignature(false); + setIsWaitingForConfirmation(true); + const { txId } = await algod.sendRawTransaction(signedTxn).do(); + await waitForConfirmation(algod, txId, 4); + setIsWaitingForConfirmation(false); } handleMessage(); }); // Send Transaction useEffect(() => { - if (!txn || !datachannel) return; - datachannel?.send(toBase64URL(txn.bytesToSign())); - }, [txn, datachannel]); + if ( + !txn || + !datachannel || + isWaitingForSignature || + isWaitingForConfirmation + ) + return; + const txnMessage = { + type: 'transaction', + txn: toBase64URL(encodeUnsignedTransaction(txn)), + }; + addMessage({ type: 'local', data: txnMessage, timestamp: Date.now() }); + datachannel?.send(JSON.stringify(txnMessage)); + setIsWaitingForSignature(true); + }, [txn, datachannel, isWaitingForSignature]); + + if (accountInfo.data && accountInfo.data.amount === 0) { + return ( + +

Account has no funds

+

{accountInfo.data.address}

+
{JSON.stringify(accountInfo.data, null, 2)}
+
+ ); + } + if (isWaitingForSignature || isWaitingForConfirmation) { + return ( + +

+ Waiting for {isWaitingForConfirmation ? 'Confirmation' : 'Signature'} +

+ +
+ ); + } return ( -
- Connected + + + Send Payment Transaction + + From + setFrom(e.target.value)} + /> + + + To + setTo(e.target.value)} + /> + + + + Amount (in microalgos) + + setAmount(value as number)} + /> + + + -
+ + Messages + + {messages.map((message, i) => ( + +
{JSON.stringify(message, null, 2)}
+
+ ))} + ); } diff --git a/sites/dapp-ui/src/pages/dashboard/Registered.tsx b/sites/dapp-ui/src/pages/dashboard/Registered.tsx deleted file mode 100644 index a82d63b..0000000 --- a/sites/dapp-ui/src/pages/dashboard/Registered.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import CardMedia from '@mui/material/CardMedia'; -import CardContent from '@mui/material/CardContent'; -import Typography from '@mui/material/Typography'; -import CardActions from '@mui/material/CardActions'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import { assertion } from '@liquid/auth-client/assertion'; -import { - Table, - TableBody, - TableCell, - TableHead, - TableRow, -} from '@mui/material'; -import { useCredentialStore } from '../../store'; - -export function RegisteredCard() { - const credentials = useCredentialStore((state) => state.addresses); - const handleTestCredentialClick = () => { - const credId = window.localStorage.getItem('credId'); - if (!credId) return; - assertion(credId); - }; - return ( - - - - - Registered (3 of 3) - - - Mobile Device is Connected and Registered - - - - - Address - Actions - - - - {Object.keys(credentials).map((address) => ( - - - {address.slice(0, 5)}...{address.slice(-5)} - - - - - - ))} - -
-
- - - -
- ); -} diff --git a/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx b/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx deleted file mode 100644 index e0f7817..0000000 --- a/sites/dapp-ui/src/pages/dashboard/WaitForRegistration.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import CardMedia from '@mui/material/CardMedia'; -import CardContent from '@mui/material/CardContent'; -import Typography from '@mui/material/Typography'; -import Card from '@mui/material/Card'; -import { CircularProgress } from '@mui/material'; -import { useContext, useEffect } from 'react'; -import { useSocket } from '../../hooks/useSocket'; -import { StateContext } from '../../Contexts'; -import { useCredentialStore } from '../../store'; -import { useAddressQuery } from '../../hooks/useAddress'; - -export function WaitForRegistrationCard() { - const credentials = useCredentialStore((state) => state.addresses); - const save = useCredentialStore((state) => state.update); - const { state: step, setState } = useContext(StateContext); - const walletStr = window.localStorage.getItem('wallet'); - const wallet = walletStr ? JSON.parse(walletStr) : null; - const { socket } = useSocket(); - const address = useAddressQuery(wallet); - - useEffect(() => { - if (step !== 'connected') { - return; - } - socket.emit( - 'wait', - { wallet }, - async ({ - data: { credId, device }, - }: { - data: { credId: string; device: string }; - }) => { - save({ - name: wallet, - credentials: [ - ...credentials[wallet].credentials, - { id: credId, device }, - ], - }); - window.localStorage.setItem('credId', credId); - setState('registered'); - }, - ); - }); - useEffect(() => { - socket.on('call-candidate', (data: any) => { - console.log(data); - }); - }, [socket]); - return ( - - - - - Connected (2 of 3) - - - Waiting for Passkey registration for - address: - - {address.isLoading && } - {address.isFetched && ( - - {' '} - {address.data} - - )} - - - ); -} diff --git a/sites/dapp-ui/src/pages/home/GetStarted.tsx b/sites/dapp-ui/src/pages/home.tsx similarity index 68% rename from sites/dapp-ui/src/pages/home/GetStarted.tsx rename to sites/dapp-ui/src/pages/home.tsx index 2018215..c167739 100644 --- a/sites/dapp-ui/src/pages/home/GetStarted.tsx +++ b/sites/dapp-ui/src/pages/home.tsx @@ -3,9 +3,13 @@ import CardContent from '@mui/material/CardContent'; import Typography from '@mui/material/Typography'; import CardActions from '@mui/material/CardActions'; import Card from '@mui/material/Card'; -import { ConnectModal } from './ConnectModal'; +import { ConnectModal } from '@/components/ConnectModal'; import Button from '@mui/material/Button'; -export function GetStartedCard() { +import { assertion } from '@liquid/auth-client'; +import { useNavigate } from 'react-router-dom'; +export function HomePage() { + const credId = window.localStorage.getItem('credId'); + const navigate = useNavigate(); return ( - Get Started (1 of 3) + Get Started (1 of 2) Start by connecting a valid wallet, this is the first step in a three @@ -35,7 +39,16 @@ export function GetStartedCard() { - + {credId && ( + + )} ); diff --git a/sites/dapp-ui/src/pages/index.ts b/sites/dapp-ui/src/pages/index.ts new file mode 100644 index 0000000..9b532e6 --- /dev/null +++ b/sites/dapp-ui/src/pages/index.ts @@ -0,0 +1,3 @@ +export * from './connected.tsx'; +export * from './home.tsx'; +export * from './peering.tsx'; diff --git a/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx b/sites/dapp-ui/src/pages/peering.tsx similarity index 74% rename from sites/dapp-ui/src/pages/peering/WaitForPeers.tsx rename to sites/dapp-ui/src/pages/peering.tsx index a829285..6e0e9b4 100644 --- a/sites/dapp-ui/src/pages/peering/WaitForPeers.tsx +++ b/sites/dapp-ui/src/pages/peering.tsx @@ -3,20 +3,19 @@ import CardContent from '@mui/material/CardContent'; import Typography from '@mui/material/Typography'; import Card from '@mui/material/Card'; import { CircularProgress } from '@mui/material'; -import { useEffect } from 'react'; -import { useSocket } from '../../hooks/useSocket'; +import { useEffect, useState } from 'react'; +import { useSocket } from '@/hooks/useSocket'; import { useNavigate } from 'react-router-dom'; -import { usePeerConnection } from '../../hooks/usePeerConnection.ts'; -import { - useDataChannel, - useDataChannelMessages, -} from '../../hooks/useDataChannel.ts'; +import { usePeerConnection } from '@/hooks/usePeerConnection'; +import { useDataChannel, useDataChannelMessages } from '@/hooks/useDataChannel'; +import { useMessageStore } from '@/store'; -export function WaitForPeersCard() { +export function PeeringPage() { const navigate = useNavigate(); const walletStr = window.localStorage.getItem('wallet'); + const addMessage = useMessageStore((state) => state.addMessage); const wallet = walletStr ? JSON.parse(walletStr) : null; - + const [credentialId, setCredentialId] = useState(null); const { socket } = useSocket(); // const address = useAddressQuery(wallet); const peerConnection = usePeerConnection((event) => { @@ -31,14 +30,25 @@ export function WaitForPeersCard() { }); const datachannel = useDataChannel('remote', peerConnection); useDataChannelMessages((event) => { - console.log(event); + addMessage({ + type: 'remote', + data: JSON.parse(event.data), + timestamp: Date.now(), + }); + const data = JSON.parse(event.data); + if (data?.type === 'credential') { + window.localStorage.setItem('credId', data.id); + setCredentialId(data.id); + } }); + + // Once we have a valid credential, continue to the connected page useEffect(() => { - if (!datachannel) return; + if (!datachannel || !credentialId) return; // datachannel.send('Hello World') navigate('/connected'); - }, [datachannel]); + }, [datachannel, credentialId, navigate]); useEffect(() => { if (!peerConnection) return; @@ -88,10 +98,10 @@ export function WaitForPeersCard() { /> - Waiting for Peer Connection (2 of 3) + Waiting for Peer Confirmation (2 of 2) - Waiting for Passkey registration for + Waiting for message from peer for address: diff --git a/sites/dapp-ui/src/store.ts b/sites/dapp-ui/src/store.ts index 68b1ef6..3a486a6 100644 --- a/sites/dapp-ui/src/store.ts +++ b/sites/dapp-ui/src/store.ts @@ -60,7 +60,7 @@ export const usePeerStore = create((set) => ({ })); export type Message = { - text: string; + data: any; type: 'local' | 'remote'; timestamp: number; }; diff --git a/sites/dapp-ui/tsconfig.json b/sites/dapp-ui/tsconfig.json index a7fc6fb..439469d 100644 --- a/sites/dapp-ui/tsconfig.json +++ b/sites/dapp-ui/tsconfig.json @@ -18,7 +18,11 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + + "paths": { + "@/*": ["./src/*"] + } }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] diff --git a/sites/dapp-ui/vite.config.ts b/sites/dapp-ui/vite.config.ts index 7bef127..f79e467 100644 --- a/sites/dapp-ui/vite.config.ts +++ b/sites/dapp-ui/vite.config.ts @@ -1,111 +1,131 @@ -import {defineConfig, splitVendorChunkPlugin} from 'vite' -import { VitePWA } from 'vite-plugin-pwa' +import { defineConfig, splitVendorChunkPlugin } from 'vite'; +import { VitePWA } from 'vite-plugin-pwa'; import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'; -import react from '@vitejs/plugin-react-swc' -import {rename} from 'node:fs/promises' -import {resolve} from 'node:path' -import {mkdirp} from 'mkdirp'; +import react from '@vitejs/plugin-react-swc'; +import { rename } from 'node:fs/promises'; +import { resolve } from 'node:path'; +import { mkdirp } from 'mkdirp'; -const API_DIR = resolve(__dirname, '..', '..', 'services', 'liquid-auth-api-js') -const PUBLIC_DIR = resolve(API_DIR, 'public') -const VIEW_DIR = resolve(API_DIR, 'views') -console.log(API_DIR) +const API_DIR = resolve( + __dirname, + '..', + '..', + 'services', + 'liquid-auth-api-js', +); +const PUBLIC_DIR = resolve(API_DIR, 'public'); +const VIEW_DIR = resolve(API_DIR, 'views'); +console.log(API_DIR); export default defineConfig({ - // base: '/app', - server: { - hmr: { - port: 8000, - host: 'localhost' - } - // proxy: { - // '^/auth/.*': 'http://localhost:3000', - // '^/connect/.*': 'http://localhost:3000', - // '^/attestation/.*': 'http://localhost:3000', - // '^/assertion/.*': 'http://localhost:3000', - // '/socket.io': { - // target: 'ws://localhost:3000', - // ws: true, - // }, - // } + // base: '/app', + server: { + hmr: { + port: 8000, + host: 'localhost', }, - build: { - // rollupOptions: { - // output: { - // manualChunks: { - // // 'algorand': ['tweetnacl', 'algosdk'], - // 'socket.io': ['socket.io-client'], - // 'react': ['react', 'react-dom', '@tanstack/react-query'], - // 'material': ['@mui/material', '@mui/icons-material'] - // } - // } - // }, - outDir: PUBLIC_DIR, + // proxy: { + // '^/auth/.*': 'http://localhost:3000', + // '^/connect/.*': 'http://localhost:3000', + // '^/attestation/.*': 'http://localhost:3000', + // '^/assertion/.*': 'http://localhost:3000', + // '/socket.io': { + // target: 'ws://localhost:3000', + // ws: true, + // }, + // } + }, + build: { + // rollupOptions: { + // output: { + // manualChunks: { + // // 'algorand': ['tweetnacl', 'algosdk'], + // 'socket.io': ['socket.io-client'], + // 'react': ['react', 'react-dom', '@tanstack/react-query'], + // 'material': ['@mui/material', '@mui/icons-material'] + // } + // } + // }, + outDir: PUBLIC_DIR, + }, + resolve: { + alias: { + '@/components': resolve(__dirname, 'src', 'components'), + '@/hooks': resolve(__dirname, 'src', 'hooks'), + '@/pages': resolve(__dirname, 'src', 'pages'), + '@/store': resolve(__dirname, 'src', 'store'), }, - plugins: [ - VitePWA({ - includeAssets: ['logo-background.svg', 'apple-touch-icon.png', 'maskable-icon.png'], - workbox: { - navigateFallback: null, - }, - manifest: { - name: 'Liquid dApp', - short_name: 'Liquid', - description: 'FIDO2/Passkey Authentication', - theme_color: '#121212', - icons: [ - { - src: 'icons/48x48.png', - sizes: '48x48', - type: 'image/png' - }, - { - src: 'icons/72x72.png', - sizes: '72x72', - type: 'image/png' - }, - { - src: 'icons/96x96.png', - sizes: '96x96', - type: 'image/png' - }, - { - src: 'icons/144x144.png', - sizes: '144x144', - type: 'image/png' - }, - { - src: 'icons/192x192.png', - sizes: '192x192', - type: 'image/png' - }, - { - src: 'icons/512x512.png', - sizes: '512x512', - type: 'image/png' - }, - { - src: 'maskable-icon.png', - sizes: '512x512', - type: 'image/png', - purpose: "maskable" - } - ] - } - }), - splitVendorChunkPlugin(), - ViteImageOptimizer(), - react(), - { - name: 'move-index-file', - closeBundle: async () => { - await mkdirp(VIEW_DIR) - try { - await rename(resolve(PUBLIC_DIR, 'index.html'), resolve(VIEW_DIR, 'index.html')) - } catch (e) { - console.log('Skipping') - } - - } - }, - ], -}) + }, + plugins: [ + VitePWA({ + includeAssets: [ + 'logo-background.svg', + 'apple-touch-icon.png', + 'maskable-icon.png', + ], + workbox: { + navigateFallback: null, + }, + manifest: { + name: 'Liquid dApp', + short_name: 'Liquid', + description: 'FIDO2/Passkey Authentication', + theme_color: '#121212', + icons: [ + { + src: 'icons/48x48.png', + sizes: '48x48', + type: 'image/png', + }, + { + src: 'icons/72x72.png', + sizes: '72x72', + type: 'image/png', + }, + { + src: 'icons/96x96.png', + sizes: '96x96', + type: 'image/png', + }, + { + src: 'icons/144x144.png', + sizes: '144x144', + type: 'image/png', + }, + { + src: 'icons/192x192.png', + sizes: '192x192', + type: 'image/png', + }, + { + src: 'icons/512x512.png', + sizes: '512x512', + type: 'image/png', + }, + { + src: 'maskable-icon.png', + sizes: '512x512', + type: 'image/png', + purpose: 'maskable', + }, + ], + }, + }), + splitVendorChunkPlugin(), + ViteImageOptimizer(), + react(), + { + name: 'move-index-file', + closeBundle: async () => { + await mkdirp(VIEW_DIR); + try { + await rename( + resolve(PUBLIC_DIR, 'index.html'), + resolve(VIEW_DIR, 'index.html'), + ); + } catch (e) { + console.log('Skipping'); + } + }, + }, + ], +}); From 102a55ceb016b4f0b07b2cdd8d80d965b17f4d33 Mon Sep 17 00:00:00 2001 From: Michael Feher Date: Mon, 15 Apr 2024 18:32:51 -0400 Subject: [PATCH 11/21] test: enforce linters and tests - adds tests to the core library - adds ci for pull requests --- .github/workflows/CI.yml | 23 + clients/liquid-auth-client-js/package.json | 3 +- clients/liquid-auth-client-js/src/connect.ts | 10 +- .../__fixtures__/base64url.fixtures.json | 5 + .../tests/connect.spec.js | 2 +- clients/liquid-auth-core/package.json | 2 +- clients/liquid-auth-core/src/encoding.ts | 19 +- clients/liquid-auth-core/src/hi-base32.ts | 354 +- .../encoding.base64url.fixtures.json | 1079 ++++++ .../__fixtures__/wallet.keys.fixtures.json | 2972 +++++++++++++++++ .../liquid-auth-core/tests/encoding.spec.js | 46 + .../tests/generate.fixtures.js | 61 + package.json | 4 +- services/liquid-auth-api-js/.eslintrc.json | 1 + .../src/adapters/redis-io.adapter.ts | 2 +- .../src/algod/algod.service.ts | 1 - .../liquid-auth-api-js/src/app.controller.ts | 4 - services/liquid-auth-api-js/src/app.module.ts | 2 +- .../liquid-auth-api-js/src/app.service.ts | 9 +- .../src/assertion/assertion.controller.ts | 5 +- .../src/attestation/attestation.service.ts | 2 +- .../src/auth/auth.controller.ts | 5 - .../src/connect/connect.controller.ts | 16 +- .../src/signals/signals.gateway.spec.ts | 2 +- services/liquid-auth-api-js/tsconfig.json | 3 +- sites/dapp-ui/src/Contexts.tsx | 12 +- sites/dapp-ui/src/entry-main.tsx | 2 +- sites/dapp-ui/src/hooks/useDataChannel.ts | 4 +- sites/dapp-ui/src/store.ts | 2 +- 29 files changed, 4241 insertions(+), 411 deletions(-) create mode 100644 .github/workflows/CI.yml create mode 100644 clients/liquid-auth-client-js/tests/__fixtures__/base64url.fixtures.json create mode 100644 clients/liquid-auth-core/tests/__fixtures__/encoding.base64url.fixtures.json create mode 100644 clients/liquid-auth-core/tests/__fixtures__/wallet.keys.fixtures.json create mode 100644 clients/liquid-auth-core/tests/encoding.spec.js create mode 100644 clients/liquid-auth-core/tests/generate.fixtures.js diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..e00b40c --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,23 @@ +name: CI +on: [pull_request] +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [ 18.x, 20.x ] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: Install Dependencies + run: npm install + - name: Run Build + run: npm run build + - name: Lint Codebase + run: npm run lint + - name: Unit Tests with Coverage + run: npm run test:cov diff --git a/clients/liquid-auth-client-js/package.json b/clients/liquid-auth-client-js/package.json index d31fe48..ebef85f 100644 --- a/clients/liquid-auth-client-js/package.json +++ b/clients/liquid-auth-client-js/package.json @@ -26,7 +26,8 @@ "dev": "tsc --watch", "build": "tsc", "preinstall": "npm run build", - "test": "tsc && c8 node --test ./tests/connect.test.js" + "test": "tsc && node --test ./tests/connect.spec.js", + "test:cov": "tsc && c8 node --test ./tests/connect.spec.js" }, "author": "", "license": "MIT", diff --git a/clients/liquid-auth-client-js/src/connect.ts b/clients/liquid-auth-client-js/src/connect.ts index b680b21..87b36ac 100644 --- a/clients/liquid-auth-client-js/src/connect.ts +++ b/clients/liquid-auth-client-js/src/connect.ts @@ -1,7 +1,7 @@ import { DEFAULT_FETCH_OPTIONS } from './constants.js'; import type { Account } from 'algosdk'; import type { SignKeyPair } from 'tweetnacl'; -import { sign } from 'tweetnacl'; +import nacl from 'tweetnacl'; import { toBase64URL, encodeAddress } from '@liquid/core/encoding'; export class Message { @@ -58,9 +58,9 @@ export class Message { // Seed or Secret Key if (key instanceof Uint8Array) { if (key.length === 32) { - keyPair = sign.keyPair.fromSeed(key); + keyPair = nacl.sign.keyPair.fromSeed(key); } else if (key.length === 64) { - keyPair = sign.keyPair.fromSecretKey(key); + keyPair = nacl.sign.keyPair.fromSecretKey(key); } else { throw new TypeError('Invalid seed or secret key'); } @@ -71,7 +71,7 @@ export class Message { typeof (key as Account).addr !== 'undefined' && typeof (key as Account).addr === 'string' ) { - keyPair = sign.keyPair.fromSecretKey((key as Account).sk); + keyPair = nacl.sign.keyPair.fromSecretKey((key as Account).sk); } // NACL @@ -86,7 +86,7 @@ export class Message { throw new TypeError('Invalid key'); } this.signature = toBase64URL( - sign.detached(encoder.encode(this.challenge), keyPair.secretKey), + nacl.sign.detached(encoder.encode(this.challenge), keyPair.secretKey), ); this.wallet = encodeAddress(keyPair.publicKey); return this; diff --git a/clients/liquid-auth-client-js/tests/__fixtures__/base64url.fixtures.json b/clients/liquid-auth-client-js/tests/__fixtures__/base64url.fixtures.json new file mode 100644 index 0000000..8c6c516 --- /dev/null +++ b/clients/liquid-auth-client-js/tests/__fixtures__/base64url.fixtures.json @@ -0,0 +1,5 @@ +[ + [ + "" + ] +] diff --git a/clients/liquid-auth-client-js/tests/connect.spec.js b/clients/liquid-auth-client-js/tests/connect.spec.js index 58d6dde..5dbd731 100644 --- a/clients/liquid-auth-client-js/tests/connect.spec.js +++ b/clients/liquid-auth-client-js/tests/connect.spec.js @@ -4,7 +4,7 @@ import algosdk from 'algosdk'; import nacl from "tweetnacl"; import { Message } from '../lib/connect.js'; -import { fromBase64Url } from "../lib/encoding.js"; +import { fromBase64Url } from "@liquid/core"; const encoder = new TextEncoder; test("create instance", async () => { const msg = new Message("hello", "1234", 1234); diff --git a/clients/liquid-auth-core/package.json b/clients/liquid-auth-core/package.json index 4d97a06..a4fb2bc 100644 --- a/clients/liquid-auth-core/package.json +++ b/clients/liquid-auth-core/package.json @@ -26,7 +26,7 @@ "dev": "tsc --watch", "build": "tsc", "preinstall": "npm run build", - "test": "tsc && c8 node --test ./tests/connect.test.js" + "test": "tsc && c8 -r html node --test ./tests/encoding.spec.js" }, "author": "", "license": "MIT", diff --git a/clients/liquid-auth-core/src/encoding.ts b/clients/liquid-auth-core/src/encoding.ts index 72655fa..1aae877 100644 --- a/clients/liquid-auth-core/src/encoding.ts +++ b/clients/liquid-auth-core/src/encoding.ts @@ -1,5 +1,5 @@ import nacl from "tweetnacl"; -import { decodeAsBytes, encode } from "./hi-base32.js"; +import { decodeAsBytes, encodeBytes } from "./hi-base32.js"; import { createMethod } from "./sha512.js"; const sha512_256 = createMethod(256); const chars = @@ -11,6 +11,8 @@ const ALGORAND_ADDRESS_LENGTH = 58; const HASH_BYTES_LENGTH = 32; export const MALFORMED_ADDRESS_ERROR_MSG = "Malformed address"; export const ALGORAND_ADDRESS_BAD_CHECKSUM_ERROR_MSG = "Bad checksum"; +export const INVALID_BASE64URL_INPUT = "Invalid base64url input"; + /** * Bytes to Base64URL * @param {Uint8Array| ArrayBuffer} arr Bytes to convert to URL safe Base64 @@ -19,7 +21,6 @@ export function toBase64URL(arr: Uint8Array | ArrayBuffer): string { const bytes = arr instanceof Uint8Array ? arr : new Uint8Array(arr); const len = bytes.length; let base64 = ""; - for (let i = 0; i < len; i += 3) { base64 += chars[bytes[i] >> 2]; base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; @@ -42,9 +43,10 @@ export function toBase64URL(arr: Uint8Array | ArrayBuffer): string { */ export function fromBase64Url(base64url: string): Uint8Array { if (typeof base64url !== "string") { - throw new TypeError("Must be string!"); + throw new Error(INVALID_BASE64URL_INPUT); } return new Uint8Array( + // TODO: Cross-platform solution since atob is deprecated in Node atob(base64url.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, "")) .split("") .map((c) => c.charCodeAt(0)), @@ -72,7 +74,7 @@ export function encodeAddress(address: Uint8Array) { nacl.sign.publicKeyLength - ALGORAND_CHECKSUM_BYTE_LENGTH, nacl.sign.publicKeyLength, ); - const addr = encode(concatArrays(address, checksum)); + const addr = encodeBytes(concatArrays(address, checksum)); return addr.toString().slice(0, ALGORAND_ADDRESS_LENGTH); // removing the extra '====' } @@ -124,12 +126,3 @@ export function decodeAddress(address: string): Uint8Array { return pk; } - -export function base64ToUint8Array(encoded) { - return new Uint8Array( - // TODO: Cross-platform solution since atob is deprecated in Node - atob(encoded) - .split("") - .map((c) => c.charCodeAt(0)), - ); -} diff --git a/clients/liquid-auth-core/src/hi-base32.ts b/clients/liquid-auth-core/src/hi-base32.ts index 4864374..c6bdc80 100644 --- a/clients/liquid-auth-core/src/hi-base32.ts +++ b/clients/liquid-auth-core/src/hi-base32.ts @@ -42,78 +42,6 @@ const BASE32_DECODE_CHAR: Record = { 7: 31, }; -const blocks = [0, 0, 0, 0, 0, 0, 0, 0]; - -const throwInvalidUtf8 = function (position: number, partial: string) { - if (partial.length > 10) { - partial = "..." + partial.substr(-10); - } - const err: Error & { position?: any } = new Error( - "Decoded data is not valid UTF-8." + - " Maybe try base32.decode.asBytes()?" + - " Partial data after reading " + - position + - " bytes: " + - partial + - " <-", - ); - err.position = position; - throw err; -}; - -const toUtf8String = function (bytes: number[]) { - let str = ""; - const length = bytes.length; - let i = 0; - let followingChars = 0; - let b; - let c; - while (i < length) { - b = bytes[i++]; - if (b <= 0x7f) { - str += String.fromCharCode(b); - continue; - } else if (b > 0xbf && b <= 0xdf) { - c = b & 0x1f; - followingChars = 1; - } else if (b <= 0xef) { - c = b & 0x0f; - followingChars = 2; - } else if (b <= 0xf7) { - c = b & 0x07; - followingChars = 3; - } else { - throwInvalidUtf8(i, str); - } - - if (typeof c === "undefined") throw new Error("c is undefined"); - - for (let j = 0; j < followingChars; ++j) { - b = bytes[i++]; - if (b < 0x80 || b > 0xbf) { - throwInvalidUtf8(i, str); - } - c <<= 6; - c += b & 0x3f; - } - if (c >= 0xd800 && c <= 0xdfff) { - throwInvalidUtf8(i, str); - } - if (c > 0x10ffff) { - throwInvalidUtf8(i, str); - } - - if (c <= 0xffff) { - str += String.fromCharCode(c); - } else { - c -= 0x10000; - str += String.fromCharCode((c >> 10) + 0xd800); - str += String.fromCharCode((c & 0x3ff) + 0xdc00); - } - } - return str; -}; - export const decodeAsBytes = function (base32Str: string): number[] { if (base32Str === "") { return []; @@ -188,188 +116,7 @@ export const decodeAsBytes = function (base32Str: string): number[] { return bytes; }; -const encodeAscii = function (str: string) { - let v1; - let v2; - let v3; - let v4; - let v5; - let base32Str = ""; - const length = str.length; - for ( - var i = 0, count = parseInt((length / 5) as unknown as string) * 5; - i < count; - - ) { - v1 = str.charCodeAt(i++); - v2 = str.charCodeAt(i++); - v3 = str.charCodeAt(i++); - v4 = str.charCodeAt(i++); - v5 = str.charCodeAt(i++); - base32Str += - BASE32_ENCODE_CHAR[v1 >>> 3] + - BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + - BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + - BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + - BASE32_ENCODE_CHAR[((v3 << 1) | (v4 >>> 7)) & 31] + - BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + - BASE32_ENCODE_CHAR[((v4 << 3) | (v5 >>> 5)) & 31] + - BASE32_ENCODE_CHAR[v5 & 31]; - } - - // remain char - const remain = length - count; - if (remain === 1) { - v1 = str.charCodeAt(i); - base32Str += - BASE32_ENCODE_CHAR[v1 >>> 3] + - BASE32_ENCODE_CHAR[(v1 << 2) & 31] + - "======"; - } else if (remain === 2) { - v1 = str.charCodeAt(i++); - v2 = str.charCodeAt(i); - base32Str += - BASE32_ENCODE_CHAR[v1 >>> 3] + - BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + - BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + - BASE32_ENCODE_CHAR[(v2 << 4) & 31] + - "===="; - } else if (remain === 3) { - v1 = str.charCodeAt(i++); - v2 = str.charCodeAt(i++); - v3 = str.charCodeAt(i); - base32Str += - BASE32_ENCODE_CHAR[v1 >>> 3] + - BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + - BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + - BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + - BASE32_ENCODE_CHAR[(v3 << 1) & 31] + - "==="; - } else if (remain === 4) { - v1 = str.charCodeAt(i++); - v2 = str.charCodeAt(i++); - v3 = str.charCodeAt(i++); - v4 = str.charCodeAt(i); - base32Str += - BASE32_ENCODE_CHAR[v1 >>> 3] + - BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + - BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + - BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + - BASE32_ENCODE_CHAR[((v3 << 1) | (v4 >>> 7)) & 31] + - BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + - BASE32_ENCODE_CHAR[(v4 << 3) & 31] + - "="; - } - return base32Str; -}; - -const encodeUtf8 = function (str: string) { - let v1; - let v2; - let v3; - let v4; - let v5; - let code; - let end = false; - let base32Str = ""; - let index = 0; - let i; - let start = 0; - let bytes = 0; - const length = str.length; - if (str === "") { - return base32Str; - } - do { - blocks[0] = blocks[5]; - blocks[1] = blocks[6]; - blocks[2] = blocks[7]; - for (i = start; index < length && i < 5; ++index) { - code = str.charCodeAt(index); - if (code < 0x80) { - blocks[i++] = code; - } else if (code < 0x800) { - blocks[i++] = 0xc0 | (code >> 6); - blocks[i++] = 0x80 | (code & 0x3f); - } else if (code < 0xd800 || code >= 0xe000) { - blocks[i++] = 0xe0 | (code >> 12); - blocks[i++] = 0x80 | ((code >> 6) & 0x3f); - blocks[i++] = 0x80 | (code & 0x3f); - } else { - code = - 0x10000 + - (((code & 0x3ff) << 10) | (str.charCodeAt(++index) & 0x3ff)); - blocks[i++] = 0xf0 | (code >> 18); - blocks[i++] = 0x80 | ((code >> 12) & 0x3f); - blocks[i++] = 0x80 | ((code >> 6) & 0x3f); - blocks[i++] = 0x80 | (code & 0x3f); - } - } - bytes += i - start; - start = i - 5; - if (index === length) { - ++index; - } - if (index > length && i < 6) { - end = true; - } - v1 = blocks[0]; - if (i > 4) { - v2 = blocks[1]; - v3 = blocks[2]; - v4 = blocks[3]; - v5 = blocks[4]; - base32Str += - BASE32_ENCODE_CHAR[v1 >>> 3] + - BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + - BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + - BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + - BASE32_ENCODE_CHAR[((v3 << 1) | (v4 >>> 7)) & 31] + - BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + - BASE32_ENCODE_CHAR[((v4 << 3) | (v5 >>> 5)) & 31] + - BASE32_ENCODE_CHAR[v5 & 31]; - } else if (i === 1) { - base32Str += - BASE32_ENCODE_CHAR[v1 >>> 3] + - BASE32_ENCODE_CHAR[(v1 << 2) & 31] + - "======"; - } else if (i === 2) { - v2 = blocks[1]; - base32Str += - BASE32_ENCODE_CHAR[v1 >>> 3] + - BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + - BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + - BASE32_ENCODE_CHAR[(v2 << 4) & 31] + - "===="; - } else if (i === 3) { - v2 = blocks[1]; - v3 = blocks[2]; - base32Str += - BASE32_ENCODE_CHAR[v1 >>> 3] + - BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + - BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + - BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + - BASE32_ENCODE_CHAR[(v3 << 1) & 31] + - "==="; - } else { - v2 = blocks[1]; - v3 = blocks[2]; - v4 = blocks[3]; - base32Str += - BASE32_ENCODE_CHAR[v1 >>> 3] + - BASE32_ENCODE_CHAR[((v1 << 2) | (v2 >>> 6)) & 31] + - BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + - BASE32_ENCODE_CHAR[((v2 << 4) | (v3 >>> 4)) & 31] + - BASE32_ENCODE_CHAR[((v3 << 1) | (v4 >>> 7)) & 31] + - BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + - BASE32_ENCODE_CHAR[(v4 << 3) & 31] + - "="; - } - } while (!end); - return base32Str; -}; - -const encodeBytes = function (bytes: Uint8Array) { +export const encodeBytes = function (bytes: Uint8Array) { let v1; let v2; let v3; @@ -443,102 +190,3 @@ const encodeBytes = function (bytes: Uint8Array) { } return base32Str; }; - -export const encode = function ( - input: string | ArrayBuffer | Uint8Array | number[], - asciiOnly?: boolean, -) { - const notString = typeof input !== "string"; - if (notString && input.constructor === ArrayBuffer) { - input = new Uint8Array(input); - } - if (notString) { - return encodeBytes(input as Uint8Array); - } else if (asciiOnly) { - return encodeAscii(input as string); - } else { - return encodeUtf8(input as string); - } -}; - -export const decode = function (base32Str: string, asciiOnly?: boolean) { - if (!asciiOnly) { - return toUtf8String(decodeAsBytes(base32Str)); - } - if (base32Str === "") { - return ""; - } else if (!/^[A-Z2-7=]+$/.test(base32Str)) { - throw new Error("Invalid base32 characters"); - } - let v1; - let v2; - let v3; - let v4; - let v5; - let v6; - let v7; - let v8; - let str = ""; - let length = base32Str.indexOf("="); - if (length === -1) { - length = base32Str.length; - } - - // 8 char to 5 bytes - for (var i = 0, count = (length >> 3) << 3; i < count; ) { - v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v8 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - str += - String.fromCharCode(((v1 << 3) | (v2 >>> 2)) & 255) + - String.fromCharCode(((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255) + - String.fromCharCode(((v4 << 4) | (v5 >>> 1)) & 255) + - String.fromCharCode(((v5 << 7) | (v6 << 2) | (v7 >>> 3)) & 255) + - String.fromCharCode(((v7 << 5) | v8) & 255); - } - - // remain bytes - const remain = length - count; - if (remain === 2) { - v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - str += String.fromCharCode(((v1 << 3) | (v2 >>> 2)) & 255); - } else if (remain === 4) { - v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - str += - String.fromCharCode(((v1 << 3) | (v2 >>> 2)) & 255) + - String.fromCharCode(((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255); - } else if (remain === 5) { - v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - str += - String.fromCharCode(((v1 << 3) | (v2 >>> 2)) & 255) + - String.fromCharCode(((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255) + - String.fromCharCode(((v4 << 4) | (v5 >>> 1)) & 255); - } else if (remain === 7) { - v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; - str += - String.fromCharCode(((v1 << 3) | (v2 >>> 2)) & 255) + - String.fromCharCode(((v2 << 6) | (v3 << 1) | (v4 >>> 4)) & 255) + - String.fromCharCode(((v4 << 4) | (v5 >>> 1)) & 255) + - String.fromCharCode(((v5 << 7) | (v6 << 2) | (v7 >>> 3)) & 255); - } - return str; -}; diff --git a/clients/liquid-auth-core/tests/__fixtures__/encoding.base64url.fixtures.json b/clients/liquid-auth-core/tests/__fixtures__/encoding.base64url.fixtures.json new file mode 100644 index 0000000..cf14ba0 --- /dev/null +++ b/clients/liquid-auth-core/tests/__fixtures__/encoding.base64url.fixtures.json @@ -0,0 +1,1079 @@ +[ + { + "origin": "9b1a1b53-0456-4fcd-b10a-4db40352d58a", + "toBase64Url": "OWIxYTFiNTMtMDQ1Ni00ZmNkLWIxMGEtNGRiNDAzNTJkNThh", + "fromBase64Url": [ + 57, + 98, + 49, + 97, + 49, + 98, + 53, + 51, + 45, + 48, + 52, + 53, + 54, + 45, + 52, + 102, + 99, + 100, + 45, + 98, + 49, + 48, + 97, + 45, + 52, + 100, + 98, + 52, + 48, + 51, + 53, + 50, + 100, + 53, + 56, + 97 + ] + }, + { + "origin": "7646809", + "toBase64Url": "NzY0NjgwOQ", + "fromBase64Url": [ + 55, + 54, + 52, + 54, + 56, + 48, + 57 + ] + }, + { + "origin": "b1338059", + "toBase64Url": "YjEzMzgwNTk", + "fromBase64Url": [ + 98, + 49, + 51, + 51, + 56, + 48, + 53, + 57 + ] + }, + { + "origin": "582bd065-f280-4574-b315-3398c75296c5", + "toBase64Url": "NTgyYmQwNjUtZjI4MC00NTc0LWIzMTUtMzM5OGM3NTI5NmM1", + "fromBase64Url": [ + 53, + 56, + 50, + 98, + 100, + 48, + 54, + 53, + 45, + 102, + 50, + 56, + 48, + 45, + 52, + 53, + 55, + 52, + 45, + 98, + 51, + 49, + 53, + 45, + 51, + 51, + 57, + 56, + 99, + 55, + 53, + 50, + 57, + 54, + 99, + 53 + ] + }, + { + "origin": "29572cc5-7c36-4016-9825-abcc9ba51dae", + "toBase64Url": "Mjk1NzJjYzUtN2MzNi00MDE2LTk4MjUtYWJjYzliYTUxZGFl", + "fromBase64Url": [ + 50, + 57, + 53, + 55, + 50, + 99, + 99, + 53, + 45, + 55, + 99, + 51, + 54, + 45, + 52, + 48, + 49, + 54, + 45, + 57, + 56, + 50, + 53, + 45, + 97, + 98, + 99, + 99, + 57, + 98, + 97, + 53, + 49, + 100, + 97, + 101 + ] + }, + { + "origin": "2ce09bdf-69d1-4b08-b93e-3af4ca6a657a", + "toBase64Url": "MmNlMDliZGYtNjlkMS00YjA4LWI5M2UtM2FmNGNhNmE2NTdh", + "fromBase64Url": [ + 50, + 99, + 101, + 48, + 57, + 98, + 100, + 102, + 45, + 54, + 57, + 100, + 49, + 45, + 52, + 98, + 48, + 56, + 45, + 98, + 57, + 51, + 101, + 45, + 51, + 97, + 102, + 52, + 99, + 97, + 54, + 97, + 54, + 53, + 55, + 97 + ] + }, + { + "origin": "15210d8d-308c-4acd-843c-ba6babada62c", + "toBase64Url": "MTUyMTBkOGQtMzA4Yy00YWNkLTg0M2MtYmE2YmFiYWRhNjJj", + "fromBase64Url": [ + 49, + 53, + 50, + 49, + 48, + 100, + 56, + 100, + 45, + 51, + 48, + 56, + 99, + 45, + 52, + 97, + 99, + 100, + 45, + 56, + 52, + 51, + 99, + 45, + 98, + 97, + 54, + 98, + 97, + 98, + 97, + 100, + 97, + 54, + 50, + 99 + ] + }, + { + "origin": "cba26c96-51ad-40c0-b29f-6a46e0d88d43", + "toBase64Url": "Y2JhMjZjOTYtNTFhZC00MGMwLWIyOWYtNmE0NmUwZDg4ZDQz", + "fromBase64Url": [ + 99, + 98, + 97, + 50, + 54, + 99, + 57, + 54, + 45, + 53, + 49, + 97, + 100, + 45, + 52, + 48, + 99, + 48, + 45, + 98, + 50, + 57, + 102, + 45, + 54, + 97, + 52, + 54, + 101, + 48, + 100, + 56, + 56, + 100, + 52, + 51 + ] + }, + { + "origin": "c13cdc12-c7ed-41db-a669-56a664f8127a", + "toBase64Url": "YzEzY2RjMTItYzdlZC00MWRiLWE2NjktNTZhNjY0ZjgxMjdh", + "fromBase64Url": [ + 99, + 49, + 51, + 99, + 100, + 99, + 49, + 50, + 45, + 99, + 55, + 101, + 100, + 45, + 52, + 49, + 100, + 98, + 45, + 97, + 54, + 54, + 57, + 45, + 53, + 54, + 97, + 54, + 54, + 52, + 102, + 56, + 49, + 50, + 55, + 97 + ] + }, + { + "origin": "8507cd12-1a02-4a48-92a5-23fbbe7ad5fe", + "toBase64Url": "ODUwN2NkMTItMWEwMi00YTQ4LTkyYTUtMjNmYmJlN2FkNWZl", + "fromBase64Url": [ + 56, + 53, + 48, + 55, + 99, + 100, + 49, + 50, + 45, + 49, + 97, + 48, + 50, + 45, + 52, + 97, + 52, + 56, + 45, + 57, + 50, + 97, + 53, + 45, + 50, + 51, + 102, + 98, + 98, + 101, + 55, + 97, + 100, + 53, + 102, + 101 + ] + }, + { + "origin": "02b073e9-f33c-4f06-b3aa-d94101a06494", + "toBase64Url": "MDJiMDczZTktZjMzYy00ZjA2LWIzYWEtZDk0MTAxYTA2NDk0", + "fromBase64Url": [ + 48, + 50, + 98, + 48, + 55, + 51, + 101, + 57, + 45, + 102, + 51, + 51, + 99, + 45, + 52, + 102, + 48, + 54, + 45, + 98, + 51, + 97, + 97, + 45, + 100, + 57, + 52, + 49, + 48, + 49, + 97, + 48, + 54, + 52, + 57, + 52 + ] + }, + { + "origin": "064b6627-6dcb-439b-979d-a38c6a4e10f4", + "toBase64Url": "MDY0YjY2MjctNmRjYi00MzliLTk3OWQtYTM4YzZhNGUxMGY0", + "fromBase64Url": [ + 48, + 54, + 52, + 98, + 54, + 54, + 50, + 55, + 45, + 54, + 100, + 99, + 98, + 45, + 52, + 51, + 57, + 98, + 45, + 57, + 55, + 57, + 100, + 45, + 97, + 51, + 56, + 99, + 54, + 97, + 52, + 101, + 49, + 48, + 102, + 52 + ] + }, + { + "origin": "f32a0d44-6d3c-4f68-ba4d-0bc26aed6c7f", + "toBase64Url": "ZjMyYTBkNDQtNmQzYy00ZjY4LWJhNGQtMGJjMjZhZWQ2Yzdm", + "fromBase64Url": [ + 102, + 51, + 50, + 97, + 48, + 100, + 52, + 52, + 45, + 54, + 100, + 51, + 99, + 45, + 52, + 102, + 54, + 56, + 45, + 98, + 97, + 52, + 100, + 45, + 48, + 98, + 99, + 50, + 54, + 97, + 101, + 100, + 54, + 99, + 55, + 102 + ] + }, + { + "origin": "73b68c21-7bab-4608-ad9c-67d2fb954fa2", + "toBase64Url": "NzNiNjhjMjEtN2JhYi00NjA4LWFkOWMtNjdkMmZiOTU0ZmEy", + "fromBase64Url": [ + 55, + 51, + 98, + 54, + 56, + 99, + 50, + 49, + 45, + 55, + 98, + 97, + 98, + 45, + 52, + 54, + 48, + 56, + 45, + 97, + 100, + 57, + 99, + 45, + 54, + 55, + 100, + 50, + 102, + 98, + 57, + 53, + 52, + 102, + 97, + 50 + ] + }, + { + "origin": "d626dd1f-50c5-4438-b920-72b852bb89cd", + "toBase64Url": "ZDYyNmRkMWYtNTBjNS00NDM4LWI5MjAtNzJiODUyYmI4OWNk", + "fromBase64Url": [ + 100, + 54, + 50, + 54, + 100, + 100, + 49, + 102, + 45, + 53, + 48, + 99, + 53, + 45, + 52, + 52, + 51, + 56, + 45, + 98, + 57, + 50, + 48, + 45, + 55, + 50, + 98, + 56, + 53, + 50, + 98, + 98, + 56, + 57, + 99, + 100 + ] + }, + { + "origin": "e598d7e0-282e-4ae9-909b-b18b9710b2b4", + "toBase64Url": "ZTU5OGQ3ZTAtMjgyZS00YWU5LTkwOWItYjE4Yjk3MTBiMmI0", + "fromBase64Url": [ + 101, + 53, + 57, + 56, + 100, + 55, + 101, + 48, + 45, + 50, + 56, + 50, + 101, + 45, + 52, + 97, + 101, + 57, + 45, + 57, + 48, + 57, + 98, + 45, + 98, + 49, + 56, + 98, + 57, + 55, + 49, + 48, + 98, + 50, + 98, + 52 + ] + }, + { + "origin": "84c50a1a-38bb-470b-8347-68d9e323f3d8", + "toBase64Url": "ODRjNTBhMWEtMzhiYi00NzBiLTgzNDctNjhkOWUzMjNmM2Q4", + "fromBase64Url": [ + 56, + 52, + 99, + 53, + 48, + 97, + 49, + 97, + 45, + 51, + 56, + 98, + 98, + 45, + 52, + 55, + 48, + 98, + 45, + 56, + 51, + 52, + 55, + 45, + 54, + 56, + 100, + 57, + 101, + 51, + 50, + 51, + 102, + 51, + 100, + 56 + ] + }, + { + "origin": "53bae0ac-5462-438f-89c4-aa1204146eca", + "toBase64Url": "NTNiYWUwYWMtNTQ2Mi00MzhmLTg5YzQtYWExMjA0MTQ2ZWNh", + "fromBase64Url": [ + 53, + 51, + 98, + 97, + 101, + 48, + 97, + 99, + 45, + 53, + 52, + 54, + 50, + 45, + 52, + 51, + 56, + 102, + 45, + 56, + 57, + 99, + 52, + 45, + 97, + 97, + 49, + 50, + 48, + 52, + 49, + 52, + 54, + 101, + 99, + 97 + ] + }, + { + "origin": "4b487db9-abba-40c5-a0ad-b2250878a332", + "toBase64Url": "NGI0ODdkYjktYWJiYS00MGM1LWEwYWQtYjIyNTA4NzhhMzMy", + "fromBase64Url": [ + 52, + 98, + 52, + 56, + 55, + 100, + 98, + 57, + 45, + 97, + 98, + 98, + 97, + 45, + 52, + 48, + 99, + 53, + 45, + 97, + 48, + 97, + 100, + 45, + 98, + 50, + 50, + 53, + 48, + 56, + 55, + 56, + 97, + 51, + 51, + 50 + ] + }, + { + "origin": "d0941714-eae3-40db-a789-f21aa6401b0d", + "toBase64Url": "ZDA5NDE3MTQtZWFlMy00MGRiLWE3ODktZjIxYWE2NDAxYjBk", + "fromBase64Url": [ + 100, + 48, + 57, + 52, + 49, + 55, + 49, + 52, + 45, + 101, + 97, + 101, + 51, + 45, + 52, + 48, + 100, + 98, + 45, + 97, + 55, + 56, + 57, + 45, + 102, + 50, + 49, + 97, + 97, + 54, + 52, + 48, + 49, + 98, + 48, + 100 + ] + }, + { + "origin": "efb79528-d0bd-433e-be9f-f1020cee08fa", + "toBase64Url": "ZWZiNzk1MjgtZDBiZC00MzNlLWJlOWYtZjEwMjBjZWUwOGZh", + "fromBase64Url": [ + 101, + 102, + 98, + 55, + 57, + 53, + 50, + 56, + 45, + 100, + 48, + 98, + 100, + 45, + 52, + 51, + 51, + 101, + 45, + 98, + 101, + 57, + 102, + 45, + 102, + 49, + 48, + 50, + 48, + 99, + 101, + 101, + 48, + 56, + 102, + 97 + ] + }, + { + "origin": "da2a799b-e1fe-460a-8f3f-0a5f4a124359", + "toBase64Url": "ZGEyYTc5OWItZTFmZS00NjBhLThmM2YtMGE1ZjRhMTI0MzU5", + "fromBase64Url": [ + 100, + 97, + 50, + 97, + 55, + 57, + 57, + 98, + 45, + 101, + 49, + 102, + 101, + 45, + 52, + 54, + 48, + 97, + 45, + 56, + 102, + 51, + 102, + 45, + 48, + 97, + 53, + 102, + 52, + 97, + 49, + 50, + 52, + 51, + 53, + 57 + ] + }, + { + "origin": "822dc1c2-556c-4cee-8c37-411ac530a088", + "toBase64Url": "ODIyZGMxYzItNTU2Yy00Y2VlLThjMzctNDExYWM1MzBhMDg4", + "fromBase64Url": [ + 56, + 50, + 50, + 100, + 99, + 49, + 99, + 50, + 45, + 53, + 53, + 54, + 99, + 45, + 52, + 99, + 101, + 101, + 45, + 56, + 99, + 51, + 55, + 45, + 52, + 49, + 49, + 97, + 99, + 53, + 51, + 48, + 97, + 48, + 56, + 56 + ] + }, + { + "origin": "d035ec77-dd78-4a51-8392-ad28870b0905", + "toBase64Url": "ZDAzNWVjNzctZGQ3OC00YTUxLTgzOTItYWQyODg3MGIwOTA1", + "fromBase64Url": [ + 100, + 48, + 51, + 53, + 101, + 99, + 55, + 55, + 45, + 100, + 100, + 55, + 56, + 45, + 52, + 97, + 53, + 49, + 45, + 56, + 51, + 57, + 50, + 45, + 97, + 100, + 50, + 56, + 56, + 55, + 48, + 98, + 48, + 57, + 48, + 53 + ] + }, + { + "origin": "c934cf59-56a9-45f6-bdbd-7273d2ae86c8", + "toBase64Url": "YzkzNGNmNTktNTZhOS00NWY2LWJkYmQtNzI3M2QyYWU4NmM4", + "fromBase64Url": [ + 99, + 57, + 51, + 52, + 99, + 102, + 53, + 57, + 45, + 53, + 54, + 97, + 57, + 45, + 52, + 53, + 102, + 54, + 45, + 98, + 100, + 98, + 100, + 45, + 55, + 50, + 55, + 51, + 100, + 50, + 97, + 101, + 56, + 54, + 99, + 56 + ] + }, + { + "origin": "b2c572bb-98e1-4d6b-b06c-865a749aacf6", + "toBase64Url": "YjJjNTcyYmItOThlMS00ZDZiLWIwNmMtODY1YTc0OWFhY2Y2", + "fromBase64Url": [ + 98, + 50, + 99, + 53, + 55, + 50, + 98, + 98, + 45, + 57, + 56, + 101, + 49, + 45, + 52, + 100, + 54, + 98, + 45, + 98, + 48, + 54, + 99, + 45, + 56, + 54, + 53, + 97, + 55, + 52, + 57, + 97, + 97, + 99, + 102, + 54 + ] + }, + { + "origin": "5f571217-335c-422e-8c76-0e8b4e1296cf", + "toBase64Url": "NWY1NzEyMTctMzM1Yy00MjJlLThjNzYtMGU4YjRlMTI5NmNm", + "fromBase64Url": [ + 53, + 102, + 53, + 55, + 49, + 50, + 49, + 55, + 45, + 51, + 51, + 53, + 99, + 45, + 52, + 50, + 50, + 101, + 45, + 56, + 99, + 55, + 54, + 45, + 48, + 101, + 56, + 98, + 52, + 101, + 49, + 50, + 57, + 54, + 99, + 102 + ] + } +] \ No newline at end of file diff --git a/clients/liquid-auth-core/tests/__fixtures__/wallet.keys.fixtures.json b/clients/liquid-auth-core/tests/__fixtures__/wallet.keys.fixtures.json new file mode 100644 index 0000000..a3d3614 --- /dev/null +++ b/clients/liquid-auth-core/tests/__fixtures__/wallet.keys.fixtures.json @@ -0,0 +1,2972 @@ +[ + { + "encoded": "65X3KSKFCNX3VUPQDVO3RQUHDZN7BONGBEC6PJWAVKX73DIC356M7M32JM", + "checksum": [ + 207, + 179, + 122, + 75 + ], + "publicKey": [ + 247, + 111, + 181, + 73, + 69, + 19, + 111, + 186, + 209, + 240, + 29, + 93, + 184, + 194, + 135, + 30, + 91, + 240, + 185, + 166, + 9, + 5, + 231, + 166, + 192, + 170, + 175, + 253, + 141, + 2, + 223, + 124 + ], + "privateKey": [ + 236, + 82, + 165, + 230, + 144, + 235, + 251, + 237, + 230, + 241, + 97, + 239, + 230, + 114, + 115, + 205, + 39, + 237, + 91, + 91, + 228, + 34, + 157, + 91, + 55, + 84, + 65, + 60, + 26, + 48, + 169, + 8, + 247, + 111, + 181, + 73, + 69, + 19, + 111, + 186, + 209, + 240, + 29, + 93, + 184, + 194, + 135, + 30, + 91, + 240, + 185, + 166, + 9, + 5, + 231, + 166, + 192, + 170, + 175, + 253, + 141, + 2, + 223, + 124 + ], + "valid": true + }, + { + "encoded": "3DZKAZ2YN5JN5QBGWM75W23O64OTW4MPPQQDDS5AILP3QYMFPKKIEUDTAM", + "checksum": [ + 130, + 80, + 115, + 3 + ], + "publicKey": [ + 216, + 242, + 160, + 103, + 88, + 111, + 82, + 222, + 192, + 38, + 179, + 63, + 219, + 107, + 110, + 247, + 29, + 59, + 113, + 143, + 124, + 32, + 49, + 203, + 160, + 66, + 223, + 184, + 97, + 133, + 122, + 148 + ], + "privateKey": [ + 152, + 114, + 225, + 108, + 240, + 100, + 213, + 212, + 172, + 48, + 32, + 144, + 151, + 231, + 32, + 30, + 173, + 153, + 195, + 20, + 174, + 169, + 232, + 82, + 236, + 159, + 30, + 96, + 252, + 142, + 39, + 13, + 216, + 242, + 160, + 103, + 88, + 111, + 82, + 222, + 192, + 38, + 179, + 63, + 219, + 107, + 110, + 247, + 29, + 59, + 113, + 143, + 124, + 32, + 49, + 203, + 160, + 66, + 223, + 184, + 97, + 133, + 122, + 148 + ], + "valid": true + }, + { + "encoded": "3CA3UDJ7AAWQ7XEBL7LKYBN4IQ4IRJMN3BO2DTAE7LPWMN4UFCFI6ZLXYQ", + "checksum": [ + 143, + 101, + 119, + 196 + ], + "publicKey": [ + 216, + 129, + 186, + 13, + 63, + 0, + 45, + 15, + 220, + 129, + 95, + 214, + 172, + 5, + 188, + 68, + 56, + 136, + 165, + 141, + 216, + 93, + 161, + 204, + 4, + 250, + 223, + 102, + 55, + 148, + 40, + 138 + ], + "privateKey": [ + 60, + 205, + 4, + 66, + 151, + 5, + 167, + 248, + 113, + 97, + 104, + 157, + 84, + 50, + 89, + 30, + 144, + 185, + 44, + 229, + 202, + 201, + 156, + 195, + 39, + 47, + 19, + 233, + 108, + 69, + 129, + 218, + 216, + 129, + 186, + 13, + 63, + 0, + 45, + 15, + 220, + 129, + 95, + 214, + 172, + 5, + 188, + 68, + 56, + 136, + 165, + 141, + 216, + 93, + 161, + 204, + 4, + 250, + 223, + 102, + 55, + 148, + 40, + 138 + ], + "valid": true + }, + { + "encoded": "IFJIXXNSNTAUCDML4QVJ6T3VXSPBRDJCNX2YLZFPRO7I7GTTW5OSXTUSFU", + "checksum": [ + 43, + 206, + 146, + 45 + ], + "publicKey": [ + 65, + 82, + 139, + 221, + 178, + 108, + 193, + 65, + 13, + 139, + 228, + 42, + 159, + 79, + 117, + 188, + 158, + 24, + 141, + 34, + 109, + 245, + 133, + 228, + 175, + 139, + 190, + 143, + 154, + 115, + 183, + 93 + ], + "privateKey": [ + 47, + 32, + 137, + 143, + 141, + 169, + 18, + 201, + 164, + 162, + 151, + 241, + 159, + 238, + 30, + 215, + 92, + 41, + 79, + 3, + 253, + 206, + 123, + 30, + 160, + 207, + 39, + 194, + 23, + 77, + 25, + 87, + 65, + 82, + 139, + 221, + 178, + 108, + 193, + 65, + 13, + 139, + 228, + 42, + 159, + 79, + 117, + 188, + 158, + 24, + 141, + 34, + 109, + 245, + 133, + 228, + 175, + 139, + 190, + 143, + 154, + 115, + 183, + 93 + ], + "valid": true + }, + { + "encoded": "DEVJC5CGWY5HW4MWAR7B2LJ27UGN6FIBDZSZHOXPYFBEOQ4CAKHOIGBAFU", + "checksum": [ + 228, + 24, + 32, + 45 + ], + "publicKey": [ + 25, + 42, + 145, + 116, + 70, + 182, + 58, + 123, + 113, + 150, + 4, + 126, + 29, + 45, + 58, + 253, + 12, + 223, + 21, + 1, + 30, + 101, + 147, + 186, + 239, + 193, + 66, + 71, + 67, + 130, + 2, + 142 + ], + "privateKey": [ + 59, + 135, + 230, + 131, + 153, + 70, + 197, + 206, + 114, + 168, + 168, + 48, + 224, + 98, + 88, + 6, + 152, + 206, + 54, + 215, + 145, + 201, + 19, + 105, + 141, + 2, + 12, + 119, + 104, + 49, + 67, + 173, + 25, + 42, + 145, + 116, + 70, + 182, + 58, + 123, + 113, + 150, + 4, + 126, + 29, + 45, + 58, + 253, + 12, + 223, + 21, + 1, + 30, + 101, + 147, + 186, + 239, + 193, + 66, + 71, + 67, + 130, + 2, + 142 + ], + "valid": true + }, + { + "encoded": "7F55SDXF6MAB4CPZDRJEJOPLAXUVC2K4UKCOFE2XZBUWM3PUZHHDAPBB6Y", + "checksum": [ + 48, + 60, + 33, + 246 + ], + "publicKey": [ + 249, + 123, + 217, + 14, + 229, + 243, + 0, + 30, + 9, + 249, + 28, + 82, + 68, + 185, + 235, + 5, + 233, + 81, + 105, + 92, + 162, + 132, + 226, + 147, + 87, + 200, + 105, + 102, + 109, + 244, + 201, + 206 + ], + "privateKey": [ + 170, + 229, + 17, + 238, + 54, + 123, + 122, + 60, + 139, + 221, + 182, + 156, + 180, + 215, + 174, + 87, + 99, + 138, + 60, + 38, + 232, + 75, + 134, + 182, + 181, + 152, + 248, + 252, + 182, + 132, + 218, + 142, + 249, + 123, + 217, + 14, + 229, + 243, + 0, + 30, + 9, + 249, + 28, + 82, + 68, + 185, + 235, + 5, + 233, + 81, + 105, + 92, + 162, + 132, + 226, + 147, + 87, + 200, + 105, + 102, + 109, + 244, + 201, + 206 + ], + "valid": true + }, + { + "encoded": "YZILV5REF7QEWU4AXWB47IPPV6NZIX3GLI5OW77DY7COHNA2Q6ZOZJEEIA", + "checksum": [ + 236, + 164, + 132, + 64 + ], + "publicKey": [ + 198, + 80, + 186, + 246, + 36, + 47, + 224, + 75, + 83, + 128, + 189, + 131, + 207, + 161, + 239, + 175, + 155, + 148, + 95, + 102, + 90, + 58, + 235, + 127, + 227, + 199, + 196, + 227, + 180, + 26, + 135, + 178 + ], + "privateKey": [ + 200, + 107, + 43, + 245, + 88, + 254, + 51, + 104, + 17, + 85, + 250, + 110, + 170, + 119, + 20, + 131, + 124, + 31, + 32, + 95, + 201, + 69, + 210, + 175, + 142, + 53, + 183, + 156, + 82, + 146, + 148, + 71, + 198, + 80, + 186, + 246, + 36, + 47, + 224, + 75, + 83, + 128, + 189, + 131, + 207, + 161, + 239, + 175, + 155, + 148, + 95, + 102, + 90, + 58, + 235, + 127, + 227, + 199, + 196, + 227, + 180, + 26, + 135, + 178 + ], + "valid": true + }, + { + "encoded": "7KQHLEGZYRASKPLX56Y2HX7IXHFKA7HQ73SNUHOGJJVAR7HG4FGN5KPJOY", + "checksum": [ + 222, + 169, + 233, + 118 + ], + "publicKey": [ + 250, + 160, + 117, + 144, + 217, + 196, + 65, + 37, + 61, + 119, + 239, + 177, + 163, + 223, + 232, + 185, + 202, + 160, + 124, + 240, + 254, + 228, + 218, + 29, + 198, + 74, + 106, + 8, + 252, + 230, + 225, + 76 + ], + "privateKey": [ + 149, + 127, + 178, + 220, + 65, + 150, + 96, + 238, + 72, + 215, + 215, + 107, + 116, + 136, + 188, + 151, + 101, + 82, + 161, + 17, + 189, + 41, + 17, + 232, + 22, + 57, + 183, + 130, + 167, + 68, + 79, + 49, + 250, + 160, + 117, + 144, + 217, + 196, + 65, + 37, + 61, + 119, + 239, + 177, + 163, + 223, + 232, + 185, + 202, + 160, + 124, + 240, + 254, + 228, + 218, + 29, + 198, + 74, + 106, + 8, + 252, + 230, + 225, + 76 + ], + "valid": true + }, + { + "encoded": "LIC5K5IW6K2GE5Q5GA3E6OZA7FQUCI4XR5A3GE3SLOF6U3LXAT2J7RIRKE", + "checksum": [ + 159, + 197, + 17, + 81 + ], + "publicKey": [ + 90, + 5, + 213, + 117, + 22, + 242, + 180, + 98, + 118, + 29, + 48, + 54, + 79, + 59, + 32, + 249, + 97, + 65, + 35, + 151, + 143, + 65, + 179, + 19, + 114, + 91, + 139, + 234, + 109, + 119, + 4, + 244 + ], + "privateKey": [ + 255, + 188, + 242, + 10, + 174, + 222, + 130, + 43, + 72, + 183, + 35, + 151, + 13, + 4, + 71, + 139, + 206, + 175, + 235, + 38, + 37, + 247, + 101, + 193, + 96, + 125, + 1, + 207, + 69, + 48, + 47, + 158, + 90, + 5, + 213, + 117, + 22, + 242, + 180, + 98, + 118, + 29, + 48, + 54, + 79, + 59, + 32, + 249, + 97, + 65, + 35, + 151, + 143, + 65, + 179, + 19, + 114, + 91, + 139, + 234, + 109, + 119, + 4, + 244 + ], + "valid": true + }, + { + "encoded": "ABRYPE4VTV7Y25NJPYA3ENHUCIZHD4AD3L57QFM6GMUFSPPH5NZNLINR2U", + "checksum": [ + 213, + 161, + 177, + 213 + ], + "publicKey": [ + 0, + 99, + 135, + 147, + 149, + 157, + 127, + 141, + 117, + 169, + 126, + 1, + 178, + 52, + 244, + 18, + 50, + 113, + 240, + 3, + 218, + 251, + 248, + 21, + 158, + 51, + 40, + 89, + 61, + 231, + 235, + 114 + ], + "privateKey": [ + 26, + 185, + 149, + 114, + 143, + 48, + 38, + 79, + 77, + 45, + 9, + 217, + 48, + 239, + 132, + 182, + 220, + 33, + 72, + 101, + 28, + 194, + 32, + 9, + 211, + 57, + 219, + 51, + 225, + 157, + 235, + 68, + 0, + 99, + 135, + 147, + 149, + 157, + 127, + 141, + 117, + 169, + 126, + 1, + 178, + 52, + 244, + 18, + 50, + 113, + 240, + 3, + 218, + 251, + 248, + 21, + 158, + 51, + 40, + 89, + 61, + 231, + 235, + 114 + ], + "valid": true + }, + { + "encoded": "TLRWCVZN22GRXQQEDWCMP64UD63DDKX5RU2KLG2SROOSHMRCW2TYBX6IPA", + "checksum": [ + 128, + 223, + 200, + 120 + ], + "publicKey": [ + 154, + 227, + 97, + 87, + 45, + 214, + 141, + 27, + 194, + 4, + 29, + 132, + 199, + 251, + 148, + 31, + 182, + 49, + 170, + 253, + 141, + 52, + 165, + 155, + 82, + 139, + 157, + 35, + 178, + 34, + 182, + 167 + ], + "privateKey": [ + 142, + 247, + 46, + 26, + 245, + 91, + 2, + 250, + 59, + 32, + 77, + 184, + 143, + 20, + 252, + 20, + 230, + 81, + 31, + 99, + 78, + 174, + 62, + 132, + 203, + 79, + 112, + 39, + 10, + 243, + 45, + 58, + 154, + 227, + 97, + 87, + 45, + 214, + 141, + 27, + 194, + 4, + 29, + 132, + 199, + 251, + 148, + 31, + 182, + 49, + 170, + 253, + 141, + 52, + 165, + 155, + 82, + 139, + 157, + 35, + 178, + 34, + 182, + 167 + ], + "valid": true + }, + { + "encoded": "RPM3AIUUYIZHZ53MTWQV57SIEMCWLLAWBZU5V2PGGCAD4LNAGYAKPTLK7M", + "checksum": [ + 167, + 205, + 106, + 251 + ], + "publicKey": [ + 139, + 217, + 176, + 34, + 148, + 194, + 50, + 124, + 247, + 108, + 157, + 161, + 94, + 254, + 72, + 35, + 5, + 101, + 172, + 22, + 14, + 105, + 218, + 233, + 230, + 48, + 128, + 62, + 45, + 160, + 54, + 0 + ], + "privateKey": [ + 83, + 91, + 204, + 156, + 166, + 183, + 152, + 183, + 89, + 116, + 168, + 194, + 158, + 207, + 200, + 225, + 5, + 217, + 96, + 232, + 204, + 119, + 171, + 68, + 149, + 88, + 0, + 69, + 226, + 124, + 156, + 40, + 139, + 217, + 176, + 34, + 148, + 194, + 50, + 124, + 247, + 108, + 157, + 161, + 94, + 254, + 72, + 35, + 5, + 101, + 172, + 22, + 14, + 105, + 218, + 233, + 230, + 48, + 128, + 62, + 45, + 160, + 54, + 0 + ], + "valid": true + }, + { + "encoded": "Y5BMK2M4RCTM4P4NI4KITKG6RDVIAFTHAWZOFQRZ3DY6LQANC47G67AQDA", + "checksum": [ + 111, + 124, + 16, + 24 + ], + "publicKey": [ + 199, + 66, + 197, + 105, + 156, + 136, + 166, + 206, + 63, + 141, + 71, + 20, + 137, + 168, + 222, + 136, + 234, + 128, + 22, + 103, + 5, + 178, + 226, + 194, + 57, + 216, + 241, + 229, + 192, + 13, + 23, + 62 + ], + "privateKey": [ + 231, + 105, + 81, + 0, + 19, + 201, + 132, + 39, + 165, + 197, + 52, + 54, + 182, + 11, + 163, + 210, + 58, + 56, + 146, + 198, + 100, + 44, + 94, + 5, + 152, + 250, + 79, + 193, + 233, + 34, + 104, + 157, + 199, + 66, + 197, + 105, + 156, + 136, + 166, + 206, + 63, + 141, + 71, + 20, + 137, + 168, + 222, + 136, + 234, + 128, + 22, + 103, + 5, + 178, + 226, + 194, + 57, + 216, + 241, + 229, + 192, + 13, + 23, + 62 + ], + "valid": true + }, + { + "encoded": "UM4F2PWJDTUT6JGMC6EHTEQH3ZYDSWIITY7LJX5WKWSMGLS3BOEREE64AI", + "checksum": [ + 18, + 19, + 220, + 2 + ], + "publicKey": [ + 163, + 56, + 93, + 62, + 201, + 28, + 233, + 63, + 36, + 204, + 23, + 136, + 121, + 146, + 7, + 222, + 112, + 57, + 89, + 8, + 158, + 62, + 180, + 223, + 182, + 85, + 164, + 195, + 46, + 91, + 11, + 137 + ], + "privateKey": [ + 248, + 82, + 21, + 209, + 157, + 247, + 81, + 107, + 89, + 168, + 80, + 67, + 122, + 44, + 171, + 169, + 41, + 181, + 9, + 192, + 166, + 134, + 137, + 128, + 149, + 201, + 214, + 31, + 117, + 193, + 190, + 80, + 163, + 56, + 93, + 62, + 201, + 28, + 233, + 63, + 36, + 204, + 23, + 136, + 121, + 146, + 7, + 222, + 112, + 57, + 89, + 8, + 158, + 62, + 180, + 223, + 182, + 85, + 164, + 195, + 46, + 91, + 11, + 137 + ], + "valid": true + }, + { + "encoded": "Z4EZILHIAVL22WSXYQ4YNOXDVZAYJCXOROQ3RV2ZPFORWXITDIGJYPT7AE", + "checksum": [ + 156, + 62, + 127, + 1 + ], + "publicKey": [ + 207, + 9, + 148, + 44, + 232, + 5, + 87, + 173, + 90, + 87, + 196, + 57, + 134, + 186, + 227, + 174, + 65, + 132, + 138, + 238, + 139, + 161, + 184, + 215, + 89, + 121, + 93, + 27, + 93, + 19, + 26, + 12 + ], + "privateKey": [ + 78, + 155, + 21, + 250, + 204, + 122, + 133, + 236, + 164, + 137, + 234, + 124, + 161, + 0, + 99, + 3, + 89, + 135, + 152, + 128, + 153, + 85, + 240, + 227, + 187, + 140, + 181, + 128, + 145, + 122, + 22, + 76, + 207, + 9, + 148, + 44, + 232, + 5, + 87, + 173, + 90, + 87, + 196, + 57, + 134, + 186, + 227, + 174, + 65, + 132, + 138, + 238, + 139, + 161, + 184, + 215, + 89, + 121, + 93, + 27, + 93, + 19, + 26, + 12 + ], + "valid": true + }, + { + "encoded": "W67VZ3BUOIHYKQTRQMRST3D3DH3GZQ43SLDPUN2UUQRQG7QV24BXZORLZ4", + "checksum": [ + 124, + 186, + 43, + 207 + ], + "publicKey": [ + 183, + 191, + 92, + 236, + 52, + 114, + 15, + 133, + 66, + 113, + 131, + 35, + 41, + 236, + 123, + 25, + 246, + 108, + 195, + 155, + 146, + 198, + 250, + 55, + 84, + 164, + 35, + 3, + 126, + 21, + 215, + 3 + ], + "privateKey": [ + 197, + 54, + 178, + 164, + 214, + 232, + 186, + 145, + 212, + 210, + 240, + 179, + 30, + 138, + 155, + 165, + 11, + 180, + 13, + 241, + 10, + 181, + 210, + 72, + 125, + 124, + 199, + 142, + 49, + 219, + 155, + 163, + 183, + 191, + 92, + 236, + 52, + 114, + 15, + 133, + 66, + 113, + 131, + 35, + 41, + 236, + 123, + 25, + 246, + 108, + 195, + 155, + 146, + 198, + 250, + 55, + 84, + 164, + 35, + 3, + 126, + 21, + 215, + 3 + ], + "valid": true + }, + { + "encoded": "5QZA22GPL44PLHHE5VQLMI7N6GTHCJIUARS4VMLF5UTV5WCWAH5N2C7IYQ", + "checksum": [ + 221, + 11, + 232, + 196 + ], + "publicKey": [ + 236, + 50, + 13, + 104, + 207, + 95, + 56, + 245, + 156, + 228, + 237, + 96, + 182, + 35, + 237, + 241, + 166, + 113, + 37, + 20, + 4, + 101, + 202, + 177, + 101, + 237, + 39, + 94, + 216, + 86, + 1, + 250 + ], + "privateKey": [ + 215, + 168, + 230, + 6, + 122, + 132, + 10, + 131, + 105, + 210, + 33, + 72, + 199, + 63, + 234, + 0, + 154, + 126, + 34, + 243, + 70, + 241, + 217, + 197, + 237, + 62, + 218, + 56, + 111, + 209, + 77, + 249, + 236, + 50, + 13, + 104, + 207, + 95, + 56, + 245, + 156, + 228, + 237, + 96, + 182, + 35, + 237, + 241, + 166, + 113, + 37, + 20, + 4, + 101, + 202, + 177, + 101, + 237, + 39, + 94, + 216, + 86, + 1, + 250 + ], + "valid": true + }, + { + "encoded": "ZA44DPMGYEPONVXWHUTBQ66SKCB5PX7BPWHIMFKAOEV5MRBJKL7ADHXGQY", + "checksum": [ + 1, + 158, + 230, + 134 + ], + "publicKey": [ + 200, + 57, + 193, + 189, + 134, + 193, + 30, + 230, + 214, + 246, + 61, + 38, + 24, + 123, + 210, + 80, + 131, + 215, + 223, + 225, + 125, + 142, + 134, + 21, + 64, + 113, + 43, + 214, + 68, + 41, + 82, + 254 + ], + "privateKey": [ + 243, + 190, + 156, + 61, + 249, + 117, + 242, + 42, + 26, + 52, + 88, + 164, + 80, + 66, + 196, + 99, + 223, + 96, + 56, + 108, + 69, + 43, + 210, + 52, + 235, + 177, + 145, + 183, + 130, + 32, + 133, + 104, + 200, + 57, + 193, + 189, + 134, + 193, + 30, + 230, + 214, + 246, + 61, + 38, + 24, + 123, + 210, + 80, + 131, + 215, + 223, + 225, + 125, + 142, + 134, + 21, + 64, + 113, + 43, + 214, + 68, + 41, + 82, + 254 + ], + "valid": true + }, + { + "encoded": "MQ3AVNCM7P7BD7NHDD3HFEMJKTAZUTHI7FVLZTV2UJBW4TDBYCANNXF2PU", + "checksum": [ + 214, + 220, + 186, + 125 + ], + "publicKey": [ + 100, + 54, + 10, + 180, + 76, + 251, + 254, + 17, + 253, + 167, + 24, + 246, + 114, + 145, + 137, + 84, + 193, + 154, + 76, + 232, + 249, + 106, + 188, + 206, + 186, + 162, + 67, + 110, + 76, + 97, + 192, + 128 + ], + "privateKey": [ + 70, + 121, + 167, + 29, + 7, + 192, + 110, + 199, + 56, + 217, + 37, + 76, + 62, + 142, + 87, + 172, + 204, + 86, + 250, + 151, + 149, + 166, + 125, + 44, + 67, + 86, + 190, + 106, + 52, + 67, + 74, + 76, + 100, + 54, + 10, + 180, + 76, + 251, + 254, + 17, + 253, + 167, + 24, + 246, + 114, + 145, + 137, + 84, + 193, + 154, + 76, + 232, + 249, + 106, + 188, + 206, + 186, + 162, + 67, + 110, + 76, + 97, + 192, + 128 + ], + "valid": true + }, + { + "encoded": "RHY76D66VAWH7CAS3TCDN4TVUKRFT6SRIBAHSIQLX65BT6ZUXHEOXFM4IU", + "checksum": [ + 235, + 149, + 156, + 69 + ], + "publicKey": [ + 137, + 241, + 255, + 15, + 222, + 168, + 44, + 127, + 136, + 18, + 220, + 196, + 54, + 242, + 117, + 162, + 162, + 89, + 250, + 81, + 64, + 64, + 121, + 34, + 11, + 191, + 186, + 25, + 251, + 52, + 185, + 200 + ], + "privateKey": [ + 121, + 22, + 228, + 11, + 197, + 95, + 122, + 61, + 187, + 147, + 228, + 14, + 156, + 6, + 128, + 96, + 111, + 111, + 242, + 234, + 197, + 159, + 147, + 121, + 109, + 154, + 155, + 40, + 78, + 35, + 74, + 226, + 137, + 241, + 255, + 15, + 222, + 168, + 44, + 127, + 136, + 18, + 220, + 196, + 54, + 242, + 117, + 162, + 162, + 89, + 250, + 81, + 64, + 64, + 121, + 34, + 11, + 191, + 186, + 25, + 251, + 52, + 185, + 200 + ], + "valid": true + }, + { + "encoded": "KDHPRHISHU25PJO5OG36NS5MMPPRMLITDWRHZKSKYVMH4YUCAGRYTHDYCY", + "checksum": [ + 137, + 156, + 120, + 22 + ], + "publicKey": [ + 80, + 206, + 248, + 157, + 18, + 61, + 53, + 215, + 165, + 221, + 113, + 183, + 230, + 203, + 172, + 99, + 223, + 22, + 45, + 19, + 29, + 162, + 124, + 170, + 74, + 197, + 88, + 126, + 98, + 130, + 1, + 163 + ], + "privateKey": [ + 194, + 133, + 62, + 0, + 75, + 220, + 24, + 137, + 21, + 68, + 233, + 242, + 236, + 145, + 44, + 49, + 218, + 158, + 249, + 177, + 236, + 11, + 85, + 94, + 224, + 98, + 236, + 28, + 172, + 204, + 26, + 140, + 80, + 206, + 248, + 157, + 18, + 61, + 53, + 215, + 165, + 221, + 113, + 183, + 230, + 203, + 172, + 99, + 223, + 22, + 45, + 19, + 29, + 162, + 124, + 170, + 74, + 197, + 88, + 126, + 98, + 130, + 1, + 163 + ], + "valid": true + }, + { + "encoded": "RXVJINFGGMBCNU7NOFKTAOFIFJ5R3A4EWHKAWCW6KNXTRDQCFCRGV3BF4M", + "checksum": [ + 106, + 236, + 37, + 227 + ], + "publicKey": [ + 141, + 234, + 148, + 52, + 166, + 51, + 2, + 38, + 211, + 237, + 113, + 85, + 48, + 56, + 168, + 42, + 123, + 29, + 131, + 132, + 177, + 212, + 11, + 10, + 222, + 83, + 111, + 56, + 142, + 2, + 40, + 162 + ], + "privateKey": [ + 174, + 110, + 127, + 107, + 13, + 238, + 168, + 163, + 107, + 207, + 64, + 140, + 144, + 65, + 3, + 179, + 50, + 248, + 133, + 145, + 228, + 201, + 13, + 149, + 94, + 80, + 9, + 24, + 89, + 183, + 48, + 177, + 141, + 234, + 148, + 52, + 166, + 51, + 2, + 38, + 211, + 237, + 113, + 85, + 48, + 56, + 168, + 42, + 123, + 29, + 131, + 132, + 177, + 212, + 11, + 10, + 222, + 83, + 111, + 56, + 142, + 2, + 40, + 162 + ], + "valid": true + }, + { + "encoded": "GSBE3TORJTLULQ5TLJWYUN4TGL2ZPDWDIJPSNZJD5UZRCUBWAKDF53OUC4", + "checksum": [ + 94, + 237, + 212, + 23 + ], + "publicKey": [ + 52, + 130, + 77, + 205, + 209, + 76, + 215, + 69, + 195, + 179, + 90, + 109, + 138, + 55, + 147, + 50, + 245, + 151, + 142, + 195, + 66, + 95, + 38, + 229, + 35, + 237, + 51, + 17, + 80, + 54, + 2, + 134 + ], + "privateKey": [ + 252, + 63, + 28, + 247, + 87, + 146, + 55, + 131, + 127, + 151, + 3, + 238, + 74, + 46, + 69, + 50, + 102, + 89, + 155, + 217, + 159, + 41, + 186, + 73, + 137, + 198, + 147, + 32, + 37, + 208, + 185, + 152, + 52, + 130, + 77, + 205, + 209, + 76, + 215, + 69, + 195, + 179, + 90, + 109, + 138, + 55, + 147, + 50, + 245, + 151, + 142, + 195, + 66, + 95, + 38, + 229, + 35, + 237, + 51, + 17, + 80, + 54, + 2, + 134 + ], + "valid": true + }, + { + "encoded": "D2L64V5Z66TLNWU5ZM54A336MTMS5AZKR5OPSMBLR4XD7VBYKETNCSAY64", + "checksum": [ + 209, + 72, + 24, + 247 + ], + "publicKey": [ + 30, + 151, + 238, + 87, + 185, + 247, + 166, + 182, + 218, + 157, + 203, + 59, + 192, + 111, + 126, + 100, + 217, + 46, + 131, + 42, + 143, + 92, + 249, + 48, + 43, + 143, + 46, + 63, + 212, + 56, + 81, + 38 + ], + "privateKey": [ + 34, + 81, + 159, + 199, + 84, + 137, + 16, + 242, + 193, + 192, + 215, + 222, + 202, + 69, + 41, + 115, + 205, + 0, + 162, + 254, + 53, + 75, + 16, + 141, + 76, + 23, + 120, + 227, + 103, + 13, + 222, + 254, + 30, + 151, + 238, + 87, + 185, + 247, + 166, + 182, + 218, + 157, + 203, + 59, + 192, + 111, + 126, + 100, + 217, + 46, + 131, + 42, + 143, + 92, + 249, + 48, + 43, + 143, + 46, + 63, + 212, + 56, + 81, + 38 + ], + "valid": true + }, + { + "encoded": "4YC7UGVCQTXTN6O4JU6NRR6II47JSMMBQFB5KQLAQOJQPPWMZBWPEYMEGU", + "checksum": [ + 242, + 97, + 132, + 53 + ], + "publicKey": [ + 230, + 5, + 250, + 26, + 162, + 132, + 239, + 54, + 249, + 220, + 77, + 60, + 216, + 199, + 200, + 71, + 62, + 153, + 49, + 129, + 129, + 67, + 213, + 65, + 96, + 131, + 147, + 7, + 190, + 204, + 200, + 108 + ], + "privateKey": [ + 235, + 119, + 55, + 43, + 251, + 155, + 99, + 84, + 236, + 172, + 40, + 207, + 137, + 142, + 14, + 1, + 219, + 100, + 11, + 11, + 173, + 130, + 192, + 235, + 137, + 60, + 202, + 202, + 98, + 211, + 15, + 223, + 230, + 5, + 250, + 26, + 162, + 132, + 239, + 54, + 249, + 220, + 77, + 60, + 216, + 199, + 200, + 71, + 62, + 153, + 49, + 129, + 129, + 67, + 213, + 65, + 96, + 131, + 147, + 7, + 190, + 204, + 200, + 108 + ], + "valid": true + }, + { + "encoded": "4ZRBSTR35CQI4HYQPF3TNTRD4UMC2JBU5HOOMXYKQT6XK73VZ7T4D727QI", + "checksum": [ + 193, + 255, + 95, + 130 + ], + "publicKey": [ + 230, + 98, + 25, + 78, + 59, + 232, + 160, + 142, + 31, + 16, + 121, + 119, + 54, + 206, + 35, + 229, + 24, + 45, + 36, + 52, + 233, + 220, + 230, + 95, + 10, + 132, + 253, + 117, + 127, + 117, + 207, + 231 + ], + "privateKey": [ + 99, + 201, + 58, + 96, + 244, + 72, + 243, + 128, + 178, + 220, + 187, + 212, + 171, + 203, + 31, + 200, + 165, + 250, + 188, + 110, + 159, + 111, + 54, + 31, + 78, + 111, + 233, + 41, + 210, + 215, + 230, + 237, + 230, + 98, + 25, + 78, + 59, + 232, + 160, + 142, + 31, + 16, + 121, + 119, + 54, + 206, + 35, + 229, + 24, + 45, + 36, + 52, + 233, + 220, + 230, + 95, + 10, + 132, + 253, + 117, + 127, + 117, + 207, + 231 + ], + "valid": true + }, + { + "encoded": "CDJO6P5IBFG2QXOPK7MAWNC5FDITMKYSE2T7RC5WERIBT2JRLINNGJBIBE", + "checksum": [ + 211, + 36, + 40, + 9 + ], + "publicKey": [ + 16, + 210, + 239, + 63, + 168, + 9, + 77, + 168, + 93, + 207, + 87, + 216, + 11, + 52, + 93, + 40, + 209, + 54, + 43, + 18, + 38, + 167, + 248, + 139, + 182, + 36, + 80, + 25, + 233, + 49, + 90, + 26 + ], + "privateKey": [ + 34, + 196, + 2, + 58, + 182, + 159, + 66, + 30, + 196, + 172, + 242, + 106, + 199, + 35, + 215, + 3, + 234, + 190, + 151, + 60, + 238, + 168, + 33, + 126, + 80, + 71, + 85, + 240, + 172, + 240, + 175, + 238, + 16, + 210, + 239, + 63, + 168, + 9, + 77, + 168, + 93, + 207, + 87, + 216, + 11, + 52, + 93, + 40, + 209, + 54, + 43, + 18, + 38, + 167, + 248, + 139, + 182, + 36, + 80, + 25, + 233, + 49, + 90, + 26 + ], + "valid": true + } +] \ No newline at end of file diff --git a/clients/liquid-auth-core/tests/encoding.spec.js b/clients/liquid-auth-core/tests/encoding.spec.js new file mode 100644 index 0000000..7115f46 --- /dev/null +++ b/clients/liquid-auth-core/tests/encoding.spec.js @@ -0,0 +1,46 @@ +import test from 'node:test'; +import assert from 'node:assert'; + +import * as encoding from '../lib/encoding.js'; +import base64UrlFixtures from './__fixtures__/encoding.base64url.fixtures.json' assert {type: 'json'}; +import walletKeysFixtures from './__fixtures__/wallet.keys.fixtures.json' assert {type: 'json'}; + + +// Invalid Inputs +test(`fromBase64URL(*) throws ${encoding.INVALID_BASE64URL_INPUT}`, function(){ + assert.throws(()=>encoding.fromBase64Url(12345), new Error(encoding.INVALID_BASE64URL_INPUT)); +}) +base64UrlFixtures.forEach((fixture, i)=>{ + const encoder = new TextEncoder(); + + test(`toBase64URL(${fixture.origin})`, () => { + assert.equal(fixture.toBase64Url, encoding.toBase64URL(i % 2 ? encoder.encode(fixture.origin) : fixture.fromBase64Url)); + }) + test(`fromBase64URL(${fixture.origin})`, () => { + assert.deepEqual(new Uint8Array(fixture.fromBase64Url), encoding.fromBase64Url(fixture.toBase64Url)); + }); +}) + + + + +// Test Basic Inputs +test(`decodeAddress(*) throws ${encoding.MALFORMED_ADDRESS_ERROR_MSG}`, function(){ + assert.throws(()=>encoding.decodeAddress(12345), new Error(encoding.MALFORMED_ADDRESS_ERROR_MSG)); +}) +// Algorand Address Tests +walletKeysFixtures.forEach(function (fixture){ + test(`decodeAddress(${fixture.encoded})`, function(){ + const decoded = encoding.decodeAddress(fixture.encoded); + assert.deepEqual(new Uint8Array(fixture.publicKey), decoded); + }) + test(`encodeAddress(${fixture.encoded})`, function() { + const address = encoding.encodeAddress(new Uint8Array(fixture.publicKey)); + assert.equal(fixture.encoded, address); + }) + + test(`decodeAddress(${fixture.encoded.slice(0, -4) + "===="}) throws ${encoding.ALGORAND_ADDRESS_BAD_CHECKSUM_ERROR_MSG}`, function(){ + assert.throws(()=>encoding.decodeAddress(fixture.encoded.slice(0, -4) + "===="), new Error(encoding.ALGORAND_ADDRESS_BAD_CHECKSUM_ERROR_MSG)) + }) + +}) diff --git a/clients/liquid-auth-core/tests/generate.fixtures.js b/clients/liquid-auth-core/tests/generate.fixtures.js new file mode 100644 index 0000000..cc97449 --- /dev/null +++ b/clients/liquid-auth-core/tests/generate.fixtures.js @@ -0,0 +1,61 @@ +import {toBase64URL, } from "../lib/index.js"; +import * as fs from "node:fs"; +import algosdk from 'algosdk' + +const data = [ + "9b1a1b53-0456-4fcd-b10a-4db40352d58a", //OWIxYTFiNTMtMDQ1Ni00ZmNkLWIxMGEtNGRiNDAzNTJkNThh - Validated + "7646809", // Catch 7 Bits + "b1338059", // Catch 8 Bits + "582bd065-f280-4574-b315-3398c75296c5", + "29572cc5-7c36-4016-9825-abcc9ba51dae", + "2ce09bdf-69d1-4b08-b93e-3af4ca6a657a", + "15210d8d-308c-4acd-843c-ba6babada62c", + "cba26c96-51ad-40c0-b29f-6a46e0d88d43", + "c13cdc12-c7ed-41db-a669-56a664f8127a", + "8507cd12-1a02-4a48-92a5-23fbbe7ad5fe", + "02b073e9-f33c-4f06-b3aa-d94101a06494", + "064b6627-6dcb-439b-979d-a38c6a4e10f4", + "f32a0d44-6d3c-4f68-ba4d-0bc26aed6c7f", + "73b68c21-7bab-4608-ad9c-67d2fb954fa2", + "d626dd1f-50c5-4438-b920-72b852bb89cd", + "e598d7e0-282e-4ae9-909b-b18b9710b2b4", + "84c50a1a-38bb-470b-8347-68d9e323f3d8", + "53bae0ac-5462-438f-89c4-aa1204146eca", + "4b487db9-abba-40c5-a0ad-b2250878a332", + "d0941714-eae3-40db-a789-f21aa6401b0d", + "efb79528-d0bd-433e-be9f-f1020cee08fa", + "da2a799b-e1fe-460a-8f3f-0a5f4a124359", + "822dc1c2-556c-4cee-8c37-411ac530a088", + "d035ec77-dd78-4a51-8392-ad28870b0905", + "c934cf59-56a9-45f6-bdbd-7273d2ae86c8", + "b2c572bb-98e1-4d6b-b06c-865a749aacf6", + "5f571217-335c-422e-8c76-0e8b4e1296cf" +] + +const walletKeys = data.map(()=>{ + const address = algosdk.generateAccount() + return { + encoded: address.addr, + checksum: new Array(...algosdk.decodeAddress(address.addr).checksum), + publicKey: new Array(...algosdk.decodeAddress(address.addr).publicKey), + privateKey: new Array(...address.sk), + valid: true + } +}) + + +fs.writeFileSync('./tests/__fixtures__/wallet.keys.fixtures.json', JSON.stringify(walletKeys, null, 2)) + + +const encodingBase64URL = data.map((d)=>{ + const encoder = new TextEncoder() + return { + origin: d, + toBase64Url: toBase64URL(encoder.encode(d)), + fromBase64Url: new Array(...encoder.encode(d)), + } +}) + +fs.writeFileSync('./tests/__fixtures__/encoding.base64url.fixtures.json', JSON.stringify(encodingBase64URL, null, 2)) + +console.log(Buffer.from('hello', 'base32')) diff --git a/package.json b/package.json index 82baed9..79fb660 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,10 @@ "dev:core": "npm run dev --if-present --workspace @liquid/core", "dev:sdk": "npm run dev --if-present --workspace @liquid/auth-client", "dev:ui": "npm run dev --if-present --workspace dapp-ui", - "build": "npm run build --workspace @liquid/auth-client && npm run build --workspace @liquid/auth-api && npm run build --workspace dapp-ui", + "build": "npm run build --workspace @liquid/core && npm run build --workspace @liquid/auth-client && npm run build --workspace @liquid/auth-api && npm run build --workspace dapp-ui", "lint": "npm run lint --if-present --ws", + "test": "npm run test --if-present --ws", + "test:cov": "npm run test:cov --if-present --ws", "start": "npm run start:prod --workspace @liquid/auth-api" }, "workspaces": [ diff --git a/services/liquid-auth-api-js/.eslintrc.json b/services/liquid-auth-api-js/.eslintrc.json index 8277ad8..8cd93a2 100644 --- a/services/liquid-auth-api-js/.eslintrc.json +++ b/services/liquid-auth-api-js/.eslintrc.json @@ -21,6 +21,7 @@ "dist/**" ], "rules": { + "@typescript-eslint/ban-ts-comment": "warn", "@typescript-eslint/interface-name-prefix": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-module-boundary-types": "off", diff --git a/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts b/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts index 21e84f9..27a1307 100644 --- a/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts +++ b/services/liquid-auth-api-js/src/adapters/redis-io.adapter.ts @@ -1,5 +1,5 @@ import { IoAdapter } from '@nestjs/platform-socket.io'; -import { ServerOptions, Socket } from 'socket.io'; +import { ServerOptions } from 'socket.io'; import { createAdapter } from '@socket.io/redis-adapter'; import { Redis } from 'ioredis'; import { NestExpressApplication } from '@nestjs/platform-express'; diff --git a/services/liquid-auth-api-js/src/algod/algod.service.ts b/services/liquid-auth-api-js/src/algod/algod.service.ts index 34922e7..5678325 100644 --- a/services/liquid-auth-api-js/src/algod/algod.service.ts +++ b/services/liquid-auth-api-js/src/algod/algod.service.ts @@ -11,4 +11,3 @@ export class AlgodService extends algosdk.Algodv2 { super(token, server, port); } } - diff --git a/services/liquid-auth-api-js/src/app.controller.ts b/services/liquid-auth-api-js/src/app.controller.ts index 22d9f55..866b9cd 100644 --- a/services/liquid-auth-api-js/src/app.controller.ts +++ b/services/liquid-auth-api-js/src/app.controller.ts @@ -1,10 +1,6 @@ import { Controller, Get, Logger, Req, Res, Session } from '@nestjs/common'; import type { Response } from 'express'; -// ignore due to jest -// @ts-ignore -import assetLinks from '../assetlinks.json' with { type: 'json' }; - @Controller() export class AppController { private readonly logger = new Logger(AppController.name); diff --git a/services/liquid-auth-api-js/src/app.module.ts b/services/liquid-auth-api-js/src/app.module.ts index 3f498d1..d21f66c 100644 --- a/services/liquid-auth-api-js/src/app.module.ts +++ b/services/liquid-auth-api-js/src/app.module.ts @@ -16,7 +16,7 @@ import { AuthModule } from './auth/auth.module.js'; // Connect/Signals import { ConnectModule } from './connect/connect.module.js'; import { SignalsModule } from './signals/signals.module.js'; -import { AppController } from "./app.controller.js"; +import { AppController } from './app.controller.js'; @Module({ imports: [ diff --git a/services/liquid-auth-api-js/src/app.service.ts b/services/liquid-auth-api-js/src/app.service.ts index 9149c85..dc1526f 100644 --- a/services/liquid-auth-api-js/src/app.service.ts +++ b/services/liquid-auth-api-js/src/app.service.ts @@ -1,7 +1,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import UAParser from 'ua-parser-js'; -import { toBase64URL } from "@liquid/core"; +import { toBase64URL } from '@liquid/core'; //@ts-ignore, required for jest import assetLinks from '../assetlinks.json' assert { type: 'json' }; @@ -18,13 +18,14 @@ export class AppService { parser.getOS().name.includes('Android') && typeof parser.getBrowser().name !== 'string' ) { - const pkgName = ua.split('/')[0] - console.log(pkgName) + const pkgName = ua.split('/')[0]; + console.log(pkgName); const statement = assetLinks.filter( (al) => al?.target?.package_name === pkgName, ); // TODO: better lookup for fingerprints using Headers - const octArray: number[] = statement[0].target.sha256_cert_fingerprints[0].split(':') + const octArray: number[] = statement[0].target.sha256_cert_fingerprints[0] + .split(':') .map((h) => parseInt(h, 16)); const androidHash = toBase64URL(new Uint8Array(octArray)); origin = `android:apk-key-hash:${androidHash}`; diff --git a/services/liquid-auth-api-js/src/assertion/assertion.controller.ts b/services/liquid-auth-api-js/src/assertion/assertion.controller.ts index ab24433..584c81a 100644 --- a/services/liquid-auth-api-js/src/assertion/assertion.controller.ts +++ b/services/liquid-auth-api-js/src/assertion/assertion.controller.ts @@ -1,7 +1,8 @@ import { Body, Controller, - Get, Inject, + Get, + Inject, Logger, Post, Req, @@ -148,7 +149,7 @@ export class AssertionController { await this.authService.update(user); const credential = await this.authService.findCredential(body.id); - console.log(credential) + console.log(credential); delete session.challenge; session.wallet = user.wallet; diff --git a/services/liquid-auth-api-js/src/attestation/attestation.service.ts b/services/liquid-auth-api-js/src/attestation/attestation.service.ts index 328daa5..8191bc5 100644 --- a/services/liquid-auth-api-js/src/attestation/attestation.service.ts +++ b/services/liquid-auth-api-js/src/attestation/attestation.service.ts @@ -63,7 +63,7 @@ export class AttestationService { credential: AttestationCredentialJSON & { device?: string }, ) { const expectedOrigin = this.appService.getOrigin(ua); - console.log(expectedOrigin) + console.log(expectedOrigin); const expectedRPID = this.configService.get('hostname'); const verifiedAttestation = await fido2.verifyAttestationResponse({ credential, diff --git a/services/liquid-auth-api-js/src/auth/auth.controller.ts b/services/liquid-auth-api-js/src/auth/auth.controller.ts index bd82d26..96ac704 100644 --- a/services/liquid-auth-api-js/src/auth/auth.controller.ts +++ b/services/liquid-auth-api-js/src/auth/auth.controller.ts @@ -1,9 +1,7 @@ import { - Body, Controller, Delete, Get, - Post, Req, Res, Session, @@ -13,9 +11,6 @@ import type { Request, Response } from 'express'; import { AuthService } from './auth.service.js'; import { AuthGuard } from './auth.guard.js'; -type LoginRequestDTO = { - wallet: string; -}; @Controller('auth') export class AuthController { constructor(private authService: AuthService) {} diff --git a/services/liquid-auth-api-js/src/connect/connect.controller.ts b/services/liquid-auth-api-js/src/connect/connect.controller.ts index 72b0b11..2625fd3 100644 --- a/services/liquid-auth-api-js/src/connect/connect.controller.ts +++ b/services/liquid-auth-api-js/src/connect/connect.controller.ts @@ -12,7 +12,7 @@ import { ClientProxy } from '@nestjs/microservices'; import { AuthService } from '../auth/auth.service.js'; import { AlgodService } from '../algod/algod.service.js'; import nacl from 'tweetnacl'; -import { decodeAddress, base64ToUint8Array } from '@liquid/core/encoding'; +import { decodeAddress, fromBase64Url } from '@liquid/core/encoding'; type LinkResponseDTO = { credId?: string; @@ -47,16 +47,14 @@ export class ConnectController { @Body() { requestId, wallet, challenge, signature, credId }: LinkResponseDTO, ) { - this.logger.log( - `POST /connect/response for RequestId: ${requestId} Session: ${session.id} with Wallet: ${wallet}`, - ); - // Decode Address - const publicKey = decodeAddress(wallet); + this.logger.log( + `POST /connect/response for RequestId: ${requestId} Session: ${session.id} with Wallet: ${wallet}`, + ); + // Decode Address + const publicKey = decodeAddress(wallet); // Decode signature - const uint8Signature = base64ToUint8Array( - signature.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, ''), - ); + const uint8Signature = fromBase64Url(signature); // Validate Signature const encoder = new TextEncoder(); diff --git a/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts b/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts index ab99c46..c8bf861 100644 --- a/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts +++ b/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SignalsGateway } from './signals.gateway.js'; -import { Server, Socket } from "socket.io"; +import { Server, Socket } from 'socket.io'; // eslint-disable-next-line @typescript-eslint/no-var-requires const candidateFixture = require('./__fixtures__/candidate.fixture.json'); diff --git a/services/liquid-auth-api-js/tsconfig.json b/services/liquid-auth-api-js/tsconfig.json index e3e741b..e8f4d91 100644 --- a/services/liquid-auth-api-js/tsconfig.json +++ b/services/liquid-auth-api-js/tsconfig.json @@ -5,7 +5,7 @@ "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "target": "ESNext", "moduleResolution": "NodeNext", "sourceMap": true, @@ -22,6 +22,7 @@ "inlineSources": true, "resolveJsonModule": true, "allowJs": true, + "esModuleInterop": true, // Set `sourceRoot` to "/" to strip the build path prefix // from generated source code references. // This improves issue grouping in Sentry. diff --git a/sites/dapp-ui/src/Contexts.tsx b/sites/dapp-ui/src/Contexts.tsx index 27973eb..bf06ac3 100644 --- a/sites/dapp-ui/src/Contexts.tsx +++ b/sites/dapp-ui/src/Contexts.tsx @@ -4,14 +4,20 @@ export const ColorModeContext = createContext({ toggle: () => {} }); export const StateContext = createContext({ state: 'debug', - setState: (_: string) => {}, + setState: (_: string) => { + console.log(_); + }, }); export const SnackbarContext = createContext({ open: false, - setOpen: (_: boolean) => {}, + setOpen: (_: boolean) => { + console.log(_); + }, message: '', - setMessage: (_: string) => {}, + setMessage: (_: string) => { + console.log(_); + }, }); export { DataChannelContext } from './hooks/useDataChannel.ts'; diff --git a/sites/dapp-ui/src/entry-main.tsx b/sites/dapp-ui/src/entry-main.tsx index 0565258..f6db3f5 100644 --- a/sites/dapp-ui/src/entry-main.tsx +++ b/sites/dapp-ui/src/entry-main.tsx @@ -1,6 +1,6 @@ import './index.css'; import * as ReactDOM from 'react-dom/client'; -import { StrictMode } from 'react'; +// import { StrictMode } from 'react'; import App from './App.tsx'; ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/sites/dapp-ui/src/hooks/useDataChannel.ts b/sites/dapp-ui/src/hooks/useDataChannel.ts index 41e15e0..bf27f29 100644 --- a/sites/dapp-ui/src/hooks/useDataChannel.ts +++ b/sites/dapp-ui/src/hooks/useDataChannel.ts @@ -6,7 +6,9 @@ type DataChannelState = { }; export const DataChannelContext = createContext({ dataChannel: null, - setDataChannel: (_: RTCDataChannel) => {}, + setDataChannel: (_: RTCDataChannel) => { + console.log(_); + }, } as DataChannelState); /** diff --git a/sites/dapp-ui/src/store.ts b/sites/dapp-ui/src/store.ts index 3a486a6..2f742e5 100644 --- a/sites/dapp-ui/src/store.ts +++ b/sites/dapp-ui/src/store.ts @@ -60,7 +60,7 @@ export const usePeerStore = create((set) => ({ })); export type Message = { - data: any; + data: unknown; type: 'local' | 'remote'; timestamp: number; }; From 8d2f3f338618f93acaca05cbf7e8ab8f5abacf03 Mon Sep 17 00:00:00 2001 From: Michael Feher Date: Mon, 15 Apr 2024 18:58:13 -0400 Subject: [PATCH 12/21] build: remove preinstall hooks - removes awesome-algorand tweetnacl fork --- clients/liquid-auth-client-js/package.json | 3 +-- clients/liquid-auth-client-js/tsconfig.json | 1 + clients/liquid-auth-core/package.json | 3 +-- clients/liquid-auth-core/tsconfig.json | 2 ++ package-lock.json | 10 ++++------ 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/clients/liquid-auth-client-js/package.json b/clients/liquid-auth-client-js/package.json index ebef85f..c3c8b99 100644 --- a/clients/liquid-auth-client-js/package.json +++ b/clients/liquid-auth-client-js/package.json @@ -25,7 +25,6 @@ "scripts": { "dev": "tsc --watch", "build": "tsc", - "preinstall": "npm run build", "test": "tsc && node --test ./tests/connect.spec.js", "test:cov": "tsc && c8 node --test ./tests/connect.spec.js" }, @@ -45,6 +44,6 @@ "dependencies": { "@liquid/core": "^1.0.0", "qr-code-styling": "^1.6.0-rc.1", - "tweetnacl": "github:awesome-algorand/tweetnacl-js" + "tweetnacl": "^1.0.3" } } diff --git a/clients/liquid-auth-client-js/tsconfig.json b/clients/liquid-auth-client-js/tsconfig.json index 02cf9d0..b2ef6cc 100644 --- a/clients/liquid-auth-client-js/tsconfig.json +++ b/clients/liquid-auth-client-js/tsconfig.json @@ -5,6 +5,7 @@ "declaration": true, "module": "NodeNext", "moduleResolution": "NodeNext", + "allowSyntheticDefaultImports": true, "skipLibCheck": true, } } diff --git a/clients/liquid-auth-core/package.json b/clients/liquid-auth-core/package.json index a4fb2bc..bca7b47 100644 --- a/clients/liquid-auth-core/package.json +++ b/clients/liquid-auth-core/package.json @@ -25,7 +25,6 @@ "scripts": { "dev": "tsc --watch", "build": "tsc", - "preinstall": "npm run build", "test": "tsc && c8 -r html node --test ./tests/encoding.spec.js" }, "author": "", @@ -40,6 +39,6 @@ "typescript": "^5.4.5" }, "dependencies": { - "tweetnacl": "github:awesome-algorand/tweetnacl-js" + "tweetnacl": "^1.0.3" } } diff --git a/clients/liquid-auth-core/tsconfig.json b/clients/liquid-auth-core/tsconfig.json index 3380b91..1263018 100644 --- a/clients/liquid-auth-core/tsconfig.json +++ b/clients/liquid-auth-core/tsconfig.json @@ -5,6 +5,8 @@ "declaration": true, "module": "NodeNext", "moduleResolution": "NodeNext", + "allowSyntheticDefaultImports": true, + "allowJs": true, "skipLibCheck": true, "strictNullChecks": true } diff --git a/package-lock.json b/package-lock.json index 3c8dd93..114bdc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,12 +17,11 @@ "clients/liquid-auth-client-js": { "name": "@liquid/auth-client", "version": "1.0.0", - "hasInstallScript": true, "license": "MIT", "dependencies": { "@liquid/core": "^1.0.0", "qr-code-styling": "^1.6.0-rc.1", - "tweetnacl": "github:awesome-algorand/tweetnacl-js" + "tweetnacl": "^1.0.3" }, "devDependencies": { "@types/qrcode": "^1.5.5", @@ -166,10 +165,9 @@ "clients/liquid-auth-core": { "name": "@liquid/core", "version": "1.0.0", - "hasInstallScript": true, "license": "MIT", "dependencies": { - "tweetnacl": "github:awesome-algorand/tweetnacl-js" + "tweetnacl": "^1.0.3" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^6.21.0", @@ -15615,8 +15613,8 @@ }, "node_modules/tweetnacl": { "version": "1.0.3", - "resolved": "git+ssh://git@github.com/awesome-algorand/tweetnacl-js.git#55c193f3ade9213b5af0c9e7956aa66c62cae10b", - "license": "Unlicense" + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, "node_modules/type-check": { "version": "0.4.0", From 6c6a257d681113579af5ec4e3bef16fb52731567 Mon Sep 17 00:00:00 2001 From: Michael Feher Date: Wed, 17 Apr 2024 10:24:42 -0400 Subject: [PATCH 13/21] refactor: change call to offer --- .../src/signals/signals.gateway.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/services/liquid-auth-api-js/src/signals/signals.gateway.ts b/services/liquid-auth-api-js/src/signals/signals.gateway.ts index 61b685c..0e1dca8 100644 --- a/services/liquid-auth-api-js/src/signals/signals.gateway.ts +++ b/services/liquid-auth-api-js/src/signals/signals.gateway.ts @@ -16,30 +16,30 @@ export class SignalsGateway { server: Server; private readonly logger = new Logger(SignalsGateway.name); - @SubscribeMessage('call-candidate') + @SubscribeMessage('offer-candidate') onCallCandidate( @MessageBody() - data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, + data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, @ConnectedSocket() client: Socket, ) { - this.logger.debug(`(call-candidate): ${JSON.stringify(data)}`); + this.logger.debug(`(offer-candidate): ${JSON.stringify(data)}`); const request = client.request as Record; const session = request.session as Record; - this.server.in(session.wallet).emit('call-candidate', data); + this.server.in(session.wallet).emit('offer-candidate', data); } - @SubscribeMessage('call-description') + @SubscribeMessage('offer-description') onCallDescription( @MessageBody() data: string, @ConnectedSocket() client: Socket, ) { - this.logger.log(`(call-description): ${data}`); + this.logger.log(`(offer-description): ${data}`); // Session from the initial Handshake const request = client.request as Record; const session = request.session as Record; // Send description to all clients in the public key's room - this.server.in(session.wallet).emit('call-description', data); + this.server.in(session.wallet).emit('offer-description', data); } @SubscribeMessage('answer-description') onAnswerDescription( @@ -54,7 +54,7 @@ export class SignalsGateway { @SubscribeMessage('answer-candidate') onAnswerCandidate( @MessageBody() - data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, + data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, @ConnectedSocket() client: Socket, ) { this.logger.debug(`(answer-candidate): ${JSON.stringify(data)}`); From 68e6cab2b174aaab20f0f3747ec39c1506eecd31 Mon Sep 17 00:00:00 2001 From: Michael Feher Date: Thu, 18 Apr 2024 16:02:13 -0400 Subject: [PATCH 14/21] test: add signaling fixture for session --- .../src/__fixtures__/session.fixtures.json | 6 ++++++ .../src/signals/signals.gateway.spec.ts | 15 ++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 services/liquid-auth-api-js/src/__fixtures__/session.fixtures.json diff --git a/services/liquid-auth-api-js/src/__fixtures__/session.fixtures.json b/services/liquid-auth-api-js/src/__fixtures__/session.fixtures.json new file mode 100644 index 0000000..218b24e --- /dev/null +++ b/services/liquid-auth-api-js/src/__fixtures__/session.fixtures.json @@ -0,0 +1,6 @@ +{ + "authorized": { + "wallet": "B7WYCZ6HRBGCH452D24TYAK7BXKNCHEXY2X7S7FWZXMHDVTDOARAOURJEU" + }, + "unauthorized": {} +} diff --git a/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts b/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts index c8bf861..917af9a 100644 --- a/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts +++ b/services/liquid-auth-api-js/src/signals/signals.gateway.spec.ts @@ -1,17 +1,18 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SignalsGateway } from './signals.gateway.js'; import { Server, Socket } from 'socket.io'; +import session from "express-session"; // eslint-disable-next-line @typescript-eslint/no-var-requires const candidateFixture = require('./__fixtures__/candidate.fixture.json'); // eslint-disable-next-line @typescript-eslint/no-var-requires const sdpFixtures = require('./__fixtures__/sdp.fixtures.json'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const sessionFixtures = require('../__fixtures__/session.fixtures.json'); const clientFixture = { request: { - session: { - wallet: 'AVALIDWALLETADDRESS', - }, + session: sessionFixtures.authorized, }, } as unknown as Socket; @@ -37,23 +38,23 @@ describe('SignalsGateway', () => { gateway = module.get(SignalsGateway); gateway.server = new Server(); }); - it('should signal a call-description', () => { + it('should signal a offer-description', () => { gateway.onCallDescription(sdpFixtures.call, clientFixture); expect(gateway.server.in).toHaveBeenCalledWith( (clientFixture.request as any).session.wallet, ); expect(gateway.server.emit).toHaveBeenCalledWith( - 'call-description', + 'offer-description', sdpFixtures.call, ); }); - it('should signal a call-candidate', () => { + it('should signal a offer-candidate', () => { gateway.onCallCandidate(candidateFixture, clientFixture); expect(gateway.server.in).toHaveBeenCalledWith( (clientFixture.request as any).session.wallet, ); expect(gateway.server.emit).toHaveBeenCalledWith( - 'call-candidate', + 'offer-candidate', candidateFixture, ); }); From af6b7e7a6e9686fc35176184708110ccd451ea2b Mon Sep 17 00:00:00 2001 From: Michael Feher Date: Thu, 18 Apr 2024 16:35:52 -0400 Subject: [PATCH 15/21] feat: js signaling client --- .gitignore | 2 + client-gen.sh | 12 + clients/README.md | 59 + clients/liquid-auth-client-js/.eslintrc.json | 4 +- clients/liquid-auth-client-js/.gitignore | 1 + clients/liquid-auth-client-js/package.json | 18 +- .../liquid-auth-client-js/public/favicon.svg | 1 + .../liquid-auth-client-js/src/assertion.ts | 256 +- .../liquid-auth-client-js/src/attestation.ts | 34 +- clients/liquid-auth-client-js/src/connect.ts | 39 +- clients/liquid-auth-client-js/src/errors.ts | 9 + clients/liquid-auth-client-js/src/index.ts | 3 + clients/liquid-auth-client-js/src/signal.ts | 273 ++ .../tests/encoding.test.js | 0 .../liquid-auth-client-js/tests/index.spec.js | 6 - clients/liquid-auth-client-js/tsconfig.json | 1 + clients/liquid-auth-core/.eslintrc.json | 2 + clients/liquid-auth-core/.gitignore | 1 + clients/liquid-auth-core/package.json | 8 +- clients/liquid-auth-core/src/sha512.ts | 42 +- package-lock.json | 3180 +++++------------ services/liquid-auth-api-js/package.json | 1 - .../src/attestation/attestation.controller.ts | 1 - .../src/connect/connect.gateway.ts | 1 + services/liquid-auth-api-js/src/main.ts | 8 - .../src/signals/signals.gateway.ts | 4 +- sites/dapp-ui/package.json | 3 +- sites/dapp-ui/src/App.tsx | 135 +- sites/dapp-ui/src/Contexts.tsx | 22 +- sites/dapp-ui/src/Layout.tsx | 14 +- sites/dapp-ui/src/components/ConnectModal.tsx | 148 +- sites/dapp-ui/src/components/Snackbar.tsx | 47 - .../src/components/user/SessionMenu.tsx | 26 +- .../src/components/user/StatusCard.tsx | 1 + sites/dapp-ui/src/hooks/index.ts | 2 - sites/dapp-ui/src/hooks/useDataChannel.ts | 54 +- sites/dapp-ui/src/hooks/usePeerConnection.ts | 102 - sites/dapp-ui/src/hooks/useSignalClient.ts | 34 + sites/dapp-ui/src/hooks/useSocket.ts | 32 - sites/dapp-ui/src/hooks/useUserState.ts | 10 +- sites/dapp-ui/src/pages/connected.tsx | 26 +- sites/dapp-ui/src/pages/home.tsx | 8 +- sites/dapp-ui/src/pages/home/ConnectModal.tsx | 0 sites/dapp-ui/src/pages/index.ts | 1 - sites/dapp-ui/src/pages/peering.tsx | 114 - sites/dapp-ui/tsconfig.json | 2 +- sites/dapp-ui/vite.config.ts | 69 +- 47 files changed, 1757 insertions(+), 3059 deletions(-) create mode 100755 client-gen.sh create mode 100644 clients/README.md create mode 100644 clients/liquid-auth-client-js/public/favicon.svg create mode 100644 clients/liquid-auth-client-js/src/errors.ts create mode 100644 clients/liquid-auth-client-js/src/signal.ts delete mode 100644 clients/liquid-auth-client-js/tests/encoding.test.js delete mode 100644 clients/liquid-auth-client-js/tests/index.spec.js delete mode 100644 sites/dapp-ui/src/components/Snackbar.tsx delete mode 100644 sites/dapp-ui/src/hooks/usePeerConnection.ts create mode 100644 sites/dapp-ui/src/hooks/useSignalClient.ts delete mode 100644 sites/dapp-ui/src/hooks/useSocket.ts delete mode 100644 sites/dapp-ui/src/pages/home/ConnectModal.tsx delete mode 100644 sites/dapp-ui/src/pages/peering.tsx diff --git a/.gitignore b/.gitignore index dbb2061..7a37398 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +swagger-codegen-cli.jar + .data .idea diff --git a/client-gen.sh b/client-gen.sh new file mode 100755 index 0000000..d32586d --- /dev/null +++ b/client-gen.sh @@ -0,0 +1,12 @@ + + +CODEGEN=swagger-codegen-cli.jar +if [ -f $CODEGEN ]; then + echo "Codegen already exists" +else + echo "Downloading codegen" + wget https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/3.0.52/swagger-codegen-cli-3.0.52.jar -O swagger-codegen-cli.jar +fi + +java -jar swagger-codegen-cli.jar generate -i http://localhost:3000/api-json -l typescript-fetch -o clients/liquid-auth-client-js/src/client +java -jar swagger-codegen-cli.jar generate -i http://localhost:3000/api-json -l kotlin-client -o clients/liquid-auth-client-kotlin diff --git a/clients/README.md b/clients/README.md new file mode 100644 index 0000000..39b6d7f --- /dev/null +++ b/clients/README.md @@ -0,0 +1,59 @@ +# Overview + +Client JSON-RPC interfaces are generated from OpenAPI 3.0 specifications. +All clients should mirror the same interfaces and include the same parameters (as much as possible). + +```typescript +interface SignalClient { + readonly url: string; // Origin of the service + type: "offer" | "answer" // Type of client + peerClient: RTCPeerConnection | PeerClient // Native WebRTC Wrapper/Interface + socket: Socket // The socket to the service + + readonly authenticated: boolean; // State of authentication + readonly requestId?: string; // The current request being signaled + + /** + * Generate a Request ID + */ + generateRequestId(): any; + + /** + * Top level Friendly interface for signaling + * @param args + */ + peer(requestId: any, type: 'offer' | 'answer', config?: RTCConfiguration): Promise; + + /** + * Link a Request ID to this client + * @param args + */ + link(...args: any[]): Promise; + + /** + * Wait for a desciption signal + * @param args + */ + signal(...args: any[]): Promise; + + /** + * Terminate the signaling session + */ + close(): void + + + /** + * Listen to Interface events + * @param args + */ + on(...args: any[]): void; + + /** + * Emit an event to the interface + * @param channel + * @param callback + */ + emit(channel: string, callback: (...args: any[])=>void) + +} +``` diff --git a/clients/liquid-auth-client-js/.eslintrc.json b/clients/liquid-auth-client-js/.eslintrc.json index be26503..14bfcde 100644 --- a/clients/liquid-auth-client-js/.eslintrc.json +++ b/clients/liquid-auth-client-js/.eslintrc.json @@ -9,12 +9,14 @@ "sourceType": "module" }, "plugins": [ - "@typescript-eslint/eslint-plugin" + "@typescript-eslint/eslint-plugin", + "eslint-plugin-tsdoc" ], "extends": [ "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended" ], "rules": { + "@typescript-eslint/no-explicit-any": "warn" } } diff --git a/clients/liquid-auth-client-js/.gitignore b/clients/liquid-auth-client-js/.gitignore index 8813e9f..e1be502 100644 --- a/clients/liquid-auth-client-js/.gitignore +++ b/clients/liquid-auth-client-js/.gitignore @@ -1,4 +1,5 @@ lib +docs # Logs logs diff --git a/clients/liquid-auth-client-js/package.json b/clients/liquid-auth-client-js/package.json index c3c8b99..cd5ab60 100644 --- a/clients/liquid-auth-client-js/package.json +++ b/clients/liquid-auth-client-js/package.json @@ -20,13 +20,23 @@ "./connect": { "default": "./lib/connect.js", "types": "./lib/connect.d.ts" + }, + "./signal": { + "default": "./lib/signal.js", + "types": "./lib/signal.d.ts" + }, + "./errors": { + "default": "./lib/errors.js", + "types": "./lib/errors.d.ts" } }, "scripts": { "dev": "tsc --watch", "build": "tsc", - "test": "tsc && node --test ./tests/connect.spec.js", - "test:cov": "tsc && c8 node --test ./tests/connect.spec.js" + "build:docs": "typedoc --plugin typedoc-plugin-markdown --out docs src", + "lint": "eslint --fix src", + "test": "tsc && node --test ./tests/*.spec.js", + "test:cov": "tsc && c8 node --test ./tests/*.spec.js" }, "author": "", "license": "MIT", @@ -39,10 +49,14 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-tsdoc": "^0.2.17", + "typedoc": "^0.25.13", + "typedoc-plugin-markdown": "^4.0.0-next.55", "typescript": "^5.4.5" }, "dependencies": { "@liquid/core": "^1.0.0", + "eventemitter3": "^5.0.1", "qr-code-styling": "^1.6.0-rc.1", "tweetnacl": "^1.0.3" } diff --git a/clients/liquid-auth-client-js/public/favicon.svg b/clients/liquid-auth-client-js/public/favicon.svg new file mode 100644 index 0000000..cba5ac1 --- /dev/null +++ b/clients/liquid-auth-client-js/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/clients/liquid-auth-client-js/src/assertion.ts b/clients/liquid-auth-client-js/src/assertion.ts index 0254a56..29ac125 100644 --- a/clients/liquid-auth-client-js/src/assertion.ts +++ b/clients/liquid-auth-client-js/src/assertion.ts @@ -1,95 +1,207 @@ +/** + * This module is only for browser and currently not used in the project. + * However, it could be useful for extension wallets or other browser-based wallets. + */ import { fromBase64Url, toBase64URL } from '@liquid/core/encoding'; import { DEFAULT_FETCH_OPTIONS } from './constants.js'; +import { + isValidResponse, + INVALID_INPUT_MESSAGE, + CREDENTIAL_ACTION_FAILURE, +} from './errors.js'; -interface SerializedAuthenticatorAssertionResponse { +export type EncodedPublicKeyCredentialDescriptor = + PublicKeyCredentialDescriptor & { + id: string; + }; +export type EncodedPublicKeyCredentialRequestOptions = + PublicKeyCredentialRequestOptions & { + allowCredentials?: EncodedPublicKeyCredentialDescriptor[]; + challenge: string; + }; +/** + * Fetch Assertion Options + * + * POST Authenticator Selector to the REST API + * to receive the PublicKeyCredentialRequestOptions + * + * @param origin + * @param credId + * @todo: Generate Typed JSON-RPC clients from Swagger/OpenAPI + */ +export async function fetchAssertionRequestOptions( + origin: string, + credId: string, +) { + if (typeof origin !== 'string' || typeof credId !== 'string') { + throw new TypeError(INVALID_INPUT_MESSAGE); + } + return await fetch(`/assertion/request/${credId}`, { + ...DEFAULT_FETCH_OPTIONS, + }).then((r) => { + if (!isValidResponse(r)) { + throw new Error(r.statusText); + } + return r.json() as Promise; + }); +} +/** + * Decode Assertion Request Options + * + * @param options + */ +export function decodeAssertionRequestOptions( + options: EncodedPublicKeyCredentialRequestOptions, +) { + if (typeof options !== 'object' || typeof options.challenge !== 'string') + throw new TypeError(INVALID_INPUT_MESSAGE); + + const decodedOptions: PublicKeyCredentialRequestOptions = { ...options }; + decodedOptions.challenge = fromBase64Url(options.challenge as string); + decodedOptions.allowCredentials = + options.allowCredentials?.map( + (cred: EncodedPublicKeyCredentialDescriptor) => { + return { + ...cred, + id: fromBase64Url(cred.id as string), + } as PublicKeyCredentialDescriptor; + }, + ) || []; + return decodedOptions; +} + +export type EncodedAuthenticatorAssertionResponse = { [k: string]: string; clientDataJSON: string; authenticatorData: string; signature: string; userHandle: string; -} -interface SerializedCredential { - [k: string]: string | SerializedAuthenticatorAssertionResponse; +}; +export type EncodedCredential = { + [k: string]: string | EncodedAuthenticatorAssertionResponse; id: string; type: string; - response: SerializedAuthenticatorAssertionResponse; + response: EncodedAuthenticatorAssertionResponse; rawId: string; -} +}; -export async function assertion(credId: string) { - console.log( - `%cFETCHING: %c/assertion/request/${credId}`, - 'color: yellow', - 'color: cyan', - ); - const options = await fetch(`/assertion/request/${credId}`, { +/** + * Fetch Assertion Response + * + * POST an Authenticator Assertion Response to the REST API + * + * @param origin + * @param credential + * @todo: Generate Typed JSON-RPC clients from Swagger/OpenAPI + */ +export async function fetchAssertionResponse( + origin: string, + credential: EncodedCredential, +) { + if (typeof origin !== 'string' || typeof credential !== 'object') { + // TODO: instance check for SerializedCredential + throw new TypeError(INVALID_INPUT_MESSAGE); + } + return await fetch(`${origin}/assertion/response`, { ...DEFAULT_FETCH_OPTIONS, - }).then(async (r) => await r.json()); + body: JSON.stringify(credential), + }).then((r) => { + if (!isValidResponse(r)) { + throw new Error(r.statusText); + } + return r.json(); + }); +} +/** + * + * @param response + */ +export function encodeAuthenticatorAssertionResponse( + response: AuthenticatorAssertionResponse & Record, +) { + return Object.keys( + AuthenticatorAssertionResponse.prototype, + ).reduce( + (prev, curr) => { + prev[curr] = toBase64URL(response[curr]); + return prev; + }, + { + clientDataJSON: toBase64URL(response.clientDataJSON), + } as EncodedAuthenticatorAssertionResponse, + ); +} - if (options.allowCredentials.length === 0) { - console.info('No registered credentials found.'); - return await Promise.resolve(null); +/** + * + * @param credential + */ +export function encodeCredential( + credential: PublicKeyCredential, +): EncodedCredential { + if (!credential) throw new Error(INVALID_INPUT_MESSAGE); + const response = credential.response as AuthenticatorAssertionResponse & + Record; + if (!response) throw new Error(CREDENTIAL_ACTION_FAILURE); + return { + id: credential.id, + type: credential.type, + rawId: toBase64URL(credential.rawId), + response: encodeAuthenticatorAssertionResponse(response), + }; +} + +/** + * Assert a known credential + * @param origin + * @param credId + * @param debug + */ +export async function assertion( + origin: string, + credId: string, + debug: boolean = false, +) { + if (typeof credId !== 'string') { + throw new TypeError(INVALID_INPUT_MESSAGE); } + debug && + console.log( + `%cFETCHING: %c/assertion/request/${credId}`, + 'color: yellow', + 'color: cyan', + ); - options.challenge = fromBase64Url(options.challenge as string); + const options = await fetchAssertionRequestOptions(origin, credId).then( + decodeAssertionRequestOptions, + ); - for (const cred of options.allowCredentials) { - cred.id = fromBase64Url(cred.id as string); + if (options.allowCredentials.length === 0) { + debug && console.info('No registered credentials found.'); + return null; } - console.log( - '%cGET_CREDENTIAL:%c navigator.credentials.get', - 'color: yellow', - 'color: cyan', - options, - ); - const cred = (await navigator.credentials.get({ - publicKey: options, - })) as PublicKeyCredential; + debug && + console.log( + '%cGET_CREDENTIAL:%c navigator.credentials.get', + 'color: yellow', + 'color: cyan', + options, + ); - if (!cred) throw new Error('Could not get credential'); - const response = cred.response as AuthenticatorAssertionResponse & - Record; - const credential: SerializedCredential = { - id: cred.id, - type: cred.type, - rawId: toBase64URL(cred.rawId), - response: Object.keys( - response, - ).reduce((prev, curr) => { - prev[curr] = toBase64URL(response[curr]); - return prev; - }, {} as SerializedAuthenticatorAssertionResponse), - }; - credential.id = cred.id; - credential.type = cred.type; - credential.rawId = toBase64URL(cred.rawId); + const credential = await navigator.credentials + .get({ + publicKey: options, + }) + .then(encodeCredential); - if (cred.response) { - const clientDataJSON = toBase64URL(response.clientDataJSON); - const authenticatorData = toBase64URL(response.authenticatorData); - const signature = toBase64URL(response.signature); - const userHandle = toBase64URL( - typeof response?.userHandle !== 'undefined' && - response.userHandle !== null - ? response.userHandle - : new Uint8Array(), + debug && + console.log( + '%cPOSTING: %c/assertion/response', + 'color: yellow', + 'color: cyan', + credential, ); - credential.response = { - clientDataJSON, - authenticatorData, - signature, - userHandle, - }; - } - console.log( - '%cPOSTING: %c/assertion/response', - 'color: yellow', - 'color: cyan', - credential, - ); - return await fetch('/assertion/response', { - ...DEFAULT_FETCH_OPTIONS, - body: JSON.stringify(credential), - }); + + return fetchAssertionResponse(origin, credential); } diff --git a/clients/liquid-auth-client-js/src/attestation.ts b/clients/liquid-auth-client-js/src/attestation.ts index a6f8286..04385af 100644 --- a/clients/liquid-auth-client-js/src/attestation.ts +++ b/clients/liquid-auth-client-js/src/attestation.ts @@ -1,7 +1,12 @@ +/** + * This module is only for browser and currently not used in the project. + * However, it could be useful for extension wallets or other browser-based wallets. + */ import { fromBase64Url, toBase64URL } from '@liquid/core/encoding'; import { DEFAULT_FETCH_OPTIONS } from './constants.js'; +import { isValidResponse } from './errors.js'; -const DEFAULT_ATTESTATION_OPTIONS = { +export const DEFAULT_ATTESTATION_OPTIONS = { attestationType: 'none', authenticatorSelection: { authenticatorAttachment: 'platform', @@ -44,22 +49,6 @@ function encodeAttestationCredential( }; } -/** - * Decoding an Encoded Attestation Credential - * @param credential - Encoded Attestation Credential - */ -function decodeAttestationCredential(credential: EncodedAttestationCredential) { - return { - id: credential.id, - rawId: fromBase64Url(credential.rawId), - type: credential.type, - response: { - clientDataJSON: fromBase64Url(credential.response.clientDataJSON), - attestationObject: fromBase64Url(credential.response.attestationObject), - }, - }; -} - function decodeAttestationOptions(options) { const attestationOptions = { ...options }; attestationOptions.user.id = fromBase64Url(options.user.id); @@ -79,6 +68,7 @@ function decodeAttestationOptions(options) { * * @param origin * @param options + * @todo: Generate Typed JSON-RPC clients from Swagger/OpenAPI */ export async function fetchAttestationRequest( origin: string, @@ -95,6 +85,7 @@ export async function fetchAttestationRequest( * * @param origin * @param credential + * @todo: Generate Typed JSON-RPC clients from Swagger/OpenAPI */ export async function fetchAttestationResponse( origin: string, @@ -103,6 +94,9 @@ export async function fetchAttestationResponse( return await fetch(`${origin}/attestation/response`, { ...DEFAULT_FETCH_OPTIONS, body: JSON.stringify(credential), + }).then((r) => { + if (!isValidResponse(r)) throw new Error(r.statusText); + return r.json(); }); } @@ -114,7 +108,6 @@ export async function fetchAttestationResponse( * - The server creates a challenge and sends it to the client * - The client creates a credential and sends it to the server * - * @return {Promise} */ export async function attestation( origin: string, @@ -123,7 +116,10 @@ export async function attestation( const encodedAttestationOptions = await fetchAttestationRequest( origin, options, - ).then(async (r) => await r.json()); + ).then((r) => { + if (!isValidResponse(r)) throw new Error(r.statusText); + return r.json(); + }); if (typeof encodedAttestationOptions.error !== 'undefined') { throw new Error(encodedAttestationOptions.error); diff --git a/clients/liquid-auth-client-js/src/connect.ts b/clients/liquid-auth-client-js/src/connect.ts index 87b36ac..e72cf38 100644 --- a/clients/liquid-auth-client-js/src/connect.ts +++ b/clients/liquid-auth-client-js/src/connect.ts @@ -1,9 +1,21 @@ +/** + * This module is deprecated + */ import { DEFAULT_FETCH_OPTIONS } from './constants.js'; import type { Account } from 'algosdk'; import type { SignKeyPair } from 'tweetnacl'; import nacl from 'tweetnacl'; import { toBase64URL, encodeAddress } from '@liquid/core/encoding'; +import { + INVALID_INPUT_MESSAGE, + isValidResponse, + UNSIGNED_MESSAGE, +} from './errors.js'; +/** + * @todo: Refactor auth message to FIDO extension + * @deprecated + */ export class Message { /** * Origin of the Request @@ -50,6 +62,7 @@ export class Message { * Sign Message with Wallet Key * * @param key + * @deprecated */ sign(key: string | Account | Uint8Array | SignKeyPair): this { const encoder = new TextEncoder(); @@ -117,19 +130,36 @@ export class Message { } } +/** + * + * @deprecated + * @param origin + * @param requestId + */ export async function fetchConnectRequest(origin: string, requestId: number) { + if (typeof origin !== 'string' || typeof requestId !== 'number') + throw new TypeError(INVALID_INPUT_MESSAGE); return await fetch(`${origin}/connect/request`, { ...DEFAULT_FETCH_OPTIONS, body: JSON.stringify({ requestId }), }); } + +/** + * @deprecated + * @param msg + */ export async function fetchConnectResponse(msg: Message) { + if (!(msg instanceof Message)) throw new TypeError(INVALID_INPUT_MESSAGE); if (typeof msg.signature === 'undefined') { - throw new TypeError('Message must be signed!'); + throw new TypeError(UNSIGNED_MESSAGE); } return await fetch('/connect/response', { ...DEFAULT_FETCH_OPTIONS, body: JSON.stringify(msg), + }).then((r) => { + if (!isValidResponse(r)) throw new Error(r.statusText); + return r.json(); }); } @@ -138,12 +168,19 @@ export async function fetchConnectResponse(msg: Message) { * @param origin * @param requestId * @param key + * @deprecated */ export async function connect( origin: string, requestId: number, key: string | Account | Uint8Array | SignKeyPair, ) { + if ( + typeof origin !== 'string' || + typeof requestId !== 'number' || + typeof key === 'undefined' + ) + throw new TypeError(INVALID_INPUT_MESSAGE); const msg = await Message.fromResponse( await fetchConnectRequest(origin, requestId), ); diff --git a/clients/liquid-auth-client-js/src/errors.ts b/clients/liquid-auth-client-js/src/errors.ts new file mode 100644 index 0000000..ec7d96c --- /dev/null +++ b/clients/liquid-auth-client-js/src/errors.ts @@ -0,0 +1,9 @@ +export const INVALID_INPUT_MESSAGE = 'Invalid input'; +export const INVALID_RESPONSE_MESSAGE = 'Invalid response'; +export const CREDENTIAL_ACTION_FAILURE = 'Credential action failed'; +export const UNSIGNED_MESSAGE = 'Message must be signed'; +export class ServiceError extends Error {} + +export function isValidResponse(r: Response) { + return r.ok && (r.status === 200 || r.status === 201); +} diff --git a/clients/liquid-auth-client-js/src/index.ts b/clients/liquid-auth-client-js/src/index.ts index 619109e..9cccec6 100644 --- a/clients/liquid-auth-client-js/src/index.ts +++ b/clients/liquid-auth-client-js/src/index.ts @@ -1,3 +1,6 @@ export * from './assertion.js'; export * from './attestation.js'; export * from './connect.js'; +export * from './constants.js'; +export * from './errors.js'; +export * from './signal.js'; diff --git a/clients/liquid-auth-client-js/src/signal.ts b/clients/liquid-auth-client-js/src/signal.ts new file mode 100644 index 0000000..2324472 --- /dev/null +++ b/clients/liquid-auth-client-js/src/signal.ts @@ -0,0 +1,273 @@ +import { io, ManagerOptions, Socket, SocketOptions } from 'socket.io-client'; +import QRCodeStyling, { Options as QRCodeOptions } from 'qr-code-styling'; +import { EventEmitter } from 'eventemitter3'; +import { toBase64URL } from '@liquid/core'; +import nacl from 'tweetnacl'; + +export type LinkMessage = { + credId?: string; + requestId: string | number; + wallet: string; +}; +export const REQUEST_IS_MISSING_MESSAGE = 'Request id is required'; +export const REQUEST_IN_PROCESS_MESSAGE = 'Request in process'; +export const UNAUTHENTICATED_MESSAGE = 'Not authenticated'; + +export const DEFAULT_QR_CODE_OPTIONS: QRCodeOptions = { + width: 500, + height: 500, + data: 'algorand://', + margin: 25, + imageOptions: { hideBackgroundDots: true, imageSize: 0.4, margin: 15 }, + dotsOptions: { + type: 'extra-rounded', + gradient: { + type: 'radial', + rotation: 0, + colorStops: [ + { offset: 0, color: '#9966ff' }, + { offset: 1, color: '#332257' }, + ], + }, + }, + backgroundOptions: { color: '#ffffff', gradient: null }, + // TODO: Host logo publicly + image: '/logo.png', + cornersSquareOptions: { + color: '#000000', + gradient: { + type: 'linear', + rotation: 0, + colorStops: [ + { offset: 0, color: '#332257' }, + { offset: 1, color: '#040908' }, + ], + }, + }, + cornersDotOptions: { + type: 'dot', + color: '#000000', + gradient: { + type: 'linear', + rotation: 0, + colorStops: [ + { offset: 0, color: '#000000' }, + { offset: 1, color: '#000000' }, + ], + }, + }, +}; + +export async function generateQRCode( + { requestId, url }: { requestId: any; url: string }, + qrCodeOptions: QRCodeOptions = DEFAULT_QR_CODE_OPTIONS, +) { + if (requestId === 'undefined') throw new Error(REQUEST_IS_MISSING_MESSAGE); + // TODO: Serialize data to standard URL for Deep-Links + qrCodeOptions.data = JSON.stringify({ + requestId: requestId, + origin: url, + // TODO: Remove challenge from QR Code + challenge: toBase64URL(nacl.randomBytes(nacl.sign.seedLength)), + }); + + // @ts-expect-error, figure out call signature issue + const qrCode = new QRCodeStyling(qrCodeOptions); + return qrCode.getRawData('png').then((blob) => { + if (!blob) throw new TypeError('Could not get qrcode blob'); + return URL.createObjectURL(blob); + }); +} + +/** + * + */ +export class SignalClient extends EventEmitter { + private url: string; + type: 'offer' | 'answer'; + private authenticated: boolean = false; + private requestId: any | undefined; + peerClient: RTCPeerConnection | undefined; + private qrCodeOptions: QRCodeOptions = DEFAULT_QR_CODE_OPTIONS; + socket: Socket; + + /** + * + * @param url + * @param options + */ + constructor( + url: string, + options: Partial = { autoConnect: true }, + ) { + super(); + this.url = url; + this.socket = io(url, options); + globalThis.socket = this.socket; + this.socket.on('connect', () => { + this.emit('connect', this.socket.id); + }); + + this.socket.on('disconnect', () => { + this.emit('disconnect', this.socket.id); + }); + } + + static generateRequestId() { + //TODO: replace with toBase64URL(nacl.randomBytes(nacl.sign.seedLength) + return Math.random(); + } + + /** + * Create QR Code + */ + async qrCode() { + if (typeof this.requestId === 'undefined') + throw new Error(REQUEST_IS_MISSING_MESSAGE); + // TODO: Serialize data to standard URL for Deep-Links + this.qrCodeOptions.data = JSON.stringify({ + requestId: this.requestId, + origin: this.url, + // TODO: Remove challenge from QR Code + challenge: toBase64URL(nacl.randomBytes(nacl.sign.seedLength)), + }); + return generateQRCode( + { requestId: this.requestId, url: this.url }, + this.qrCodeOptions, + ); + } + + /** + * # Create a peer connection + * + * Send the nonce to the server and listen to a specified type. + * + * ## Offer + * - Will wait for an offer-description from the server + * - Will send an answer-description to the server + * - Will send candidates to the server + * + * ## Answer + * - Will send an offer-description to the server + * - Will wait for an answer-description from the server + * - Will send candidates to the server + * + * @param requestId + * @param type + * @param config + */ + async peer( + requestId: any, + type: 'offer' | 'answer', + config?: RTCConfiguration, + ): Promise { + if (typeof this.requestId !== 'undefined') + throw new Error(REQUEST_IN_PROCESS_MESSAGE); + + return new Promise(async (resolve) => { + // Create Peer Connection + this.peerClient = new RTCPeerConnection(config); + globalThis.peerClient = this.peerClient; + this.type = type === 'offer' ? 'answer' : 'offer'; + // Wait for a link message + await this.link(requestId); + this.socket.disconnect(); + this.socket.connect(); + console.log('linked!'); + // Listen for Local Candidates + this.peerClient.onicecandidate = (event) => { + if (event.candidate) { + console.log(event.candidate); + this.socket.emit(`${this.type}-candidate`, event.candidate.toJSON()); + } + }; + // Listen to Remote Candidates + this.socket.on( + `${type}-candidate`, + async (candidate: RTCIceCandidateInit) => { + await this.peerClient.addIceCandidate(new RTCIceCandidate(candidate)); + }, + ); + + this.peerClient.onicecandidate = (event) => { + if (event.candidate) { + this.socket.emit(`${this.type}-candidate`, event.candidate.toJSON()); + } + }; + + // Listen for Remote DataChannel and Resolve + this.peerClient.ondatachannel = (event) => { + console.log(event); + globalThis.dc = event.channel; + this.emit('data-channel', event.channel); + resolve(event.channel); + }; + // Handle Session Descriptions + if (type === 'offer') { + const sdp = await this.signal(type); + await this.peerClient.setRemoteDescription(sdp); + const answer = await this.peerClient.createAnswer(); + await this.peerClient.setLocalDescription(answer); + this.socket.emit(`${this.type}-description`, answer.sdp); + } else { + const localSdp = await this.peerClient.createOffer(); + const dataChannel = this.peerClient.createDataChannel('liquid'); + await this.peerClient.setLocalDescription(localSdp); + this.socket.emit(`${this.type}-description`, localSdp.sdp); + const sdp = await this.signal(type); + await this.peerClient.setRemoteDescription(sdp); + this.emit('data-channel', dataChannel); + resolve(dataChannel); + } + }); + } + + /** + * Await for a link message for a given requestId + * @param requestId + */ + async link(requestId: any) { + if (typeof this.requestId !== 'undefined') + throw new Error(REQUEST_IN_PROCESS_MESSAGE); + this.requestId = requestId; + this.emit('link', { requestId }); + + return new Promise((resolve) => { + this.socket.emit( + 'link', + { requestId }, + ({ data }: { data: LinkMessage }) => { + this.authenticated = true; + delete this.requestId; + + this.emit('link-message', data); + resolve(data); + }, + ); + }); + } + + /** + * + * @param type + */ + async signal(type: 'offer' | 'answer') { + if (!this.authenticated) throw new Error(UNAUTHENTICATED_MESSAGE); + this.emit('signal', { type }); + return new Promise((resolve) => { + this.socket.once(`${type}-description`, (sdp: string) => { + const description = { type, sdp } as RTCSessionDescriptionInit; + this.emit(`${type}-description`, description); + resolve(description); + }); + }); + } + + close(disconnect = false) { + this.socket.removeAllListeners(); + delete this.requestId; + this.authenticated = false; + if (disconnect) this.socket.disconnect(); + this.emit('close'); + } +} diff --git a/clients/liquid-auth-client-js/tests/encoding.test.js b/clients/liquid-auth-client-js/tests/encoding.test.js deleted file mode 100644 index e69de29..0000000 diff --git a/clients/liquid-auth-client-js/tests/index.spec.js b/clients/liquid-auth-client-js/tests/index.spec.js deleted file mode 100644 index 9793b02..0000000 --- a/clients/liquid-auth-client-js/tests/index.spec.js +++ /dev/null @@ -1,6 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert'; -// import * as api from '../lib/index.js'; -test("smoke test", () => { - assert.equal(1, 1); -}); diff --git a/clients/liquid-auth-client-js/tsconfig.json b/clients/liquid-auth-client-js/tsconfig.json index b2ef6cc..77c36d8 100644 --- a/clients/liquid-auth-client-js/tsconfig.json +++ b/clients/liquid-auth-client-js/tsconfig.json @@ -7,5 +7,6 @@ "moduleResolution": "NodeNext", "allowSyntheticDefaultImports": true, "skipLibCheck": true, + "allowJs": true } } diff --git a/clients/liquid-auth-core/.eslintrc.json b/clients/liquid-auth-core/.eslintrc.json index be26503..cb00282 100644 --- a/clients/liquid-auth-core/.eslintrc.json +++ b/clients/liquid-auth-core/.eslintrc.json @@ -16,5 +16,7 @@ "plugin:prettier/recommended" ], "rules": { + "@typescript-eslint/no-explicit-any": "warn", + "no-var": "warn" } } diff --git a/clients/liquid-auth-core/.gitignore b/clients/liquid-auth-core/.gitignore index 8813e9f..e1be502 100644 --- a/clients/liquid-auth-core/.gitignore +++ b/clients/liquid-auth-core/.gitignore @@ -1,4 +1,5 @@ lib +docs # Logs logs diff --git a/clients/liquid-auth-core/package.json b/clients/liquid-auth-core/package.json index bca7b47..7647365 100644 --- a/clients/liquid-auth-core/package.json +++ b/clients/liquid-auth-core/package.json @@ -25,7 +25,10 @@ "scripts": { "dev": "tsc --watch", "build": "tsc", - "test": "tsc && c8 -r html node --test ./tests/encoding.spec.js" + "build:docs": "typedoc --plugin typedoc-plugin-markdown --out docs src", + "lint": "eslint --fix src", + "test": "tsc && node --test ./tests/*.spec.js", + "test:cov": "tsc && c8 node --test ./tests/*.spec.js" }, "author": "", "license": "MIT", @@ -36,6 +39,9 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-tsdoc": "^0.2.17", + "typedoc": "^0.25.13", + "typedoc-plugin-markdown": "^4.0.0-next.55", "typescript": "^5.4.5" }, "dependencies": { diff --git a/clients/liquid-auth-core/src/sha512.ts b/clients/liquid-auth-core/src/sha512.ts index 8339a82..ae6f81f 100644 --- a/clients/liquid-auth-core/src/sha512.ts +++ b/clients/liquid-auth-core/src/sha512.ts @@ -1,4 +1,3 @@ -// @ts-nocheck /* * [js-sha512]{@link https://github.com/emn178/js-sha512} * @@ -46,7 +45,7 @@ const K = [ const OUTPUT_TYPES = ["hex", "array", "digest", "arrayBuffer"]; -const blocks = []; +const blocks: number[] = []; const isArray = Array.isArray; const isView = ArrayBuffer.isView; @@ -71,7 +70,7 @@ const formatMessage = function ( return [message, false]; }; -const createOutputMethod = function (outputType, bits) { +const createOutputMethod = function (outputType, bits) : (message) => typeof Sha512 { return function (message) { return new (Sha512 as any)(bits, true).update(message)[outputType](); }; @@ -81,39 +80,20 @@ export const createMethod = function (bits): { array: (d: Uint8Array) => Uint8Array; } { const method = createOutputMethod("hex", bits); + //@ts-expect-error, create does exits on the prototype method.create = function () { return new (Sha512 as any)(bits); }; + //@ts-expect-error, update does exits on the prototype method.update = function (message) { + //@ts-expect-error, this is a valid return return method.create().update(message); }; for (let i = 0; i < OUTPUT_TYPES.length; ++i) { const type = OUTPUT_TYPES[i]; method[type] = createOutputMethod(type, bits); } - return method; -}; - -const createHmacOutputMethod = function (outputType, bits) { - return function (key, message) { - return new (HmacSha512 as any)(key, bits, true) - .update(message) - [outputType](); - }; -}; - -const createHmacMethod = function (bits) { - const method = createHmacOutputMethod("hex", bits); - method.create = function (key) { - return new (HmacSha512 as any)(key, bits); - }; - method.update = function (key, message) { - return method.create(key).update(message); - }; - for (let i = 0; i < OUTPUT_TYPES.length; ++i) { - const type = OUTPUT_TYPES[i]; - method[type] = createHmacOutputMethod(type, bits); - } + //@ts-expect-error, this is a valid return return method; }; @@ -123,7 +103,7 @@ const createHmacMethod = function (bits) { * @param sharedMemory * @constructor */ -function Sha512(bits, sharedMemory) { +function Sha512(bits?: number, sharedMemory?) { if (sharedMemory) { blocks[0] = blocks[1] = @@ -1251,11 +1231,11 @@ Sha512.prototype.copyTo = function (hash) { }; function HmacSha512(key, bits, sharedMemory) { - var i; + var i: number; const result = formatMessage(key); key = result[0]; if (result[1]) { - const bytes = []; + const bytes: number[] = []; const length = key.length; let index = 0; let code; @@ -1286,8 +1266,8 @@ function HmacSha512(key, bits, sharedMemory) { key = new Sha512(bits, true).update(key).array(); } - const oKeyPad = []; - const iKeyPad = []; + const oKeyPad: number[] = []; + const iKeyPad: number[] = []; for (var i = 0; i < 128; ++i) { const b = key[i] || 0; oKeyPad[i] = 0x5c ^ b; diff --git a/package-lock.json b/package-lock.json index 114bdc6..46d9c88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "license": "MIT", "dependencies": { "@liquid/core": "^1.0.0", + "eventemitter3": "^5.0.1", "qr-code-styling": "^1.6.0-rc.1", "tweetnacl": "^1.0.3" }, @@ -32,14 +33,30 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-tsdoc": "^0.2.17", + "typedoc": "^0.25.13", + "typedoc-plugin-markdown": "^4.0.0-next.55", + "typescript": "^5.4.5" + } + }, + "clients/liquid-auth-client-js/docs": { + "version": "0.0.1", + "extraneous": true, + "dependencies": { + "@astrojs/check": "^0.5.10", + "@astrojs/starlight": "^0.21.5", + "astro": "^4.3.5", + "sharp": "^0.32.5", + "starlight-typedoc": "^0.11.0", + "typedoc": "^0.25.13", + "typedoc-plugin-markdown": "^4.0.0-next.55", "typescript": "^5.4.5" } }, "clients/liquid-auth-client-js/node_modules/@typescript-eslint/parser": { "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "7.6.0", "@typescript-eslint/types": "7.6.0", @@ -65,9 +82,8 @@ }, "clients/liquid-auth-client-js/node_modules/@typescript-eslint/scope-manager": { "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", - "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.6.0", "@typescript-eslint/visitor-keys": "7.6.0" @@ -82,9 +98,8 @@ }, "clients/liquid-auth-client-js/node_modules/@typescript-eslint/types": { "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", - "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -95,9 +110,8 @@ }, "clients/liquid-auth-client-js/node_modules/@typescript-eslint/typescript-estree": { "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", - "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "7.6.0", "@typescript-eslint/visitor-keys": "7.6.0", @@ -123,9 +137,8 @@ }, "clients/liquid-auth-client-js/node_modules/@typescript-eslint/visitor-keys": { "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", - "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.6.0", "eslint-visitor-keys": "^3.4.3" @@ -140,18 +153,16 @@ }, "clients/liquid-auth-client-js/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "clients/liquid-auth-client-js/node_modules/minimatch": { "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -176,14 +187,16 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-tsdoc": "^0.2.17", + "typedoc": "^0.25.13", + "typedoc-plugin-markdown": "^4.0.0-next.55", "typescript": "^5.4.5" } }, "clients/liquid-auth-core/node_modules/@typescript-eslint/parser": { "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "7.6.0", "@typescript-eslint/types": "7.6.0", @@ -209,9 +222,8 @@ }, "clients/liquid-auth-core/node_modules/@typescript-eslint/scope-manager": { "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", - "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.6.0", "@typescript-eslint/visitor-keys": "7.6.0" @@ -226,9 +238,8 @@ }, "clients/liquid-auth-core/node_modules/@typescript-eslint/types": { "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", - "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -239,9 +250,8 @@ }, "clients/liquid-auth-core/node_modules/@typescript-eslint/typescript-estree": { "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", - "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "7.6.0", "@typescript-eslint/visitor-keys": "7.6.0", @@ -267,9 +277,8 @@ }, "clients/liquid-auth-core/node_modules/@typescript-eslint/visitor-keys": { "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", - "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.6.0", "eslint-visitor-keys": "^3.4.3" @@ -284,18 +293,16 @@ }, "clients/liquid-auth-core/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "clients/liquid-auth-core/node_modules/minimatch": { "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -433,9 +440,8 @@ }, "node_modules/@apideck/better-ajv-errors": { "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", - "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", "dev": true, + "license": "MIT", "dependencies": { "json-schema": "^0.4.0", "jsonpointer": "^5.0.0", @@ -449,98 +455,40 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", + "version": "7.24.2", "license": "MIT", "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "license": "MIT" - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.17", + "version": "7.24.4", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.22.17", - "@babel/helpers": "^7.22.15", - "@babel/parser": "^7.22.16", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.17", - "@babel/types": "^7.22.17", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.4", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.4", + "@babel/parser": "^7.24.4", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", @@ -554,11 +502,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.9.0", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "dev": true, @@ -568,13 +511,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", + "version": "7.24.4", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -583,9 +526,8 @@ }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.22.5" }, @@ -595,9 +537,8 @@ }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.22.15" }, @@ -607,9 +548,8 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.23.5", "@babel/helper-validator-option": "^7.23.5", @@ -631,9 +571,8 @@ }, "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.23.10", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz", - "integrity": "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", @@ -654,18 +593,16 @@ }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-regexp-features-plugin": { "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", @@ -680,18 +617,16 @@ }, "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-define-polyfill-provider": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -736,9 +671,8 @@ }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.23.0" }, @@ -758,9 +692,8 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", @@ -777,9 +710,8 @@ }, "node_modules/@babel/helper-optimise-call-expression": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.22.5" }, @@ -788,7 +720,7 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", + "version": "7.24.0", "dev": true, "license": "MIT", "engines": { @@ -797,9 +729,8 @@ }, "node_modules/@babel/helper-remap-async-to-generator": { "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", @@ -814,9 +745,8 @@ }, "node_modules/@babel/helper-replace-supers": { "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", @@ -842,9 +772,8 @@ }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.22.5" }, @@ -864,7 +793,7 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", + "version": "7.24.1", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -879,18 +808,16 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-function-name": "^7.22.5", "@babel/template": "^7.22.15", @@ -901,25 +828,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.22.15", + "version": "7.24.4", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.13", + "version": "7.24.2", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -983,7 +911,7 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", + "version": "7.24.4", "dev": true, "license": "MIT", "bin": { @@ -995,9 +923,8 @@ }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", - "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1010,9 +937,8 @@ }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", - "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -1027,9 +953,8 @@ }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", - "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5" @@ -1043,9 +968,8 @@ }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" }, @@ -1088,9 +1012,8 @@ }, "node_modules/@babel/plugin-syntax-class-static-block": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1103,9 +1026,8 @@ }, "node_modules/@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1115,9 +1037,8 @@ }, "node_modules/@babel/plugin-syntax-export-namespace-from": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" }, @@ -1127,9 +1048,8 @@ }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", - "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1142,9 +1062,8 @@ }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", - "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1178,11 +1097,11 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", + "version": "7.24.1", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1259,9 +1178,8 @@ }, "node_modules/@babel/plugin-syntax-private-property-in-object": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1302,9 +1220,8 @@ }, "node_modules/@babel/plugin-syntax-unicode-sets-regex": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -1318,9 +1235,8 @@ }, "node_modules/@babel/plugin-transform-arrow-functions": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", - "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1333,9 +1249,8 @@ }, "node_modules/@babel/plugin-transform-async-generator-functions": { "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", - "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5", @@ -1351,9 +1266,8 @@ }, "node_modules/@babel/plugin-transform-async-to-generator": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", @@ -1368,9 +1282,8 @@ }, "node_modules/@babel/plugin-transform-block-scoped-functions": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", - "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1383,9 +1296,8 @@ }, "node_modules/@babel/plugin-transform-block-scoping": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", - "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1398,9 +1310,8 @@ }, "node_modules/@babel/plugin-transform-class-properties": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", - "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" @@ -1414,9 +1325,8 @@ }, "node_modules/@babel/plugin-transform-class-static-block": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", - "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", @@ -1431,9 +1341,8 @@ }, "node_modules/@babel/plugin-transform-classes": { "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", - "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-compilation-targets": "^7.23.6", @@ -1453,18 +1362,16 @@ }, "node_modules/@babel/plugin-transform-classes/node_modules/globals": { "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", - "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/template": "^7.22.15" @@ -1478,9 +1385,8 @@ }, "node_modules/@babel/plugin-transform-destructuring": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", - "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1493,9 +1399,8 @@ }, "node_modules/@babel/plugin-transform-dotall-regex": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", - "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" @@ -1509,9 +1414,8 @@ }, "node_modules/@babel/plugin-transform-duplicate-keys": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", - "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1524,9 +1428,8 @@ }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", - "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3" @@ -1540,9 +1443,8 @@ }, "node_modules/@babel/plugin-transform-exponentiation-operator": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", - "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" @@ -1556,9 +1458,8 @@ }, "node_modules/@babel/plugin-transform-export-namespace-from": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", - "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" @@ -1572,9 +1473,8 @@ }, "node_modules/@babel/plugin-transform-for-of": { "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", - "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" @@ -1588,9 +1488,8 @@ }, "node_modules/@babel/plugin-transform-function-name": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", - "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-function-name": "^7.23.0", @@ -1605,9 +1504,8 @@ }, "node_modules/@babel/plugin-transform-json-strings": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", - "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-json-strings": "^7.8.3" @@ -1621,9 +1519,8 @@ }, "node_modules/@babel/plugin-transform-literals": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", - "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1636,9 +1533,8 @@ }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", - "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" @@ -1652,9 +1548,8 @@ }, "node_modules/@babel/plugin-transform-member-expression-literals": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", - "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1667,9 +1562,8 @@ }, "node_modules/@babel/plugin-transform-modules-amd": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", - "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5" @@ -1683,9 +1577,8 @@ }, "node_modules/@babel/plugin-transform-modules-commonjs": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", @@ -1700,9 +1593,8 @@ }, "node_modules/@babel/plugin-transform-modules-systemjs": { "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", - "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-module-transforms": "^7.23.3", @@ -1718,9 +1610,8 @@ }, "node_modules/@babel/plugin-transform-modules-umd": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", - "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5" @@ -1734,9 +1625,8 @@ }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1750,9 +1640,8 @@ }, "node_modules/@babel/plugin-transform-new-target": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", - "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1765,9 +1654,8 @@ }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", - "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -1781,9 +1669,8 @@ }, "node_modules/@babel/plugin-transform-numeric-separator": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", - "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -1797,9 +1684,8 @@ }, "node_modules/@babel/plugin-transform-object-rest-spread": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", - "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.23.3", "@babel/helper-compilation-targets": "^7.22.15", @@ -1816,9 +1702,8 @@ }, "node_modules/@babel/plugin-transform-object-super": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", - "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20" @@ -1832,9 +1717,8 @@ }, "node_modules/@babel/plugin-transform-optional-catch-binding": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", - "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" @@ -1848,9 +1732,8 @@ }, "node_modules/@babel/plugin-transform-optional-chaining": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", - "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -1865,9 +1748,8 @@ }, "node_modules/@babel/plugin-transform-parameters": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", - "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1880,9 +1762,8 @@ }, "node_modules/@babel/plugin-transform-private-methods": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", - "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" @@ -1896,9 +1777,8 @@ }, "node_modules/@babel/plugin-transform-private-property-in-object": { "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", - "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-create-class-features-plugin": "^7.22.15", @@ -1914,9 +1794,8 @@ }, "node_modules/@babel/plugin-transform-property-literals": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", - "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1929,9 +1808,8 @@ }, "node_modules/@babel/plugin-transform-regenerator": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", - "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "regenerator-transform": "^0.15.2" @@ -1945,9 +1823,8 @@ }, "node_modules/@babel/plugin-transform-reserved-words": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", - "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1960,9 +1837,8 @@ }, "node_modules/@babel/plugin-transform-shorthand-properties": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", - "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1975,9 +1851,8 @@ }, "node_modules/@babel/plugin-transform-spread": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", - "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" @@ -1991,9 +1866,8 @@ }, "node_modules/@babel/plugin-transform-sticky-regex": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", - "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -2006,9 +1880,8 @@ }, "node_modules/@babel/plugin-transform-template-literals": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", - "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -2021,9 +1894,8 @@ }, "node_modules/@babel/plugin-transform-typeof-symbol": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", - "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -2036,9 +1908,8 @@ }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", - "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -2051,9 +1922,8 @@ }, "node_modules/@babel/plugin-transform-unicode-property-regex": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", - "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" @@ -2067,9 +1937,8 @@ }, "node_modules/@babel/plugin-transform-unicode-regex": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", - "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" @@ -2083,9 +1952,8 @@ }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", - "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" @@ -2099,9 +1967,8 @@ }, "node_modules/@babel/preset-env": { "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", - "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.23.5", "@babel/helper-compilation-targets": "^7.23.6", @@ -2193,18 +2060,16 @@ }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/preset-modules": { "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -2216,14 +2081,12 @@ }, "node_modules/@babel/regjsgen": { "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/runtime": { "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2232,32 +2095,32 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", + "version": "7.24.0", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", + "version": "7.24.1", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -2273,10 +2136,10 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", + "version": "7.24.0", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, @@ -2318,20 +2181,9 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@emnapi/runtime": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", - "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", - "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emotion/babel-plugin": { "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", @@ -2348,21 +2200,18 @@ }, "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "license": "MIT" }, "node_modules/@emotion/babel-plugin/node_modules/source-map": { "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/@emotion/cache": { "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "license": "MIT", "dependencies": { "@emotion/memoize": "^0.8.1", "@emotion/sheet": "^1.2.2", @@ -2373,26 +2222,22 @@ }, "node_modules/@emotion/hash": { "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + "license": "MIT" }, "node_modules/@emotion/is-prop-valid": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "license": "MIT", "dependencies": { "@emotion/memoize": "^0.8.1" } }, "node_modules/@emotion/memoize": { "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + "license": "MIT" }, "node_modules/@emotion/react": { "version": "11.11.3", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz", - "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.11.0", @@ -2414,8 +2259,7 @@ }, "node_modules/@emotion/serialize": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", - "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", + "license": "MIT", "dependencies": { "@emotion/hash": "^0.9.1", "@emotion/memoize": "^0.8.1", @@ -2426,8 +2270,7 @@ }, "node_modules/@emotion/server": { "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/server/-/server-11.11.0.tgz", - "integrity": "sha512-6q89fj2z8VBTx9w93kJ5n51hsmtYuFPtZgnc1L8VzRx9ti4EU6EyvF6Nn1H1x3vcCQCF7u2dB2lY4AYJwUW4PA==", + "license": "MIT", "dependencies": { "@emotion/utils": "^1.2.1", "html-tokenize": "^2.0.0", @@ -2445,13 +2288,11 @@ }, "node_modules/@emotion/sheet": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + "license": "MIT" }, "node_modules/@emotion/styled": { "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", - "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.11.0", @@ -2472,422 +2313,49 @@ }, "node_modules/@emotion/unitless": { "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + "license": "MIT" }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "license": "MIT", "peerDependencies": { "react": ">=16.8.0" } }, "node_modules/@emotion/utils": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + "license": "MIT" }, "node_modules/@emotion/weak-memoize": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } + "license": "MIT" }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", - "cpu": [ - "arm" - ], + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", "dev": true, - "optional": true, - "os": [ - "android" - ], + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.8.0", "dev": true, - "optional": true, - "os": [ - "android" - ], + "license": "MIT", "engines": { - "node": ">=12" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", "dev": true, "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.8.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -2908,9 +2376,8 @@ }, "node_modules/@eslint/eslintrc/node_modules/ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2924,31 +2391,27 @@ }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@eslint/js": { "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@floating-ui/core": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", - "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "license": "MIT", "dependencies": { "@floating-ui/utils": "^0.2.1" } }, "node_modules/@floating-ui/dom": { "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.1.tgz", - "integrity": "sha512-iA8qE43/H5iGozC3W0YSnVSW42Vh522yyM1gj+BqRwVsTNOyr231PsXDaV04yT39PsO0QL2QpbI/M0ZaLUQgRQ==", + "license": "MIT", "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.1" @@ -2956,8 +2419,7 @@ }, "node_modules/@floating-ui/react-dom": { "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", - "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.6.1" }, @@ -2968,355 +2430,87 @@ }, "node_modules/@floating-ui/utils": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + "license": "MIT" }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", "dev": true, "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.2.tgz", - "integrity": "sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.1" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.2.tgz", - "integrity": "sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.1" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.1.tgz", - "integrity": "sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "macos": ">=11", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.1.tgz", - "integrity": "sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "macos": ">=10.13", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.1.tgz", - "integrity": "sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.28", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.1.tgz", - "integrity": "sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.26", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.1.tgz", - "integrity": "sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.28", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.1.tgz", - "integrity": "sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.26", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.1.tgz", - "integrity": "sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "musl": ">=1.2.2", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=10.10.0" } }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.1.tgz", - "integrity": "sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==", - "cpu": [ - "x64" - ], "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", "engines": { - "musl": ">=1.2.2", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" + "node": ">=12.22" }, "funding": { - "url": "https://opencollective.com/libvips" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.2.tgz", - "integrity": "sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==", - "cpu": [ - "arm" - ], + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "glibc": ">=2.28", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.1" - } + "license": "BSD-3-Clause" }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.2.tgz", - "integrity": "sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==", + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.1", "cpu": [ - "arm64" + "x64" ], "dev": true, + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], "engines": { "glibc": ">=2.26", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", "npm": ">=9.6.5", "pnpm": ">=7.1.0", "yarn": ">=3.2.0" }, "funding": { "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.1" } }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.2.tgz", - "integrity": "sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==", + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.1", "cpu": [ - "s390x" + "x64" ], "dev": true, + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], "engines": { - "glibc": ">=2.28", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "musl": ">=1.2.2", "npm": ">=9.6.5", "pnpm": ">=7.1.0", "yarn": ">=3.2.0" }, "funding": { "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.1" } }, "node_modules/@img/sharp-linux-x64": { "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.2.tgz", - "integrity": "sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==", "cpu": [ "x64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -3335,40 +2529,13 @@ "@img/sharp-libvips-linux-x64": "1.0.1" } }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.2.tgz", - "integrity": "sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "musl": ">=1.2.2", - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.1" - } - }, "node_modules/@img/sharp-linuxmusl-x64": { "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.2.tgz", - "integrity": "sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==", "cpu": [ "x64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -3387,72 +2554,6 @@ "@img/sharp-libvips-linuxmusl-x64": "1.0.1" } }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.2.tgz", - "integrity": "sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "optional": true, - "dependencies": { - "@emnapi/runtime": "^0.45.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.2.tgz", - "integrity": "sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.2.tgz", - "integrity": "sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0", - "yarn": ">=3.2.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@ioredis/commands": { "version": "1.2.0", "license": "MIT", @@ -3905,13 +3006,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", + "version": "0.3.5", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -3926,7 +3027,7 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", + "version": "1.2.1", "dev": true, "license": "MIT", "engines": { @@ -3948,7 +3049,7 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", + "version": "0.3.25", "dev": true, "license": "MIT", "dependencies": { @@ -3975,6 +3076,54 @@ "node": ">=8" } }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { + "version": "1.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/@mole-inc/bin-wrapper": { "version": "8.0.1", "dev": true, @@ -4005,8 +3154,7 @@ }, "node_modules/@mui/base": { "version": "5.0.0-beta.34", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.34.tgz", - "integrity": "sha512-e2mbTGTtReD/y5RFwnhkl1Tgl3XwgJhY040IlfkTVaU9f5LWrVhEnpRsYXu3B1CtLrwiWs4cu7aMHV9yRd4jpw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.23.9", "@floating-ui/react-dom": "^2.0.8", @@ -4036,8 +3184,7 @@ }, "node_modules/@mui/core-downloads-tracker": { "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.7.tgz", - "integrity": "sha512-AuF+Wo2Mp/edaO6vJnWjg+gj4tzEz5ChMZnAQpc22DXpSvM8ddgGcZvM7D7F99pIBoSv8ub+Iz0viL+yuGVmhg==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" @@ -4045,8 +3192,7 @@ }, "node_modules/@mui/icons-material": { "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.7.tgz", - "integrity": "sha512-EDAc8TVJGIA/imAvR3u4nANl2W5h3QeHieu2gK7Ypez/nIA55p08tHjf8UrMXEpxCAvfZO6piY9S9uaxETdicA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.23.9" }, @@ -4070,8 +3216,7 @@ }, "node_modules/@mui/material": { "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.7.tgz", - "integrity": "sha512-l6+AiKZH3iOJmZCnlpel8ghYQe9Lq0BEuKP8fGj3g5xz4arO9GydqYAtLPMvuHKtArj8lJGNuT2yHYxmejincA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.23.9", "@mui/base": "5.0.0-beta.34", @@ -4114,8 +3259,7 @@ }, "node_modules/@mui/private-theming": { "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.7.tgz", - "integrity": "sha512-bcEeeXm7GyQCQvN9dwo8htGv8/6tP05p0i02Z7GXm5EoDPlBcqTNGugsjNLoGq6B0SsdyanjJGw0Jw00o1yAOA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.23.9", "@mui/utils": "^5.15.7", @@ -4140,8 +3284,7 @@ }, "node_modules/@mui/styled-engine": { "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.7.tgz", - "integrity": "sha512-ixSdslOjK1kzdGcxqj7O3d14By/LPQ7EWknsViQ8RaeT863EAQemS+zvUJDTcOpkfJh6q6gPnYMIb2TJCs9eWA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", @@ -4171,8 +3314,7 @@ }, "node_modules/@mui/system": { "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.7.tgz", - "integrity": "sha512-9alZ4/dLxsTwUOdqakgzxiL5YW6ntqj0CfzWImgWnBMTZhgGcPsbYpBLniNkkk7/jptma4/bykWXHwju/ls/pg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.23.9", "@mui/private-theming": "^5.15.7", @@ -4210,8 +3352,7 @@ }, "node_modules/@mui/types": { "version": "7.2.13", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.13.tgz", - "integrity": "sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==", + "license": "MIT", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0" }, @@ -4223,8 +3364,7 @@ }, "node_modules/@mui/utils": { "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.7.tgz", - "integrity": "sha512-8qhsxQRNV6aEOjjSk6YQIYJxkF5klhj8oG1FEEU4z6HV78TjNqRxMP08QGcdsibEbez+nihAaz6vu83b4XqbAg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.23.9", "@types/prop-types": "^15.7.11", @@ -4706,9 +3846,8 @@ }, "node_modules/@pkgr/core": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, @@ -4718,8 +3857,7 @@ }, "node_modules/@popperjs/core": { "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -4749,210 +3887,64 @@ "license": "ISC" }, "node_modules/@redis/graph": { - "version": "1.1.0", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/json": { - "version": "1.0.6", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/search": { - "version": "1.1.5", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/time-series": { - "version": "1.0.5", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@remix-run/router": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", - "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz", - "integrity": "sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz", - "integrity": "sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz", - "integrity": "sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz", - "integrity": "sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz", - "integrity": "sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] + "version": "1.1.0", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz", - "integrity": "sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] + "node_modules/@redis/json": { + "version": "1.0.6", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz", - "integrity": "sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] + "node_modules/@redis/search": { + "version": "1.1.5", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz", - "integrity": "sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] + "node_modules/@redis/time-series": { + "version": "1.0.5", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@remix-run/router": { + "version": "1.15.3", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz", - "integrity": "sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==", + "version": "4.14.3", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz", - "integrity": "sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==", + "version": "4.14.3", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz", - "integrity": "sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz", - "integrity": "sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz", - "integrity": "sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@sentry-internal/tracing": { "version": "7.74.1", "license": "MIT", @@ -5126,9 +4118,8 @@ }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", - "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "ejs": "^3.1.6", "json5": "^2.2.0", @@ -5138,9 +4129,8 @@ }, "node_modules/@surma/rollup-plugin-off-main-thread/node_modules/magic-string": { "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", "dev": true, + "license": "MIT", "dependencies": { "sourcemap-codec": "^1.4.8" } @@ -5189,10 +4179,9 @@ }, "node_modules/@swc/core": { "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.107.tgz", - "integrity": "sha512-zKhqDyFcTsyLIYK1iEmavljZnf4CCor5pF52UzLAz4B6Nu/4GLU+2LQVAf+oRHjusG39PTPjd2AlRT3f3QWfsQ==", "dev": true, "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.1", "@swc/types": "^0.1.5" @@ -5225,94 +4214,13 @@ } } }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.107.tgz", - "integrity": "sha512-47tD/5vSXWxPd0j/ZllyQUg4bqalbQTsmqSw0J4dDdS82MWqCAwUErUrAZPRjBkjNQ6Kmrf5rpCWaGTtPw+ngw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.107.tgz", - "integrity": "sha512-hwiLJ2ulNkBGAh1m1eTfeY1417OAYbRGcb/iGsJ+LuVLvKAhU/itzsl535CvcwAlt2LayeCFfcI8gdeOLeZa9A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.107.tgz", - "integrity": "sha512-I2wzcC0KXqh0OwymCmYwNRgZ9nxX7DWnOOStJXV3pS0uB83TXAkmqd7wvMBuIl9qu4Hfomi9aDM7IlEEn9tumQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.107.tgz", - "integrity": "sha512-HWgnn7JORYlOYnGsdunpSF8A+BCZKPLzLtEUA27/M/ZuANcMZabKL9Zurt7XQXq888uJFAt98Gy+59PU90aHKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.107.tgz", - "integrity": "sha512-vfPF74cWfAm8hyhS8yvYI94ucMHIo8xIYU+oFOW9uvDlGQRgnUf/6DEVbLyt/3yfX5723Ln57U8uiMALbX5Pyw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@swc/core-linux-x64-gnu": { "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.107.tgz", - "integrity": "sha512-uBVNhIg0ip8rH9OnOsCARUFZ3Mq3tbPHxtmWk9uAa5u8jQwGWeBx5+nTHpDOVd3YxKb6+5xDEI/edeeLpha/9g==", "cpu": [ "x64" ], "dev": true, + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -5323,12 +4231,11 @@ }, "node_modules/@swc/core-linux-x64-musl": { "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.107.tgz", - "integrity": "sha512-mvACkUvzSIB12q1H5JtabWATbk3AG+pQgXEN95AmEX2ZA5gbP9+B+mijsg7Sd/3tboHr7ZHLz/q3SHTvdFJrEw==", "cpu": [ "x64" ], "dev": true, + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -5337,65 +4244,15 @@ "node": ">=10" } }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.107.tgz", - "integrity": "sha512-J3P14Ngy/1qtapzbguEH41kY109t6DFxfbK4Ntz9dOWNuVY3o9/RTB841ctnJk0ZHEG+BjfCJjsD2n8H5HcaOA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.107.tgz", - "integrity": "sha512-ZBUtgyjTHlz8TPJh7kfwwwFma+ktr6OccB1oXC8fMSopD0AxVnQasgun3l3099wIsAB9eEsJDQ/3lDkOLs1gBA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.107.tgz", - "integrity": "sha512-Eyzo2XRqWOxqhE1gk9h7LWmUf4Bp4Xn2Ttb0ayAXFp6YSTxQIThXcT9kipXZqcpxcmDwoq8iWbbf2P8XL743EA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@swc/counter": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", - "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/@swc/types": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", - "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", @@ -5412,8 +4269,7 @@ }, "node_modules/@tanstack/query-core": { "version": "5.20.5", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.20.5.tgz", - "integrity": "sha512-T1W28gGgWn0A++tH3lxj3ZuUVZZorsiKcv+R50RwmPYz62YoDEkG4/aXHZELGkRp4DfrW07dyq2K5dvJ4Wl1aA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -5421,8 +4277,7 @@ }, "node_modules/@tanstack/query-devtools": { "version": "5.20.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.20.2.tgz", - "integrity": "sha512-BZfSjhk/NGPbqte5E3Vc1Zbj28uWt///4I0DgzAdWrOtMVvdl0WlUXK23K2daLsbcyfoDR4jRI4f2Z5z/mMzuw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -5430,8 +4285,7 @@ }, "node_modules/@tanstack/react-query": { "version": "5.20.5", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.20.5.tgz", - "integrity": "sha512-6MHwJ8G9cnOC/XKrwt56QMc91vN7hLlAQNUA0ubP7h9Jj3a/CmkUwT6ALdFbnVP+PsYdhW3WONa8WQ4VcTaSLQ==", + "license": "MIT", "dependencies": { "@tanstack/query-core": "5.20.5" }, @@ -5445,8 +4299,7 @@ }, "node_modules/@tanstack/react-query-devtools": { "version": "5.20.5", - "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.20.5.tgz", - "integrity": "sha512-Wl7IzNuKCb4h41a5iH/YXNwalHItqJPCAr4r8+0iUYOLHNOf3E9P0G4kzZ9sqDoWKxY04qst6Vrij9bwPzLQRQ==", + "license": "MIT", "dependencies": { "@tanstack/query-devtools": "5.20.2" }, @@ -5475,9 +4328,8 @@ }, "node_modules/@trysound/sax": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10.13.0" } @@ -5503,7 +4355,7 @@ "license": "MIT" }, "node_modules/@types/babel__core": { - "version": "7.20.1", + "version": "7.20.5", "dev": true, "license": "MIT", "dependencies": { @@ -5613,9 +4465,8 @@ }, "node_modules/@types/estree": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/express": { "version": "4.17.18", @@ -5628,6 +4479,15 @@ "@types/serve-static": "*" } }, + "node_modules/@types/express-http-proxy": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/express-http-proxy/-/express-http-proxy-1.6.6.tgz", + "integrity": "sha512-J8ZqHG76rq1UB716IZ3RCmUhg406pbWxsM3oFCFccl5xlWUPzoR4if6Og/cE4juK8emH0H9quZa5ltn6ZdmQJg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/express-serve-static-core": { "version": "4.17.36", "dev": true, @@ -5667,9 +4527,8 @@ }, "node_modules/@types/hbs": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/hbs/-/hbs-4.0.4.tgz", - "integrity": "sha512-GH3SIb2tzDBnTByUSOIVcD6AcLufnydBllTuFAIAGMhqPNbz8GL4tLryVdNqhq0NQEb5mVpu2FJOrUeqwJrPtg==", "dev": true, + "license": "MIT", "dependencies": { "handlebars": "^4.1.0" } @@ -5746,8 +4605,7 @@ }, "node_modules/@types/prop-types": { "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + "license": "MIT" }, "node_modules/@types/qrcode": { "version": "1.5.5", @@ -5769,8 +4627,7 @@ }, "node_modules/@types/react": { "version": "18.2.48", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz", - "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", + "license": "MIT", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -5779,26 +4636,23 @@ }, "node_modules/@types/react-dom": { "version": "18.2.18", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", - "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", "dev": true, + "license": "MIT", "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-transition-group": { "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "license": "MIT", "dependencies": { "@types/react": "*" } }, "node_modules/@types/resolve": { "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -5815,14 +4669,12 @@ }, "node_modules/@types/scheduler": { "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" + "license": "MIT" }, "node_modules/@types/semver": { "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/send": { "version": "0.17.1", @@ -5885,9 +4737,8 @@ }, "node_modules/@types/trusted-types": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/ua-parser-js": { "version": "0.7.37", @@ -5921,9 +4772,8 @@ }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.21.0", @@ -5956,9 +4806,8 @@ }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" @@ -5973,9 +4822,8 @@ }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, + "license": "MIT", "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -5986,9 +4834,8 @@ }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" @@ -6003,9 +4850,8 @@ }, "node_modules/@typescript-eslint/parser": { "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz", - "integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "6.20.0", "@typescript-eslint/types": "6.20.0", @@ -6031,9 +4877,8 @@ }, "node_modules/@typescript-eslint/scope-manager": { "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", - "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.20.0", "@typescript-eslint/visitor-keys": "6.20.0" @@ -6048,9 +4893,8 @@ }, "node_modules/@typescript-eslint/type-utils": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/utils": "6.21.0", @@ -6075,9 +4919,8 @@ }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, + "license": "MIT", "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -6088,9 +4931,8 @@ }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", @@ -6116,9 +4958,8 @@ }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" @@ -6133,18 +4974,16 @@ }, "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6157,9 +4996,8 @@ }, "node_modules/@typescript-eslint/types": { "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", - "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", "dev": true, + "license": "MIT", "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -6170,9 +5008,8 @@ }, "node_modules/@typescript-eslint/typescript-estree": { "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", - "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "6.20.0", "@typescript-eslint/visitor-keys": "6.20.0", @@ -6198,18 +5035,16 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6222,9 +5057,8 @@ }, "node_modules/@typescript-eslint/utils": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", @@ -6247,9 +5081,8 @@ }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" @@ -6264,9 +5097,8 @@ }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, + "license": "MIT", "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -6277,9 +5109,8 @@ }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", @@ -6305,9 +5136,8 @@ }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" @@ -6322,18 +5152,16 @@ }, "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/@typescript-eslint/utils/node_modules/minimatch": { "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6346,9 +5174,8 @@ }, "node_modules/@typescript-eslint/visitor-keys": { "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", - "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.20.0", "eslint-visitor-keys": "^3.4.1" @@ -6363,15 +5190,13 @@ }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@vitejs/plugin-react-swc": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.6.0.tgz", - "integrity": "sha512-XFRbsGgpGxGzEV5i5+vRiro1bwcIaZDIdBRP16qwm+jP68ue/S8FJTBEgOeojtVDYrbSua3XFp71kC8VJE6v+g==", "dev": true, + "license": "MIT", "dependencies": { "@swc/core": "^1.3.107" }, @@ -6536,7 +5361,7 @@ } }, "node_modules/acorn": { - "version": "8.10.0", + "version": "8.11.3", "dev": true, "license": "MIT", "bin": { @@ -6556,9 +5381,8 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -6723,6 +5547,12 @@ "node": ">=8" } }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, "node_modules/ansi-styles": { "version": "4.3.0", "license": "MIT", @@ -6807,15 +5637,13 @@ }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "is-array-buffer": "^3.0.4" @@ -6838,18 +5666,16 @@ }, "node_modules/array-union": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.5", @@ -6903,9 +5729,8 @@ }, "node_modules/async": { "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/asynckit": { "version": "0.4.0", @@ -6914,18 +5739,16 @@ }, "node_modules/at-least-node": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, + "license": "ISC", "engines": { "node": ">= 4.0.0" } }, "node_modules/available-typed-arrays": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -7010,8 +5833,7 @@ }, "node_modules/babel-plugin-macros": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", @@ -7024,9 +5846,8 @@ }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.8", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz", - "integrity": "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.5.0", @@ -7038,18 +5859,16 @@ }, "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/babel-plugin-polyfill-corejs3": { "version": "0.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", - "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.5.0", "core-js-compat": "^3.34.0" @@ -7060,9 +5879,8 @@ }, "node_modules/babel-plugin-polyfill-regenerator": { "version": "0.5.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", - "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.5.0" }, @@ -7397,9 +6215,8 @@ }, "node_modules/boolbase": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -7426,8 +6243,6 @@ }, "node_modules/browserslist": { "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, "funding": [ { @@ -7443,6 +6258,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "caniuse-lite": "^1.0.30001587", "electron-to-chromium": "^1.4.668", @@ -7510,9 +6326,8 @@ }, "node_modules/builtin-modules": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -7682,8 +6497,7 @@ }, "node_modules/call-bind": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -7715,8 +6529,6 @@ }, "node_modules/caniuse-lite": { "version": "1.0.30001587", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz", - "integrity": "sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==", "dev": true, "funding": [ { @@ -7731,7 +6543,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/cbor": { "version": "5.2.0", @@ -7934,8 +6747,7 @@ }, "node_modules/clsx": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -7963,9 +6775,8 @@ }, "node_modules/color": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" @@ -7990,9 +6801,8 @@ }, "node_modules/color-string": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -8041,9 +6851,8 @@ }, "node_modules/common-tags": { "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.0.0" } @@ -8072,9 +6881,8 @@ }, "node_modules/concurrently": { "version": "8.2.2", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", - "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.2", "date-fns": "^2.30.0", @@ -8099,9 +6907,8 @@ }, "node_modules/concurrently/node_modules/supports-color": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -8193,9 +7000,8 @@ }, "node_modules/core-js-compat": { "version": "3.36.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz", - "integrity": "sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.22.3" }, @@ -8296,18 +7102,16 @@ }, "node_modules/crypto-random-string": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/css-select": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -8321,9 +7125,8 @@ }, "node_modules/css-tree": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" @@ -8334,9 +7137,8 @@ }, "node_modules/css-what": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">= 6" }, @@ -8346,9 +7148,8 @@ }, "node_modules/csso": { "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, + "license": "MIT", "dependencies": { "css-tree": "~2.2.0" }, @@ -8359,9 +7160,8 @@ }, "node_modules/csso/node_modules/css-tree": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" @@ -8373,14 +7173,12 @@ }, "node_modules/csso/node_modules/mdn-data": { "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/csstype": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + "license": "MIT" }, "node_modules/dapp-ui": { "resolved": "sites/dapp-ui", @@ -8388,9 +7186,8 @@ }, "node_modules/data-view-buffer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -8405,9 +7202,8 @@ }, "node_modules/data-view-byte-length": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -8422,9 +7218,8 @@ }, "node_modules/data-view-byte-offset": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -8439,9 +7234,8 @@ }, "node_modules/date-fns": { "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -8546,8 +7340,7 @@ }, "node_modules/define-data-property": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -8562,9 +7355,8 @@ }, "node_modules/define-properties": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -8579,9 +7371,8 @@ }, "node_modules/define-properties/node_modules/object-keys": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -8664,9 +7455,8 @@ }, "node_modules/dir-glob": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, + "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -8687,8 +7477,7 @@ }, "node_modules/dom-helpers": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" @@ -8696,9 +7485,8 @@ }, "node_modules/dom-serializer": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -8710,21 +7498,19 @@ }, "node_modules/domelementtype": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/fb55" } - ] + ], + "license": "BSD-2-Clause" }, "node_modules/domhandler": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -8737,9 +7523,8 @@ }, "node_modules/domutils": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -8768,8 +7553,7 @@ }, "node_modules/duplexer2": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "license": "BSD-3-Clause", "dependencies": { "readable-stream": "^2.0.2" } @@ -8784,9 +7568,8 @@ }, "node_modules/ejs": { "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" }, @@ -8799,9 +7582,8 @@ }, "node_modules/electron-to-chromium": { "version": "1.4.670", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.670.tgz", - "integrity": "sha512-hcijYOWjOtjKrKPtNA6tuLlA/bTLO3heFG8pQA6mLpq7dRydSWicXova5lyxDzp1iVJaYhK7J2OQlGE52KYn7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/elliptic": { "version": "6.5.4", @@ -8886,8 +7668,7 @@ }, "node_modules/engine.io-client": { "version": "6.5.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", - "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", @@ -8924,9 +7705,8 @@ }, "node_modules/entities": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -8954,9 +7734,8 @@ }, "node_modules/es-abstract": { "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", @@ -9014,17 +7793,15 @@ }, "node_modules/es-abstract/node_modules/object-keys": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-define-property": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -9034,22 +7811,20 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-module-lexer": { - "version": "1.3.1", + "version": "1.5.0", "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -9059,9 +7834,8 @@ }, "node_modules/es-set-tostringtag": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4", "has-tostringtag": "^1.0.2", @@ -9073,9 +7847,8 @@ }, "node_modules/es-to-primitive": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -9088,43 +7861,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" - } + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, "node_modules/escalade": { "version": "3.1.1", @@ -9150,9 +7890,8 @@ }, "node_modules/eslint": { "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -9205,9 +7944,8 @@ }, "node_modules/eslint-config-prettier": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, + "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -9217,9 +7955,8 @@ }, "node_modules/eslint-plugin-prettier": { "version": "5.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", - "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", "dev": true, + "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", "synckit": "^0.8.6" @@ -9247,9 +7984,8 @@ }, "node_modules/eslint-plugin-react-hooks": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -9259,13 +7995,22 @@ }, "node_modules/eslint-plugin-react-refresh": { "version": "0.4.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.5.tgz", - "integrity": "sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==", "dev": true, + "license": "MIT", "peerDependencies": { "eslint": ">=7" } }, + "node_modules/eslint-plugin-tsdoc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", + "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "0.16.2" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "dev": true, @@ -9325,9 +8070,8 @@ }, "node_modules/espree": { "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -9384,9 +8128,8 @@ }, "node_modules/estree-walker": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/esutils": { "version": "2.0.3", @@ -9403,6 +8146,10 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "dev": true, @@ -9512,6 +8259,27 @@ "node": ">= 0.10.0" } }, + "node_modules/express-http-proxy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/express-http-proxy/-/express-http-proxy-2.0.0.tgz", + "integrity": "sha512-TXxcPFTWVUMSEmyM6iX2sT/JtmqhqngTq29P+eXTVFdtxZrTmM8THUYK59rUXiln0FfPGvxEpGRnVrgvHksXDw==", + "dependencies": { + "debug": "^3.0.1", + "es6-promise": "^4.1.1", + "raw-body": "^2.3.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/express-http-proxy/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/express-session": { "version": "1.17.3", "license": "MIT", @@ -9669,9 +8437,8 @@ }, "node_modules/fast-glob": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -9766,27 +8533,24 @@ }, "node_modules/filelist": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, + "license": "Apache-2.0", "dependencies": { "minimatch": "^5.0.1" } }, "node_modules/filelist/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/filelist/node_modules/minimatch": { "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -9865,8 +8629,7 @@ }, "node_modules/find-root": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + "license": "MIT" }, "node_modules/find-up": { "version": "5.0.0", @@ -9933,17 +8696,15 @@ }, "node_modules/for-each": { "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.1.3" } }, "node_modules/foreachasync": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", - "integrity": "sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==" + "license": "Apache2" }, "node_modules/foreground-child": { "version": "3.1.1", @@ -10076,33 +8837,17 @@ "version": "1.0.0", "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/function.prototype.name": { "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -10118,9 +8863,8 @@ }, "node_modules/functions-have-names": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10167,8 +8911,7 @@ }, "node_modules/get-intrinsic": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -10185,9 +8928,8 @@ }, "node_modules/get-own-enumerable-property-symbols": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/get-package-type": { "version": "0.1.0", @@ -10210,9 +8952,8 @@ }, "node_modules/get-symbol-description": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "es-errors": "^1.3.0", @@ -10261,9 +9002,8 @@ }, "node_modules/globals": { "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -10276,9 +9016,8 @@ }, "node_modules/globalthis": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.1.3" }, @@ -10291,9 +9030,8 @@ }, "node_modules/globby": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, + "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -10311,8 +9049,7 @@ }, "node_modules/gopd": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -10357,8 +9094,7 @@ }, "node_modules/handlebars": { "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "license": "MIT", "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.0", @@ -10377,17 +9113,15 @@ }, "node_modules/handlebars/node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/has-bigints": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10409,8 +9143,7 @@ }, "node_modules/has-property-descriptors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -10420,8 +9153,7 @@ }, "node_modules/has-proto": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -10441,9 +9173,8 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -10468,8 +9199,7 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -10479,8 +9209,7 @@ }, "node_modules/hbs": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hbs/-/hbs-4.2.0.tgz", - "integrity": "sha512-dQwHnrfWlTk5PvG9+a45GYpg0VpX47ryKF8dULVd6DtwOE6TEcYQXQ5QM6nyOx/h7v3bvEQbdn19EDAcfUAgZg==", + "license": "MIT", "dependencies": { "handlebars": "4.7.7", "walk": "2.3.15" @@ -10500,8 +9229,7 @@ }, "node_modules/hi-base32": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", - "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==" + "license": "MIT" }, "node_modules/hmac-drbg": { "version": "1.0.1", @@ -10514,16 +9242,14 @@ }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", "dependencies": { "react-is": "^16.7.0" } }, "node_modules/hoist-non-react-statics/node_modules/react-is": { "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "license": "MIT" }, "node_modules/html-escaper": { "version": "2.0.2", @@ -10532,8 +9258,7 @@ }, "node_modules/html-tokenize": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-tokenize/-/html-tokenize-2.0.1.tgz", - "integrity": "sha512-QY6S+hZ0f5m1WT8WffYN+Hg+xm/w5I8XeUcAq/ZYP5wVC8xbKi4Whhru3FtrAebD5EhBW8rmFzkDI6eCAuFe2w==", + "license": "MIT", "dependencies": { "buffer-from": "~0.1.1", "inherits": "~2.0.1", @@ -10547,18 +9272,15 @@ }, "node_modules/html-tokenize/node_modules/buffer-from": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", - "integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==" + "license": "MIT" }, "node_modules/html-tokenize/node_modules/isarray": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + "license": "MIT" }, "node_modules/html-tokenize/node_modules/readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -10568,8 +9290,7 @@ }, "node_modules/html-tokenize/node_modules/string_decoder": { "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + "license": "MIT" }, "node_modules/http-cache-semantics": { "version": "4.1.1", @@ -10653,9 +9374,8 @@ }, "node_modules/idb": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", - "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/ieee754": { "version": "1.2.1", @@ -10768,9 +9488,8 @@ }, "node_modules/internal-slot": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.0", @@ -10825,9 +9544,8 @@ }, "node_modules/is-array-buffer": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.1" @@ -10845,9 +9563,8 @@ }, "node_modules/is-bigint": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, + "license": "MIT", "dependencies": { "has-bigints": "^1.0.1" }, @@ -10868,9 +9585,8 @@ }, "node_modules/is-boolean-object": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -10884,9 +9600,8 @@ }, "node_modules/is-callable": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -10896,8 +9611,7 @@ }, "node_modules/is-core-module": { "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "license": "MIT", "dependencies": { "hasown": "^2.0.0" }, @@ -10907,9 +9621,8 @@ }, "node_modules/is-data-view": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "dev": true, + "license": "MIT", "dependencies": { "is-typed-array": "^1.1.13" }, @@ -10922,9 +9635,8 @@ }, "node_modules/is-date-object": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -10983,15 +9695,13 @@ }, "node_modules/is-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-negative-zero": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11009,9 +9719,8 @@ }, "node_modules/is-number-object": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -11024,9 +9733,8 @@ }, "node_modules/is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11051,9 +9759,8 @@ }, "node_modules/is-regex": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -11067,18 +9774,16 @@ }, "node_modules/is-regexp": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-shared-array-buffer": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7" }, @@ -11102,9 +9807,8 @@ }, "node_modules/is-string": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -11117,9 +9821,8 @@ }, "node_modules/is-symbol": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.2" }, @@ -11132,9 +9835,8 @@ }, "node_modules/is-typed-array": { "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, + "license": "MIT", "dependencies": { "which-typed-array": "^1.1.14" }, @@ -11158,9 +9860,8 @@ }, "node_modules/is-weakref": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2" }, @@ -11270,9 +9971,8 @@ }, "node_modules/jake": { "version": "10.8.7", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", - "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -11830,6 +10530,11 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jju": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, "node_modules/js-sha256": { "version": "0.9.0", "dev": true, @@ -11851,9 +10556,8 @@ }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -11891,9 +10595,8 @@ }, "node_modules/json-schema": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" }, "node_modules/json-schema-traverse": { "version": "1.0.0", @@ -11934,9 +10637,8 @@ }, "node_modules/jsonpointer": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -12042,9 +10744,8 @@ }, "node_modules/lodash.debounce": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.defaults": { "version": "4.2.0", @@ -12070,9 +10771,8 @@ }, "node_modules/lodash.sortby": { "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", @@ -12091,8 +10791,7 @@ }, "node_modules/loose-envify": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -12122,6 +10821,12 @@ "yallist": "^3.0.2" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, "node_modules/macos-release": { "version": "2.5.1", "dev": true, @@ -12209,11 +10914,22 @@ "tmpl": "1.0.5" } }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/mdn-data": { "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/media-typer": { "version": "0.3.0", @@ -12643,8 +11359,7 @@ }, "node_modules/multipipe": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-1.0.2.tgz", - "integrity": "sha512-6uiC9OvY71vzSGX8lZvSqscE7ft9nPupJ8fMjrCNRAUy2LREUW42UL+V/NTrogr6rFgRydUrCX4ZitfpSNkSCQ==", + "license": "MIT", "dependencies": { "duplexer2": "^0.1.2", "object-assign": "^4.1.0" @@ -12657,8 +11372,6 @@ }, "node_modules/nanoid": { "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, "funding": [ { @@ -12666,6 +11379,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -12773,9 +11487,8 @@ }, "node_modules/node-releases": { "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-rsa": { "version": "1.1.1", @@ -12855,9 +11568,8 @@ }, "node_modules/nth-check": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0" }, @@ -12881,22 +11593,19 @@ }, "node_modules/object-inspect": { "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-keys": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==" + "license": "MIT" }, "node_modules/object.assign": { "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "define-properties": "^1.2.1", @@ -12912,9 +11621,8 @@ }, "node_modules/object.assign/node_modules/object-keys": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -13200,9 +11908,8 @@ }, "node_modules/pathe": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/peek-readable": { "version": "5.0.0", @@ -13220,7 +11927,6 @@ }, "node_modules/picocolors": { "version": "1.0.0", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -13321,17 +12027,14 @@ }, "node_modules/possible-typed-array-names": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.4.33", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", - "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "version": "8.4.38", "dev": true, "funding": [ { @@ -13347,10 +12050,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -13391,9 +12095,8 @@ }, "node_modules/pretty-bytes": { "version": "6.1.1", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", - "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", "dev": true, + "license": "MIT", "engines": { "node": "^14.13.1 || >=16.0.0" }, @@ -13454,8 +12157,7 @@ }, "node_modules/prop-types": { "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -13464,8 +12166,7 @@ }, "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "license": "MIT" }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -13532,16 +12233,14 @@ }, "node_modules/qr-code-styling": { "version": "1.6.0-rc.1", - "resolved": "https://registry.npmjs.org/qr-code-styling/-/qr-code-styling-1.6.0-rc.1.tgz", - "integrity": "sha512-ModRIiW6oUnsP18QzrRYZSc/CFKFKIdj7pUs57AEVH20ajlglRpN3HukjHk0UbNMTlKGuaYl7Gt6/O5Gg2NU2Q==", + "license": "MIT", "dependencies": { "qrcode-generator": "^1.4.3" } }, "node_modules/qrcode-generator": { "version": "1.4.4", - "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz", - "integrity": "sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==" + "license": "MIT" }, "node_modules/qs": { "version": "6.11.0", @@ -13625,8 +12324,7 @@ }, "node_modules/react": { "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" }, @@ -13636,8 +12334,7 @@ }, "node_modules/react-dom": { "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -13652,8 +12349,7 @@ }, "node_modules/react-router": { "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", - "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "license": "MIT", "dependencies": { "@remix-run/router": "1.15.3" }, @@ -13666,8 +12362,7 @@ }, "node_modules/react-router-dom": { "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", - "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "license": "MIT", "dependencies": { "@remix-run/router": "1.15.3", "react-router": "6.22.3" @@ -13682,8 +12377,7 @@ }, "node_modules/react-transition-group": { "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -13807,15 +12501,13 @@ }, "node_modules/regenerate": { "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", "dev": true, + "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, @@ -13825,23 +12517,20 @@ }, "node_modules/regenerator-runtime": { "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + "license": "MIT" }, "node_modules/regenerator-transform": { "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.4" } }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "define-properties": "^1.2.1", @@ -13857,9 +12546,8 @@ }, "node_modules/regexpu-core": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", @@ -13874,9 +12562,8 @@ }, "node_modules/regjsparser": { "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "jsesc": "~0.5.0" }, @@ -13886,8 +12573,6 @@ }, "node_modules/regjsparser/node_modules/jsesc": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", "dev": true, "bin": { "jsesc": "bin/jsesc" @@ -14071,10 +12756,9 @@ } }, "node_modules/rollup": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz", - "integrity": "sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==", + "version": "4.14.3", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "1.0.5" }, @@ -14086,19 +12770,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.9.6", - "@rollup/rollup-android-arm64": "4.9.6", - "@rollup/rollup-darwin-arm64": "4.9.6", - "@rollup/rollup-darwin-x64": "4.9.6", - "@rollup/rollup-linux-arm-gnueabihf": "4.9.6", - "@rollup/rollup-linux-arm64-gnu": "4.9.6", - "@rollup/rollup-linux-arm64-musl": "4.9.6", - "@rollup/rollup-linux-riscv64-gnu": "4.9.6", - "@rollup/rollup-linux-x64-gnu": "4.9.6", - "@rollup/rollup-linux-x64-musl": "4.9.6", - "@rollup/rollup-win32-arm64-msvc": "4.9.6", - "@rollup/rollup-win32-ia32-msvc": "4.9.6", - "@rollup/rollup-win32-x64-msvc": "4.9.6", + "@rollup/rollup-android-arm-eabi": "4.14.3", + "@rollup/rollup-android-arm64": "4.14.3", + "@rollup/rollup-darwin-arm64": "4.14.3", + "@rollup/rollup-darwin-x64": "4.14.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.3", + "@rollup/rollup-linux-arm-musleabihf": "4.14.3", + "@rollup/rollup-linux-arm64-gnu": "4.14.3", + "@rollup/rollup-linux-arm64-musl": "4.14.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.3", + "@rollup/rollup-linux-riscv64-gnu": "4.14.3", + "@rollup/rollup-linux-s390x-gnu": "4.14.3", + "@rollup/rollup-linux-x64-gnu": "4.14.3", + "@rollup/rollup-linux-x64-musl": "4.14.3", + "@rollup/rollup-win32-arm64-msvc": "4.14.3", + "@rollup/rollup-win32-ia32-msvc": "4.14.3", + "@rollup/rollup-win32-x64-msvc": "4.14.3", "fsevents": "~2.3.2" } }, @@ -14141,9 +12828,8 @@ }, "node_modules/safe-array-concat": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "get-intrinsic": "^1.2.4", @@ -14159,9 +12845,8 @@ }, "node_modules/safe-array-concat/node_modules/isarray": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/safe-buffer": { "version": "5.2.1", @@ -14183,9 +12868,8 @@ }, "node_modules/safe-regex-test": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -14204,8 +12888,7 @@ }, "node_modules/scheduler": { "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } @@ -14257,8 +12940,7 @@ }, "node_modules/semver": { "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -14376,8 +13058,7 @@ }, "node_modules/set-function-length": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "license": "MIT", "dependencies": { "define-data-property": "^1.1.2", "es-errors": "^1.3.0", @@ -14392,9 +13073,8 @@ }, "node_modules/set-function-name": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "functions-have-names": "^1.2.3", @@ -14410,10 +13090,9 @@ }, "node_modules/sharp": { "version": "0.33.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.2.tgz", - "integrity": "sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==", "dev": true, "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.2", @@ -14467,9 +13146,8 @@ }, "node_modules/shell-quote": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -14512,18 +13190,16 @@ }, "node_modules/simple-swizzle": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "dev": true, + "license": "MIT", "dependencies": { "is-arrayish": "^0.3.1" } }, "node_modules/simple-swizzle/node_modules/is-arrayish": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/sisteransi": { "version": "1.0.5", @@ -14570,9 +13246,8 @@ } }, "node_modules/socket.io-client": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz", - "integrity": "sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==", + "version": "4.7.5", + "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", @@ -14653,10 +13328,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -14680,10 +13354,8 @@ }, "node_modules/sourcemap-codec": { "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/sparse-bitfield": { "version": "3.0.3", @@ -14695,8 +13367,6 @@ }, "node_modules/spawn-command": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", - "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", "dev": true }, "node_modules/sprintf-js": { @@ -14809,9 +13479,8 @@ }, "node_modules/string.prototype.matchall": { "version": "4.0.10", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", - "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -14829,9 +13498,8 @@ }, "node_modules/string.prototype.trim": { "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -14847,9 +13515,8 @@ }, "node_modules/string.prototype.trimend": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -14861,9 +13528,8 @@ }, "node_modules/string.prototype.trimstart": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -14878,9 +13544,8 @@ }, "node_modules/stringify-object": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "get-own-enumerable-property-symbols": "^3.0.0", "is-obj": "^1.0.1", @@ -14921,9 +13586,8 @@ }, "node_modules/strip-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", - "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } @@ -14990,8 +13654,7 @@ }, "node_modules/stylis": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + "license": "MIT" }, "node_modules/superagent": { "version": "8.1.2", @@ -15058,9 +13721,8 @@ }, "node_modules/svgo": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", - "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", "dev": true, + "license": "MIT", "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", @@ -15083,9 +13745,8 @@ }, "node_modules/svgo/node_modules/commander": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" } @@ -15100,9 +13761,8 @@ }, "node_modules/synckit": { "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", "dev": true, + "license": "MIT", "dependencies": { "@pkgr/core": "^0.1.0", "tslib": "^2.6.2" @@ -15180,18 +13840,16 @@ }, "node_modules/temp-dir": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/tempy": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", - "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", "dev": true, + "license": "MIT", "dependencies": { "is-stream": "^2.0.0", "temp-dir": "^2.0.0", @@ -15207,9 +13865,8 @@ }, "node_modules/tempy/node_modules/type-fest": { "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -15323,8 +13980,7 @@ }, "node_modules/through2": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", - "integrity": "sha512-45Llu+EwHKtAZYTPPVn3XZHBgakWMN3rokhEv5hu596XP+cNgplMg+Gj+1nmAvj+L0K7+N49zBKx5rah5u0QIQ==", + "license": "MIT", "dependencies": { "readable-stream": "~1.0.17", "xtend": "~2.1.1" @@ -15332,13 +13988,11 @@ }, "node_modules/through2/node_modules/isarray": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + "license": "MIT" }, "node_modules/through2/node_modules/readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -15348,13 +14002,10 @@ }, "node_modules/through2/node_modules/string_decoder": { "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + "license": "MIT" }, "node_modules/through2/node_modules/xtend": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha512-vMNKzr2rHP9Dp/e1NQFnLQlwlhp9L/LfvnsVdHxN1f+uggyVI3i08uD14GPvCToPkdsRfyPqIyYGmIk58V98ZQ==", "dependencies": { "object-keys": "~0.4.0" }, @@ -15461,9 +14112,8 @@ }, "node_modules/ts-api-utils": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -15613,8 +14263,7 @@ }, "node_modules/tweetnacl": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + "license": "Unlicense" }, "node_modules/type-check": { "version": "0.4.0", @@ -15637,9 +14286,8 @@ }, "node_modules/type-fest": { "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -15660,9 +14308,8 @@ }, "node_modules/typed-array-buffer": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -15674,9 +14321,8 @@ }, "node_modules/typed-array-byte-length": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -15693,9 +14339,8 @@ }, "node_modules/typed-array-byte-offset": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", @@ -15713,9 +14358,8 @@ }, "node_modules/typed-array-length": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -15735,11 +14379,76 @@ "version": "0.0.6", "license": "MIT" }, + "node_modules/typedoc": { + "version": "0.25.13", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.13.tgz", + "integrity": "sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.7" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "4.0.0-next.55", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.0.0-next.55.tgz", + "integrity": "sha512-bwEEBkAP0kHnjE10QzPBXAri5yHYmgb+vZP+xn9GirBLCk3lt7SMUiUx0fj7lEtTmbgIH0rv20f3xP+JWxCPHg==", + "dev": true, + "peerDependencies": { + "typedoc": "0.25.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typedoc/node_modules/shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, "node_modules/typescript": { "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15771,8 +14480,7 @@ }, "node_modules/uglify-js": { "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "license": "BSD-2-Clause", "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -15810,9 +14518,8 @@ }, "node_modules/unbox-primitive": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -15825,18 +14532,16 @@ }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/unicode-match-property-ecmascript": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, + "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -15847,18 +14552,16 @@ }, "node_modules/unicode-match-property-value-ecmascript": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/unicode-property-aliases-ecmascript": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -15885,9 +14588,8 @@ }, "node_modules/unique-string": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", "dev": true, + "license": "MIT", "dependencies": { "crypto-random-string": "^2.0.0" }, @@ -15912,9 +14614,8 @@ }, "node_modules/upath": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4", "yarn": "*" @@ -15922,8 +14623,6 @@ }, "node_modules/update-browserslist-db": { "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -15939,6 +14638,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -15960,8 +14660,7 @@ }, "node_modules/use-sync-external-store": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } @@ -16015,14 +14714,13 @@ } }, "node_modules/vite": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", - "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", + "version": "5.2.9", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.32", - "rollup": "^4.2.0" + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" }, "bin": { "vite": "bin/vite.js" @@ -16071,9 +14769,8 @@ }, "node_modules/vite-plugin-image-optimizer": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/vite-plugin-image-optimizer/-/vite-plugin-image-optimizer-1.1.7.tgz", - "integrity": "sha512-KPJWndwqVi7Z2hYCudzKeNDw5U7w1DxAc266bqDBKV8taG8W3EtripFuUM4Y05IlFC19yBQndJCFA8+NJymH+w==", "dev": true, + "license": "MIT", "dependencies": { "ansi-colors": "^4.1.3", "pathe": "^1.1.1" @@ -16087,9 +14784,8 @@ }, "node_modules/vite-plugin-pwa": { "version": "0.18.1", - "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.18.1.tgz", - "integrity": "sha512-2A3BF52l9F8hCkdPy/VP2C+hA+fmBvzJGynCZc9XS6mHTcMvo9046FKc2NqlnkKwTOGtQEwXLEIduML/+eYtdw==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4", "fast-glob": "^3.3.2", @@ -16109,15 +14805,78 @@ "workbox-window": "^7.0.0" } }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.20.2", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, "node_modules/vlq": { "version": "2.0.4", "dev": true, "license": "MIT" }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, "node_modules/walk": { "version": "2.3.15", - "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.15.tgz", - "integrity": "sha512-4eRTBZljBfIISK1Vnt69Gvr2w/wc3U6Vtrw7qiN5iqYJPH7LElcYh/iU4XWhdCy2dZqv1ToMyYlybDylfG/5Vg==", + "license": "(MIT OR Apache-2.0)", "dependencies": { "foreachasync": "^3.0.0" } @@ -16259,9 +15018,8 @@ }, "node_modules/which-boxed-primitive": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, + "license": "MIT", "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -16275,9 +15033,8 @@ }, "node_modules/which-typed-array": { "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", @@ -16359,14 +15116,12 @@ }, "node_modules/wordwrap": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + "license": "MIT" }, "node_modules/workbox-background-sync": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.0.0.tgz", - "integrity": "sha512-S+m1+84gjdueM+jIKZ+I0Lx0BDHkk5Nu6a3kTVxP4fdj3gKouRNmhO8H290ybnJTOPfBDtTMXSQA/QLTvr7PeA==", "dev": true, + "license": "MIT", "dependencies": { "idb": "^7.0.1", "workbox-core": "7.0.0" @@ -16374,18 +15129,16 @@ }, "node_modules/workbox-broadcast-update": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.0.0.tgz", - "integrity": "sha512-oUuh4jzZrLySOo0tC0WoKiSg90bVAcnE98uW7F8GFiSOXnhogfNDGZelPJa+6KpGBO5+Qelv04Hqx2UD+BJqNQ==", "dev": true, + "license": "MIT", "dependencies": { "workbox-core": "7.0.0" } }, "node_modules/workbox-build": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-7.0.0.tgz", - "integrity": "sha512-CttE7WCYW9sZC+nUYhQg3WzzGPr4IHmrPnjKiu3AMXsiNQKx+l4hHl63WTrnicLmKEKHScWDH8xsGBdrYgtBzg==", "dev": true, + "license": "MIT", "dependencies": { "@apideck/better-ajv-errors": "^0.3.1", "@babel/core": "^7.11.1", @@ -16431,9 +15184,8 @@ }, "node_modules/workbox-build/node_modules/@rollup/plugin-babel": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", - "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.10.4", "@rollup/pluginutils": "^3.1.0" @@ -16454,9 +15206,8 @@ }, "node_modules/workbox-build/node_modules/@rollup/plugin-node-resolve": { "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", "dev": true, + "license": "MIT", "dependencies": { "@rollup/pluginutils": "^3.1.0", "@types/resolve": "1.17.1", @@ -16474,9 +15225,8 @@ }, "node_modules/workbox-build/node_modules/@rollup/plugin-replace": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", - "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", "dev": true, + "license": "MIT", "dependencies": { "@rollup/pluginutils": "^3.1.0", "magic-string": "^0.25.7" @@ -16487,9 +15237,8 @@ }, "node_modules/workbox-build/node_modules/@rollup/pluginutils": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", @@ -16504,15 +15253,13 @@ }, "node_modules/workbox-build/node_modules/@types/estree": { "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/workbox-build/node_modules/fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -16525,9 +15272,8 @@ }, "node_modules/workbox-build/node_modules/jest-worker": { "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -16539,18 +15285,16 @@ }, "node_modules/workbox-build/node_modules/magic-string": { "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", "dev": true, + "license": "MIT", "dependencies": { "sourcemap-codec": "^1.4.8" } }, "node_modules/workbox-build/node_modules/pretty-bytes": { "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -16560,9 +15304,8 @@ }, "node_modules/workbox-build/node_modules/rollup": { "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, + "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, @@ -16575,10 +15318,8 @@ }, "node_modules/workbox-build/node_modules/rollup-plugin-terser": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.10.4", "jest-worker": "^26.2.1", @@ -16591,18 +15332,16 @@ }, "node_modules/workbox-build/node_modules/serialize-javascript": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/workbox-build/node_modules/source-map": { "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "whatwg-url": "^7.0.0" }, @@ -16612,24 +15351,21 @@ }, "node_modules/workbox-build/node_modules/tr46": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, + "license": "MIT", "dependencies": { "punycode": "^2.1.0" } }, "node_modules/workbox-build/node_modules/webidl-conversions": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/workbox-build/node_modules/whatwg-url": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", "dev": true, + "license": "MIT", "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", @@ -16638,24 +15374,21 @@ }, "node_modules/workbox-cacheable-response": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.0.0.tgz", - "integrity": "sha512-0lrtyGHn/LH8kKAJVOQfSu3/80WDc9Ma8ng0p2i/5HuUndGttH+mGMSvOskjOdFImLs2XZIimErp7tSOPmu/6g==", "dev": true, + "license": "MIT", "dependencies": { "workbox-core": "7.0.0" } }, "node_modules/workbox-core": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-7.0.0.tgz", - "integrity": "sha512-81JkAAZtfVP8darBpfRTovHg8DGAVrKFgHpOArZbdFd78VqHr5Iw65f2guwjE2NlCFbPFDoez3D3/6ZvhI/rwQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/workbox-expiration": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-7.0.0.tgz", - "integrity": "sha512-MLK+fogW+pC3IWU9SFE+FRStvDVutwJMR5if1g7oBJx3qwmO69BNoJQVaMXq41R0gg3MzxVfwOGKx3i9P6sOLQ==", "dev": true, + "license": "MIT", "dependencies": { "idb": "^7.0.1", "workbox-core": "7.0.0" @@ -16663,10 +15396,8 @@ }, "node_modules/workbox-google-analytics": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-7.0.0.tgz", - "integrity": "sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==", - "deprecated": "It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained", "dev": true, + "license": "MIT", "dependencies": { "workbox-background-sync": "7.0.0", "workbox-core": "7.0.0", @@ -16676,18 +15407,16 @@ }, "node_modules/workbox-navigation-preload": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-7.0.0.tgz", - "integrity": "sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==", "dev": true, + "license": "MIT", "dependencies": { "workbox-core": "7.0.0" } }, "node_modules/workbox-precaching": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-7.0.0.tgz", - "integrity": "sha512-EC0vol623LJqTJo1mkhD9DZmMP604vHqni3EohhQVwhJlTgyKyOkMrZNy5/QHfOby+39xqC01gv4LjOm4HSfnA==", "dev": true, + "license": "MIT", "dependencies": { "workbox-core": "7.0.0", "workbox-routing": "7.0.0", @@ -16696,18 +15425,16 @@ }, "node_modules/workbox-range-requests": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-7.0.0.tgz", - "integrity": "sha512-SxAzoVl9j/zRU9OT5+IQs7pbJBOUOlriB8Gn9YMvi38BNZRbM+RvkujHMo8FOe9IWrqqwYgDFBfv6sk76I1yaQ==", "dev": true, + "license": "MIT", "dependencies": { "workbox-core": "7.0.0" } }, "node_modules/workbox-recipes": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-7.0.0.tgz", - "integrity": "sha512-DntcK9wuG3rYQOONWC0PejxYYIDHyWWZB/ueTbOUDQgefaeIj1kJ7pdP3LZV2lfrj8XXXBWt+JDRSw1lLLOnww==", "dev": true, + "license": "MIT", "dependencies": { "workbox-cacheable-response": "7.0.0", "workbox-core": "7.0.0", @@ -16719,27 +15446,24 @@ }, "node_modules/workbox-routing": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-7.0.0.tgz", - "integrity": "sha512-8YxLr3xvqidnbVeGyRGkaV4YdlKkn5qZ1LfEePW3dq+ydE73hUUJJuLmGEykW3fMX8x8mNdL0XrWgotcuZjIvA==", "dev": true, + "license": "MIT", "dependencies": { "workbox-core": "7.0.0" } }, "node_modules/workbox-strategies": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-7.0.0.tgz", - "integrity": "sha512-dg3qJU7tR/Gcd/XXOOo7x9QoCI9nk74JopaJaYAQ+ugLi57gPsXycVdBnYbayVj34m6Y8ppPwIuecrzkpBVwbA==", "dev": true, + "license": "MIT", "dependencies": { "workbox-core": "7.0.0" } }, "node_modules/workbox-streams": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-7.0.0.tgz", - "integrity": "sha512-moVsh+5to//l6IERWceYKGiftc+prNnqOp2sgALJJFbnNVpTXzKISlTIsrWY+ogMqt+x1oMazIdHj25kBSq/HQ==", "dev": true, + "license": "MIT", "dependencies": { "workbox-core": "7.0.0", "workbox-routing": "7.0.0" @@ -16747,15 +15471,13 @@ }, "node_modules/workbox-sw": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.0.0.tgz", - "integrity": "sha512-SWfEouQfjRiZ7GNABzHUKUyj8pCoe+RwjfOIajcx6J5mtgKkN+t8UToHnpaJL5UVVOf5YhJh+OHhbVNIHe+LVA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/workbox-window": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.0.0.tgz", - "integrity": "sha512-j7P/bsAWE/a7sxqTzXo3P2ALb1reTfZdvVp6OJ/uLr/C2kZAMvjeWGm8V4htQhor7DOvYg0sSbFN2+flT5U0qA==", "dev": true, + "license": "MIT", "dependencies": { "@types/trusted-types": "^2.0.2", "workbox-core": "7.0.0" @@ -16827,8 +15549,6 @@ }, "node_modules/xmlhttprequest-ssl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", "engines": { "node": ">=0.4.0" } @@ -16906,8 +15626,7 @@ }, "node_modules/zustand": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.0.tgz", - "integrity": "sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A==", + "license": "MIT", "dependencies": { "use-sync-external-store": "1.2.0" }, @@ -16931,123 +15650,6 @@ } } }, - "packages/avicennia-api": { - "name": "@algorandfoundation/avicennia-js", - "version": "1.0.0-beta.1", - "extraneous": true, - "license": "UNLICENSED", - "workspaces": [ - "./packages/**" - ], - "dependencies": { - "@nestjs/common": "^10.2.6", - "@nestjs/config": "^3.1.1", - "@nestjs/core": "^10.2.6", - "@nestjs/microservices": "^10.2.7", - "@nestjs/mongoose": "^10.0.1", - "@nestjs/platform-express": "^10.2.6", - "@nestjs/platform-socket.io": "^10.2.7", - "@nestjs/websockets": "^10.2.7", - "@sentry/node": "^7.74.1", - "@sentry/profiling-node": "^1.2.1", - "@simplewebauthn/server": "^0.10.3", - "@socket.io/redis-adapter": "^8.2.1", - "base64url": "^3.0.1", - "bull": "^4.11.4", - "connect-mongo": "^5.0.0", - "express-session": "^1.17.3", - "express-socket.io-session": "^1.3.5", - "hbs": "^4.2.0", - "mongoose": "^7.6.3", - "redis": "^4.6.10", - "reflect-metadata": "^0.1.13", - "rxjs": "^7.8.1", - "ua-parser-js": "^1.0.36" - }, - "devDependencies": { - "@nestjs/cli": "^10.1.18", - "@nestjs/schematics": "^10.0.2", - "@nestjs/testing": "^10.2.6", - "@types/express": "^4.17.18", - "@types/express-session": "^1.17.8", - "@types/express-socket.io-session": "^1.3.7", - "@types/hbs": "^4.0.2", - "@types/jest": "^29.5.5", - "@types/node": "^20.7.0", - "@types/supertest": "^2.0.13", - "@types/ua-parser-js": "^0.7.37", - "@typescript-eslint/eslint-plugin": "^6.7.3", - "@typescript-eslint/parser": "^6.7.3", - "algosdk": "^2.6.0", - "cross-env": "^7.0.3", - "eslint": "^8.50.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.0", - "jest": "^29.7.0", - "prettier": "^3.0.3", - "source-map-support": "^0.5.21", - "supertest": "^6.3.3", - "ts-jest": "^29.1.1", - "ts-loader": "^9.4.4", - "ts-node": "^10.9.1", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.3.3" - }, - "engines": { - "node": ">=18.0.0 <21.0.0" - } - }, - "packages/dapp-ui": { - "version": "0.0.1", - "extraneous": true, - "license": "UNLICENSED", - "dependencies": { - "@algorandfoundation/propagule-js": "^1.0.0", - "@emotion/react": "^11.11.3", - "@emotion/server": "^11.11.0", - "@emotion/styled": "^11.11.0", - "@mui/icons-material": "^5.15.4", - "@mui/material": "^5.15.4", - "@tanstack/react-query": "^5.17.15", - "qr-code-styling": "^1.6.0-rc.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "reflect-metadata": "^0.1.13", - "socket.io-client": "^4.7.4", - "zustand": "^4.4.7" - }, - "devDependencies": { - "@types/react": "^18.2.43", - "@types/react-dom": "^18.2.17", - "@typescript-eslint/eslint-plugin": "^6.14.0", - "@typescript-eslint/parser": "^6.14.0", - "@vitejs/plugin-react-swc": "^3.5.0", - "eslint": "^8.55.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.5", - "mkdirp": "^3.0.1", - "typescript": "^5.2.2", - "vite": "^5.0.8" - } - }, - "packages/propagule-js": { - "name": "@algorandfoundation/propagule-js", - "version": "1.0.0", - "extraneous": true, - "license": "MIT", - "dependencies": { - "qrcode": "^1.5.3" - }, - "devDependencies": { - "@types/qrcode": "^1.5.5", - "c8": "^9.1.0", - "typescript": "^5.3.3" - }, - "peerDependencies": { - "algosdk": "^2.7.0", - "tweetnacl": "^1.0.3" - } - }, "services/liquid-auth-api-js": { "name": "@liquid/auth-api", "version": "1.0.0-beta.1", @@ -17069,6 +15671,7 @@ "@socket.io/redis-adapter": "^8.2.1", "base64url": "^3.0.1", "connect-mongo": "^5.0.0", + "express-http-proxy": "^2.0.0", "express-session": "^1.17.3", "express-socket.io-session": "^1.3.5", "hbs": "^4.2.0", @@ -17084,6 +15687,7 @@ "@nestjs/schematics": "^10.0.2", "@nestjs/testing": "^10.2.6", "@types/express": "^4.17.18", + "@types/express-http-proxy": "^1.6.6", "@types/express-session": "^1.17.8", "@types/express-socket.io-session": "^1.3.7", "@types/hbs": "^4.0.2", @@ -17154,9 +15758,8 @@ }, "sites/dapp-ui/node_modules/mkdirp": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true, + "license": "MIT", "bin": { "mkdirp": "dist/cjs/src/bin.js" }, @@ -17166,6 +15769,17 @@ "funding": { "url": "https://github.com/sponsors/isaacs" } + }, + "sites/major-zero": { + "version": "0.0.1", + "extraneous": true, + "dependencies": { + "@astrojs/check": "^0.5.10", + "@astrojs/starlight": "^0.21.5", + "astro": "^4.3.5", + "sharp": "^0.32.5", + "typescript": "^5.4.5" + } } } } diff --git a/services/liquid-auth-api-js/package.json b/services/liquid-auth-api-js/package.json index bbfa786..4c3a11b 100644 --- a/services/liquid-auth-api-js/package.json +++ b/services/liquid-auth-api-js/package.json @@ -40,7 +40,6 @@ "connect-mongo": "^5.0.0", "express-session": "^1.17.3", "express-socket.io-session": "^1.3.5", - "hbs": "^4.2.0", "hi-base32": "^0.5.1", "mongoose": "^7.6.3", "redis": "^4.6.10", diff --git a/services/liquid-auth-api-js/src/attestation/attestation.controller.ts b/services/liquid-auth-api-js/src/attestation/attestation.controller.ts index 1642c36..80d3743 100644 --- a/services/liquid-auth-api-js/src/attestation/attestation.controller.ts +++ b/services/liquid-auth-api-js/src/attestation/attestation.controller.ts @@ -84,7 +84,6 @@ export class AttestationController { @Req() req: Request, @Res() res: Response, ) { - console.log(req.headers.host); this.logger.log( `POST /attestation/request for Session: ${session.id} and Wallet: ${session.wallet}`, ); diff --git a/services/liquid-auth-api-js/src/connect/connect.gateway.ts b/services/liquid-auth-api-js/src/connect/connect.gateway.ts index bb69a89..060cb80 100644 --- a/services/liquid-auth-api-js/src/connect/connect.gateway.ts +++ b/services/liquid-auth-api-js/src/connect/connect.gateway.ts @@ -95,6 +95,7 @@ export class ConnectGateway clearInterval(this.timers.get(request.sessionID)); } } + /** * On Link Connection, wait for the wallet to connect * @param client diff --git a/services/liquid-auth-api-js/src/main.ts b/services/liquid-auth-api-js/src/main.ts index e53dcb4..1d08066 100644 --- a/services/liquid-auth-api-js/src/main.ts +++ b/services/liquid-auth-api-js/src/main.ts @@ -16,8 +16,6 @@ import * as Sentry from '@sentry/node'; import { ProfilingIntegration } from '@sentry/profiling-node'; import { SentryFilter } from './sentry.filter.js'; -import { resolve } from 'path'; -import hbs from 'hbs'; async function bootstrap() { const app = await NestFactory.create(AppModule, { logger: ['error', 'warn', 'debug', 'log', 'verbose'], @@ -56,8 +54,6 @@ async function bootstrap() { ttl: 20000, }); - app.enableCors(); - const sessionHandler = session({ secret: 'my-secret', saveUninitialized: true, @@ -74,10 +70,6 @@ async function bootstrap() { app.useWebSocketAdapter(redisIoAdapter); - app.useStaticAssets(resolve('./public')); - app.setBaseViewsDir(resolve('./views')); - app.setViewEngine('html'); - app.engine('html', hbs.__express); await app.listen(process.env.PORT || 3000); } diff --git a/services/liquid-auth-api-js/src/signals/signals.gateway.ts b/services/liquid-auth-api-js/src/signals/signals.gateway.ts index 0e1dca8..8eb13b2 100644 --- a/services/liquid-auth-api-js/src/signals/signals.gateway.ts +++ b/services/liquid-auth-api-js/src/signals/signals.gateway.ts @@ -19,7 +19,7 @@ export class SignalsGateway { @SubscribeMessage('offer-candidate') onCallCandidate( @MessageBody() - data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, + data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, @ConnectedSocket() client: Socket, ) { this.logger.debug(`(offer-candidate): ${JSON.stringify(data)}`); @@ -54,7 +54,7 @@ export class SignalsGateway { @SubscribeMessage('answer-candidate') onAnswerCandidate( @MessageBody() - data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, + data: { candidate: string; sdpMid: string; sdpMLineIndex: number }, @ConnectedSocket() client: Socket, ) { this.logger.debug(`(answer-candidate): ${JSON.stringify(data)}`); diff --git a/sites/dapp-ui/package.json b/sites/dapp-ui/package.json index a406486..3ccfed0 100644 --- a/sites/dapp-ui/package.json +++ b/sites/dapp-ui/package.json @@ -7,8 +7,7 @@ "license": "MIT", "type": "module", "scripts": { - "dev": "vite build --watch --emptyOutDir", - "dev:no-api": "vite dev --host 0.0.0.0", + "dev": "vite dev --host 0.0.0.0", "build": "vite build --emptyOutDir", "lint": "eslint \"{src,apps,libs,test}/**/*.{ts,tsx}\" --fix" }, diff --git a/sites/dapp-ui/src/App.tsx b/sites/dapp-ui/src/App.tsx index b3a20dd..f324aa2 100644 --- a/sites/dapp-ui/src/App.tsx +++ b/sites/dapp-ui/src/App.tsx @@ -1,15 +1,7 @@ -import { useState, useMemo } from 'react'; +import { useState, useMemo, useEffect } from 'react'; -import { - ColorModeContext, - DataChannelContext, - PeerConnectionContext, - SnackbarContext, - StateContext, -} from './Contexts'; +import { ColorModeContext } from './Contexts'; import Layout from './Layout'; - -import { HomePage } from './pages/home.tsx'; import { createTheme, CssBaseline } from '@mui/material'; import { DEFAULT_THEME } from './theme.tsx'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; @@ -17,29 +9,18 @@ import { ThemeProvider } from '@emotion/react'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { createHashRouter, RouterProvider } from 'react-router-dom'; -import { PeeringPage } from './pages/peering.tsx'; -import ConnectedPage from './pages/connected.tsx'; +import { HomePage, ConnectedPage } from '@/pages'; import { Algodv2 } from 'algosdk'; -import { AlgodContext } from './hooks/useAlgod.ts'; +import { AlgodContext } from '@/hooks'; +import { SignalClientContext } from '@/hooks/useSignalClient.ts'; +import { LinkMessage, SignalClient } from '@liquid/auth-client/signal'; const queryClient = new QueryClient(); const algod = new Algodv2( - process.env.VITE_ALGOD_TOKEN || '', - process.env.VITE_ALGOD_SERVER || 'https://testnet-api.algonode.cloud', - process.env.VITE_ALGOD_PORT || 443, + import.meta.env.VITE_ALGOD_TOKEN || '', + import.meta.env.VITE_ALGOD_SERVER || 'https://testnet-api.algonode.cloud', + import.meta.env.VITE_ALGOD_PORT || 443, ); -const DEFAULT_CONFIG: RTCConfiguration = { - iceServers: [ - { - urls: [ - 'stun:stun.l.google.com:19302', - 'stun:stun1.l.google.com:19302', - 'stun:stun2.l.google.com:19302', - ], - }, - ], - iceCandidatePoolSize: 10, -}; const router = createHashRouter([ { @@ -50,14 +31,6 @@ const router = createHashRouter([ ), }, - { - path: '/peering', - element: ( - - - - ), - }, { path: '/connected', element: ( @@ -68,15 +41,8 @@ const router = createHashRouter([ }, ]); export default function ProviderApp() { - const [open, setOpen] = useState(false); - const [message, setMessage] = useState(''); - const [state, setState] = useState('peering'); const [dataChannel, setDataChannel] = useState(null); - const peerConnection = useMemo( - () => new RTCPeerConnection(DEFAULT_CONFIG), - [], - ); const [mode, setMode] = useState<'light' | 'dark'>( window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)') ? 'dark' @@ -105,30 +71,65 @@ export default function ProviderApp() { }), [mode], ); - console.log(theme, DEFAULT_THEME.palette); + + const [client, setClient] = useState( + new SignalClient(window.origin), + ); + const [status, setStatus] = useState<'connected' | 'disconnected'>( + 'disconnected', + ); + useEffect(() => { + if (!client) return; + function handleDataChannel(dc: RTCDataChannel) { + console.log('SignalClient datachannel', dc); + setDataChannel(dc); + } + client.on('data-channel', handleDataChannel); + function handleSocketConnect() { + console.log('Socket Connect'); + setStatus('connected'); + } + client.on('connect', handleSocketConnect); + function handleLinkMessage(msg: LinkMessage) { + window.localStorage.setItem('wallet', JSON.stringify(msg.wallet)); + } + client.on('link-message', handleLinkMessage); + + function handleSocketDisconnect() { + console.log('Socket Disconnect'); + setStatus('disconnected'); + } + client.on('disconnect', handleSocketDisconnect); + return () => { + console.log('removing emitters'); + client.off('link-message', handleLinkMessage); + client.off('data-channel', handleDataChannel); + client.off('connect', handleSocketConnect); + client.off('disconnect', handleSocketDisconnect); + }; + }, [client]); return ( - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + ); } diff --git a/sites/dapp-ui/src/Contexts.tsx b/sites/dapp-ui/src/Contexts.tsx index bf06ac3..8406aaf 100644 --- a/sites/dapp-ui/src/Contexts.tsx +++ b/sites/dapp-ui/src/Contexts.tsx @@ -1,24 +1,4 @@ import { createContext } from 'react'; export const ColorModeContext = createContext({ toggle: () => {} }); - -export const StateContext = createContext({ - state: 'debug', - setState: (_: string) => { - console.log(_); - }, -}); - -export const SnackbarContext = createContext({ - open: false, - setOpen: (_: boolean) => { - console.log(_); - }, - message: '', - setMessage: (_: string) => { - console.log(_); - }, -}); - -export { DataChannelContext } from './hooks/useDataChannel.ts'; -export { PeerConnectionContext } from './hooks/usePeerConnection.ts'; +export { SignalClientContext } from './hooks/useSignalClient.ts'; diff --git a/sites/dapp-ui/src/Layout.tsx b/sites/dapp-ui/src/Layout.tsx index af20a32..a8fb89a 100644 --- a/sites/dapp-ui/src/Layout.tsx +++ b/sites/dapp-ui/src/Layout.tsx @@ -12,9 +12,10 @@ import { ColorModeContext } from './Contexts'; import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore'; import NavigateNextIcon from '@mui/icons-material/NavigateNext'; import { SessionMenu } from './components/user/SessionMenu.tsx'; -import { MessageSnackbar } from './components/Snackbar.tsx'; import { useLocation, useNavigate } from 'react-router-dom'; +import { useSignalClient } from '@/hooks/useSignalClient.ts'; export default function Layout({ children }: PropsWithChildren) { + const { dataChannel } = useSignalClient(); const location = useLocation(); const navigate = useNavigate(); const colorMode = useContext(ColorModeContext); @@ -23,8 +24,9 @@ export default function Layout({ children }: PropsWithChildren) { const bubbleStyle = { backgroundColor: isDarkMode ? 'white' : 'black', }; - const breadcrumbs = ['/', '/peering', '/connected', '/registered']; + const breadcrumbs = ['/', '/connected']; const index = breadcrumbs.indexOf(location.pathname); + console.log(index); return ( <> { + console.log('navigate to ', breadcrumbs[index - 1]); index > 0 && navigate(breadcrumbs[index - 1]); }} aria-label="delete" @@ -52,7 +55,7 @@ export default function Layout({ children }: PropsWithChildren) { navigate(breadcrumbs[index + 1]); }} aria-label="delete" - disabled={index === breadcrumbs.length - 1} + disabled={index === breadcrumbs.length - 1 || dataChannel === null} color="inherit" > @@ -77,10 +80,7 @@ export default function Layout({ children }: PropsWithChildren) { maxWidth="md" sx={{ display: 'flex', height: 'calc(100vh - 64px)', overflow: 'auto' }} > - - {children} - - + {children}
diff --git a/sites/dapp-ui/src/components/ConnectModal.tsx b/sites/dapp-ui/src/components/ConnectModal.tsx index 3be09b3..41f9b66 100644 --- a/sites/dapp-ui/src/components/ConnectModal.tsx +++ b/sites/dapp-ui/src/components/ConnectModal.tsx @@ -2,15 +2,11 @@ import * as React from 'react'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Modal from '@mui/material/Modal'; -import { toBase64URL } from '@liquid/core/encoding'; -import { Message } from '@liquid/auth-client/connect'; -import QRCodeStyling, { Options } from 'qr-code-styling'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { Fade } from '@mui/material'; -import { useSocket } from '../hooks/useSocket'; -import nacl from 'tweetnacl'; -import { useCredentialStore, Credential } from '../store'; import { useNavigate } from 'react-router-dom'; +import { useSignalClient } from '@/hooks/useSignalClient.ts'; +import { SignalClient } from '@liquid/auth-client/signal'; const style = { position: 'absolute' as const, top: '50%', @@ -22,6 +18,12 @@ const style = { border: '2px solid #000', boxShadow: 24, }; + +/** + * Connect Modal + * @param color + * @todo: Make into a component that can be used in providers + */ export function ConnectModal({ color, }: { @@ -34,119 +36,45 @@ export function ConnectModal({ | 'info' | 'warning'; }) { + const { client, dataChannel } = useSignalClient(); const navigate = useNavigate(); - const { socket } = useSocket(); - const credentials = useCredentialStore((state) => state.addresses); - const save = useCredentialStore((state) => state.update); - const [state] = useState({ - requestId: Math.random(), - challenge: toBase64URL(nacl.randomBytes(nacl.sign.seedLength)), - }); - const qrOpts = { - width: 500, - height: 500, - data: 'algorand://', - margin: 25, - imageOptions: { hideBackgroundDots: true, imageSize: 0.4, margin: 15 }, - dotsOptions: { - type: 'extra-rounded', - gradient: { - type: 'radial', - rotation: 0, - colorStops: [ - { offset: 0, color: '#9966ff' }, - { offset: 1, color: '#332257' }, - ], - }, - }, - backgroundOptions: { color: '#ffffff', gradient: null }, - image: '/logo.png', - cornersSquareOptions: { - type: '', - color: '#000000', - gradient: { - type: 'linear', - rotation: 0, - colorStops: [ - { offset: 0, color: '#332257' }, - { offset: 1, color: '#040908' }, - ], - }, - }, - cornersDotOptions: { - type: 'dot', - color: '#000000', - gradient: { - type: 'linear', - rotation: 0, - colorStops: [ - { offset: 0, color: '#000000' }, - { offset: 1, color: '#000000' }, - ], - }, - }, - }; - useEffect(() => { - socket.on('link', (data) => { - console.log(data); - }); - }, [socket]); - const [open, setOpen] = React.useState(false); - useEffect(() => { - if (!open) { - return; - } - socket.emit( - 'link', - { requestId: state.requestId }, - async ({ - data, - }: { - data: { credId?: string; requestId: string | number; wallet: string }; - }) => { - let newCredentials: Credential[] = []; - if (typeof credentials[data.wallet] !== 'undefined') { - newCredentials = credentials[data.wallet].credentials; - } - save({ name: data.wallet, credentials: [...newCredentials] }); - window.localStorage.setItem('wallet', JSON.stringify(data.wallet)); - if (typeof data.credId !== 'undefined') { - console.log(data.credId); - window.localStorage.setItem('credId', data.credId); - navigate('/peering'); - } else { - navigate('/peering'); - } - }, - ); - }, [open]); + const [requestId] = useState(SignalClient.generateRequestId()); + const [open, setOpen] = React.useState(false); const [barcode, setBarcode] = React.useState('/qr-loading.png'); - const handleOpen = () => { - setBarcode('/qr-loading.png'); - const message = new Message( - window.location.origin, - state.challenge, - state.requestId, - ); - // JSON encoding - qrOpts.data = `${message}`; - console.log(qrOpts.data); - const qrCode = new QRCodeStyling(qrOpts as unknown as Options); - qrCode.getRawData('png').then((blob) => { - if (!blob) throw TypeError('Could not get qrcode blob'); - setBarcode(URL.createObjectURL(blob)); - setOpen(true); + const handleOpen = async () => { + setBarcode('/qr-loading.png'); + client.once('data-channel', () => { + navigate('/connected'); }); - // message.toBarcode({color: {light: "#00000000"}}).then(setBarcode) + client + .peer(requestId, 'offer', { + iceServers: [ + { + urls: [ + 'stun:stun.l.google.com:19302', + 'stun:stun1.l.google.com:19302', + 'stun:stun2.l.google.com:19302', + ], + }, + ], + iceCandidatePoolSize: 10, + }) + .catch((e) => { + console.error(e); + client.close(); + setOpen(false); + }); + setBarcode(await client.qrCode()); setOpen(true); }; const handleClose = () => setOpen(false); - + const hasDataChannel = + (dataChannel && dataChannel?.readyState === 'open') || false; return (
- { - setOpen(false); - setMessage(''); - }; - return ( - - - - } - > - - {message} - - - ); -} diff --git a/sites/dapp-ui/src/components/user/SessionMenu.tsx b/sites/dapp-ui/src/components/user/SessionMenu.tsx index 09e5ec9..a6d97ec 100644 --- a/sites/dapp-ui/src/components/user/SessionMenu.tsx +++ b/sites/dapp-ui/src/components/user/SessionMenu.tsx @@ -1,14 +1,18 @@ import { Avatar, Badge, CircularProgress, Menu } from '@mui/material'; import IconButton from '@mui/material/IconButton'; -import React, { useState } from 'react'; -import { useSocket } from '@/hooks/useSocket.ts'; +import React, { useEffect, useState } from 'react'; import { useUserState } from '@/hooks/useUserState.ts'; import { StatusCard } from './StatusCard.tsx'; +import { useSignalClient } from '@/hooks/useSignalClient.ts'; export function SessionMenu() { const state = useUserState(); - const { isConnected } = useSocket(); - + const { status, dataChannel } = useSignalClient(); + const hasDataChannel = !!dataChannel && dataChannel.readyState === 'open'; + const isConnected = status === 'connected'; + const [badgeColor, setBadgeColor] = useState<'error' | 'success' | 'warning'>( + 'error', + ); const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); const handleClick = (event: React.MouseEvent) => { @@ -18,6 +22,16 @@ export function SessionMenu() { setAnchorEl(null); }; + // Update badge color based on connection status + useEffect(() => { + if (isConnected && hasDataChannel) { + setBadgeColor('success'); + } + if (isConnected && !hasDataChannel) { + setBadgeColor('warning'); + } + }, [dataChannel, isConnected]); + return ( <> - + @@ -59,7 +73,7 @@ export function SessionMenu() { {state.isLoading && } {state.isFetched && ( diff --git a/sites/dapp-ui/src/components/user/StatusCard.tsx b/sites/dapp-ui/src/components/user/StatusCard.tsx index 1f30b1a..5ef0b93 100644 --- a/sites/dapp-ui/src/components/user/StatusCard.tsx +++ b/sites/dapp-ui/src/components/user/StatusCard.tsx @@ -9,6 +9,7 @@ import { User } from './types.ts'; export type ProfileCardProps = { socket: { isConnected: boolean; + hasDataChannel: boolean; }; session: { wallet?: string; diff --git a/sites/dapp-ui/src/hooks/index.ts b/sites/dapp-ui/src/hooks/index.ts index fbbe4b9..dc662d7 100644 --- a/sites/dapp-ui/src/hooks/index.ts +++ b/sites/dapp-ui/src/hooks/index.ts @@ -2,6 +2,4 @@ export * from './useAccountInfo'; export * from './useAddress'; export * from './useAlgod'; export * from './useDataChannel'; -export * from './usePeerConnection'; -export * from './useSocket'; export * from './useUserState'; diff --git a/sites/dapp-ui/src/hooks/useDataChannel.ts b/sites/dapp-ui/src/hooks/useDataChannel.ts index bf27f29..3d37cd4 100644 --- a/sites/dapp-ui/src/hooks/useDataChannel.ts +++ b/sites/dapp-ui/src/hooks/useDataChannel.ts @@ -1,63 +1,19 @@ -import { createContext, useContext, useEffect } from 'react'; - -type DataChannelState = { - dataChannel: RTCDataChannel | null; - setDataChannel: (_: RTCDataChannel) => void; -}; -export const DataChannelContext = createContext({ - dataChannel: null, - setDataChannel: (_: RTCDataChannel) => { - console.log(_); - }, -} as DataChannelState); +import { useContext, useEffect } from 'react'; +import { SignalClientContext } from '@/hooks/useSignalClient.ts'; /** * Hook to use data channel messages * @param onMessage */ -export function useDataChannelMessages( - onMessage: (event: MessageEvent) => void, -) { - const { dataChannel } = useContext(DataChannelContext); +export function useDataChannel(onMessage?: (event: MessageEvent) => void) { + const { dataChannel } = useContext(SignalClientContext); useEffect(() => { - if (!dataChannel) return; + if (!dataChannel || !onMessage) return; dataChannel.addEventListener('message', onMessage); return () => { dataChannel.removeEventListener('message', onMessage); }; }, [dataChannel, onMessage]); -} - -/** - * Hook to create a Data Channel - * - * Creates a RTCDataChannel if type is 'local'. If the type is 'remote', it listens for the 'datachannel' event - * - * @param type - * @param peerConnection - */ -export function useDataChannel( - type: 'local' | 'remote', - peerConnection: RTCPeerConnection | null, -) { - const { dataChannel, setDataChannel } = useContext(DataChannelContext); - useEffect(() => { - if (!peerConnection) return; - function handleOnDataChannel(event: RTCDataChannelEvent) { - setDataChannel(event.channel); - } - if (type === 'local') { - setDataChannel(peerConnection.createDataChannel('data')); - } else { - peerConnection.addEventListener('datachannel', handleOnDataChannel); - } - - return () => { - if (type === 'remote') { - peerConnection.removeEventListener('datachannel', handleOnDataChannel); - } - }; - }, [peerConnection, setDataChannel, type]); return dataChannel; } diff --git a/sites/dapp-ui/src/hooks/usePeerConnection.ts b/sites/dapp-ui/src/hooks/usePeerConnection.ts deleted file mode 100644 index fb4b3a8..0000000 --- a/sites/dapp-ui/src/hooks/usePeerConnection.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { createContext, useContext, useEffect, useState } from 'react'; - -type PeerConnectionState = { peerConnection: RTCPeerConnection | null }; -export const PeerConnectionContext = createContext({ - peerConnection: null, -} as PeerConnectionState); - -export function usePeerConnectionState() { - const { peerConnection } = useContext(PeerConnectionContext); - const [connectionState, setConnectionState] = useState( - peerConnection?.connectionState || null, - ); - - useEffect(() => { - if (!peerConnection) return; - function handleConnectionStateChange() { - if (!peerConnection) return; - console.log(`Connection state change: ${peerConnection.connectionState}`); - setConnectionState(peerConnection.connectionState); - } - - peerConnection.addEventListener( - 'connectionstatechange', - handleConnectionStateChange, - ); - return () => { - peerConnection.removeEventListener( - 'connectionstatechange', - handleConnectionStateChange, - ); - }; - }, [peerConnection]); - - return connectionState; -} -export function usePeerConnection( - onIceCandidate: (event: RTCPeerConnectionIceEvent) => void, -) { - const { peerConnection } = useContext(PeerConnectionContext); - - useEffect(() => { - if (!peerConnection) return; - function handleICEGatheringStateChange() { - console.log(`ICE gathering state: ${peerConnection?.iceGatheringState}`); - } - function handleConnectionStateChange() { - console.log( - `Connection state change: ${peerConnection?.connectionState}`, - ); - } - - function handleSignalingStateChange() { - console.log(`Signaling state change: ${peerConnection?.signalingState}`); - } - - function handleICEConnectionStateChange() { - console.log( - `ICE connection state change: ${peerConnection?.iceConnectionState}`, - ); - } - - peerConnection.addEventListener( - 'icegatheringstatechange', - handleICEGatheringStateChange, - ); - peerConnection.addEventListener( - 'connectionstatechange', - handleConnectionStateChange, - ); - peerConnection.addEventListener( - 'signalingstatechange', - handleSignalingStateChange, - ); - peerConnection.addEventListener( - 'iceconnectionstatechange ', - handleICEConnectionStateChange, - ); - peerConnection.addEventListener('icecandidate', onIceCandidate); - - return () => { - peerConnection.removeEventListener( - 'icegatheringstatechange', - handleICEGatheringStateChange, - ); - peerConnection.removeEventListener( - 'connectionstatechange', - handleConnectionStateChange, - ); - peerConnection.removeEventListener( - 'signalingstatechange', - handleSignalingStateChange, - ); - peerConnection.removeEventListener( - 'iceconnectionstatechange ', - handleICEConnectionStateChange, - ); - peerConnection.removeEventListener('icecandidate', onIceCandidate); - }; - }, [peerConnection, onIceCandidate]); - - return peerConnection; -} diff --git a/sites/dapp-ui/src/hooks/useSignalClient.ts b/sites/dapp-ui/src/hooks/useSignalClient.ts new file mode 100644 index 0000000..7d1cf3d --- /dev/null +++ b/sites/dapp-ui/src/hooks/useSignalClient.ts @@ -0,0 +1,34 @@ +import { createContext, useContext } from 'react'; +import { SignalClient } from '@liquid/auth-client/signal'; + +type SignalClientState = { + client: SignalClient | null; + setClient: (_: SignalClient) => void; + status: 'connected' | 'disconnected'; + setStatus: (_: 'connected' | 'disconnected') => void; + dataChannel: RTCDataChannel | null; + setDataChannel: (_: RTCDataChannel) => void; +}; +export const SignalClientContext = createContext({ + client: null, + setClient: (_: SignalClient) => { + console.log(_); + }, + status: 'disconnected', + setStatus: (_: 'connected' | 'disconnected') => { + console.log(_); + }, + dataChannel: null, + setDataChannel: (_: RTCDataChannel) => { + console.log(_); + }, +} as SignalClientState); + +export function useSignalClient() { + const { client, status, dataChannel } = useContext(SignalClientContext); + if (!client) + throw new Error( + 'SignalClient not found, make sure to wrap your component with ', + ); + return { client, status, dataChannel }; +} diff --git a/sites/dapp-ui/src/hooks/useSocket.ts b/sites/dapp-ui/src/hooks/useSocket.ts deleted file mode 100644 index db93f37..0000000 --- a/sites/dapp-ui/src/hooks/useSocket.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { io } from 'socket.io-client'; -import { useEffect, useState } from 'react'; - -// "undefined" means the URL will be computed from the `window.location` object -const URL = `${window.location.origin}`; - -export const socket = io(URL, { autoConnect: true }); - -export function useSocket() { - const [isConnected, setIsConnected] = useState(socket.connected); - - useEffect(() => { - function onConnect() { - console.log('Connected'); - setIsConnected(true); - } - - function onDisconnect() { - console.log('Disconnected'); - setIsConnected(false); - } - socket.on('connect', onConnect); - socket.on('disconnect', onDisconnect); - - return () => { - socket.off('connect', onConnect); - socket.off('disconnect', onDisconnect); - }; - }, []); - - return { isConnected, socket }; -} diff --git a/sites/dapp-ui/src/hooks/useUserState.ts b/sites/dapp-ui/src/hooks/useUserState.ts index 987bb1a..a3c7bc9 100644 --- a/sites/dapp-ui/src/hooks/useUserState.ts +++ b/sites/dapp-ui/src/hooks/useUserState.ts @@ -4,6 +4,14 @@ export function useUserState() { return useQuery({ refetchInterval: 3000, queryKey: ['auth-session'], - queryFn: () => fetch('/auth/session').then((res) => res.json()), + queryFn: () => + fetch('/auth/session').then(async (res) => { + const state = await res.json(); + const wallet = window.localStorage.getItem('wallet'); + if (wallet && typeof state?.user?.wallet === 'undefined') { + window.localStorage.removeItem('wallet'); + } + return state; + }), }); } diff --git a/sites/dapp-ui/src/pages/connected.tsx b/sites/dapp-ui/src/pages/connected.tsx index 53104f6..618db43 100644 --- a/sites/dapp-ui/src/pages/connected.tsx +++ b/sites/dapp-ui/src/pages/connected.tsx @@ -1,9 +1,5 @@ -import { - useDataChannel, - useDataChannelMessages, -} from '../hooks/useDataChannel.ts'; -import { PeerConnectionContext } from '../hooks/usePeerConnection.ts'; -import { useContext, useEffect, useState } from 'react'; +import { useDataChannel } from '@/hooks/useDataChannel.ts'; +import { useEffect, useState } from 'react'; import Button from '@mui/material/Button'; import { Transaction, @@ -12,20 +8,21 @@ import { makePaymentTxnWithSuggestedParamsFromObject, } from 'algosdk'; import { toBase64URL, fromBase64Url } from '@liquid/core/encoding'; -import { useAlgod } from '../hooks/useAlgod.ts'; -import { useAccountInfo } from '../hooks/useAccountInfo.ts'; +import { useAlgod } from '@/hooks/useAlgod.ts'; +import { useAccountInfo } from '@/hooks/useAccountInfo.ts'; import FormControl from '@mui/material/FormControl'; import { Box, CircularProgress, Input, Slider } from '@mui/material'; import Typography from '@mui/material/Typography'; import { useMessageStore } from '../store.ts'; +import { useNavigate } from 'react-router-dom'; -export default function ConnectedPage() { +export function ConnectedPage() { + const navigate = useNavigate(); const algod = useAlgod(); const walletStr = window.localStorage.getItem('wallet'); const wallet = walletStr ? JSON.parse(walletStr) : null; const [txn, setTxn] = useState(null); - const { peerConnection } = useContext(PeerConnectionContext); - const datachannel = useDataChannel('remote', peerConnection); + // const datachannel = useDataChannel('remote', peerConnection); const accountInfo = useAccountInfo(wallet, 3000); const [from, setFrom] = useState(wallet); const [to, setTo] = useState(wallet); @@ -37,7 +34,8 @@ export default function ConnectedPage() { const addMessage = useMessageStore((state) => state.addMessage); const messages = useMessageStore((state) => state.messages); // Receive response - useDataChannelMessages((event) => { + const datachannel = useDataChannel((event) => { + console.log(event.data); addMessage({ type: 'remote', data: JSON.parse(event.data), @@ -81,6 +79,10 @@ export default function ConnectedPage() { setIsWaitingForSignature(true); }, [txn, datachannel, isWaitingForSignature]); + useEffect(() => { + if (!datachannel) navigate('/'); + }, [datachannel]); + if (accountInfo.data && accountInfo.data.amount === 0) { return ( diff --git a/sites/dapp-ui/src/pages/home.tsx b/sites/dapp-ui/src/pages/home.tsx index c167739..c11dff3 100644 --- a/sites/dapp-ui/src/pages/home.tsx +++ b/sites/dapp-ui/src/pages/home.tsx @@ -3,10 +3,12 @@ import CardContent from '@mui/material/CardContent'; import Typography from '@mui/material/Typography'; import CardActions from '@mui/material/CardActions'; import Card from '@mui/material/Card'; -import { ConnectModal } from '@/components/ConnectModal'; import Button from '@mui/material/Button'; -import { assertion } from '@liquid/auth-client'; + import { useNavigate } from 'react-router-dom'; +import { ConnectModal } from '@/components/ConnectModal'; +import { assertion } from '@liquid/auth-client/assertion'; + export function HomePage() { const credId = window.localStorage.getItem('credId'); const navigate = useNavigate(); @@ -42,7 +44,7 @@ export function HomePage() { {credId && (