Merge branch 'anis_link_with_sdk_client' into dev

This commit is contained in:
Sosthene 2024-10-22 11:02:23 +02:00
commit 427252e4a7
22 changed files with 1621 additions and 633 deletions

440
package-lock.json generated
View File

@ -9,9 +9,12 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@types/qrcode": "^1.5.5",
"@vitejs/plugin-react": "^4.3.1", "@vitejs/plugin-react": "^4.3.1",
"@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue": "^5.0.5",
"vite": "^5.3.3", "html5-qrcode": "^2.3.8",
"qrcode": "^1.5.3",
"vite": "^5.4.9",
"vite-plugin-copy": "^0.1.6", "vite-plugin-copy": "^0.1.6",
"vite-plugin-html": "^3.2.2", "vite-plugin-html": "^3.2.2",
"vite-plugin-wasm": "^3.3.0" "vite-plugin-wasm": "^3.3.0"
@ -912,192 +915,208 @@
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
"integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"android" "android"
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz",
"integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"android" "android"
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz",
"integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz",
"integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz",
"integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-musleabihf": { "node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz",
"integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz",
"integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz",
"integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": { "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz",
"integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz",
"integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz",
"integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz",
"integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz",
"integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz",
"integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz",
"integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz",
"integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
@ -1211,9 +1230,10 @@
} }
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.5", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"license": "MIT"
}, },
"node_modules/@types/express": { "node_modules/@types/express": {
"version": "4.17.21", "version": "4.17.21",
@ -1276,7 +1296,6 @@
"version": "20.14.9", "version": "20.14.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz",
"integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==",
"devOptional": true,
"dependencies": { "dependencies": {
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
@ -1290,6 +1309,14 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/qrcode": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz",
"integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/qs": { "node_modules/@types/qs": {
"version": "6.9.15", "version": "6.9.15",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz",
@ -1794,7 +1821,6 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@ -2028,6 +2054,14 @@
"tslib": "^2.0.3" "tslib": "^2.0.3"
} }
}, },
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"engines": {
"node": ">=6"
}
},
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001640", "version": "1.0.30001640",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz",
@ -2116,6 +2150,77 @@
"node": ">= 10.0" "node": ">= 10.0"
} }
}, },
"node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"node_modules/cliui/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/cliui/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/cliui/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/cliui/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/cliui/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/cliui/node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/clone-deep": { "node_modules/clone-deep": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
@ -2358,6 +2463,14 @@
} }
} }
}, },
"node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/default-browser": { "node_modules/default-browser": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz",
@ -2452,6 +2565,11 @@
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
"dev": true "dev": true
}, },
"node_modules/dijkstrajs": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="
},
"node_modules/dns-packet": { "node_modules/dns-packet": {
"version": "5.6.1", "version": "5.6.1",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
@ -2589,6 +2707,11 @@
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true "dev": true
}, },
"node_modules/encode-utf8": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz",
"integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw=="
},
"node_modules/encodeurl": { "node_modules/encodeurl": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@ -3011,7 +3134,6 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"dependencies": { "dependencies": {
"locate-path": "^5.0.0", "locate-path": "^5.0.0",
"path-exists": "^4.0.0" "path-exists": "^4.0.0"
@ -3138,6 +3260,14 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-intrinsic": { "node_modules/get-intrinsic": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
@ -3459,6 +3589,11 @@
} }
} }
}, },
"node_modules/html5-qrcode": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/html5-qrcode/-/html5-qrcode-2.3.8.tgz",
"integrity": "sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ=="
},
"node_modules/htmlparser2": { "node_modules/htmlparser2": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
@ -3680,7 +3815,6 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@ -4025,7 +4159,6 @@
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"dependencies": { "dependencies": {
"p-locate": "^4.1.0" "p-locate": "^4.1.0"
}, },
@ -4391,7 +4524,6 @@
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"dependencies": { "dependencies": {
"p-try": "^2.0.0" "p-try": "^2.0.0"
}, },
@ -4406,7 +4538,6 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"dependencies": { "dependencies": {
"p-limit": "^2.2.0" "p-limit": "^2.2.0"
}, },
@ -4435,7 +4566,6 @@
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@ -4477,7 +4607,6 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@ -4546,9 +4675,10 @@
"integrity": "sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==" "integrity": "sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw=="
}, },
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.1", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
"license": "ISC"
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "2.3.1", "version": "2.3.1",
@ -4573,10 +4703,18 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/pngjs": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.39", "version": "8.4.47",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
"integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -4591,10 +4729,11 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.3.7", "nanoid": "^3.3.7",
"picocolors": "^1.0.1", "picocolors": "^1.1.0",
"source-map-js": "^1.2.0" "source-map-js": "^1.2.1"
}, },
"engines": { "engines": {
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
@ -4647,6 +4786,23 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/qrcode": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz",
"integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==",
"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/qs": { "node_modules/qs": {
"version": "6.11.0", "version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@ -4790,6 +4946,14 @@
"strip-ansi": "^6.0.1" "strip-ansi": "^6.0.1"
} }
}, },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-from-string": { "node_modules/require-from-string": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@ -4799,6 +4963,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"node_modules/requires-port": { "node_modules/requires-port": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@ -4880,11 +5049,12 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.18.0", "version": "4.24.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
"integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "1.0.5" "@types/estree": "1.0.6"
}, },
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
@ -4894,22 +5064,22 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.18.0", "@rollup/rollup-android-arm-eabi": "4.24.0",
"@rollup/rollup-android-arm64": "4.18.0", "@rollup/rollup-android-arm64": "4.24.0",
"@rollup/rollup-darwin-arm64": "4.18.0", "@rollup/rollup-darwin-arm64": "4.24.0",
"@rollup/rollup-darwin-x64": "4.18.0", "@rollup/rollup-darwin-x64": "4.24.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.18.0", "@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
"@rollup/rollup-linux-arm-musleabihf": "4.18.0", "@rollup/rollup-linux-arm-musleabihf": "4.24.0",
"@rollup/rollup-linux-arm64-gnu": "4.18.0", "@rollup/rollup-linux-arm64-gnu": "4.24.0",
"@rollup/rollup-linux-arm64-musl": "4.18.0", "@rollup/rollup-linux-arm64-musl": "4.24.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
"@rollup/rollup-linux-riscv64-gnu": "4.18.0", "@rollup/rollup-linux-riscv64-gnu": "4.24.0",
"@rollup/rollup-linux-s390x-gnu": "4.18.0", "@rollup/rollup-linux-s390x-gnu": "4.24.0",
"@rollup/rollup-linux-x64-gnu": "4.18.0", "@rollup/rollup-linux-x64-gnu": "4.24.0",
"@rollup/rollup-linux-x64-musl": "4.18.0", "@rollup/rollup-linux-x64-musl": "4.24.0",
"@rollup/rollup-win32-arm64-msvc": "4.18.0", "@rollup/rollup-win32-arm64-msvc": "4.24.0",
"@rollup/rollup-win32-ia32-msvc": "4.18.0", "@rollup/rollup-win32-ia32-msvc": "4.24.0",
"@rollup/rollup-win32-x64-msvc": "4.18.0", "@rollup/rollup-win32-x64-msvc": "4.24.0",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
@ -5166,6 +5336,11 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
},
"node_modules/set-function-length": { "node_modules/set-function-length": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@ -5287,9 +5462,10 @@
} }
}, },
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -5420,7 +5596,6 @@
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": { "dependencies": {
"ansi-regex": "^5.0.1" "ansi-regex": "^5.0.1"
}, },
@ -5794,8 +5969,7 @@
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "5.26.5", "version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
"devOptional": true
}, },
"node_modules/unicorn-magic": { "node_modules/unicorn-magic": {
"version": "0.1.0", "version": "0.1.0",
@ -5904,13 +6078,14 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "5.3.3", "version": "5.4.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz",
"integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==", "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==",
"license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.21.3", "esbuild": "^0.21.3",
"postcss": "^8.4.39", "postcss": "^8.4.43",
"rollup": "^4.13.0" "rollup": "^4.20.0"
}, },
"bin": { "bin": {
"vite": "bin/vite.js" "vite": "bin/vite.js"
@ -5929,6 +6104,7 @@
"less": "*", "less": "*",
"lightningcss": "^1.21.0", "lightningcss": "^1.21.0",
"sass": "*", "sass": "*",
"sass-embedded": "*",
"stylus": "*", "stylus": "*",
"sugarss": "*", "sugarss": "*",
"terser": "^5.4.0" "terser": "^5.4.0"
@ -5946,6 +6122,9 @@
"sass": { "sass": {
"optional": true "optional": true
}, },
"sass-embedded": {
"optional": true
},
"stylus": { "stylus": {
"optional": true "optional": true
}, },
@ -6381,6 +6560,11 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/which-module": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="
},
"node_modules/wildcard": { "node_modules/wildcard": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
@ -6535,10 +6719,66 @@
} }
} }
}, },
"node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
},
"node_modules/yallist": { "node_modules/yallist": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
},
"node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"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/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/yargs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/yargs/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
} }
} }
} }

View File

@ -2,12 +2,12 @@
"name": "sdk_client", "name": "sdk_client",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "dist/index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"build_wasm": "wasm-pack build --out-dir ../../dist/pkg ./crates/sp_client --target bundler --dev", "build_wasm": "wasm-pack build --out-dir ../ihm_client/dist/pkg ../sdk_client --target bundler --dev",
"start": "vite --host 0.0.0.0", "start": "vite --host 0.0.0.0",
"build": "webpack", "build": "tsc && vite build",
"deploy": "sudo cp -r dist/* /var/www/html/" "deploy": "sudo cp -r dist/* /var/www/html/"
}, },
"keywords": [], "keywords": [],
@ -24,9 +24,12 @@
"webpack-dev-server": "^5.0.2" "webpack-dev-server": "^5.0.2"
}, },
"dependencies": { "dependencies": {
"@types/qrcode": "^1.5.5",
"@vitejs/plugin-react": "^4.3.1", "@vitejs/plugin-react": "^4.3.1",
"@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue": "^5.0.5",
"vite": "^5.3.3", "html5-qrcode": "^2.3.8",
"qrcode": "^1.5.3",
"vite": "^5.4.9",
"vite-plugin-copy": "^0.1.6", "vite-plugin-copy": "^0.1.6",
"vite-plugin-html": "^3.2.2", "vite-plugin-html": "^3.2.2",
"vite-plugin-wasm": "^3.3.0" "vite-plugin-wasm": "^3.3.0"

View File

@ -20,6 +20,10 @@ body {
background-blend-mode :soft-light; background-blend-mode :soft-light;
height: 100vh; height: 100vh;
} }
.message {
font-size: 14px;
overflow-wrap: anywhere;
}
/** Modal Css */ /** Modal Css */
.modal { .modal {
@ -35,10 +39,10 @@ body {
} }
.modal-content { .modal-content {
width: 40%; width: 55%;
height: 40%; height: 30%;
background-color: white; background-color: white;
border-radius: 8px; border-radius: 4px;
padding: 20px; padding: 20px;
text-align: center; text-align: center;
display: flex; display: flex;
@ -48,23 +52,21 @@ body {
.modal-title { .modal-title {
margin: 0; margin: 0;
padding-bottom: 20px; padding-bottom: 8px;
width: 100%; width: 100%;
font-size: 1.5em; font-size: 0.9em;
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
} }
.confirmation-box { .confirmation-box {
margin-top: 20px; /* margin-top: 20px; */
align-content: center; align-content: center;
width: 40%; width: 70%;
height: 20%; height: 20%;
padding: 20px; /* padding: 20px; */
background-color: var(--secondary-color); font-size: 1.5em;
border-radius: 8px;
font-size: 1.2em;
color: #333333; color: #333333;
top: 20%; top: 5%;
position: relative; position: relative;
} }
@ -106,6 +108,32 @@ body {
font-weight: bold; font-weight: bold;
} }
} }
.notification-board {
position: absolute;
width: 20rem;
min-height: 8rem;
background-color: white;
right: 0.5rem;
display: none;
border-radius: 4px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
display: none;
.notification-element {
padding: .8rem 0;
width: 100%;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.notification-element:not(:last-child) {
border-bottom: 1px solid;
}
}
} }
.brand-logo { .brand-logo {
@ -130,6 +158,10 @@ body {
grid-column: 2 / 7; grid-column: 2 / 7;
grid-row: 3 ; grid-row: 3 ;
justify-content: center; justify-content: center;
display: flex;
padding: 1rem;
box-sizing: border-box;
max-height: 50vh;
} }
h1 { h1 {
@ -219,7 +251,7 @@ body {
.tab-content.active { .tab-content.active {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-evenly; justify-content: center;
align-items: center; align-items: center;
height: 80%; height: 80%;
} }
@ -235,6 +267,12 @@ body {
align-items: center; align-items: center;
height: 200px; height: 200px;
} }
.camera-card {
display: flex;
justify-content: center;
align-items: center;
/* height: 200px; */
}
.btn { .btn {
display: inline-block; display: inline-block;
@ -258,6 +296,7 @@ body {
border-radius: 8px; border-radius: 8px;
background-color: white; background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
box-sizing: border-box;
overflow: hidden; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -265,13 +304,18 @@ body {
text-align: center; text-align: center;
min-height: 40vh; min-height: 40vh;
max-height: 60vh; max-height: 60vh;
justify-content: space-between; justify-content: flex-start;
padding: 1rem; padding: 1rem;
overflow-y: auto; overflow-y: auto;
} }
.card-content { .card-content {
flex-grow: 1;
flex-direction: column;
display: flex;
justify-content: flex-start;
align-items: center;
text-align: left; text-align: left;
font-size: .8em; font-size: .8em;
position: relative; position: relative;
@ -282,7 +326,13 @@ body {
padding: 1rem 0; padding: 1rem 0;
} }
.process-element { .process-element {
padding: .3rem 0; padding: .4rem 0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
&.selected {
background-color: rgba(26, 28, 24, .08);
}
} }
} }
@ -291,6 +341,11 @@ body {
font-size: 1em; font-size: 1em;
color: #333; color: #333;
width: 90%; width: 90%;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
} }
@ -324,8 +379,22 @@ body {
border-bottom: none; border-bottom: none;
} }
.qr-code-scanner {
display: none;
}
/* QR READER */
#qr-reader div {
position: inherit;
}
#qr-reader div img{
top: 15px ;
right: 25px;
margin-top: 5px;
}
/* INPUT CSS **/ /* INPUT CSS **/
.input-container { .input-container {

View File

@ -1,178 +0,0 @@
class Database {
private static instance: Database;
private db: IDBDatabase | null = null;
private dbName: string = '4nk';
private dbVersion: number = 1;
private storeDefinitions = {
AnkUser: {
name: "user",
options: {'keyPath': 'pre_id'},
indices: []
},
AnkSession: {
name: "session",
options: {},
indices: []
},
AnkProcess: {
name: "process",
options: {'keyPath': 'id'},
indices: [{
name: 'by_name',
keyPath: 'name',
options: {
'unique': true
}
}]
},
AnkMessages: {
name: "messages",
options: {'keyPath': 'id'},
indices: []
}
}
// Private constructor to prevent direct instantiation from outside
private constructor() {}
// Method to access the singleton instance of Database
public static async getInstance(): Promise<Database> {
if (!Database.instance) {
Database.instance = new Database();
await Database.instance.init();
}
return Database.instance;
}
// Initialize the database
private async init(): Promise<void> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.dbVersion);
request.onupgradeneeded = () => {
const db = request.result;
Object.values(this.storeDefinitions).forEach(({name, options, indices}) => {
if (!db.objectStoreNames.contains(name)) {
let store = db.createObjectStore(name, options);
indices.forEach(({name, keyPath, options}) => {
store.createIndex(name, keyPath, options);
})
}
});
};
request.onsuccess = () => {
this.db = request.result;
resolve();
};
request.onerror = () => {
console.error("Database error:", request.error);
reject(request.error);
};
});
}
public async getDb(): Promise<IDBDatabase> {
if (!this.db) {
await this.init();
}
return this.db!;
}
public getStoreList(): {[key: string]: string} {
const objectList: {[key: string]: string} = {};
Object.keys(this.storeDefinitions).forEach(key => {
objectList[key] = this.storeDefinitions[key as keyof typeof this.storeDefinitions].name;
});
return objectList;
}
public writeObject(db: IDBDatabase, storeName: string, obj: any, key: IDBValidKey | null): Promise<IDBRequest> {
return new Promise((resolve, reject) => {
const transaction = db.transaction(storeName, 'readwrite');
const store = transaction.objectStore(storeName);
let request: IDBRequest<any>;
if (key) {
request = store.add(obj, key);
} else {
request = store.add(obj);
}
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
});
}
public getObject<T>(db: IDBDatabase, storeName: string, key: IDBValidKey): Promise<T> {
return new Promise((resolve, reject) => {
const transaction = db.transaction(storeName, 'readonly');
const store = transaction.objectStore(storeName);
const request = store.get(key);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
});
}
public rmObject(db: IDBDatabase, storeName: string, key: IDBValidKey): Promise<void> {
return new Promise((resolve, reject) => {
const transaction = db.transaction(storeName, 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.delete(key);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
});
}
public getFirstMatchWithIndex<T>(db: IDBDatabase, storeName: string, indexName: string, lookup: string): Promise<T | null> {
return new Promise((resolve, reject) => {
const transaction = db.transaction(storeName, 'readonly');
const store = transaction.objectStore(storeName);
const index = store.index(indexName);
const request = index.openCursor(IDBKeyRange.only(lookup));
request.onerror = () => reject(request.error);
request.onsuccess = () => {
const cursor = request.result;
if (cursor) {
resolve(cursor.value);
} else {
resolve(null)
}
}
});
}
public setObject(db: IDBDatabase, storeName: string, obj: any, key: string | null): Promise<IDBRequest> {
return new Promise((resolve, reject) => {
const transaction = db.transaction(storeName, 'readwrite');
const store = transaction.objectStore(storeName);
let request: IDBRequest<any>;
if (key) {
request = store.put(obj, key);
} else {
request = store.put(obj);
}
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
});
}
public getAll<T>(db: IDBDatabase, storeName: string): Promise<T[]> {
return new Promise((resolve, reject) => {
const transaction = db.transaction(storeName, 'readonly');
const store = transaction.objectStore(storeName);
const request = store.getAll();
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
});
}
}
export default Database;

View File

@ -0,0 +1,16 @@
<div id="modal" class="modal">
<div class="modal-content">
<div class="modal-title">Login</div>
<div class="message">
Do you want to pair device?
<!-- Attempting to pair device with address
<strong>{{device1}}</strong>
with device with address
<strong>{{device2}}</strong> -->
</div>
<div class="confirmation-box">
<a class="btn confirmation-btn" onclick="confirm()">Confirm</a>
<a class="btn refusal-btn" onclick="closeConfirmationModal()">Refuse</a>
</div>
</div>
</div>

View File

@ -0,0 +1,13 @@
import Routing from "/src/services/routing.service.ts";
const router = await Routing.getInstance();
export async function confirm() {
router.confirmPairing()
}
export async function closeConfirmationModal() {
router.closeConirmationModal()
}
window.confirm = confirm;
window.closeConfirmationModal = closeConfirmationModal;

View File

@ -4,11 +4,14 @@
<div class="brand-logo">4NK</div> <div class="brand-logo">4NK</div>
<div class="nav-right-icons"> <div class="nav-right-icons">
<div class="notification-container"> <div class="notification-container">
<div class="bell-icon"> <div id="notification-bell" class="bell-icon">
<svg class="notification-bell" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"> <svg class="notification-bell" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path d="M224 0c-17.7 0-32 14.3-32 32V51.2C119 66 64 130.6 64 208v25.4c0 45.4-15.5 89.5-43.8 124.9L5.3 377c-5.8 7.2-6.9 17.1-2.9 25.4S14.8 416 24 416H424c9.2 0 17.6-5.3 21.6-13.6s2.9-18.2-2.9-25.4l-14.9-18.6C399.5 322.9 384 278.8 384 233.4V208c0-77.4-55-142-128-156.8V32c0-17.7-14.3-32-32-32zm0 96c61.9 0 112 50.1 112 112v25.4c0 47.9 13.9 94.6 39.7 134.6H72.3C98.1 328 112 281.3 112 233.4V208c0-61.9 50.1-112 112-112zm64 352H224 160c0 17 6.7 33.3 18.7 45.3s28.3 18.7 45.3 18.7s33.3-6.7 45.3-18.7s18.7-28.3 18.7-45.3z"/></svg> <path d="M224 0c-17.7 0-32 14.3-32 32V51.2C119 66 64 130.6 64 208v25.4c0 45.4-15.5 89.5-43.8 124.9L5.3 377c-5.8 7.2-6.9 17.1-2.9 25.4S14.8 416 24 416H424c9.2 0 17.6-5.3 21.6-13.6s2.9-18.2-2.9-25.4l-14.9-18.6C399.5 322.9 384 278.8 384 233.4V208c0-77.4-55-142-128-156.8V32c0-17.7-14.3-32-32-32zm0 96c61.9 0 112 50.1 112 112v25.4c0 47.9 13.9 94.6 39.7 134.6H72.3C98.1 328 112 281.3 112 233.4V208c0-61.9 50.1-112 112-112zm64 352H224 160c0 17 6.7 33.3 18.7 45.3s28.3 18.7 45.3 18.7s33.3-6.7 45.3-18.7s18.7-28.3 18.7-45.3z"/></svg>
</div> </div>
<div class="notification-badge">1</div> <div class="notification-badge"></div>
<div id="notification-board" class="notification-board">
<div class="no-notification">No notifications available</div>
</div>
</div> </div>
<div class="burger-menu"> <div class="burger-menu">
@ -18,7 +21,7 @@
<div class="menu-content" id="menu"> <div class="menu-content" id="menu">
<a href="#">Import</a> <a href="#">Import</a>
<button onclick="openModal()">Open Modal</button> <!-- <button onclick="openModal()">Open Modal</button> -->
</div> </div>
</div> </div>
</div> </div>
@ -43,28 +46,28 @@
<div class="card-image qr-code"> <div class="card-image qr-code">
<img src="assets/qr_code.png" alt="QR Code" width="150" height="150"> <img src="assets/qr_code.png" alt="QR Code" width="150" height="150">
</div> </div>
<div class="card-action">
<a id="scan-this-device" class="btn">OK</a>
</div>
</div> </div>
<div class="separator"></div> <div class="separator"></div>
<div id="tab2" class="card tab-content"> <div id="tab2" class="card tab-content">
<div class="card-description"> <div class="card-description">
Scan your other device : Scan your other device :
</div> </div>
<div class="card-image qr-code"> <div class="card-image camera-card">
<img src="assets/camera.jpg" alt="QR Code" width="150" height="150"> <img id="scanner" src="assets/camera.jpg" alt="QR Code" width="150" height="150">
<div class="qr-code-scanner">
<div id="qr-reader" style="width:200px; display: contents;"></div>
<div id="qr-reader-results"></div>
</div> </div>
<div class="card-action">
<a id="scan-device" class="btn">OK</a>
</div> </div>
</div> </div>
<div id="modal" class="modal"> <!-- <div id="modal" class="modal">
<div class="modal-content"> <div class="modal-content">
<div class="modal-title">Login</div> <div class="modal-title">Login</div>
<div class="confirmation-box">Waiting for confirmation...</div> <div class="confirmation-box">Waiting for confirmation...</div>
</div> </div>
</div> </div> -->
</div> </div>

View File

@ -1,3 +1,6 @@
import Routing from "/src/services/routing.service.ts";
import Services from "/src/services/service.ts";
document.querySelectorAll('.tab').forEach(tab => { document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => { tab.addEventListener('click', () => {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
@ -7,7 +10,11 @@ document.querySelectorAll('.tab').forEach(tab => {
document.getElementById(tab.getAttribute('data-tab')).classList.add('active'); document.getElementById(tab.getAttribute('data-tab')).classList.add('active');
}); });
}); });
function toggleMenu() {
document.getElementById('notification-bell').addEventListener('click', openCloseNotifications);
export function toggleMenu() {
var menu = document.getElementById('menu'); var menu = document.getElementById('menu');
if (menu.style.display === 'block') { if (menu.style.display === 'block') {
menu.style.display = 'none'; menu.style.display = 'none';
@ -16,19 +23,80 @@ document.querySelectorAll('.tab').forEach(tab => {
} }
} }
//// Modal //// Modal
function openModal() { export async function openModal(myAddress, receiverAddress) {
document.getElementById('modal').style.display = 'flex'; const router = await Routing.getInstance();
router.openLoginModal(myAddress, receiverAddress)
} }
function closeModal() {
document.getElementById('modal').style.display = 'none'; function openCloseNotifications() {
const notifications = document.querySelector('.notification-board')
notifications.style.display = notifications?.style.display === 'none' ? 'block' : 'none'
} }
// Close modal when clicking outside of it // const service = await Services.getInstance()
window.onclick = function(event) { // service.setNotification()
const modal = document.getElementById('modal');
if (event.target === modal) { window.toggleMenu = toggleMenu;
closeModal(); window.openModal = openModal;
/// Scan QR Code
function docReady(fn) {
// see if DOM is already available
if (document.readyState === "complete"
|| document.readyState === "interactive") {
// call on next available tick
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
}
}
docReady(function () {
scanDevice();
var resultContainer = document.getElementById('qr-reader-results');
var lastResult, countResults = 0;
async function onScanSuccess(decodedText, decodedResult) {
if (lastResult === decodedText) { return; }
lastResult = decodedText;
++countResults;
// Handle on success condition with the decoded message.
console.log(`Scan result ${decodedText}`, decodedResult);
try {
// Attempt to parse the decoded text as a URL
const scannedUrl = new URL(decodedText);
// Extract the 'sp_address' parameter
const spAddress = scannedUrl.searchParams.get('sp_address');
if (spAddress) {
html5QrcodeScanner.clear();
const service = await Services.getInstance()
// Call the sendPairingTx function with the extracted sp_address
await service.sendPairingTx(spAddress);
} else {
console.error('The scanned URL does not contain the sp_address parameter.');
alert('Invalid QR code: sp_address parameter missing.');
}
} catch (error) {
// Handle cases where decodedText is not a valid URL
console.error('Scanned text is not a valid URL:', error);
alert('Invalid QR code: Unable to parse URL.');
} }
} }
var html5QrcodeScanner = new Html5QrcodeScanner(
"qr-reader", { fps: 10, qrbox: 250 });
html5QrcodeScanner.render(onScanSuccess);
});
function scanDevice() {
const scannerImg = document.querySelector('#scanner')
if(scannerImg) scannerImg.style.display = 'none'
const scannerQrCode = document.querySelector('.qr-code-scanner')
if(scannerQrCode) scannerQrCode.style.display = 'block'
}
window.scanDevice = scanDevice

16
src/html/login-modal.html Normal file
View File

@ -0,0 +1,16 @@
<div id="login-modal" class="modal">
<div class="modal-content">
<div class="modal-title">Login</div>
<div class="confirmation-box">
<div class="message">
Attempting to pair device with address
<strong>{{device1}}</strong>
with device with address
<strong>{{device2}}</strong>
</div>
<div>
Awaiting pairing validation...
</div>
</div>
</div>
</div>

13
src/html/login-modal.js Normal file
View File

@ -0,0 +1,13 @@
import Routing from "/src/services/routing.service.ts";
const router = await Routing.getInstance();
export async function confirmLogin() {
router.confirmLogin()
}
export async function closeLoginModal() {
router.closeLoginModal()
}
window.confirmLogin = confirmLogin;
window.closeLoginModal = closeLoginModal;

View File

@ -17,7 +17,7 @@
</svg> </svg>
<div class="menu-content" id="menu"> <div class="menu-content" id="menu">
<a href="#">Revoke</a> <a href="#" onclick="unpair()">Revoke</a>
<a href="#">Export</a> <a href="#">Export</a>
<a href="#">Import</a> <a href="#">Import</a>
<a href="#">Disconnect</a> <a href="#">Disconnect</a>
@ -34,7 +34,7 @@
<div class="card"> <div class="card">
<div class="card-description"> <div class="card-description">
<div class="input-container"> <div class="input-container">
<select multiple data-multi-select-plugin id="autocoplete" placeholder="Filter processes..." class="select-field"> <select multiple data-multi-select-plugin id="autocomplete" placeholder="Filter processes..." class="select-field">
</select> </select>
<label for="autocomplete" class="input-label">Filter processes :</label> <label for="autocomplete" class="input-label">Filter processes :</label>
<div class="selected-processes"></div> <div class="selected-processes"></div>

View File

@ -1,3 +1,5 @@
import Services from "/src/services/service.ts";
function toggleMenu() { function toggleMenu() {
const menu = document.getElementById("menu"); const menu = document.getElementById("menu");
if (menu.style.display === "block") { if (menu.style.display === "block") {
@ -7,43 +9,6 @@ function toggleMenu() {
} }
} }
// Input for filtering script
// var processeList = [
// {
// id: 1,
// name: "Messaging",
// description: "Encrypted messages",
// zoneList: [
// {
// id: 1,
// name: "General",
// },
// ],
// },
// {
// id: 2,
// name: "Storage",
// description: "Distributed storage",
// zoneList: [
// {
// id: 1,
// name: "Paris",
// },
// {
// id: 2,
// name: "Normandy",
// },
// {
// id: 3,
// name: "New York",
// },
// {
// id: 4,
// name: "Moscow",
// },
// ],
// },
// ];
// Initialize function, create initial tokens with itens that are already selected by the user // Initialize function, create initial tokens with itens that are already selected by the user
function init(element) { function init(element) {
@ -407,3 +372,11 @@ document.addEventListener("click", () => {
} }
} }
}); });
export async function unpair() {
const service = await Services.getInstance()
await service.unpairDevice()
}
window.unpair = unpair;

View File

@ -6,7 +6,8 @@
<meta name="description" content="4NK Web5 Platform"> <meta name="description" content="4NK Web5 Platform">
<meta name="keywords" content="4NK web5 bitcoin blockchain decentralize dapps relay contract"> <meta name="keywords" content="4NK web5 bitcoin blockchain decentralize dapps relay contract">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/style/4nk.css"> <link rel="stylesheet" href="./style/4nk.css">
<script src="https://unpkg.com/html5-qrcode"></script>
<title>4NK Application</title> <title>4NK Application</title>
</head> </head>
<body> <body>

View File

@ -1,12 +1,31 @@
import Services from './services'; import Services from './services/service';
import { WebSocketClient } from './websockets';
const wsurl = `wss://${window.location.hostname}/ws/`;
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', async () => {
try { try {
const services = await Services.getInstance(); const services = await Services.getInstance();
await services.addWebsocketConnection(wsurl); setTimeout( async () => {
await services.recoverInjectHtml() let device = await services.getDevice()
if(!device) {
device = await services.createNewDevice();
} else {
await services.restoreDevice(device)
}
await services.restoreProcesses();
await services.restoreMessages();
if (services.isPaired()) { await services.injectProcessListPage() }
else {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString)
const pairingAddress = urlParams.get('sp_address')
if(pairingAddress) {
setTimeout(async () => await services.sendPairingTx(pairingAddress), 1000)
}
}
}, 500);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }

View File

@ -0,0 +1,22 @@
export interface INotification {
id: number;
title: string;
description: string;
sendToNotificationPage?: boolean;
path?: string;
}
// Quelles sont les données utiles pour le user ???
export interface IUser {
id: string;
information?: any
}
// Quelles sont les données utiles pour les messages ???
export interface IMessage {
id: string;
message: any
}

View File

@ -0,0 +1,23 @@
export interface IProcess {
id: number;
name: string;
description: string;
icon?: string;
zoneList: IZone[],
}
export interface IZone {
id: number;
name: string;
path: string;
// Est-ce que la zone a besoin d'une icone ?
icon?: string;
}
export interface INotification {
id: number;
title: string;
description: string;
sendToNotificationPage?: boolean;
path?: string;
}

16
src/scanner.js Normal file
View File

@ -0,0 +1,16 @@
function onScanSuccess(decodedText, decodedResult) {
// handle the scanned code as you like, for example:
console.log(`Code matched = ${decodedText}`, decodedResult);
}
function onScanFailure(error) {
// handle scan failure, usually better to ignore and keep scanning.
// for example:
console.warn(`Code scan error = ${error}`);
}
let html5QrcodeScanner = new Html5QrcodeScanner(
"reader",
{ fps: 10, qrbox: {width: 250, height: 250} },
/* verbose= */ false);
html5QrcodeScanner.render(onScanSuccess, onScanFailure);

View File

@ -1,183 +0,0 @@
import { WebSocketClient } from './websockets';
import homePage from './html/home.html?raw';
import homeScript from './html/home.js?raw';
import processPage from './html/process.html?raw';
import processScript from './html/process.js?raw';
export default class Services {
private static instance: Services;
private current_process: string | null = null;
private websocketConnection: WebSocketClient[] = [];
private sp_address: string | null = null;
private processes = [
{
id: 1,
name: "Messaging",
description: "Encrypted messages",
zoneList: [
{
id: 1,
name: "General",
},
],
},
{
id: 2,
name: "Storage",
description: "Distributed storage",
zoneList: [
{
id: 1,
name: "Paris",
},
{
id: 2,
name: "Normandy",
},
{
id: 3,
name: "New York",
},
{
id: 4,
name: "Moscow",
},
],
},
];
private subscriptions: {element: Element; event: string; eventHandler: string;}[] = [] ;
// Private constructor to prevent direct instantiation from outside
private constructor() {}
// Method to access the singleton instance of Services
public static async getInstance(): Promise<Services> {
if (!Services.instance) {
Services.instance = new Services();
// await Services.instance.init();
}
return Services.instance;
}
public async addWebsocketConnection(url: string): Promise<void> {
const services = await Services.getInstance();
const newClient = new WebSocketClient(url, services);
if (!services.websocketConnection.includes(newClient)) {
services.websocketConnection.push(newClient);
}
}
public async recoverInjectHtml(): Promise<void> {
const container = document.getElementById('containerId');
if (!container) {
console.error("No html container");
return;
}
const services = await Services.getInstance();
container.innerHTML = homePage;
const newScript = document.createElement('script')
newScript.textContent = homeScript;
document.head.appendChild(newScript).parentNode?.removeChild(newScript);
const btn = container.querySelector('#scan-this-device')
if(btn) {
this.addSubscription(btn, 'click', 'injectProcessListPage')
}
}
private addSubscription(element: Element, event: string, eventHandler: string): void {
this.subscriptions.push({ element, event, eventHandler });
element.addEventListener(event, (this as any)[eventHandler].bind(this));
}
private cleanSubsciptions(): void {
for (const sub of this.subscriptions) {
const el = sub.element;
const eventHandler = sub.eventHandler;
el.removeEventListener(sub.event, (this as any)[eventHandler].bind(this));
}
this.subscriptions = [];
}
async injectProcessListPage(): Promise<void> {
const container = document.getElementById('containerId');
if (!container) {
console.error("No html container");
return;
}
this.cleanSubsciptions()
const services = await Services.getInstance();
container.innerHTML = processPage;
const newScript = document.createElement('script');
newScript.textContent = processScript;
document.head.appendChild(newScript).parentNode?.removeChild(newScript);
if(this.processes) {
services.setProcessesInSelectElement(this.processes)
}
}
public async setProcessesInSelectElement(processList: any[]) {
const select = document.querySelector(".select-field");
if(select) {
for (const process of processList) {
const option = document.createElement("option");
option.setAttribute("value", process.name);
option.innerText = process.name;
select.appendChild(option);
}
}
const optionList = document.querySelector('.autocomplete-list');
if(optionList) {
const observer = new MutationObserver((mutations, observer) => {
console.log(mutations, observer);
const options = optionList.querySelectorAll('li')
if(options) {
for(const option of options) {
this.addSubscription(option, 'click', 'showSelectedProcess')
}
}
});
observer.observe(document, {
subtree: true,
attributes: true,
});
}
}
public async listenToOptionListPopulating(event: Event) {
const target = event.target as HTMLUListElement;
const options = target?.querySelectorAll('li')
console.log(options)
}
public async showSelectedProcess(event: MouseEvent) {
const elem = event.target;
if(elem) {
const cardContent = document.querySelector(".card-content");
const processes = this.processes;
const process = processes.find((process: any) => process.name === (elem as any).dataset.value);
if (process) {
const processDiv = document.createElement("div");
processDiv.className = "process";
processDiv.id = process.name;
const titleDiv = document.createElement("div");
titleDiv.className = "process-title";
titleDiv.innerHTML = `${process.name} : ${process.description}`;
processDiv.appendChild(titleDiv);
for (const zone of process.zoneList) {
const zoneElement = document.createElement("div");
zoneElement.className = "process-element";
zoneElement.innerHTML = `Zone ${zone.id} : ${zone.name}`;
processDiv.appendChild(zoneElement);
}
if(cardContent) cardContent.appendChild(processDiv);
}
}
}
}

View File

@ -0,0 +1,114 @@
import modalHtml from '../html/login-modal.html?raw';
import confirmationModalHtml from '../html/confirmation-modal.html?raw';
import modalScript from '../html/login-modal.js?raw';
import confirmationModalScript from '../html/confirmation-modal.js?raw';
import Services from './service';
import { U32_MAX } from './service';
export default class Routing {
private static instance: Routing;
private sdkClient: any;
private currentPrd: any;
private currentOutpoint?: string;
private constructor() {}
private paired_addresses: string[] = [];
// Method to access the singleton instance of Services
public static async getInstance(): Promise<Routing> {
if (!Routing.instance) {
Routing.instance = new Routing();
await Routing.instance.init();
}
return Routing.instance;
}
public async init(): Promise<void> {
this.sdkClient = await import("../../dist/pkg/sdk_client");
}
public openLoginModal(myAddress: string, receiverAddress: string) {
const container = document.querySelector('.page-container');
let html = modalHtml
html = html.replace('{{device1}}', myAddress)
html = html.replace('{{device2}}', receiverAddress)
if (container) container.innerHTML += html;
const modal = document.getElementById('login-modal')
if (modal) modal.style.display = 'flex';
const newScript = document.createElement('script');
newScript.setAttribute('type', 'module')
newScript.textContent = modalScript;
document.head.appendChild(newScript).parentNode?.removeChild(newScript);
}
public openConfirmationModal(pcd: any, outpointCommitment: string) {
console.log("")
let roles = JSON.parse(pcd['roles']);// ['members'][0];
console.log(roles);
let members = roles['owner']['members'];
console.log(members);
// We take all the addresses except our own
const local_address = this.sdkClient.get_address();
console.log("🚀 ~ Routing ~ openConfirmationModal ~ pcd:", pcd)
for (const address of members[0]['sp_addresses']) {
if (address !== local_address) { this.paired_addresses.push(address) }
}
let html = confirmationModalHtml;
// html = html.replace('{{device1}}', members[0]['sp_addresses'][0]);
// html = html.replace('{{device2}}', members[0]['sp_addresses'][1]);
this.currentOutpoint = outpointCommitment as string;
const container = document.querySelector('.page-container');
if (container) container.innerHTML += html;
const modal = document.getElementById('modal')
if (modal) modal.style.display = 'flex';
const newScript = document.createElement('script');
newScript.setAttribute('type', 'module')
newScript.textContent = confirmationModalScript;
document.head.appendChild(newScript).parentNode?.removeChild(newScript);
// Add correct text
// Close modal when clicking outside of it
window.onclick = (event) => {
const modal = document.getElementById('modal');
if (event.target === modal) {
this.confirmPairing();
}
}
}
confirmLogin() {
console.log('=============> Confirm Login')
const loginTx = this.sdkClient.create_login_transaction(1)
console.log("🚀 ~ Routing ~ confirmLogin ~ loginTx:", loginTx)
this.sdkClient.login('LOGIN', loginTx)
}
async closeLoginModal() {
const modal = document.getElementById('login-modal')
if (modal) modal.style.display = 'none';
}
async confirmPairing() {
const service = await Services.getInstance()
const modal = document.getElementById('modal')
// console.log("🚀 ~ Routing ~ confirm ~ prd:", prd)
if (modal) modal.style.display = 'none';
// Just make an empty commitment for now
const emptyTxid = '0'.repeat(64)
const commitmentOutpoint = `${emptyTxid}:${U32_MAX}`;
// We take the paired device(s) from the contract
await this.sdkClient.pair_device(commitmentOutpoint, this.paired_addresses)
this.paired_addresses = []
const newDevice = await service.dumpDevice();
await service.saveDevice(newDevice);
service.injectProcessListPage();
}
async closeConfirmationModal() {
const modal = document.getElementById('modal')
if (modal) modal.style.display = 'none';
}
}

719
src/services/service.ts Normal file
View File

@ -0,0 +1,719 @@
// import { WebSocketClient } from '../websockets';
import { INotification } from '~/models/notification.model';
import homePage from '../html/home.html?raw';
import homeScript from '../html/home.js?raw';
import processPage from '../html/process.html?raw';
import processScript from '../html/process.js?raw';
import { IProcess } from '~/models/process.model';
// import Database from './database';
import { WebSocketClient } from '../websockets';
import QRCode from 'qrcode'
import { servicesVersion } from 'typescript';
import { ApiReturn, CachedMessage, Member } from '../../dist/pkg/sdk_client';
import Routing from './routing.service';
type ProcessesCache = {
[key: string]: any;
};
export const U32_MAX = 4294967295;
const wsurl = `https://demo.4nkweb.com/ws/`;
export default class Services {
private static initializing: Promise<Services> | null = null;
private static instance: Services;
private current_process: string | null = null;
private sdkClient: any;
private websocketConnection: WebSocketClient | null = null;
private processes: IProcess[] | null = null;
private notifications: INotification[] | null = null;
private subscriptions: {element: Element; event: string; eventHandler: string;}[] = [] ;
private database: any
// Private constructor to prevent direct instantiation from outside
private constructor() {}
// Method to access the singleton instance of Services
public static async getInstance(): Promise<Services> {
if (Services.instance) {
return Services.instance;
}
if (!Services.initializing) {
Services.initializing = (async () => {
const instance = new Services();
await instance.init();
return instance;
})();
}
console.log('initializing services');
Services.instance = await Services.initializing;
Services.initializing = null; // Reset for potential future use
return Services.instance;
}
public async init(): Promise<void> {
this.notifications = this.getNotifications();
this.sdkClient = await import("../../dist/pkg/sdk_client");
this.sdkClient.setup();
await this.addWebsocketConnection(wsurl);
await this.recoverInjectHtml();
}
public async addWebsocketConnection(url: string): Promise<void> {
// const services = await Services.getInstance();
if (!this.websocketConnection) {
console.log('Opening new websocket connection');
const newClient = new WebSocketClient(url, this);
this.websocketConnection = newClient;
}
}
public async recoverInjectHtml(): Promise<void> {
const container = document.getElementById('containerId');
if (!container) {
console.error("No html container");
return;
}
container.innerHTML = homePage;
const newScript = document.createElement('script')
newScript.setAttribute('type', 'module')
newScript.textContent = homeScript;
document.head.appendChild(newScript).parentNode?.removeChild(newScript);
}
private generateQRCode = async (text: string) => {
console.log("🚀 ~ Services ~ generateQRCode= ~ text:", text)
try {
const container = document.getElementById('containerId');
const currentUrl = window.location.href;
const url = await QRCode.toDataURL(currentUrl + "?sp_address=" + text);
const qrCode = container?.querySelector('.qr-code img');
qrCode?.setAttribute('src', url)
} catch (err) {
console.error(err);
}
}
public isPaired(): boolean | undefined {
try {
return this.sdkClient.is_linking();
} catch (e) {
console.error("isPaired ~ Error:", e);
}
}
public async unpairDevice(): Promise<void> {
const service = await Services.getInstance()
await service.sdkClient.unpair_device()
const newDevice = await this.dumpDevice();
await this.saveDevice(newDevice);
}
private prepareProcessTx(myAddress: string, recipientAddress: string) {
const initial_session_privkey = new Uint8Array(32);
const initial_session_pubkey = new Uint8Array(32);
const pairingTemplate = {
"description": "AliceBob",
"roles": {
"owner": {
"members":
[{sp_addresses: [myAddress, recipientAddress]}],
"validation_rules":
[
{
"quorum": 1.0,
"fields": [
"description",
"roles",
"session_privkey",
"session_pubkey",
"key_parity"
],
"min_sig_member": 1.0
}
]
}
},
"session_privkey": initial_session_privkey,
"session_pubkey": initial_session_pubkey,
"key_parity": true,
}
const apiReturn = this.sdkClient.create_update_transaction(undefined, JSON.stringify(pairingTemplate), 1)
return apiReturn
}
public async sendPairingTx(spAddress: string): Promise<void> {
const amount = this.sdkClient.get_available_amount();
if (amount === 0n) {
const faucetMsg = this.sdkClient.create_faucet_msg();
await this.sendFaucetMessage(faucetMsg);
}
const localAddress = this.sdkClient.get_address();
const emptyTxid = '0'.repeat(64)
try {
let commitmentOutpoint = `${emptyTxid}:${U32_MAX}`;
this.sdkClient.pair_device(commitmentOutpoint, [spAddress])
} catch (e) {
console.error("Services ~ Error:", e);
return
}
setTimeout( async () => {
const apiReturn = this.prepareProcessTx(localAddress, spAddress)
await this.handleApiReturn(apiReturn);
}, 100)
return
}
async resetDevice() {
await this.sdkClient.reset_device()
}
async sendNewTxMessage(message: string) {
if (!this.websocketConnection) {
throw new Error('No websocket connection');
}
// console.log("🚀 ~ WebSocketClient ~ this.ws.onopen= ~ newTxMessage:", message)
await this.websocketConnection.sendMessage('NewTx', message)
}
async sendCommitMessage(message: string) {
// console.log("🚀 ~ WebSocketClient ~ this.ws.onopen= ~ CommitMessage:", message)
await this.websocketConnection?.sendMessage('Commit', message)
}
async sendCipherMessages(ciphers: string[]) {
for (let i = 0; i < ciphers.length; i++) {
const cipher = ciphers[i];
await this.websocketConnection?.sendMessage('Cipher', cipher);
}
}
async sendFaucetMessage(message: string): Promise<void> {
// console.log("🚀 ~ WebSocketClient ~ this.ws.onopen= ~ faucetMessage:", message)
await this.websocketConnection?.sendMessage('Faucet', message);
}
async parseCipher(message: string) {
// try {
// JSON.parse(message)
// const router = await Routing.getInstance();
// router.closeLoginModal()
// this.injectProcessListPage()
// } catch {
// console.log('Not proper format for cipher')
// }
try {
console.log('parsing new cipher');
const apiReturn = await this.sdkClient.parse_cipher(message, 0.00001);
console.log("🚀 ~ Services ~ parseCipher ~ apiReturn:", apiReturn);
await this.handleApiReturn(apiReturn)
} catch (e) {
console.log("Cipher isn't for us");
}
// await this.saveCipherTxToDb(parsedTx)
}
async parseNewTx(tx: string) {
try {
// console.log('==============> sending txxxxxxx parser', tx)
const parsedTx = await this.sdkClient.parse_new_tx(tx, 0, 0.0001)
if(parsedTx) {
console.log("🚀 ~ Services ~ parseNewTx ~ parsedTx:", parsedTx)
try {
await this.handleApiReturn(parsedTx);
const newDevice = await this.dumpDevice();
await this.saveDevice(newDevice);
} catch (e) {
console.error("Failed to update device with new tx");
}
}
} catch(e) {
console.trace(e);
}
}
private async handleApiReturn(apiReturn: ApiReturn) {
if (apiReturn.ciphers_to_send && apiReturn.ciphers_to_send.length) {
await this.sendCipherMessages(apiReturn.ciphers_to_send)
}
setTimeout(async () => {
if (apiReturn.updated_process && apiReturn.updated_process.length) {
const [processCommitment, process] = apiReturn.updated_process;
console.debug('Updated Process Commitment:', processCommitment);
console.debug('Process Details:', process);
// Save process to storage
localStorage.setItem(processCommitment, JSON.stringify(process));
// Check if the newly updated process reveals some new information
try {
const proposals: string[] = this.sdkClient.get_update_proposals(processCommitment);
if (proposals && proposals.length != 0) {
const actual_proposal = JSON.parse(proposals[0]); // We just don't acknowledge concurrent proposals for now
console.info(actual_proposal);
let router = await Routing.getInstance();
router.openConfirmationModal(actual_proposal, processCommitment);
}
} catch (e) {
console.error(e);
}
}
if(apiReturn.updated_cached_msg && apiReturn.updated_cached_msg.length) {
apiReturn.updated_cached_msg.forEach((msg, index) => {
// console.debug(`CachedMessage ${index}:`, msg);
// Save the message to local storage
localStorage.setItem(msg.id.toString(), JSON.stringify(msg));
});
}
if (apiReturn.commit_to_send) {
const commit = apiReturn.commit_to_send;
await this.sendCommitMessage(JSON.stringify(commit));
}
if (apiReturn.new_tx_to_send && apiReturn.new_tx_to_send.transaction.length != 0) {
await this.sendNewTxMessage(JSON.stringify(apiReturn.new_tx_to_send))
}
}, 0)
}
async pairDevice(prd: any, outpointCommitment: string) {
console.log("🚀 ~ Services ~ pairDevice ~ prd:", prd)
// const service = await Services.getInstance();
// const spAddress = await this.getDeviceAddress() as any;
// const sender = JSON.parse(prd?.sender)
// const senderAddress = sender?.sp_addresses?.find((address: string) => address !== spAddress)
// console.log("🚀 ~ Services ~ pairDevice ~ senderAddress:", senderAddress)
// if(senderAddress) {
// const proposal = service.sdkClient.get_update_proposals(outpointCommitment);
// console.log("🚀 ~ Services ~ pairDevice ~ proposal:", proposal)
// // const pairingTx = proposal.pairing_tx.replace(/^\"+|\"+$/g, '')
// const parsedProposal = JSON.parse(proposal[0])
// console.log("🚀 ~ Services ~ pairDevice ~ parsedProposal:", parsedProposal)
// const roles = JSON.parse(parsedProposal.roles)
// console.log("🚀 ~ Services ~ pairDevice ~ roles:", roles, Array.isArray(roles), !roles.owner)
// if(Array.isArray(roles) || !roles.owner) return
// const proposalMembers = roles?.owner?.members
// const isFirstDevice = proposalMembers.some((member: Member) => member.sp_addresses.some(address => address === spAddress))
// const isSecondDevice = proposalMembers.some((member: Member) => member.sp_addresses.some(address => address === senderAddress))
// console.log("🚀 ~ Services ~ pairDevice ~ proposalMembers:", proposalMembers)
// if(proposalMembers?.length !== 2 || !isFirstDevice || !isSecondDevice) return
// const pairingTx = parsedProposal?.pairing_tx?.replace(/^\"+|\"+$/g, '')
// let txid = '0'.repeat(64)
// console.log("🚀 ~ Services ~ pairDevice ~ pairingTx:", pairingTx, `${txid}:4294967295`)
// const pairing = await service.sdkClient.pair_device(`${txid}:4294967295`, [senderAddress])
// const device = this.dumpDevice()
// console.log("🚀 ~ Services ~ pairDevice ~ device:", device)
// this.saveDevice(device)
// // await service.sdkClient.pair_device(pairingTx, [senderAddress])
// console.log("🚀 ~ Services ~ pairDevice ~ pairing:", pairing)
// // const process = await this.prepareProcessTx(spAddress, senderAddress)
// console.log("🚀 ~ Services ~ pairDevice ~ process:", outpointCommitment, prd, prd.payload)
// const prdString = JSON.stringify(prd).trim()
// console.log("🚀 ~ Services ~ pairDevice ~ prdString:", prdString)
// let tx = await service.sdkClient.response_prd(outpointCommitment, prdString, true)
// console.log("🚀 ~ Services ~ pairDevice ~ tx:", tx)
// if(tx.ciphers_to_send) {
// tx.ciphers_to_send.forEach((cipher: string) => service.websocketConnection?.sendMessage('Cipher', cipher))
// }
// this.injectProcessListPage()
// }
}
// async saveTxToDb(tx: CachedMessage) {
// const database = await Database.getInstance();
// const indexedDb = await database.getDb();
// await database.writeObject(indexedDb, 'messages', tx, null);
// }
// async saveCipherTxToDb(tx: string) {
// const database = await Database.getInstance();
// const indexedDb = await database.getDb();
// if(tx) {
// await database.writeObject(indexedDb, database.getStoreList().AnkCipherMessages, tx, null);
// }
// }
async getAmount() {
const amount = await this.sdkClient.get_available_amount()
console.log("🚀 ~ Services ~ getAmount ~ amount:", amount)
return amount
}
async getDeviceAddress() {
return await this.sdkClient.get_address();
}
async dumpDevice() {
const device = await this.sdkClient.dump_device()
// console.log("🚀 ~ Services ~ dumpDevice ~ device:", device)
return device
}
async saveDevice(device: any): Promise<any> {
// console.log("🚀 ~ Services ~ saveDevice ~ device:", device)
localStorage.setItem('wallet', device);
}
async getDevice(): Promise<string | null> {
return localStorage.getItem('wallet')
}
async dumpWallet() {
const wallet = await this.sdkClient.dump_wallet()
console.log("🚀 ~ Services ~ dumpWallet ~ wallet:", wallet)
return wallet
}
async createFaucetMessage() {
const message = await this.sdkClient.create_faucet_msg()
console.log("🚀 ~ Services ~ createFaucetMessage ~ message:", message)
return message;
}
private addSubscription(element: Element, event: string, eventHandler: string): void {
this.subscriptions.push({ element, event, eventHandler });
element.addEventListener(event, (this as any)[eventHandler].bind(this));
}
async createNewDevice() {
let spAddress = '';
try {
spAddress = await this.sdkClient.create_new_device(0, 'regtest')
const device = await this.dumpDevice()
console.log("🚀 ~ Services ~ device:", device)
await this.saveDevice(device)
} catch (e) {
console.error("Services ~ Error:", e);
}
this.generateQRCode(spAddress || '')
return spAddress;
}
async restoreDevice(device: string) {
try {
await this.sdkClient.restore_device(device)
const spAddress = this.sdkClient.get_address();
this.generateQRCode(spAddress || '')
} catch (e) {
console.error(e);
}
}
private getProcessesCache(): ProcessesCache {
// Regular expression to match 64-character hexadecimal strings
const hexU32KeyRegex: RegExp = /^[0-9a-fA-F]{64}:\d+$/;
const hexObjects: ProcessesCache = {}
// Iterate over all keys in localStorage
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (!key) {
return hexObjects;
}
// Check if the key matches the 32-byte hex pattern
if (hexU32KeyRegex.test(key)) {
const value = localStorage.getItem(key);
if (!value) {
continue;
}
hexObjects[key] = JSON.parse(value);
}
}
return hexObjects;
}
private getCachedMessages(): string[] {
const u32KeyRegex = /^\d+$/;
const U32_MAX = 4294967295;
const messages: string[] = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (!key) {
return messages;
}
if (u32KeyRegex.test(key)) {
const num = parseInt(key, 10);
if (num < 0 || num > U32_MAX) {
console.warn(`Key ${key} is outside the u32 range and will be ignored.`);
continue;
}
const value = localStorage.getItem(key);
if (!value) {
console.warn(`No value found for key: ${key}`);
continue;
}
messages.push(value);
}
}
return messages;
}
public async restoreProcesses() {
const processesCache = this.getProcessesCache();
console.log("🚀 ~ Services ~ restoreProcesses ~ processesCache:", processesCache);
if (processesCache.length == 0) {
console.debug("🚀 ~ Services ~ restoreProcesses ~ no processes in local storage");
return;
}
try {
await this.sdkClient.set_process_cache(JSON.stringify(processesCache));
} catch (e) {
console.error('Services ~ restoreProcesses ~ Error:', e);
}
}
public async restoreMessages() {
const cachedMessages = this.getCachedMessages();
console.log("🚀 ~ Services ~ restoreMessages ~ chachedMessages:", cachedMessages);
if (cachedMessages && cachedMessages.length != 0) {
try {
await this.sdkClient.set_message_cache(cachedMessages);
} catch (e) {
console.error('Services ~ restoreMessages ~ Error:', e);
}
}
}
private cleanSubscriptions(): void {
for (const sub of this.subscriptions) {
const el = sub.element;
const eventHandler = sub.eventHandler;
el.removeEventListener(sub.event, (this as any)[eventHandler].bind(this));
}
this.subscriptions = [];
}
async injectProcessListPage(): Promise<void> {
const container = document.getElementById('containerId');
if (!container) {
console.error("No html container");
return;
}
this.cleanSubscriptions()
// const user = services.sdkClient.create_user('Test', 'test', 10, 1, 'Messaging')
// console.log("🚀 ~ Services ~ injectProcessListPage ~ user:", user)
// const database = await Database.getInstance();
// const indexedDb = await database.getDb();
// await database.writeObject(indexedDb, database.getStoreList().AnkUser, user.user, null);
container.innerHTML = processPage;
const newScript = document.createElement('script');
newScript.setAttribute('type', 'module')
newScript.textContent = processScript;
document.head.appendChild(newScript).parentNode?.removeChild(newScript);
this.processes = await this.getProcesses();
if(this.processes) {
this.setProcessesInSelectElement(this.processes)
}
}
public async setProcessesInSelectElement(processList: any[]) {
const select = document.querySelector(".select-field");
if(select) {
for (const process of processList) {
const option = document.createElement("option");
option.setAttribute("value", process.name);
option.innerText = process.name;
select.appendChild(option);
}
}
const optionList = document.querySelector('.autocomplete-list');
if(optionList) {
const observer = new MutationObserver((mutations, observer) => {
const options = optionList.querySelectorAll('li')
if(options) {
for(const option of options) {
this.addSubscription(option, 'click', 'showSelectedProcess')
}
}
});
observer.observe(document, {
subtree: true,
attributes: true,
});
}
}
public async listenToOptionListPopulating(event: Event) {
const target = event.target as HTMLUListElement;
const options = target?.querySelectorAll('li')
}
public async showSelectedProcess(event: MouseEvent) {
const elem = event.target;
if(elem) {
const cardContent = document.querySelector(".card-content");
const processes = await this.getProcesses();
console.log("🚀 ~ Services ~ showSelectedProcess ~ processes:", processes)
const process = processes.find((process: any) => process.name === (elem as any).dataset.value);
if (process) {
const processDiv = document.createElement("div");
processDiv.className = "process";
processDiv.id = process.name;
const titleDiv = document.createElement("div");
titleDiv.className = "process-title";
titleDiv.innerHTML = `${process.name} : ${process.description}`;
processDiv.appendChild(titleDiv);
for (const zone of process.zoneList) {
const zoneElement = document.createElement("div");
zoneElement.className = "process-element";
zoneElement.setAttribute('zone-id', zone.id.toString())
zoneElement.innerHTML = `Zone ${zone.id} : ${zone.name}`;
this.addSubscription(zoneElement, 'click', 'goToProcessPage')
processDiv.appendChild(zoneElement);
}
if(cardContent) cardContent.appendChild(processDiv);
}
}
}
goToProcessPage(event: MouseEvent) {
const target = event.target as HTMLDivElement;
const zoneId = target?.getAttribute('zone-id');
const processList = document.querySelectorAll('.process-element');
if(processList) {
for(const process of processList) {
process.classList.remove('selected')
}
}
target.classList.add('selected')
console.log('=======================> going to process page', zoneId)
}
async getProcesses(): Promise<IProcess[]> {
const processes = this.sdkClient.get_processes()
console.log("🚀 ~ Services ~ getProcesses ~ processes:", processes)
return [
{
id: 1,
name: "Messaging",
description: "Encrypted messages",
zoneList: [
{
id: 1,
name: "General",
path: '/test'
},
],
},
{
id: 2,
name: "Storage",
description: "Distributed storage",
zoneList: [
{
id: 1,
name: "Paris",
path: '/test'
},
{
id: 2,
name: "Normandy",
path: '/test'
},
{
id: 3,
name: "New York",
path: '/test'
},
{
id: 4,
name: "Moscow",
path: '/test'
},
],
},
];
}
getNotifications(): INotification[] {
return [
{
id: 1,
title: 'Notif 1',
description: 'A normal notification',
sendToNotificationPage: false,
path: '/notif1'
},
{
id: 2,
title: 'Notif 2',
description: 'A normal notification',
sendToNotificationPage: false,
path: '/notif2'
},
{
id: 3,
title: 'Notif 3',
description: 'A normal notification',
sendToNotificationPage: false,
path: '/notif3'
}
]
}
async setNotification(): Promise<void> {
const badge = document.querySelector('.notification-badge') as HTMLDivElement
const notifications = this.notifications
const noNotifications = document.querySelector('.no-notification') as HTMLDivElement
if(notifications?.length) {
badge.innerText = notifications.length.toString()
const notificationBoard = document.querySelector('.notification-board') as HTMLDivElement
noNotifications.style.display = 'none'
for(const notif of notifications) {
const notifElement = document.createElement("div");
notifElement.className = "notification-element";
notifElement.setAttribute('notif-id', notif.id.toString())
notifElement.innerHTML = `
<div>${notif.title}</div>
<div>${notif.description}</div>
`;
// this.addSubscription(notifElement, 'click', 'goToProcessPage')
notificationBoard.appendChild(notifElement);
}
} else {
noNotifications.style.display = 'block'
}
}
}

View File

@ -1,5 +1,6 @@
import Services from "./services"; import { AnkFlag, CachedMessage } from "dist/pkg/sdk_client";
import { AnkFlag, AnkNetworkMsg, CachedMessage } from "../dist/pkg/sdk_client"; import Services from "./services/service";
// import { AnkFlag, AnkNetworkMsg, CachedMessage } from "../dist/pkg/sdk_client";
class WebSocketClient { class WebSocketClient {
private ws: WebSocket; private ws: WebSocket;
@ -8,67 +9,67 @@ class WebSocketClient {
constructor(url: string, private services: Services) { constructor(url: string, private services: Services) {
this.ws = new WebSocket(url); this.ws = new WebSocket(url);
this.ws.addEventListener('open', (event) => { if(this.ws !== null) {
this.ws.onopen = async (event) => {
console.log('WebSocket connection established'); console.log('WebSocket connection established');
// Once the connection is open, send all messages in the queue
while (this.messageQueue.length > 0) { while (this.messageQueue.length > 0) {
const message = this.messageQueue.shift(); const message = this.messageQueue.shift();
if (message) { if (message) {
this.ws.send(message); this.ws.send(message);
} }
} }
}); };
// Listen for messages // Listen for messages
this.ws.addEventListener('message', (event) => { this.ws.onmessage = (event) => {
const msgData = event.data; const msgData = event.data;
// console.log("Received text message: ", msgData);
(async () => { (async () => {
if (typeof(msgData) === 'string') { if (typeof msgData === 'string') {
console.log("Received text message: "+msgData);
try { try {
const feeRate = 1; const feeRate = 0.0001;
// By parsing the message, we can link it with existing cached message and return the updated version of the message const parsedMessage = JSON.parse(msgData)
let res: CachedMessage = await services.parseNetworkMessage(msgData, feeRate); // console.log("🚀 ~ WebSocketClient ~ parsedMessage:", parsedMessage)
console.debug(res); const services = await Services.getInstance()
if (res.status === 'FaucetComplete') { switch(parsedMessage.flag) {
// we received a faucet tx, there's nothing else to do case 'NewTx':
window.alert(`New faucet output\n${res.commited_in}`); await services.parseNewTx(parsedMessage.content)
await services.updateMessages(res); break;
await services.updateOwnedOutputsForUser(); case 'Cipher':
} else if (res.status === 'TxWaitingCipher') { await services.parseCipher(parsedMessage.content)
// we received a tx but we don't have the cipher // device = await services.dumpWallet()
console.debug(`received notification in output ${res.commited_in}, waiting for cipher message`); // console.log("🚀 ~ WebSocketClient ~ device:", device)
await services.updateMessages(res); // await services.saveDevice(device)
await services.updateOwnedOutputsForUser(); break;
} else if (res.status === 'CipherWaitingTx') {
// we received a cipher but we don't have the key
console.debug(`received a cipher`);
await services.updateMessages(res);
} else if (res.status === 'SentWaitingConfirmation') {
// We are sender and we're waiting for the challenge that will confirm recipient got the transaction and the message
await services.updateMessages(res);
await services.updateOwnedOutputsForUser();
} else if (res.status === 'MustSpendConfirmation') {
// we received a challenge for a notification we made
// that means we can stop rebroadcasting the tx and we must spend the challenge to confirm
window.alert(`Spending ${res.confirmed_by} to prove our identity`);
console.debug(`sending confirm message to ${res.recipient}`);
await services.updateMessages(res);
await services.answer_confirmation_message(res);
} else if (res.status === 'ReceivedMustConfirm') {
// we found a notification and decrypted the cipher
window.alert(`Received message from ${res.sender}\n${res.plaintext}`);
// we must spend the commited_in output to sender
await services.updateMessages(res);
await services.confirm_sender_address(res);
} else if (res.status === 'Complete') {
window.alert(`Received confirmation that ${res.sender} is the author of message ${res.plaintext}`)
await services.updateMessages(res);
await services.updateOwnedOutputsForUser();
} else {
console.debug('Received an unimplemented valid message');
} }
// By parsing the message, we can link it with existing cached message and return the updated version of the message
// if (res.status === 'FaucetComplete') {
// // we received a faucet tx, there's nothing else to do
// window.alert(`New faucet output\n${res.commited_in}`);
// } else if (res.status === 'TxWaitingCipher') {
// // we received a tx but we don't have the cipher
// console.debug(`received notification in output ${res.commited_in}, waiting for cipher message`);
// } else if (res.status === 'CipherWaitingTx') {
// // we received a cipher but we don't have the key
// console.debug(`received a cipher`);
// } else if (res.status === 'SentWaitingConfirmation') {
// // We are sender and we're waiting for the challenge that will confirm recipient got the transaction and the message
// } else if (res.status === 'MustSpendConfirmation') {
// // we received a challenge for a notification we made
// // that means we can stop rebroadcasting the tx and we must spend the challenge to confirm
// window.alert(`Spending ${res.confirmed_by} to prove our identity`);
// console.debug(`sending confirm message to ${res.recipient}`);
// } else if (res.status === 'ReceivedMustConfirm') {
// // we found a notification and decrypted the cipher
// window.alert(`Received message from ${res.sender}\n${res.plaintext}`);
// // we must spend the commited_in output to sender
// } else if (res.status === 'Complete') {
// window.alert(`Received confirmation that ${res.sender} is the author of message ${res.plaintext}`)
// } else {
// console.debug('Received an unimplemented valid message');
// }
} catch (error) { } catch (error) {
console.error('Received an invalid message:', error); console.error('Received an invalid message:', error);
} }
@ -76,30 +77,36 @@ class WebSocketClient {
console.error('Received a non-string message'); console.error('Received a non-string message');
} }
})(); })();
}); };
// Listen for possible errors // Listen for possible errors
this.ws.addEventListener('error', (event) => { this.ws.onerror = (event) => {
console.error('WebSocket error:', event); console.error('WebSocket error:', event);
}); };
// Listen for when the connection is closed // Listen for when the connection is closed
this.ws.addEventListener('close', (event) => { this.ws.onclose = (event) => {
console.log('WebSocket is closed now.'); console.log('WebSocket is closed now.');
}); }
}
} }
// Method to send messages // Method to send messages
public sendMessage(flag: AnkFlag, message: string): void { public async sendMessage(flag: AnkFlag, message: string): Promise<void> {
if (this.ws.readyState === WebSocket.OPEN) { if (this.ws.readyState === WebSocket.OPEN) {
const networkMessage: AnkNetworkMsg = { const networkMessage = {
'flag': flag, 'flag': flag,
'content': message 'content': message
} }
// console.debug("Sending message:", JSON.stringify(networkMessage)); // if(flag === 'Cipher') {
// const services = await Services.getInstance();
// services.parseCipher(JSON.stringify(networkMessage))
// }
console.log("Sending message:", JSON.stringify(networkMessage));
this.ws.send(JSON.stringify(networkMessage)); this.ws.send(JSON.stringify(networkMessage));
} else { } else {
console.warn('WebSocket is not open. ReadyState:', this.ws.readyState); console.error('WebSocket is not open. ReadyState:', this.ws.readyState);
this.messageQueue.push(message); this.messageQueue.push(message);
} }
} }
@ -108,6 +115,12 @@ class WebSocketClient {
return this.ws.url; return this.ws.url;
} }
public sendNormalMessage(message: string) {
this.ws.send(message);
console.log("Sending message:", JSON.stringify(message));
}
// Method to close the WebSocket connection // Method to close the WebSocket connection
public close(): void { public close(): void {
this.ws.close(); this.ws.close();

View File

@ -2,8 +2,13 @@ import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue'; // or react from '@vitejs/plugin-react' if using React import vue from '@vitejs/plugin-vue'; // or react from '@vitejs/plugin-react' if using React
import wasm from 'vite-plugin-wasm'; import wasm from 'vite-plugin-wasm';
import {createHtmlPlugin} from 'vite-plugin-html'; import {createHtmlPlugin} from 'vite-plugin-html';
import fs from 'fs'
import path from 'path'
export default defineConfig({ export default defineConfig({
optimizeDeps: {
include: ['qrcode']
},
plugins: [ plugins: [
vue(), // or react() if using React vue(), // or react() if using React
wasm(), wasm(),
@ -14,6 +19,7 @@ export default defineConfig({
], ],
build: { build: {
outDir: 'dist', outDir: 'dist',
target: 'esnext',
rollupOptions: { rollupOptions: {
input: './src/index.ts', input: './src/index.ts',
output: { output: {
@ -28,7 +34,9 @@ export default defineConfig({
extensions: ['.ts', '.tsx', '.js'], extensions: ['.ts', '.tsx', '.js'],
}, },
server: { server: {
host: 'localhost',
open: false, open: false,
port: 3001, port: 3001,
strictPort: true, // Empêche de changer de port si le 3001 est occupé
}, },
}); });