Update Utils version. Add abstract Client class. Code Cleanup

This commit is contained in:
2026-08-12 05:16:16 +00:00
parent d42efd0f56
commit 55d09d048e
7 changed files with 265 additions and 252 deletions
+120 -148
View File
@@ -10,7 +10,6 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@bitauth/libauth": "^3.0.0", "@bitauth/libauth": "^3.0.0",
"@xo-cash/crypto": "^0.0.2",
"@xo-cash/primitives": "^0.0.2", "@xo-cash/primitives": "^0.0.2",
"@xo-cash/utils": "git+https://git.harvmaster.com/Harvmaster/xo-cash-utils.git", "@xo-cash/utils": "git+https://git.harvmaster.com/Harvmaster/xo-cash-utils.git",
"zod": "^4.4.3" "zod": "^4.4.3"
@@ -31,9 +30,9 @@
} }
}, },
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
"integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -48,9 +47,9 @@
} }
}, },
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
"integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -65,9 +64,9 @@
} }
}, },
"node_modules/@esbuild/android-arm64": { "node_modules/@esbuild/android-arm64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
"integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -82,9 +81,9 @@
} }
}, },
"node_modules/@esbuild/android-x64": { "node_modules/@esbuild/android-x64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
"integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -99,9 +98,9 @@
} }
}, },
"node_modules/@esbuild/darwin-arm64": { "node_modules/@esbuild/darwin-arm64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
"integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -116,9 +115,9 @@
} }
}, },
"node_modules/@esbuild/darwin-x64": { "node_modules/@esbuild/darwin-x64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
"integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -133,9 +132,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-arm64": { "node_modules/@esbuild/freebsd-arm64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
"integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -150,9 +149,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-x64": { "node_modules/@esbuild/freebsd-x64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
"integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -167,9 +166,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm": { "node_modules/@esbuild/linux-arm": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
"integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -184,9 +183,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm64": { "node_modules/@esbuild/linux-arm64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
"integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -201,9 +200,9 @@
} }
}, },
"node_modules/@esbuild/linux-ia32": { "node_modules/@esbuild/linux-ia32": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
"integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -218,9 +217,9 @@
} }
}, },
"node_modules/@esbuild/linux-loong64": { "node_modules/@esbuild/linux-loong64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
"integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@@ -235,9 +234,9 @@
} }
}, },
"node_modules/@esbuild/linux-mips64el": { "node_modules/@esbuild/linux-mips64el": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
"integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
"cpu": [ "cpu": [
"mips64el" "mips64el"
], ],
@@ -252,9 +251,9 @@
} }
}, },
"node_modules/@esbuild/linux-ppc64": { "node_modules/@esbuild/linux-ppc64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
"integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -269,9 +268,9 @@
} }
}, },
"node_modules/@esbuild/linux-riscv64": { "node_modules/@esbuild/linux-riscv64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
"integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -286,9 +285,9 @@
} }
}, },
"node_modules/@esbuild/linux-s390x": { "node_modules/@esbuild/linux-s390x": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
"integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -303,9 +302,9 @@
} }
}, },
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
"integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -320,9 +319,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-arm64": { "node_modules/@esbuild/netbsd-arm64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
"integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -337,9 +336,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-x64": { "node_modules/@esbuild/netbsd-x64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
"integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -354,9 +353,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-arm64": { "node_modules/@esbuild/openbsd-arm64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
"integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -371,9 +370,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-x64": { "node_modules/@esbuild/openbsd-x64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
"integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -388,9 +387,9 @@
} }
}, },
"node_modules/@esbuild/openharmony-arm64": { "node_modules/@esbuild/openharmony-arm64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
"integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -405,9 +404,9 @@
} }
}, },
"node_modules/@esbuild/sunos-x64": { "node_modules/@esbuild/sunos-x64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
"integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -422,9 +421,9 @@
} }
}, },
"node_modules/@esbuild/win32-arm64": { "node_modules/@esbuild/win32-arm64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
"integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -439,9 +438,9 @@
} }
}, },
"node_modules/@esbuild/win32-ia32": { "node_modules/@esbuild/win32-ia32": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
"integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -456,9 +455,9 @@
} }
}, },
"node_modules/@esbuild/win32-x64": { "node_modules/@esbuild/win32-x64": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
"integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -473,9 +472,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "26.1.1", "version": "26.2.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz",
"integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -822,27 +821,6 @@
"node": ">=16.20.0" "node": ">=16.20.0"
} }
}, },
"node_modules/@xo-cash/crypto": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@xo-cash/crypto/-/crypto-0.0.2.tgz",
"integrity": "sha512-23rJQ7PwOZW1IaBZTwTVIKpDMa8Rrqjf8tAzqFNBjqRPCQpHB2fMAokezu3x8ql31wJywRHZpZHBGfXLLvglLA==",
"license": "MIT",
"dependencies": {
"@bitauth/libauth": "^3.1.0-next.8",
"@xo-cash/primitives": "0.0.2",
"@xo-cash/types": "0.0.3",
"@xo-cash/utils": "0.0.2"
}
},
"node_modules/@xo-cash/crypto/node_modules/@bitauth/libauth": {
"version": "3.1.0-next.8",
"resolved": "https://registry.npmjs.org/@bitauth/libauth/-/libauth-3.1.0-next.8.tgz",
"integrity": "sha512-Pm+Ju+YP3JeBLLTiVrBnia2wwE4G17r4XqpvPRMcklElJTe8J6x3JgKRg1by0Xm3ZY6UFxACkEAoSA+x419/zA==",
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
}
},
"node_modules/@xo-cash/primitives": { "node_modules/@xo-cash/primitives": {
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/@xo-cash/primitives/-/primitives-0.0.2.tgz", "resolved": "https://registry.npmjs.org/@xo-cash/primitives/-/primitives-0.0.2.tgz",
@@ -862,9 +840,9 @@
} }
}, },
"node_modules/@xo-cash/types": { "node_modules/@xo-cash/types": {
"version": "0.0.3", "version": "0.0.4",
"resolved": "https://registry.npmjs.org/@xo-cash/types/-/types-0.0.3.tgz", "resolved": "https://registry.npmjs.org/@xo-cash/types/-/types-0.0.4.tgz",
"integrity": "sha512-TgPEgp7C+GqGEyN7a7fpQa9uvUyY75Xir5BDBM/+C29OLdBoL8Rj+P8MWfcALtGxt+REFUdceNgFic26uCihng==", "integrity": "sha512-XBB/jNC1orDcMUi5Q0g+fnPXdKdb+n7OyqmJ8pxFrfM0y9OAdLjfddMih/JWfOWQtBkvY0CtdaZapgBSrvLeSw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@bitauth/libauth": "^3.1.0-next.8" "@bitauth/libauth": "^3.1.0-next.8"
@@ -880,13 +858,13 @@
} }
}, },
"node_modules/@xo-cash/utils": { "node_modules/@xo-cash/utils": {
"version": "0.0.2", "version": "0.0.3",
"resolved": "git+https://git.harvmaster.com/Harvmaster/xo-cash-utils.git#bc6f9a8c7f55681794c1af0f2e51393c78ea8a31", "resolved": "git+https://git.harvmaster.com/Harvmaster/xo-cash-utils.git#fb3362783c749265630e0881c07614777c147050",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@bitauth/libauth": "^3.1.0-next.8", "@bitauth/libauth": "^3.1.0-next.8",
"@xo-cash/types": "0.0.3", "@xo-cash/primitives": "0.0.2",
"eventemitter3": "^5.0.4", "@xo-cash/types": "0.0.4",
"zod": "^4.3.6" "zod": "^4.3.6"
} }
}, },
@@ -900,9 +878,9 @@
} }
}, },
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.28.1", "version": "0.28.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
@@ -913,40 +891,34 @@
"node": ">=18" "node": ">=18"
}, },
"optionalDependencies": { "optionalDependencies": {
"@esbuild/aix-ppc64": "0.28.1", "@esbuild/aix-ppc64": "0.28.2",
"@esbuild/android-arm": "0.28.1", "@esbuild/android-arm": "0.28.2",
"@esbuild/android-arm64": "0.28.1", "@esbuild/android-arm64": "0.28.2",
"@esbuild/android-x64": "0.28.1", "@esbuild/android-x64": "0.28.2",
"@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-arm64": "0.28.2",
"@esbuild/darwin-x64": "0.28.1", "@esbuild/darwin-x64": "0.28.2",
"@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.2",
"@esbuild/freebsd-x64": "0.28.1", "@esbuild/freebsd-x64": "0.28.2",
"@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm": "0.28.2",
"@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-arm64": "0.28.2",
"@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-ia32": "0.28.2",
"@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-loong64": "0.28.2",
"@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-mips64el": "0.28.2",
"@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-ppc64": "0.28.2",
"@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-riscv64": "0.28.2",
"@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-s390x": "0.28.2",
"@esbuild/linux-x64": "0.28.1", "@esbuild/linux-x64": "0.28.2",
"@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.2",
"@esbuild/netbsd-x64": "0.28.1", "@esbuild/netbsd-x64": "0.28.2",
"@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.2",
"@esbuild/openbsd-x64": "0.28.1", "@esbuild/openbsd-x64": "0.28.2",
"@esbuild/openharmony-arm64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.2",
"@esbuild/sunos-x64": "0.28.1", "@esbuild/sunos-x64": "0.28.2",
"@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-arm64": "0.28.2",
"@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-ia32": "0.28.2",
"@esbuild/win32-x64": "0.28.1" "@esbuild/win32-x64": "0.28.2"
} }
}, },
"node_modules/eventemitter3": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
"license": "MIT"
},
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -963,9 +935,9 @@
} }
}, },
"node_modules/tsx": { "node_modules/tsx": {
"version": "4.23.1", "version": "4.23.12",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz",
"integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==", "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
-1
View File
@@ -18,7 +18,6 @@
}, },
"dependencies": { "dependencies": {
"@bitauth/libauth": "^3.0.0", "@bitauth/libauth": "^3.0.0",
"@xo-cash/crypto": "^0.0.2",
"@xo-cash/primitives": "^0.0.2", "@xo-cash/primitives": "^0.0.2",
"@xo-cash/utils": "git+https://git.harvmaster.com/Harvmaster/xo-cash-utils.git", "@xo-cash/utils": "git+https://git.harvmaster.com/Harvmaster/xo-cash-utils.git",
"zod": "^4.4.3" "zod": "^4.4.3"
+2 -3
View File
@@ -3,7 +3,6 @@ import { PrivateKey } from "@xo-cash/primitives";
import { SSEClient } from "./sse/client.js"; import { SSEClient } from "./sse/client.js";
import { WsClient } from './ws/client.js'; import { WsClient } from './ws/client.js';
const testSSE = async () => { const testSSE = async () => {
const privateKey = PrivateKey.fromString('c440ce6ac5b63ae7bc4af9891b80b7a88ccdeebba5e43246885e9ec56f912ac2'); const privateKey = PrivateKey.fromString('c440ce6ac5b63ae7bc4af9891b80b7a88ccdeebba5e43246885e9ec56f912ac2');
const client = new SSEClient('https://v2.sync.xo.harvmaster.com', privateKey, { const client = new SSEClient('https://v2.sync.xo.harvmaster.com', privateKey, {
@@ -50,5 +49,5 @@ const testWS = async () => {
// await client.disconnect(); // await client.disconnect();
} }
// testSSE(); testSSE();
testWS(); // testWS();
+117
View File
@@ -0,0 +1,117 @@
import { binToHex, flattenBinArray, secp256k1, sha256 } from "@bitauth/libauth";
import { toExtendedJson } from "@xo-cash/utils";
import type { WriteRequest } from "../types.js";
export type AuthenticatedRequestHeaders = {
'X-Public-Key': string;
'X-Timestamp': string;
'X-Signature': string;
};
export type SignedPayload = { publicKey: string; signature: string };
export abstract class SyncClient {
abstract connect(): Promise<void>;
abstract disconnect(): Promise<void>;
abstract write(resourceId: string, value: Record<string, unknown>): Promise<unknown>;
abstract read(resourceId: string): Promise<unknown>;
abstract subscribe(resourceId: string): Promise<void>;
abstract unsubscribe(resourceId: string): Promise<unknown>;
/**
* Derives the resource private key from the private key and resource ID
* @param privateKey - The private key to derive the resource private key from
* @param resourceId - The resource ID to derive the resource private key from
* @returns The resource private key
*/
static deriveResourcePrivateKey(privateKey: Uint8Array, resourceId: string): Uint8Array {
// Convert the resource ID to bytes
const resourceIdBytes = new TextEncoder().encode(resourceId);
// Hash the private key and resource ID
return sha256.hash(flattenBinArray([ privateKey, resourceIdBytes ]));
}
/**
* Signs a payload with the private key and returns the public key and signature
* @param privateKey - The private key to sign the payload with
* @param payload - The payload to sign
* @returns The public key and signature
*/
static signPayload(privateKey: Uint8Array, payload: string): SignedPayload {
// Convert the payload to Binary and hash it
const payloadHash = sha256.hash(new TextEncoder().encode(payload));
// Derive the public key
const publicKey = secp256k1.derivePublicKeyCompressed(privateKey);
// If the public key is a string, throw an error
if (typeof publicKey === 'string') {
throw new Error('Failed to derive public key');
}
// Sign the payload
const signature = secp256k1.signMessageHashDER(privateKey, payloadHash);
// If the signature is a string, throw an error
if (typeof signature === 'string') {
throw new Error('Failed to sign message');
}
return {
publicKey: binToHex(publicKey),
signature: binToHex(signature),
};
}
/**
* Signs a write request with the private key and returns the write request
* @param privateKey - The private key to sign the write request with
* @param resourceId - The resource ID to write to
* @param value - The value to write
* @returns The write request
*/
static signWriteRequest(privateKey: Uint8Array, resourceId: string, value: Uint8Array): WriteRequest {
// Derive the resource private key
const derivedKey = this.deriveResourcePrivateKey(privateKey, resourceId);
// Create the payload
const timestamp = Date.now();
const payload = `${timestamp}${resourceId}${toExtendedJson(value)}`;
// Sign the payload
const { publicKey, signature } = this.signPayload(derivedKey, payload);
return {
id: resourceId,
value,
publicKey,
timestamp,
signature,
};
}
/**
* Signs a request with the private key and returns the public key, timestamp, and signature
* @param privateKey - The private key to sign the request with
* @param path - The path of the request
* @param body - The body of the request
* @returns The public key, timestamp, and signature
*/
static signRequest(privateKey: Uint8Array, path: string, body: unknown): AuthenticatedRequestHeaders {
// Create the payload
const timestamp = Date.now();
const payload = `${timestamp}:${path}:${toExtendedJson(body)}`;
// Sign the payload
const { publicKey, signature } = this.signPayload(privateKey, payload);
return {
'X-Public-Key': publicKey,
'X-Timestamp': timestamp.toString(),
'X-Signature': signature,
};
}
}
+15 -91
View File
@@ -1,20 +1,14 @@
import { SSESession, toExtendedJson } from '@xo-cash/utils'; import { SSESession, toExtendedJson } from '@xo-cash/utils';
import { PrivateKey } from '@xo-cash/primitives' import { PrivateKey } from '@xo-cash/primitives'
import { binToHex, flattenBinArray, secp256k1, sha256 } from '@bitauth/libauth'
import type { WriteRequest } from '../types.js';
export type AuthenticatedRequestHeaders = { import { SyncClient } from '../shared/client.js';
'X-Public-Key': string;
'X-Timestamp': string;
'X-Signature': string;
};
export type SSEClientOptions = { export type SSEClientOptions = {
onMessage: (message: string) => void; onMessage: (message: string) => void;
onError: (error: Error) => void; onError: (error: Error) => void;
}; };
export class SSEClient { export class SSEClient extends SyncClient {
private sseSession: SSESession; private sseSession: SSESession;
private readonly subscriptions: Set<string> = new Set(); private readonly subscriptions: Set<string> = new Set();
private readonly messageListeners = new Set<(message: string) => void>(); private readonly messageListeners = new Set<(message: string) => void>();
@@ -28,6 +22,8 @@ export class SSEClient {
} }
constructor(private readonly url: string, privateKey: PrivateKey, options: Partial<SSEClientOptions> = {}) { constructor(private readonly url: string, privateKey: PrivateKey, options: Partial<SSEClientOptions> = {}) {
super();
this.privateKey = privateKey; this.privateKey = privateKey;
this.sseSession = new SSESession(`${this.url}/data/subscribe`, { this.sseSession = new SSESession(`${this.url}/data/subscribe`, {
method: 'POST', method: 'POST',
@@ -59,7 +55,13 @@ export class SSEClient {
onRequest: async (request) => { onRequest: async (request) => {
// Create the request authentication headers // Create the request authentication headers
const requestBody = toExtendedJson(request.body); const requestBody = toExtendedJson(request.body);
const authHeaders = SSEClient.authenticateRequest(this.privateKey, requestBody);
// Get the path from the URL
const url = new URL(this.url);
const path = url.pathname;
// Sign the request body
const authHeaders = SyncClient.signRequest(this.privateKey.toBytes(), path, requestBody);
// Initialize the request headers if they don't exist // Initialize the request headers if they don't exist
request.headers ??= {}; request.headers ??= {};
@@ -83,7 +85,7 @@ export class SSEClient {
// Create a listener to re-emit the messages // Create a listener to re-emit the messages
this.sseSession.on('message', (message) => { this.sseSession.on('message', (message) => {
for (const listener of this.messageListeners) { for (const listener of this.messageListeners) {
listener(message); listener(message.data);
} }
}); });
@@ -104,7 +106,7 @@ export class SSEClient {
const valueStr = toExtendedJson(value); const valueStr = toExtendedJson(value);
const valueBytes = this.textEncoder.encode(valueStr); const valueBytes = this.textEncoder.encode(valueStr);
const resource = SSEClient.makeWriteRequest(this.privateKey, resourceId, valueBytes); const resource = SyncClient.signWriteRequest(this.privateKey.toBytes(), resourceId, valueBytes);
const bodyStr = toExtendedJson({ resources: [resource] }); const bodyStr = toExtendedJson({ resources: [resource] });
@@ -121,7 +123,8 @@ export class SSEClient {
}); });
if (!res.ok) { if (!res.ok) {
throw new Error(`Failed to write resource: ${res.statusText}`); const error = await res.json();
throw new Error(`Failed to write resource [${resourceId}] (${res.statusText}): ${JSON.stringify(error)}`);
} }
return res.json(); return res.json();
@@ -161,83 +164,4 @@ export class SSEClient {
await this.reconnect(); await this.reconnect();
} }
/**
* Derives a resource-scoped private key from the root key and resource id.
*/
private static deriveResourcePrivateKey(privateKey: Uint8Array, resourceId: string): Uint8Array {
const resourceIdBytes = new TextEncoder().encode(resourceId);
return sha256.hash(flattenBinArray([ privateKey, resourceIdBytes ]));
}
/**
* Signs a canonical payload string and returns the compressed public key and DER signature.
*/
private static signPayload(
privateKey: Uint8Array,
payload: string,
): { publicKey: string; signature: string } {
const payloadHash = sha256.hash(new TextEncoder().encode(payload));
const publicKey = secp256k1.derivePublicKeyCompressed(privateKey);
if (typeof publicKey === 'string') {
throw new Error('Failed to derive public key');
}
const signature = secp256k1.signMessageHashDER(privateKey, payloadHash);
if (typeof signature === 'string') {
throw new Error('Failed to sign message');
}
return {
publicKey: binToHex(publicKey),
signature: binToHex(signature),
};
}
/**
* Builds a signed write resource for POST /data/write.
*
* The signing key is derived per resource via sha256(privateKey + resourceId).
* The signature covers only the resource value, not the full request body.
*/
static makeWriteRequest(
privateKey: PrivateKey,
resourceId: string,
value: Uint8Array,
): WriteRequest {
const derivedKey = SSEClient.deriveResourcePrivateKey(privateKey.toBytes(), resourceId);
const timestamp = Date.now();
const payload = `${timestamp}${resourceId}${toExtendedJson(value)}`;
const { publicKey, signature } = SSEClient.signPayload(derivedKey, payload);
return {
id: resourceId,
value,
publicKey,
timestamp,
signature,
};
}
/**
* Signs an entire request body for transport-layer authentication headers.
*
* Unlike {@link makeWriteRequest}, this uses the root private key and signs
* the full serialized body rather than an individual resource value.
*/
static authenticateRequest(
privateKey: PrivateKey,
body: string,
): AuthenticatedRequestHeaders {
const timestamp = Date.now();
const payload = `${timestamp}${body}`;
const { publicKey, signature } = SSEClient.signPayload(privateKey.toBytes(), payload);
return {
'X-Public-Key': publicKey,
'X-Timestamp': timestamp.toString(),
'X-Signature': signature,
};
}
} }
+10 -8
View File
@@ -1,14 +1,15 @@
import { fromExtendedJson, toExtendedJson } from '@xo-cash/utils'; import { fromExtendedJson, toExtendedJson } from '@xo-cash/utils';
import type { PrivateKey } from '@xo-cash/primitives'; import type { PrivateKey } from '@xo-cash/primitives';
import { SSEClient } from '../sse/client.js';
import { WsMessageSchema, WsSuccessResponseSchema, WsErrorResponseSchema, type WsMessage } from './types.js'; import { WsMessageSchema, WsSuccessResponseSchema, WsErrorResponseSchema, type WsMessage } from './types.js';
import { SyncClient } from '../shared/client.js';
/** A request sent over the sync server's WebSocket connection. */ /** A request sent over the sync server's WebSocket connection. */
type WsRequest = { type WsRequest = {
id?: string; id?: string;
path: string; path: string;
body?: unknown; body?: unknown;
headers?: Record<string, string>;
}; };
type PendingRequest = { type PendingRequest = {
@@ -38,7 +39,7 @@ export type WsClientOptions = {
* resubscription, and request-timeout behavior. Those concerns are omitted * resubscription, and request-timeout behavior. Those concerns are omitted
* here to keep the transport demo easy to follow. * here to keep the transport demo easy to follow.
*/ */
export class WsClient { export class WsClient extends SyncClient {
private socket: WebSocket | undefined; private socket: WebSocket | undefined;
private readonly pendingRequests = new Map<string, PendingRequest>(); private readonly pendingRequests = new Map<string, PendingRequest>();
private readonly messageListeners = new Set<(message: WsMessage) => void>(); private readonly messageListeners = new Set<(message: WsMessage) => void>();
@@ -49,6 +50,8 @@ export class WsClient {
private readonly privateKey: PrivateKey, private readonly privateKey: PrivateKey,
options: Partial<WsClientOptions> = {}, options: Partial<WsClientOptions> = {},
) { ) {
super();
this.messageListeners.add(options.onMessage ?? (() => {})); this.messageListeners.add(options.onMessage ?? (() => {}));
this.errorListeners.add(options.onError ?? (() => {})); this.errorListeners.add(options.onError ?? (() => {}));
} }
@@ -118,11 +121,7 @@ export class WsClient {
*/ */
async write(resourceId: string, value: Record<string, unknown>): Promise<unknown> { async write(resourceId: string, value: Record<string, unknown>): Promise<unknown> {
const valueBytes = new TextEncoder().encode(toExtendedJson(value)); const valueBytes = new TextEncoder().encode(toExtendedJson(value));
const resource = SSEClient.makeWriteRequest( const resource = SyncClient.signWriteRequest(this.privateKey.toBytes(), resourceId, valueBytes);
this.privateKey,
resourceId,
valueBytes,
);
return this.request('/data/write', { resources: [resource] }); return this.request('/data/write', { resources: [resource] });
} }
@@ -154,8 +153,11 @@ export class WsClient {
this.pendingRequests.set(id, { resolve, reject }); this.pendingRequests.set(id, { resolve, reject });
}); });
// Sign the request body
const headers = SyncClient.signRequest(this.privateKey.toBytes(), path, toExtendedJson(body));
try { try {
this.send({ id, path, body }); this.send({ id, path, body, headers });
} catch (error) { } catch (error) {
// Avoid leaving a promise in the map when the socket was not open. // Avoid leaving a promise in the map when the socket was not open.
this.pendingRequests.delete(id); this.pendingRequests.delete(id);
+1 -1
View File
@@ -18,7 +18,7 @@
// Stricter Typechecking Options // Stricter Typechecking Options
"noUncheckedIndexedAccess": true, "noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true, // "exactOptionalPropertyTypes": true,
// Recommended Options // Recommended Options
"strict": true, "strict": true,