Update Utils version. Add abstract Client class. Code Cleanup
This commit is contained in:
Generated
+120
-148
@@ -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": {
|
||||||
|
|||||||
@@ -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
@@ -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();
|
||||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user