From 8b9f6a13da61bd646dcd7ae65651837bfe501847 Mon Sep 17 00:00:00 2001 From: Rudy-Perrin Date: Thu, 2 May 2024 02:05:20 +0200 Subject: [PATCH 1/5] feat(typedoc): add docs script and devDependencies to package.json and setup typedoc --- .gitignore | 4 +- package-lock.json | 315 +++++++- package.json | 12 +- src/assets/docs/styles.css | 1470 ++++++++++++++++++++++++++++++++++++ tsconfig.json | 38 +- 5 files changed, 1834 insertions(+), 5 deletions(-) create mode 100644 src/assets/docs/styles.css diff --git a/.gitignore b/.gitignore index 76add87..43138d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules -dist \ No newline at end of file +dist +docs +!src/assets/docs \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d5b60a1..5f4aa7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,14 @@ "nock": "^13.3.0", "prettier": "3.1.0", "ts-jest": "^29.1.0", - "ts-jest-resolver": "^2.0.1" + "ts-jest-resolver": "^2.0.1", + "typedoc": "^0.25.13", + "typedoc-plugin-coverage": "^3.1.0", + "typedoc-plugin-extra-footer": "^1.0.1", + "typedoc-plugin-extras": "^3.0.0", + "typedoc-plugin-merge-modules": "^5.1.0", + "typedoc-plugin-missing-exports": "^2.2.0", + "typedoc-plugin-rename-defaults": "^0.7.0" } }, "node_modules/@absinthe/socket": { @@ -1634,6 +1641,12 @@ "node": ">=8" } }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3469,6 +3482,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -3526,6 +3545,12 @@ "yallist": "^3.0.2" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -3589,6 +3614,18 @@ "tmpl": "1.0.5" } }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4064,6 +4101,18 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -4380,6 +4429,126 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typedoc": { + "version": "0.25.13", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.13.tgz", + "integrity": "sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.7" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x" + } + }, + "node_modules/typedoc-plugin-coverage": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-coverage/-/typedoc-plugin-coverage-3.1.0.tgz", + "integrity": "sha512-fxeJRrxQR3yM/aFZU7mOuatgRCztiMCbeNiCRVZKY6VNgOcVMC1HS+ZfZnlbLLteUOdeWleSQ6yntuipz5zi6A==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typedoc": "0.25.x" + } + }, + "node_modules/typedoc-plugin-extra-footer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typedoc-plugin-extra-footer/-/typedoc-plugin-extra-footer-1.0.1.tgz", + "integrity": "sha512-24h93mKkOdiQivXRLtSKzCFPms2llGP0cqUT0xz4n/lntFl9pP8uPWtxUgX8/2jcfdEIKw3xZj+RRpJk3ZCYtQ==", + "dev": true, + "engines": { + "node": "^18 || ^20 || >=21" + }, + "peerDependencies": { + "typedoc": "^0.25.13" + } + }, + "node_modules/typedoc-plugin-extras": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-extras/-/typedoc-plugin-extras-3.0.0.tgz", + "integrity": "sha512-eiAe3qtm2WbV5owdncpt0zHZPqsNZH2mzNGILPd4zqrvEZie3Et9es4cpGZ+8lHO/SI0pVKwsAj7IuMxPNOdYg==", + "dev": true, + "peerDependencies": { + "typedoc": "0.25.x" + } + }, + "node_modules/typedoc-plugin-merge-modules": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-merge-modules/-/typedoc-plugin-merge-modules-5.1.0.tgz", + "integrity": "sha512-jXH27L/wlxFjErgBXleh3opVgjVTXFEuBo68Yfl18S9Oh/IqxK6NV94jlEJ9hl4TXc9Zm2l7Rfk41CEkcCyvFQ==", + "dev": true, + "peerDependencies": { + "typedoc": "0.24.x || 0.25.x" + } + }, + "node_modules/typedoc-plugin-missing-exports": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-2.2.0.tgz", + "integrity": "sha512-2+XR1IcyQ5UwXZVJe9NE6HrLmNufT9i5OwoIuuj79VxuA3eYq+Y6itS9rnNV1D7UeQnUSH8kISYD73gHE5zw+w==", + "dev": true, + "peerDependencies": { + "typedoc": "0.24.x || 0.25.x" + } + }, + "node_modules/typedoc-plugin-rename-defaults": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-rename-defaults/-/typedoc-plugin-rename-defaults-0.7.0.tgz", + "integrity": "sha512-NudSQ1o/XLHNF9c4y7LzIZxfE9ltz09yCDklBPJpP5VMRvuBpYGIbQ0ZgmPz+EIV8vPx9Z/OyKwzp4HT2vDtfg==", + "dev": true, + "dependencies": { + "camelcase": "^8.0.0" + }, + "peerDependencies": { + "typedoc": "0.22.x || 0.23.x || 0.24.x || 0.25.x" + } + }, + "node_modules/typedoc-plugin-rename-defaults/node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", @@ -4449,6 +4618,18 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -5767,6 +5948,12 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, + "ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -7139,6 +7326,12 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -7187,6 +7380,12 @@ "yallist": "^3.0.2" } }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, "make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -7237,6 +7436,12 @@ "tmpl": "1.0.5" } }, + "marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -7575,6 +7780,18 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", + "dev": true, + "requires": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -7797,6 +8014,90 @@ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, + "typedoc": { + "version": "0.25.13", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.13.tgz", + "integrity": "sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==", + "dev": true, + "requires": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.7" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "typedoc-plugin-coverage": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-coverage/-/typedoc-plugin-coverage-3.1.0.tgz", + "integrity": "sha512-fxeJRrxQR3yM/aFZU7mOuatgRCztiMCbeNiCRVZKY6VNgOcVMC1HS+ZfZnlbLLteUOdeWleSQ6yntuipz5zi6A==", + "dev": true, + "requires": {} + }, + "typedoc-plugin-extra-footer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typedoc-plugin-extra-footer/-/typedoc-plugin-extra-footer-1.0.1.tgz", + "integrity": "sha512-24h93mKkOdiQivXRLtSKzCFPms2llGP0cqUT0xz4n/lntFl9pP8uPWtxUgX8/2jcfdEIKw3xZj+RRpJk3ZCYtQ==", + "dev": true, + "requires": {} + }, + "typedoc-plugin-extras": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-extras/-/typedoc-plugin-extras-3.0.0.tgz", + "integrity": "sha512-eiAe3qtm2WbV5owdncpt0zHZPqsNZH2mzNGILPd4zqrvEZie3Et9es4cpGZ+8lHO/SI0pVKwsAj7IuMxPNOdYg==", + "dev": true, + "requires": {} + }, + "typedoc-plugin-merge-modules": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-merge-modules/-/typedoc-plugin-merge-modules-5.1.0.tgz", + "integrity": "sha512-jXH27L/wlxFjErgBXleh3opVgjVTXFEuBo68Yfl18S9Oh/IqxK6NV94jlEJ9hl4TXc9Zm2l7Rfk41CEkcCyvFQ==", + "dev": true, + "requires": {} + }, + "typedoc-plugin-missing-exports": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-2.2.0.tgz", + "integrity": "sha512-2+XR1IcyQ5UwXZVJe9NE6HrLmNufT9i5OwoIuuj79VxuA3eYq+Y6itS9rnNV1D7UeQnUSH8kISYD73gHE5zw+w==", + "dev": true, + "requires": {} + }, + "typedoc-plugin-rename-defaults": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-rename-defaults/-/typedoc-plugin-rename-defaults-0.7.0.tgz", + "integrity": "sha512-NudSQ1o/XLHNF9c4y7LzIZxfE9ltz09yCDklBPJpP5VMRvuBpYGIbQ0ZgmPz+EIV8vPx9Z/OyKwzp4HT2vDtfg==", + "dev": true, + "requires": { + "camelcase": "^8.0.0" + }, + "dependencies": { + "camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "dev": true + } + } + }, "typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", @@ -7838,6 +8139,18 @@ } } }, + "vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, "walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 6720978..d61c7cd 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "test": "jest", "build": "tsc && node build.js", "prepublishOnly": "npm run build", - "postpublish": "rm -r dist" + "postpublish": "rm -r dist", + "docs": "typedoc" }, "repository": { "type": "git", @@ -38,7 +39,14 @@ "nock": "^13.3.0", "prettier": "3.1.0", "ts-jest": "^29.1.0", - "ts-jest-resolver": "^2.0.1" + "ts-jest-resolver": "^2.0.1", + "typedoc": "^0.25.13", + "typedoc-plugin-coverage": "^3.1.0", + "typedoc-plugin-extra-footer": "^1.0.1", + "typedoc-plugin-extras": "^3.0.0", + "typedoc-plugin-merge-modules": "^5.1.0", + "typedoc-plugin-missing-exports": "^2.2.0", + "typedoc-plugin-rename-defaults": "^0.7.0" }, "dependencies": { "@absinthe/socket": "^0.2.1", diff --git a/src/assets/docs/styles.css b/src/assets/docs/styles.css new file mode 100644 index 0000000..e336db7 --- /dev/null +++ b/src/assets/docs/styles.css @@ -0,0 +1,1470 @@ +:root { + /* Archethic Neutral */ + --neutral-1000: #000000; + --neutral-900: #131313; + --neutral-800: #202020; + --neutral-700: #4f4f4f; + --neutral-600: #6f6f6f; + --neutral-500: #8f8f8f; + --neutral-400: #b1b1b1; + --neutral-300: #d7d7d7; + --neutral-200: #eaedf0; + --neutral-100: #f1f2f5; + --neutral-50: #f5f6f8; + --neutral-40: #f8f9fa; + --neutral-0: #ffffff; + + /* Archethic Brand */ + --purple800: #201844; + --purple500: #4027a2; + --purple300: #8c7dc7; + --purple200: #b3a9da; + --purple50: #ece9f6; + + --blue900: #140a2f; + --blue800: #39147f; + --blue500: #562fed; + --blue300: #896df2; + --blue200: #ccc1fa; + --blue50: #eeeafd; + + --plum800: #3d1d63; + --plum500: #7a3ac6; + --plum300: #9948f7; + --plum200: #cca4fb; + --plum50: #f5edfe; + + --raspberry800: #552566; + --raspberry500: #9816c5; + --raspberry300: #d55cff; + --raspberry200: #eaaeff; + --raspberry50: #fbefff; + + /* Archethic Gradients */ + --gadientpinkpurpule500: linear-gradient(180deg, #d55cff 100%, #562fed 100%); + + /* Archethic System */ + --systeminfo800: #2071bd; + --systeminfo500: #3693e9; + --systeminfo300: #c3dff9; + --systeminfo100: #deefff; + --systeminfo50: #ebf4fd; + + --systemdanger800: #ae2229; + --systemdanger500: #da2a33; + --systemdanger300: #fabcc2; + --systemdanger100: #ffdde0; + --systemdanger50: #fde9eb; + + --systemwarning800: #d4ad24; + --systemwarning500: #edc844; + --systemwarning300: #ffeaa1; + --systemwarning100: #fff2c6; + --systemwarning50: #fff9db; + + --systempositive800: #3c8550; + --systempositive500: #4eb46a; + --systempositive300: #cae8d2; + --systempositive100: #ddeee2; + --systempositive50: #edf7f0; + + /* Light */ + --light-color-background: var(--plum50); + --light-color-background-secondary: var(--blue300); + --light-color-background-gradient: radial-gradient(circle, var(--plum200) 0%, var(--blue300) 95%); + --light-color-warning-text: var(--neutral-800); + --light-color-background-warning: var(--systemwarning500); + --light-color-background-col-content: radial-gradient( + 24.45% 47.58% at 9.96% 24%, + var(--plum50) 0, + var(--plum200) 100% + ) + var(--purple200); + --light-color-icon-background: var(--light-color-background); + --light-color-accent: var(--neutral-700); + --light-color-active-menu-item: var(--blue50); + --light-color-text: var(--neutral-800); + --light-color-text-aside: var(--neutral-600); + --light-color-link: var(--systeminfo500); + + --light-color-ts-keyword: #056bd6; + --light-color-ts-project: #b111c9; + --light-color-ts-module: var(--light-color-ts-project); + --light-color-ts-namespace: var(--light-color-ts-project); + --light-color-ts-enum: #7e6f15; + --light-color-ts-enum-member: var(--light-color-ts-enum); + --light-color-ts-variable: #4760ec; + --light-color-ts-function: #572be7; + --light-color-ts-class: #1f70c2; + --light-color-ts-interface: #108024; + --light-color-ts-constructor: var(--light-color-ts-class); + --light-color-ts-property: var(--light-color-ts-variable); + --light-color-ts-method: var(--light-color-ts-function); + --light-color-ts-call-signature: var(--light-color-ts-method); + --light-color-ts-index-signature: var(--light-color-ts-property); + --light-color-ts-constructor-signature: var(--light-color-ts-constructor); + --light-color-ts-parameter: var(--light-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --light-color-ts-type-parameter: #a55c0e; + --light-color-ts-accessor: var(--light-color-ts-property); + --light-color-ts-get-signature: var(--light-color-ts-accessor); + --light-color-ts-set-signature: var(--light-color-ts-accessor); + --light-color-ts-type-alias: #d51270; + /* reference not included as links will be colored with the kind that it points to */ + + --light-external-icon: url("data:image/svg+xml;utf8,"); + --light-color-scheme: light; + + /* Dark */ + --dark-color-background: var(--neutral-900); + --dark-color-background-secondary: var(--blue800); + --dark-color-background-gradient: radial-gradient(circle, var(--plum500) 0%, var(--neutral-1000) 95%); + --dark-color-warning-text: var(--neutral-800); + --dark-color-background-warning: var(--systemwarning500); + --dark-color-background-col-content: radial-gradient( + 24.45% 47.58% at 9.96% 24%, + var(--plum500) 0, + var(--plum800) 100% + ) + var(--purple500); + --dark-color-icon-background: var(--dark-color-background-secondary); + --dark-color-accent: var(--neutral-500); + --dark-color-active-menu-item: var(--blue300); + --dark-color-text: var(--neutral-100); + --dark-color-text-aside: var(--neutral-50); + --dark-color-link: var(--systeminfo500); + + --dark-color-ts-keyword: #3399ff; + --dark-color-ts-project: #e358ff; + --dark-color-ts-module: var(--dark-color-ts-project); + --dark-color-ts-namespace: var(--dark-color-ts-project); + --dark-color-ts-enum: #f4d93e; + --dark-color-ts-enum-member: var(--dark-color-ts-enum); + --dark-color-ts-variable: #798dff; + --dark-color-ts-function: #a280ff; + --dark-color-ts-class: #8ac4ff; + --dark-color-ts-interface: #6cff87; + --dark-color-ts-constructor: var(--dark-color-ts-class); + --dark-color-ts-property: var(--dark-color-ts-variable); + --dark-color-ts-method: var(--dark-color-ts-function); + --dark-color-ts-call-signature: var(--dark-color-ts-method); + --dark-color-ts-index-signature: var(--dark-color-ts-property); + --dark-color-ts-constructor-signature: var(--dark-color-ts-constructor); + --dark-color-ts-parameter: var(--dark-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --dark-color-ts-type-parameter: #e07d13; + --dark-color-ts-accessor: var(--dark-color-ts-property); + --dark-color-ts-get-signature: var(--dark-color-ts-accessor); + --dark-color-ts-set-signature: var(--dark-color-ts-accessor); + --dark-color-ts-type-alias: #ff6492; + /* reference not included as links will be colored with the kind that it points to */ + + --dark-external-icon: url("data:image/svg+xml;utf8,"); + --dark-color-scheme: dark; +} + +html { + color-scheme: var(--color-scheme); +} + +:root[data-theme="light"] { + --color-background: var(--light-color-background); + --color-background-secondary: var(--light-color-background-secondary); + --color-background-gradient: var(--light-color-background-gradient); + --color-background-warning: var(--light-color-background-warning); + --color-background-col-content: var(--light-color-background-col-content); + --color-warning-text: var(--light-color-warning-text); + --color-icon-background: var(--light-color-icon-background); + --color-accent: var(--light-color-accent); + --color-active-menu-item: var(--light-color-active-menu-item); + --color-text: var(--light-color-text); + --color-text-aside: var(--light-color-text-aside); + --color-link: var(--light-color-link); + + --color-ts-keyword: var(--light-color-ts-keyword); + --color-ts-module: var(--light-color-ts-module); + --color-ts-namespace: var(--light-color-ts-namespace); + --color-ts-enum: var(--light-color-ts-enum); + --color-ts-enum-member: var(--light-color-ts-enum-member); + --color-ts-variable: var(--light-color-ts-variable); + --color-ts-function: var(--light-color-ts-function); + --color-ts-class: var(--light-color-ts-class); + --color-ts-interface: var(--light-color-ts-interface); + --color-ts-constructor: var(--light-color-ts-constructor); + --color-ts-property: var(--light-color-ts-property); + --color-ts-method: var(--light-color-ts-method); + --color-ts-call-signature: var(--light-color-ts-call-signature); + --color-ts-index-signature: var(--light-color-ts-index-signature); + --color-ts-constructor-signature: var(--light-color-ts-constructor-signature); + --color-ts-parameter: var(--light-color-ts-parameter); + --color-ts-type-parameter: var(--light-color-ts-type-parameter); + --color-ts-accessor: var(--light-color-ts-accessor); + --color-ts-get-signature: var(--light-color-ts-get-signature); + --color-ts-set-signature: var(--light-color-ts-set-signature); + --color-ts-type-alias: var(--light-color-ts-type-alias); + + --external-icon: var(--light-external-icon); + --color-scheme: var(--light-color-scheme); +} + +:root[data-theme="dark"] { + --color-background: var(--dark-color-background); + --color-background-secondary: var(--dark-color-background-secondary); + --color-background-gradient: var(--dark-color-background-gradient); + --color-background-warning: var(--dark-color-background-warning); + --color-background-col-content: var(--dark-color-background-col-content); + --color-warning-text: var(--dark-color-warning-text); + --color-icon-background: var(--dark-color-icon-background); + --color-accent: var(--dark-color-accent); + --color-active-menu-item: var(--dark-color-active-menu-item); + --color-text: var(--dark-color-text); + --color-text-aside: var(--dark-color-text-aside); + --color-link: var(--dark-color-link); + + --color-ts-keyword: var(--dark-color-ts-keyword); + --color-ts-module: var(--dark-color-ts-module); + --color-ts-namespace: var(--dark-color-ts-namespace); + --color-ts-enum: var(--dark-color-ts-enum); + --color-ts-enum-member: var(--dark-color-ts-enum-member); + --color-ts-variable: var(--dark-color-ts-variable); + --color-ts-function: var(--dark-color-ts-function); + --color-ts-class: var(--dark-color-ts-class); + --color-ts-interface: var(--dark-color-ts-interface); + --color-ts-constructor: var(--dark-color-ts-constructor); + --color-ts-property: var(--dark-color-ts-property); + --color-ts-method: var(--dark-color-ts-method); + --color-ts-call-signature: var(--dark-color-ts-call-signature); + --color-ts-index-signature: var(--dark-color-ts-index-signature); + --color-ts-constructor-signature: var(--dark-color-ts-constructor-signature); + --color-ts-parameter: var(--dark-color-ts-parameter); + --color-ts-type-parameter: var(--dark-color-ts-type-parameter); + --color-ts-accessor: var(--dark-color-ts-accessor); + --color-ts-get-signature: var(--dark-color-ts-get-signature); + --color-ts-set-signature: var(--dark-color-ts-set-signature); + --color-ts-type-alias: var(--dark-color-ts-type-alias); + + --external-icon: var(--dark-external-icon); + --color-scheme: var(--dark-color-scheme); +} + +.always-visible, +.always-visible .tsd-signatures { + display: inherit !important; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + line-height: 1.2; +} + +h1 > a:not(.link), +h2 > a:not(.link), +h3 > a:not(.link), +h4 > a:not(.link), +h5 > a:not(.link), +h6 > a:not(.link) { + text-decoration: none; + color: var(--color-text); +} + +h1 { + font-size: 1.875rem; + margin: 0.67rem 0; +} + +h2 { + font-size: 1.5rem; + margin: 0.83rem 0; +} + +h3 { + font-size: 1.25rem; + margin: 1rem 0; +} + +h4 { + font-size: 1.05rem; + margin: 1.33rem 0; +} + +h5 { + font-size: 1rem; + margin: 1.5rem 0; +} + +h6 { + font-size: 0.875rem; + margin: 2.33rem 0; +} + +.uppercase { + text-transform: uppercase; +} + +dl, +menu, +ol, +ul { + margin: 1em 0; +} + +dd { + margin: 0 0 0 40px; +} + +.container { + max-width: 1700px; + padding: 0 2rem; + color: var(--color-text); +} + +footer { + background: radial-gradient(circle, var(--color-background-secondary) 0%, var(--color-background) 95%); + height: 2.6rem; +} + +.tsd-generator { + margin: 0 1em; +} + +.container-main { + margin: 0 auto; + /* toolbar, footer, margin */ + min-height: calc(100vh - 41px - 56px - 3.1rem); +} + +.col-content { + background: var(--color-background-col-content); + border-radius: 24px; + box-shadow: 0px 5px 10px #131313; + padding: 1em; + margin-top: -20px; +} + +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +@keyframes fade-out { + from { + opacity: 1; + visibility: visible; + } + to { + opacity: 0; + } +} +@keyframes fade-in-delayed { + 0% { + opacity: 0; + } + 33% { + opacity: 0; + } + 100% { + opacity: 1; + } +} +@keyframes fade-out-delayed { + 0% { + opacity: 1; + visibility: visible; + } + 66% { + opacity: 0; + } + 100% { + opacity: 0; + } +} +@keyframes pop-in-from-right { + from { + transform: translate(100%, 0); + } + to { + transform: translate(0, 0); + } +} +@keyframes pop-out-to-right { + from { + transform: translate(0, 0); + visibility: visible; + } + to { + transform: translate(100%, 0); + } +} + +body { + margin: 0; + overflow-x: hidden; + font-size: 0.9rem; + color: var(--neutral-0); + min-height: 100vh; + background: var(--color-background); + background: var(--color-background-gradient); + font-family: + Telegraf, + telegraf-fallback, + sans-serif, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + "Noto Sans", + Helvetica, + Arial, + "Apple Color Emoji", + "Segoe UI Emoji"; +} + +a { + color: var(--color-link); + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +a.external[target="_blank"] { + background-image: var(--external-icon); + background-position: top 3px right; + background-repeat: no-repeat; + padding-right: 13px; +} + +code, +pre { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + padding: 0.2em; + margin: 0; + font-size: 0.875rem; + border-radius: 0.8em; +} + +pre { + position: relative; + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; + padding: 10px; + border: 1px solid var(--color-accent); +} +pre code { + padding: 0; + font-size: 100%; +} +pre > button { + position: absolute; + top: 10px; + right: 10px; + opacity: 0; + transition: opacity 0.1s; + box-sizing: border-box; +} +pre:hover > button, +pre > button.visible { + opacity: 1; +} + +blockquote { + margin: 1em 0; + padding-left: 1em; + border-left: 4px solid gray; +} + +.tsd-typography { + line-height: 1.333em; +} +.tsd-typography ul { + list-style: square; + padding: 0 0 0 20px; + margin: 0; +} +.tsd-typography .tsd-index-panel h3, +.tsd-index-panel .tsd-typography h3, +.tsd-typography h4, +.tsd-typography h5, +.tsd-typography h6 { + font-size: 1em; +} +.tsd-typography h5, +.tsd-typography h6 { + font-weight: normal; +} +.tsd-typography p, +.tsd-typography ul, +.tsd-typography ol { + margin: 1em 0; +} +.tsd-typography table { + border-collapse: collapse; + border: none; +} +.tsd-typography td, +.tsd-typography th { + padding: 6px 13px; + border: 1px solid var(--color-accent); +} +.tsd-typography thead, +.tsd-typography tr:nth-child(even) { + background-color: var(--color-background-secondary); +} + +.tsd-breadcrumb { + margin: 0; + padding: 0; + color: var(--color-text-aside); +} +.tsd-breadcrumb a { + color: var(--color-text-aside); + text-decoration: none; +} +.tsd-breadcrumb a:hover { + text-decoration: underline; +} +.tsd-breadcrumb li { + display: inline; +} +.tsd-breadcrumb li:after { + content: " / "; +} + +.tsd-comment-tags { + display: flex; + flex-direction: column; +} +dl.tsd-comment-tag-group { + display: flex; + align-items: center; + overflow: hidden; + margin: 0.5em 0; +} +dl.tsd-comment-tag-group dt { + display: flex; + margin-right: 0.5em; + font-size: 0.875em; + font-weight: normal; +} +dl.tsd-comment-tag-group dd { + margin: 0; +} +code.tsd-tag { + padding: 0.25em 0.4em; + border: 0.1em solid var(--color-accent); + margin-right: 0.25em; + font-size: 70%; +} +h1 code.tsd-tag:first-of-type { + margin-left: 0.25em; +} + +dl.tsd-comment-tag-group dd:before, +dl.tsd-comment-tag-group dd:after { + content: " "; +} +dl.tsd-comment-tag-group dd pre, +dl.tsd-comment-tag-group dd:after { + clear: both; +} +dl.tsd-comment-tag-group p { + margin: 0; +} + +.tsd-panel.tsd-comment .lead { + font-size: 1.1em; + line-height: 1.333em; + margin-bottom: 2em; +} +.tsd-panel.tsd-comment .lead:last-child { + margin-bottom: 0; +} + +.tsd-filter-visibility h4 { + font-size: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.5rem; + margin: 0; +} +.tsd-filter-item:not(:last-child) { + margin-bottom: 0.5rem; +} +.tsd-filter-input { + display: flex; + width: fit-content; + width: -moz-fit-content; + align-items: center; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + cursor: pointer; +} +.tsd-filter-input input[type="checkbox"] { + cursor: pointer; + position: absolute; + width: 1.5em; + height: 1.5em; + opacity: 0; +} +.tsd-filter-input input[type="checkbox"]:disabled { + pointer-events: none; +} +.tsd-filter-input svg { + cursor: pointer; + width: 1.5em; + height: 1.5em; + margin-right: 0.5em; + border-radius: 0.33em; + /* Leaving this at full opacity breaks event listeners on Firefox. + Don't remove unless you know what you're doing. */ + opacity: 0.99; +} +.tsd-filter-input input[type="checkbox"]:focus + svg { + transform: scale(0.95); +} +.tsd-filter-input input[type="checkbox"]:focus:not(:focus-visible) + svg { + transform: scale(1); +} +.tsd-checkbox-background { + fill: var(--color-background-secondary); +} +input[type="checkbox"]:checked ~ svg .tsd-checkbox-checkmark { + stroke: var(--color-text); +} +.tsd-filter-input input:disabled ~ svg > .tsd-checkbox-background { + fill: var(--color-background-secondary); + stroke: var(--color-accent); + stroke-width: 0.25rem; +} +.tsd-filter-input input:disabled ~ svg > .tsd-checkbox-checkmark { + stroke: var(--color-accent); +} + +.tsd-theme-toggle { + padding-top: 0.75rem; +} +.tsd-theme-toggle > h4 { + display: inline; + vertical-align: middle; + margin-right: 0.75rem; +} +#tsd-theme { + padding: 0.5rem 0.5rem; + border-radius: 14px; + border: 1px solid var(--color-accent); + background: var(--color-background-secondary); + color: var(--color-text); +} +#tsd-theme option { + padding: 0.5rem 0.5rem; + background: var(--color-background-secondary); + color: var(--color-text); + border-radius: 4px; +} + +option:hover { + background: var(--color-background-warning); +} + +.tsd-hierarchy { + list-style: square; + margin: 0; +} +.tsd-hierarchy .target { + font-weight: bold; +} + +.tsd-full-hierarchy:not(:last-child) { + margin-bottom: 1em; + padding-bottom: 1em; + border-bottom: 1px solid var(--color-accent); +} +.tsd-full-hierarchy, +.tsd-full-hierarchy ul { + list-style: none; + margin: 0; + padding: 0; +} +.tsd-full-hierarchy ul { + padding-left: 1.5rem; +} +.tsd-full-hierarchy a { + padding: 0.25rem 0 !important; + font-size: 1rem; + display: inline-flex; + align-items: center; + color: var(--color-text); +} + +.tsd-panel-group.tsd-index-group { + margin: 2rem 0; +} + +.tsd-index-panel .tsd-index-list { + list-style: none; + line-height: 1.333em; + margin: 0; + padding: 0.25rem 0 0 0; + overflow: hidden; + display: grid; + grid-template-columns: repeat(3, 1fr); + column-gap: 1rem; + grid-template-rows: auto; +} +@media (max-width: 1024px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(2, 1fr); + } +} +@media (max-width: 768px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(1, 1fr); + } +} +.tsd-index-panel .tsd-index-list li { + -webkit-page-break-inside: avoid; + -moz-page-break-inside: avoid; + -ms-page-break-inside: avoid; + -o-page-break-inside: avoid; + page-break-inside: avoid; +} + +.tsd-flag { + display: inline-block; + padding: 0.25em 0.4em; + border-radius: 4px; + color: var(--color-comment-tag-text); + background-color: var(--color-comment-tag); + text-indent: 0; + font-size: 75%; + line-height: 1; + font-weight: normal; +} + +.tsd-anchor { + position: relative; + top: -100px; +} + +.tsd-member { + position: relative; +} +.tsd-member .tsd-anchor + h3 { + display: flex; + align-items: center; + margin-top: 0; + margin-bottom: 0; + border-bottom: none; +} + +.tsd-navigation.settings { + margin: 1rem 0; +} +.tsd-navigation > a, +.tsd-navigation .tsd-accordion-summary { + width: calc(100% - 0.25rem); + display: flex; + align-items: center; +} +.tsd-navigation a, +.tsd-navigation summary > span, +.tsd-page-navigation a { + display: flex; + width: calc(100% - 0.25rem); + align-items: center; + padding: 0.25rem; + color: var(--color-text); + text-decoration: none; + box-sizing: border-box; +} +.tsd-navigation a.current, +.tsd-page-navigation a.current { + background: var(--color-active-menu-item); + border-radius: 4px; + opacity: 0.7; + backdrop-filter: blur(40px); + max-width: 90%; +} +.tsd-navigation a:hover, +.tsd-page-navigation a:hover { + text-decoration: underline; +} +.tsd-navigation ul, +.tsd-page-navigation ul { + margin-top: 0; + margin-bottom: 0; + padding: 0; + list-style: none; +} +.tsd-navigation li, +.tsd-page-navigation li { + padding: 0; + max-width: 100%; +} +.tsd-nested-navigation { + margin-left: 3rem; +} +.tsd-nested-navigation > li > details { + margin-left: -1.5rem; +} +.tsd-small-nested-navigation { + margin-left: 1.5rem; +} +.tsd-small-nested-navigation > li > details { + margin-left: -1.5rem; +} + +.tsd-page-navigation ul { + padding-left: 1.75rem; +} + +#tsd-sidebar-links a { + margin-top: 0; + margin-bottom: 0.5rem; + line-height: 1.25rem; +} +#tsd-sidebar-links a:last-of-type { + margin-bottom: 0; +} + +a.tsd-index-link { + padding: 0.25rem 0 !important; + font-size: 1rem; + line-height: 1.25rem; + display: inline-flex; + align-items: center; + color: var(--color-text); +} +.tsd-accordion-summary { + list-style-type: none; /* hide marker on non-safari */ + outline: none; /* broken on safari, so just hide it */ +} +.tsd-accordion-summary::-webkit-details-marker { + display: none; /* hide marker on safari */ +} +.tsd-accordion-summary, +.tsd-accordion-summary a { + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + + cursor: pointer; +} +.tsd-accordion-summary a { + width: calc(100% - 1.5rem); +} +.tsd-accordion-summary > * { + margin-top: 0; + margin-bottom: 0; + padding-top: 0; + padding-bottom: 0; +} +.tsd-index-accordion .tsd-accordion-summary > svg { + margin-left: 0.25rem; +} +.tsd-index-content > :not(:first-child) { + margin-top: 0.75rem; +} +.tsd-index-heading { + margin-top: 1.5rem; + margin-bottom: 0.75rem; +} + +.tsd-kind-icon { + margin-right: 0.5rem; + width: 1.25rem; + height: 1.25rem; + min-width: 1.25rem; + min-height: 1.25rem; +} +.tsd-kind-icon path { + transform-origin: center; + transform: scale(1.1); +} +.tsd-signature > .tsd-kind-icon { + margin-right: 0.8rem; +} + +.tsd-panel { + margin-bottom: 2.5rem; +} +.tsd-panel.tsd-member { + margin-bottom: 4rem; +} +.tsd-panel:empty { + display: none; +} +.tsd-panel > h1, +.tsd-panel > h2, +.tsd-panel > h3 { + margin: 1.5rem -1.5rem 0.75rem -1.5rem; + padding: 0 1.5rem 0.75rem 1.5rem; +} +.tsd-panel > h1.tsd-before-signature, +.tsd-panel > h2.tsd-before-signature, +.tsd-panel > h3.tsd-before-signature { + margin-bottom: 0; + border-bottom: none; +} + +.tsd-panel-group { + margin: 4rem 0; +} +.tsd-panel-group.tsd-index-group details { + margin: 2rem 0; +} + +#tsd-search { + transition: background-color 0.2s; + height: 36px; + margin-top: 2px; +} +#tsd-search .title { + position: relative; + z-index: 2; +} +#tsd-search .field { + position: absolute; + left: 0; + top: 0; + right: 2.5rem; + height: 100%; +} +#tsd-search .field input { + box-sizing: border-box; + position: relative; + top: -50px; + z-index: 1; + width: 100%; + padding: 0 10px; + opacity: 0; + outline: 0; + border: 0; + color: var(--color-text); +} +#tsd-search .field label { + position: absolute; + overflow: hidden; + right: -40px; +} +#tsd-search .field input, +#tsd-search .title, +#tsd-toolbar-links a { + transition: opacity 0.2s; +} +#tsd-search .results { + position: absolute; + visibility: hidden; + top: 40px; + width: 100%; + margin: 0; + padding: 0; + list-style: none; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); + color: var(--color-text); +} +#tsd-search .no-results span { + color: var(--systemwarning800); + display: flex; + padding: 0.6rem; + box-sizing: border-box; + background: var(--color-background-secondary); + border-radius: 4px; +} +#tsd-search .results li { + background: var(--color-background); + line-height: initial; + padding: 5px; + border-radius: 4px; +} +#tsd-search .results li:nth-child(even) { + background: var(--color-background-secondary); +} +#tsd-search .results li.state { + display: none; +} +#tsd-search .results li.current:not(.no-results), +#tsd-search .results li:hover:not(.no-results) { + background: var(--color-active-menu-item); +} +#tsd-search .results a { + display: flex; + align-items: center; + padding: 0.25rem; + box-sizing: border-box; +} +#tsd-search .results a:before { + top: 10px; +} +#tsd-search .results span.parent { + color: var(--color-text); + font-weight: normal; +} +#tsd-search.has-focus { + background: var(--color-active-menu-item); + border-radius: 4px; + backdrop-filter: blur(40px); +} +#tsd-search.has-focus .field input { + top: 0; + opacity: 1; +} +#tsd-search.has-focus .title, +#tsd-search.has-focus #tsd-toolbar-links a { + z-index: 0; + opacity: 0; +} +#tsd-search.has-focus .results { + visibility: visible; +} +#tsd-search.loading .results li.state.loading { + display: block; +} +#tsd-search.failure .results li.state.failure { + display: block; +} + +#tsd-toolbar-links { + position: absolute; + top: 0; + right: 2rem; + height: 100%; + display: flex; + align-items: center; + justify-content: flex-end; +} +#tsd-toolbar-links a { + margin-left: 1.5rem; +} +#tsd-toolbar-links a:hover { + text-decoration: underline; +} + +.tsd-signature { + margin: 0 0 1rem 0; + padding: 1rem 0.5rem; + border: 1px solid var(--color-accent); + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + font-size: 14px; + overflow-x: auto; +} + +.tsd-signature-keyword { + color: var(--color-ts-keyword); + font-weight: normal; +} + +.tsd-signature-symbol { + color: var(--color-text-aside); + font-weight: normal; +} + +.tsd-signature-type { + font-style: italic; + font-weight: normal; +} + +.tsd-signatures { + padding: 0; + margin: 0 0 1em 0; + list-style-type: none; +} +.tsd-signatures .tsd-signature { + margin: 0; + border-color: var(--color-accent); + border-width: 1px 0; + transition: background-color 0.1s; +} +.tsd-description .tsd-signatures .tsd-signature { + border-width: 1px; +} + +ul.tsd-parameter-list, +ul.tsd-type-parameter-list { + list-style: square; + margin: 0; + padding-left: 20px; +} +ul.tsd-parameter-list > li.tsd-parameter-signature, +ul.tsd-type-parameter-list > li.tsd-parameter-signature { + list-style: none; + margin-left: -20px; +} +ul.tsd-parameter-list h5, +ul.tsd-type-parameter-list h5 { + font-size: 16px; + margin: 1em 0 0.5em 0; +} +.tsd-sources { + margin-top: 1rem; + font-size: 0.875em; +} +.tsd-sources a { + color: var(--color-text-aside); + text-decoration: underline; +} +.tsd-sources ul { + list-style: none; + padding: 0; +} + +.tsd-page-title { + padding-top: 1em; +} + +.tsd-page-toolbar { + position: sticky; + z-index: 1; + top: 0; + left: 0; + width: 100%; + border-bottom: 1px var(--color-accent) solid; + background: radial-gradient(circle, var(--color-background-secondary) 0%, var(--color-background) 95%); + transition: transform 0.3s ease-in-out; + backdrop-filter: blur(40px); + opacity: 0.97; +} +.tsd-page-toolbar a { + color: var(--color-text); + text-decoration: none; +} +.tsd-page-toolbar a.title { + font-weight: bold; +} +.tsd-page-toolbar a.title:hover { + text-decoration: underline; +} +.tsd-page-toolbar .tsd-toolbar-contents { + display: flex; + justify-content: space-between; + height: 2.5rem; + margin: 0 auto; + opacity: 1; +} +.tsd-page-toolbar .table-cell { + position: relative; + white-space: nowrap; + line-height: 40px; +} +.tsd-page-toolbar .table-cell:first-child { + width: 100%; +} +.tsd-page-toolbar .tsd-toolbar-icon { + box-sizing: border-box; + line-height: 0; + padding: 12px 0; +} + +.tsd-widget { + display: inline-block; + overflow: hidden; + opacity: 0.8; + height: 40px; + transition: + opacity 0.1s, + background-color 0.2s; + vertical-align: bottom; + cursor: pointer; +} +.tsd-widget:hover { + opacity: 0.9; +} +.tsd-widget.active { + opacity: 1; + background-color: var(--color-accent); +} +.tsd-widget.no-caption { + width: 40px; +} +.tsd-widget.no-caption:before { + margin: 0; +} + +.tsd-widget.options, +.tsd-widget.menu { + display: none; +} +input[type="checkbox"] + .tsd-widget:before { + background-position: -120px 0; +} +input[type="checkbox"]:checked + .tsd-widget:before { + background-position: -160px 0; +} + +img { + max-width: 100%; +} + +.tsd-anchor-icon { + display: inline-flex; + align-items: center; + margin-left: 0.5rem; + vertical-align: middle; + color: var(--color-text); +} + +.tsd-anchor-icon svg { + width: 1em; + height: 1em; + visibility: hidden; +} + +.tsd-anchor-link:hover > .tsd-anchor-icon svg { + visibility: visible; +} + +.deprecated { + text-decoration: line-through !important; +} + +.warning { + padding: 1rem; + color: var(--color-warning-text); + background: var(--color-background-warning); +} + +.tsd-kind-project { + color: var(--color-ts-project); +} +.tsd-kind-module { + color: var(--color-ts-module); +} +.tsd-kind-namespace { + color: var(--color-ts-namespace); +} +.tsd-kind-enum { + color: var(--color-ts-enum); +} +.tsd-kind-enum-member { + color: var(--color-ts-enum-member); +} +.tsd-kind-variable { + color: var(--color-ts-variable); +} +.tsd-kind-function { + color: var(--color-ts-function); +} +.tsd-kind-class { + color: var(--color-ts-class); +} +.tsd-kind-interface { + color: var(--color-ts-interface); +} +.tsd-kind-constructor { + color: var(--color-ts-constructor); +} +.tsd-kind-property { + color: var(--color-ts-property); +} +.tsd-kind-method { + color: var(--color-ts-method); +} +.tsd-kind-call-signature { + color: var(--color-ts-call-signature); +} +.tsd-kind-index-signature { + color: var(--color-ts-index-signature); +} +.tsd-kind-constructor-signature { + color: var(--color-ts-constructor-signature); +} +.tsd-kind-parameter { + color: var(--color-ts-parameter); +} +.tsd-kind-type-literal { + color: var(--color-ts-type-literal); +} +.tsd-kind-type-parameter { + color: var(--color-ts-type-parameter); +} +.tsd-kind-accessor { + color: var(--color-ts-accessor); +} +.tsd-kind-get-signature { + color: var(--color-ts-get-signature); +} +.tsd-kind-set-signature { + color: var(--color-ts-set-signature); +} +.tsd-kind-type-alias { + color: var(--color-ts-type-alias); +} + +/* if we have a kind icon, don't color the text by kind */ +.tsd-kind-icon ~ span { + color: var(--color-text); +} + +* { + box-sizing: border-box; + scrollbar-width: thin; + scrollbar-color: var(--color-background-secondary) var(--color-background); + padding: 0; + margin: 0; +} + +*::-webkit-scrollbar { + width: 0.75rem; +} + +*::-webkit-scrollbar-track { + background: var(--color-background); +} + +*::-webkit-scrollbar-thumb { + background-color: var(--color-background); + border-radius: 999rem; + border: 0.25rem solid var(--color-background); +} + +/* mobile */ +@media (max-width: 769px) { + .tsd-widget.options, + .tsd-widget.menu { + display: inline-block; + } + + .container-main { + display: flex; + } + html .col-content { + float: none; + max-width: 100%; + width: 100%; + } + html .col-sidebar { + position: fixed !important; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + z-index: 1024; + top: 0 !important; + bottom: 0 !important; + left: auto !important; + right: 0 !important; + padding: 1.5rem 1.5rem 0 0; + width: 75vw; + visibility: hidden; + background-color: var(--color-background); + transform: translate(100%, 0); + } + html .col-sidebar > *:last-child { + padding-bottom: 20px; + } + html .overlay { + content: ""; + display: block; + position: fixed; + z-index: 1023; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.75); + visibility: hidden; + } + + .to-has-menu .overlay { + animation: fade-in 0.4s; + } + + .to-has-menu .col-sidebar { + animation: pop-in-from-right 0.4s; + } + + .from-has-menu .overlay { + animation: fade-out 0.4s; + } + + .from-has-menu .col-sidebar { + animation: pop-out-to-right 0.4s; + } + + .has-menu body { + overflow: hidden; + } + .has-menu .overlay { + visibility: visible; + } + .has-menu .col-sidebar { + visibility: visible; + transform: translate(0, 0); + display: flex; + flex-direction: column; + gap: 1.5rem; + max-height: 100vh; + padding: 1rem 2rem; + } + .has-menu .tsd-navigation { + max-height: 100%; + } +} + +/* one sidebar */ +@media (min-width: 770px) { + .container-main { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); + grid-template-areas: "sidebar content"; + margin: 2rem auto; + } + + .col-sidebar { + grid-area: sidebar; + } + .col-content { + grid-area: content; + padding: 0 1rem; + } +} +@media (min-width: 770px) and (max-width: 1399px) { + .col-sidebar { + max-height: calc(100vh - 2rem - 42px); + overflow: auto; + position: sticky; + top: 42px; + padding-top: 1rem; + } + .site-menu { + margin-top: 1rem; + } +} + +/* two sidebars */ +@media (min-width: 1200px) { + .container-main { + grid-template-columns: minmax(0, 1fr) minmax(0, 2.5fr) minmax(0, 20rem); + grid-template-areas: "sidebar content toc"; + } + + .col-sidebar { + display: contents; + } + + .page-menu { + grid-area: toc; + padding-left: 1rem; + } + .site-menu { + grid-area: sidebar; + } + + .site-menu { + margin-top: 1rem 0; + } + + .page-menu, + .site-menu { + max-height: calc(100vh - 2rem - 42px); + overflow: auto; + position: sticky; + top: 42px; + } +} diff --git a/tsconfig.json b/tsconfig.json index dda9c61..d03ea58 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,5 +14,41 @@ "forceConsistentCasingInFileNames": true, "allowSyntheticDefaultImports": true }, - "exclude": ["node_modules", "dist", "tests"] + "exclude": ["node_modules", "dist", "tests"], + "typedocOptions": { + "entryPoints": ["src/index.ts"], + "entryPointStrategy": "expand", + "categorizeByGroup": true, + "plugin": [ + "typedoc-plugin-coverage", + "typedoc-plugin-missing-exports", + "typedoc-plugin-merge-modules", + "typedoc-plugin-extra-footer", + "typedoc-plugin-extras" + ], + "out": "docs", + "exclude": ["src/api/absinthe.ts", "src/api.ts", "src/typed_encoding.ts", "src/varint.ts"], + "includeVersion": true, + "excludeProtected": true, + "excludePrivate": true, + "excludeExternals": true, + "excludeInternal": true, + "hideGenerator": true, + "placeInternalsInOwningModule": false, + "internalModule": "internal", + "mergeModulesRenameDefaults": true, + "mergeModulesMergeMode": "module", + "navigation": { + "includeCategories": false, + "includeGroups": false, + "includeFolders": false + }, + "coverageLabel": "document", + "customCss": "src/assets/docs/styles.css", + "extraFooter": "
Coverage
", + "extraFooterNoDefaultWrapper": true, + "favicon": "https://github.com/archethic-foundation/archethic-assets/blob/main/assets/logos/archethic/Archethic%20-%20Logo%20crystal.png?raw=true", + "customTitle": "Archethic SDK", + "footerLastModified": true + } } From 4a2aba2266c17927ecd8a472bb187f773482d31b Mon Sep 17 00:00:00 2001 From: Rudy-Perrin Date: Wed, 15 May 2024 22:29:43 +0200 Subject: [PATCH 2/5] docs: add some JSDoc and missing types --- src/account.ts | 81 ++++++++++- src/api.ts | 10 +- src/api/node_rpc.ts | 6 +- src/api/wallet_rpc.ts | 23 +-- src/endpoint.ts | 9 +- src/index.ts | 51 ++++++- src/keychain.ts | 277 +++++++++++++++++++++++++++++------ src/network.ts | 125 +++++++++++++++- src/transaction.ts | 121 +++++++++++++++- src/transaction_builder.ts | 290 +++++++++++++++++++++++++++++++++---- src/transaction_sender.ts | 36 +++-- src/types.ts | 47 +++++- src/utils.ts | 49 ++++--- 13 files changed, 986 insertions(+), 139 deletions(-) diff --git a/src/account.ts b/src/account.ts index 6a13331..a48adfe 100644 --- a/src/account.ts +++ b/src/account.ts @@ -12,12 +12,56 @@ import { maybeUint8ArrayToHex, uint8ArrayToHex } from "./utils.js"; import Archethic from "./index.js"; import { ExtendedTransactionBuilder } from "./transaction.js"; +/** + * Account class to manage {@link Keychain | keychains} + */ export default class Account { + /** @private */ core: Archethic; + /** @hidden */ constructor(core: Archethic) { this.core = core; } + /** + * Creates a new transaction to build (or update) a keychain by embedding the on-chain encrypted wallet + * @param keychain - The keychain to create + * @param transactionChainIndex - The index of the transaction created (0 for new keychain) + * @returns An instance of the ExtendedTransactionBuilder + * @example Keychain creation + * ```ts + * import Archethic, { Crypto, Keychain } from "@archethicjs/sdk"; + * + * const accessSeed = "myseed"; + * const { publicKey } = Crypto.deriveKeyPair(accessSeed, 0); + * const keychain = new Keychain(Crypto.randomSecretKey()) + * .addService("uco", "m/650'/0/0") + * .addAuthorizedPublicKey(publicKey); + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const tx = archethic.account.newKeychainTransaction(keychain, 0); + * // The transaction can then be signed with origin private key + * ``` + * + * @example Keychain update + * ```ts + * import Archethic, { Crypto } from "@archethicjs/sdk"; + * + * const accessSeed = "myseed"; + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * let keychain = await archethic.account.getKeychain(accessSeed); + * keychain.addService("mywallet", "m/650'/1/0"); + * + * // determine the new transaction index + * const keychainGenesisAddress = Crypto.deriveAddress(keychain.seed, 0); + * const transactionChainIndex = await archethic.transaction.getTransactionIndex(keychainGenesisAddress); + * + * const tx = archethic.account.newKeychainTransaction(keychain, transactionChainIndex); + * // The transaction can then be signed with origin private key + * ``` + */ newKeychainTransaction(keychain: Keychain, transactionChainIndex: number): ExtendedTransactionBuilder { const aesKey = randomSecretKey(); @@ -28,13 +72,27 @@ export default class Account { }; }); - return new this.core.transaction.builder(this.core) + return new ExtendedTransactionBuilder(this.core) .setType("keychain") .setContent(JSON.stringify(keychain.toDID())) .addOwnership(aesEncrypt(keychain.encode(), aesKey), authorizedKeys) .build(keychain.seed, transactionChainIndex); } + /** + * Creates a new keychain access transaction to allow a seed and its key to access a keychain + * @param seed - Keychain access's seed + * @param keychainAddress - Keychain's tx address + * @returns An instance of the ExtendedTransactionBuilder + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const tx = archethic.account.newAccessTransaction("myseed", "0000AB...CD"); + * + */ newAccessTransaction(seed: string | Uint8Array, keychainAddress: string | Uint8Array): ExtendedTransactionBuilder { const aesKey = randomSecretKey(); @@ -49,12 +107,25 @@ export default class Account { } ]; - return new this.core.transaction.builder(this.core) + return new ExtendedTransactionBuilder(this.core) .setType("keychain_access") .addOwnership(aesEncrypt(keychainAddress, aesKey), authorizedKeys) .build(seed, 0); } + /** + * Retrieve a keychain from the keychain access transaction and decrypt the wallet to retrieve the services associated + * @param seed - Keychain access's seed + * @returns The keychain object + * @example + * ```ts + * import Archethic from "@archethicjs/sdk" + * + * const archethic = new Archethic("https://testnet.archethic.net") + * await archethic.connect() + * const keychain = await archethic.account.getKeychain("myseed") + * ``` + */ async getKeychain(seed: string | Uint8Array) { const { publicKey: accessPublicKey, privateKey: accessPrivateKey } = deriveKeyPair(seed, 0); const accessKeychainAddress = deriveAddress(seed, 1); @@ -64,7 +135,7 @@ export default class Account { uint8ArrayToHex(accessKeychainAddress) ); - if (accessOwnerships.length == 0) { + if (accessOwnerships.length === 0) { throw new Error("Keychain doesn't exist"); } @@ -99,7 +170,7 @@ export default class Account { const keychainAESKey = ecDecrypt(keychainSecretKey, accessPrivateKey); const encodedKeychain = aesDecrypt(keychainSecret, keychainAESKey); - let keychain = Keychain.decode(encodedKeychain); + const keychain = Keychain.decode(encodedKeychain); keychainAuthorizedKeys.forEach(({ publicKey }) => { keychain.addAuthorizedPublicKey(publicKey); @@ -108,5 +179,3 @@ export default class Account { return keychain; } } - -export { Keychain }; diff --git a/src/api.ts b/src/api.ts index ad1cf0f..ee37e42 100644 --- a/src/api.ts +++ b/src/api.ts @@ -7,6 +7,7 @@ import { Balance, NearestEndpoint, OracleData, Ownership, Token } from "./types. * Send a custom query to the Archethic API * @param query * @param endpoint + * @returns {Promise} The response of the query */ export async function rawGraphQLQuery(query: string, endpoint: string): Promise { const url = new URL("/api", endpoint); @@ -66,6 +67,7 @@ export async function getNearestEndpoints(endpoint: string): Promise} The transaction index */ export async function getTransactionIndex(address: string | Uint8Array, endpoint: string): Promise { address = maybeUint8ArrayToHex(address); @@ -132,6 +134,7 @@ export async function getStorageNoncePublicKey(endpoint: string): Promise} The ownerships of the transaction */ export async function getTransactionOwnerships( address: string | Uint8Array, @@ -178,6 +181,7 @@ export async function getTransactionOwnerships( * Get token information * @param tokenAddress address of the token * @param endpoint The Archethic API endpoint + * @returns {Promise} The token information */ export async function getToken(tokenAddress: string | Uint8Array, endpoint: string): Promise<{} | Token> { tokenAddress = maybeUint8ArrayToHex(tokenAddress); @@ -212,6 +216,7 @@ export async function getToken(tokenAddress: string | Uint8Array, endpoint: stri * Get Oracle data * @param endpoint The Archethic API endpoint * @param timestamp The timestamp of the data to get + * @returns {Promise} The Oracle data */ export async function getOracleData(endpoint: string, timestamp: undefined | number = undefined): Promise { let query; @@ -266,10 +271,11 @@ export async function getOracleData(endpoint: string, timestamp: undefined | num * Subscribe to oracle updates * @param endpoint The Archethic API endpoint * @param handler The handler to call when a new update is received + * @returns {Promise} The subscription */ export async function subscribeToOracleUpdates(endpoint: string, handler: Function): Promise { const { host, protocol } = new URL(endpoint); - const ws_protocol = protocol == "https:" ? "wss" : "ws"; + const ws_protocol = protocol === "https:" ? "wss" : "ws"; const absintheSocket = absinthe.create(`${ws_protocol}://${host}/socket`); @@ -297,6 +303,7 @@ export async function subscribeToOracleUpdates(endpoint: string, handler: Functi * Get the balance of an address * @param endpoint The Archethic API endpoint * @param address The address to get the balance of + * @returns {Promise} The balance of the address */ export async function getBalance(address: string | Uint8Array, endpoint: string): Promise { address = maybeUint8ArrayToHex(address); @@ -334,6 +341,7 @@ export async function getBalance(address: string | Uint8Array, endpoint: string) /** * handle an api response * @param response + * @returns {Promise} The response */ async function handleResponse(response: Response): Promise { return new Promise(function (resolve, reject) { diff --git a/src/api/node_rpc.ts b/src/api/node_rpc.ts index 450d83f..576a46e 100644 --- a/src/api/node_rpc.ts +++ b/src/api/node_rpc.ts @@ -12,8 +12,8 @@ import TransactionBuilder from "../transaction_builder"; import { TransactionFee } from "../types"; export class NodeRPCClient { - private client: TypedJSONRPCClient; - private core: Archethic; + private readonly client: TypedJSONRPCClient; + private readonly core: Archethic; constructor(archethic: Archethic) { this.core = archethic; this.client = new JSONRPCClient((request) => this.handleRequest(request)); @@ -26,7 +26,7 @@ export class NodeRPCClient { * @param {any[]} args * @returns {Promise} */ - async callFunction(contractAddress: string, functionName: string, args: any[]) { + async callFunction(contractAddress: string, functionName: string, args: any[]): Promise { return this.client.request("contract_fun", { contract: contractAddress, function: functionName, diff --git a/src/api/wallet_rpc.ts b/src/api/wallet_rpc.ts index 2d03658..8065bd7 100644 --- a/src/api/wallet_rpc.ts +++ b/src/api/wallet_rpc.ts @@ -15,9 +15,9 @@ import { ExtendedTransactionBuilder } from "../transaction"; import TransportWebSocket from "isomorphic-ws"; export class RpcRequest { - private origin: RpcRequestOrigin; - private version: number; - private payload: {}; + private readonly origin: RpcRequestOrigin; + private readonly version: number; + private readonly payload: {}; /** * @param {RpcRequestOrigin} origin Application emitting the request * @param {Object} payload Request payload @@ -34,8 +34,8 @@ export class ArchethicRPCClient { private origin: RpcRequestOrigin; private client: JSONRPCServerAndClient | undefined; private websocket: TransportWebSocket | undefined; - private _connectionStateEventTarget: EventTarget; - private _rpcNotificationEventTarget: EventTarget; + private readonly _connectionStateEventTarget: EventTarget; + private readonly _rpcNotificationEventTarget: EventTarget; private static _instance: ArchethicRPCClient | undefined; constructor() { this.origin = { name: "" }; @@ -73,7 +73,7 @@ export class ArchethicRPCClient { */ async connect(host: string, port: number): Promise { return new Promise((resolve, reject) => { - if (this.connectionState != ConnectionState.Closed) { + if (this.connectionState !== ConnectionState.Closed) { return reject(new Error("Connection already established. Cancelling new connection request")); } this.websocket = new TransportWebSocket(`ws://${host}:${port}`); @@ -130,7 +130,7 @@ export class ArchethicRPCClient { } _ensuresConnectionAlive(): void { - if (this.client == null || this.connectionState != ConnectionState.Open) + if (this.client === null || this.connectionState !== ConnectionState.Open) throw new Error("RPC connection must be alive."); } @@ -194,8 +194,9 @@ export class ArchethicRPCClient { return ConnectionState.Connecting; case TransportWebSocket.OPEN: return ConnectionState.Open; + default: + return ConnectionState.Closed; } - return ConnectionState.Closed; } /** @@ -337,7 +338,11 @@ export class ArchethicRPCClient { * @param {String} pathSuffix * @returns {Promise<{"publicKey": string}>} */ - async keychainDeriveKeypair(serviceName: string, index = 0, pathSuffix = ""): Promise<{ publicKey: string }> { + async keychainDeriveKeypair( + serviceName: string, + index: number = 0, + pathSuffix: string = "" + ): Promise<{ publicKey: string }> { this._ensuresConnectionAlive(); return this.client?.request( diff --git a/src/endpoint.ts b/src/endpoint.ts index 9e2212b..2f3641f 100644 --- a/src/endpoint.ts +++ b/src/endpoint.ts @@ -1,5 +1,6 @@ import { ArchethicRPCClient } from "./api/wallet_rpc.js"; +/** @internal */ export class Endpoint { /** * @param {String} endpoint @@ -20,6 +21,9 @@ export class Endpoint { } } +/** + * Direct endpoint class + */ export class DirectEndpoint { public origin: string; public nodeEndpoint: URL; @@ -42,10 +46,13 @@ export class DirectEndpoint { } } +/** + * Wallet RPC endpoint class + */ export class WalletRPCEndpoint { public rpcClient: ArchethicRPCClient; public origin: string; - private rpcEndpoint: URL; + private readonly rpcEndpoint: URL; public nodeEndpoint: URL | string; /** * @return {Boolean} diff --git a/src/index.ts b/src/index.ts index 4b1f390..2daa925 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,19 +6,35 @@ import { ArchethicRPCClient } from "./api/wallet_rpc.js"; import { NodeRPCClient } from "./api/node_rpc.js"; import Network from "./network.js"; import Transaction from "./transaction.js"; -import Account, { Keychain } from "./account.js"; +import Account from "./account.js"; +import Keychain from "./keychain.js"; export { Utils, Crypto, Keychain }; export default class Archethic { + /** @internal */ endpoint: DirectEndpoint | WalletRPCEndpoint; - rpcWallet: ArchethicRPCClient | undefined; - rpcNode: NodeRPCClient | undefined; transaction: Transaction; - nearestEndpoints: Set; account: Account; network: Network; + /** @internal */ + nearestEndpoints: Set; + /** @internal */ + rpcWallet: ArchethicRPCClient | undefined; + /** @internal */ + rpcNode: NodeRPCClient | undefined; + /** + * Create a new Archethic instance + * @param {String} endpoint + * @return {Archethic} + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * ``` + */ constructor(endpoint: string) { this.endpoint = Endpoint.build(endpoint); if (this.endpoint instanceof WalletRPCEndpoint) { @@ -31,22 +47,41 @@ export default class Archethic { this.rpcNode = new NodeRPCClient(this); } - async connect() { + /** + * Connect to the Archethic network + * @return {Promise} + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * ``` + */ + async connect(): Promise { if (this.endpoint instanceof WalletRPCEndpoint) { await this.endpoint.resolve(); } const nodes = await Api.getNearestEndpoints(this.endpoint.nodeEndpoint.toString()); - let nearestEndpoints = nodes.map(({ ip, port }) => { + const nearestEndpoints = nodes.map(({ ip, port }) => { return `http://${ip}:${port}`; }); - nearestEndpoints.push(this.endpoint.origin.toString()); // Add the main endpoint as fallback + // Add the main endpoint as fallback + nearestEndpoints.push(this.endpoint.origin.toString()); this.nearestEndpoints = new Set(nearestEndpoints); return this; } + /** + * Request a node from the nearest nodes + * @param {Function} call The function to call on the node + * @return {String} The nearest node + * @throws {Error} If no node is available + * @private + */ async requestNode(call: (endpoint: string) => Promise): Promise { const node = this.nearestEndpoints.values().next().value; @@ -54,7 +89,7 @@ export default class Archethic { return await call(node); } catch (err) { this.nearestEndpoints.delete(node); - if (this.nearestEndpoints.size == 0) { + if (this.nearestEndpoints.size === 0) { console.log(err); throw new Error("Cannot reach Archethic node"); } diff --git a/src/keychain.ts b/src/keychain.ts index 7f45833..61b4a5a 100644 --- a/src/keychain.ts +++ b/src/keychain.ts @@ -7,7 +7,15 @@ import { uint8ArrayToInt, wordArrayToUint8Array } from "./utils.js"; -import { Curve, HashAlgorithm, Keypair, Services } from "./types.js"; +import { + Curve, + DIDDocument, + DIDVerificationMethod, + HashAlgorithm, + Keypair, + Services, + ecEncryptServiceSeed +} from "./types.js"; import TransactionBuilder from "./transaction_builder.js"; import { curveToID, @@ -34,6 +42,17 @@ export default class Keychain { services: Services; authorizedPublicKeys: Uint8Array[]; + /** + * Create a new keychain + * @param {String | Uint8Array} seed Seed of the keychain + * @param {number} version Version of the keychain + * @example + * ```ts + * import { Keychain } from "@archethicjs/sdk"; + * + * const keychain = new Keychain("myseed"); + * ``` + */ constructor(seed: string | Uint8Array, version: number = 1) { if (typeof seed === "string") { this.seed = new TextEncoder().encode(seed); @@ -47,19 +66,30 @@ export default class Keychain { } /** - * Add a service to the keychain + * Add a service into the keychain + * @param {String} name Name of the service to add + * @param {String} derivationPath Crypto derivation path + * @param {Curve} curve Elliptic curve to use + * @param {HashAlgorithm} hashAlgo Hash algo + * @returns {Keychain} The keychain instance + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); * - * @param {String} name - * @param {String} derivationPath - * @param {Curve} curve - * @param {HashAlgorithm} hashAlgo + * const accessKeychainSeed = "myseed"; + * const keychain = await archethic.account.getKeychain(accessKeychainSeed); + * keychain.addService("nft1", "m/650'/1/0") + * ``` */ addService( name: string, derivationPath: string, curve: Curve = Curve.ed25519, hashAlgo: HashAlgorithm = HashAlgorithm.sha256 - ) { + ): this { this.services[name] = { derivationPath: derivationPath, curve: curve, @@ -70,19 +100,44 @@ export default class Keychain { /** * Remove a service from the keychain - * @param {String} name + * @param {String} name Name of the service to add + * @returns {Keychain} The keychain instance + * @example + * ```ts + * import Archethic, { Crypto } from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * + * const accessSeed = "myseed"; + * const { publicKey } = Crypto.deriveKeyPair(accessSeed, 0); + * const keychain = await archethic.account.getKeychain(accessKeychainSeed); + * keychain.removeService("uco"); + * ``` */ - removeService(name: string) { + removeService(name: string): this { delete this.services[name]; return this; } /** * Add a public key to the authorized public keys list + * @param {String | Uint8Array} key Public key to add + * @returns {Keychain} The keychain instance + * @example + * ```ts + * import Archethic, { Crypto } from "@archethicjs/sdk"; * - * @param {String | Uint8Array} key + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * + * const accessSeed = "myseed"; + * const { publicKey } = Crypto.deriveKeyPair(accessSeed, 0); + * const keychain = await archethic.account.getKeychain(accessKeychainSeed); + * keychain.addAuthorizedPublicKey(publicKey); + * ``` */ - addAuthorizedPublicKey(key: string | Uint8Array) { + addAuthorizedPublicKey(key: string | Uint8Array): this { key = maybeHexToUint8Array(key); // prevent duplicate @@ -94,18 +149,54 @@ export default class Keychain { /** * Remove a public key from the authorized public keys list + * @param {String | Uint8Array} key Public key to remove + * @returns {Keychain} The keychain instance + * @example + * ```ts + * import Archethic, { Crypto } from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); * - * @param {String | Uint8Array} key + * const accessSeed = "myseed"; + * const { publicKey } = Crypto.deriveKeyPair(accessSeed, 0); + * const keychain = await archethic.account.getKeychain(accessKeychainSeed); + * keychain.removeAuthorizedPublicKey(publicKey); + * ``` */ - removeAuthorizedPublicKey(key: string | Uint8Array) { + removeAuthorizedPublicKey(key: string | Uint8Array): this { this.authorizedPublicKeys = this.authorizedPublicKeys.filter((k) => { // javascript can't compare objects so we compare strings - return uint8ArrayToHex(k) != uint8ArrayToHex(maybeHexToUint8Array(key)); + return uint8ArrayToHex(k) !== uint8ArrayToHex(maybeHexToUint8Array(key)); }); return this; } - buildTransaction(tx: TransactionBuilder, service: string, index: number, suffix: string = "") { + /** + * Generate address, previousPublicKey, previousSignature of the transaction and serialize it + * @param tx The transaction to build is an instance of TransactionBuilder + * @param service The service name to use for getting the derivation path, the curve and the hash algo + * @param index The number of transactions in the chain, to generate the actual and the next public key (see the cryptography section) + * @param suffix Additional information to add to a service derivation path (default to empty) + * @returns {TransactionBuilder} The transaction with the address, previousPublicKey and previousSignature set + * @throws {Error} If the service doesn't exist in the keychain + * @throws {Error} If the index is not a positive number + * @throws {Error} If the suffix is not a string + * @example + * Notice that the function also sign the TransactionBuilder given in param, so getting the return is not mandatory + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * + * const keychain = await archethic.account.getKeychain(accessKeychainSeed); + * + * const index = archethic.transaction.getTransactionIndex(keychain.deriveAddress("uco", 0)); + * const signedTx = keychain.buildTransaction(tx, "uco", index); + * ``` + */ + buildTransaction(tx: TransactionBuilder, service: string, index: number, suffix: string = ""): TransactionBuilder { if (!this.services[service]) { throw new Error("Service doesn't exist in the keychain"); } @@ -132,10 +223,12 @@ export default class Keychain { } /** * Encode the keychain + * @returns {Uint8Array} The keychain encoded in binary + * @internal */ encode(): Uint8Array { - let servicesBuffer = []; - for (let service in this.services) { + const servicesBuffer = []; + for (const service of Object.keys(this.services)) { const { derivationPath, curve, hashAlgo } = this.services[service]; servicesBuffer.push( concatUint8Arrays( @@ -161,26 +254,28 @@ export default class Keychain { /** * Decode a keychain from a binary * @param binary {Uint8Array} + * @returns {Keychain} The keychain decoded from the binary + * @internal */ - static decode(binary: Uint8Array) { - let { bytes: version, pos: versionPos } = readBytes(binary, 0, 4); - let { byte: seedSize, pos: seedSizePos } = readByte(binary, versionPos, 1); - let { bytes: seed, pos: seedPos } = readBytes(binary, seedSizePos, seedSize); + static decode(binary: Uint8Array): Keychain { + const { bytes: version, pos: versionPos } = readBytes(binary, 0, 4); + const { byte: seedSize, pos: seedSizePos } = readByte(binary, versionPos, 1); + const { bytes: seed, pos: seedPos } = readBytes(binary, seedSizePos, seedSize); let { byte: nbServices, pos: nbServicesPos } = readByte(binary, seedPos, 1); - let keychain = new Keychain(seed, uint8ArrayToInt(version)); + const keychain = new Keychain(seed, uint8ArrayToInt(version)); for (let i = 0; i < nbServices; i++) { - let { byte: serviceNameLength, pos: serviceNameLengthPos } = readByte(binary, nbServicesPos, 1); - let { bytes: serviceName, pos: serviceNamePos } = readBytes(binary, serviceNameLengthPos, serviceNameLength); - let { byte: derivationPathLength, pos: derivationPathLengthPos } = readByte(binary, serviceNamePos, 1); - let { bytes: derivationPath, pos: derivationPathPos } = readBytes( + const { byte: serviceNameLength, pos: serviceNameLengthPos } = readByte(binary, nbServicesPos, 1); + const { bytes: serviceName, pos: serviceNamePos } = readBytes(binary, serviceNameLengthPos, serviceNameLength); + const { byte: derivationPathLength, pos: derivationPathLengthPos } = readByte(binary, serviceNamePos, 1); + const { bytes: derivationPath, pos: derivationPathPos } = readBytes( binary, derivationPathLengthPos, derivationPathLength ); - let { byte: curveID, pos: curveIDPos } = readByte(binary, derivationPathPos, 1); - let { byte: hashAlgoID, pos: hashAlgoIDPos } = readByte(binary, curveIDPos, 1); + const { byte: curveID, pos: curveIDPos } = readByte(binary, derivationPathPos, 1); + const { byte: hashAlgoID, pos: hashAlgoIDPos } = readByte(binary, curveIDPos, 1); const serviceNameString = new TextDecoder().decode(serviceName); const derivationPathString = new TextDecoder().decode(derivationPath); @@ -191,7 +286,27 @@ export default class Keychain { return keychain; } - deriveKeypair(service: string, index: number = 0, pathSuffix: string = "") { + /** + * Derive a keypair for the given service at the index given + * @param service Service name to identify the derivation path to use. + * @param index Chain index to derive (default to 0). + * @param pathSuffix Additional information to add to a service derivation path (default to empty). + * @returns {Keypair} The keypair derived from the keychain seed, the service derivation path, the index and the path suffix. + * @throws {Error} If the service doesn't exist in the keychain. + * @throws {Error} If the index is not a positive number. + * @throws {Error} If the pathSuffix is not a string. + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * + * const keychain = await archethic.account.getKeychain(accessKeychainSeed); + * const { publicKey } = keychain.deriveKeypair("uco", 0); + * ``` + */ + deriveKeypair(service: string, index: number = 0, pathSuffix: string = ""): Keypair { if (!this.services[service]) { throw Error("Service doesn't exist in the keychain"); } @@ -204,7 +319,27 @@ export default class Keychain { return deriveArchethicKeypair(this.seed, derivationPath, index, curve, pathSuffix); } - deriveAddress(service: string, index: number = 0, pathSuffix: string = "") { + /** + * Derive an address for the given service at the index given + * @param service Service name to identify the derivation path to use. + * @param index Chain index to derive (default to 0). + * @param pathSuffix Additional information to add to a service derivation path (default to empty). + * @returns {Uint8Array} The address derived from the keychain seed, the service derivation path, the index and the path suffix. + * @throws {Error} If the service doesn't exist in the keychain. + * @throws {Error} If the index is not a positive number. + * @throws {Error} If the pathSuffix is not a string. + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * + * const keychain = await archethic.account.getKeychain(accessKeychainSeed); + * const genesisUCOAddress = keychain.deriveAddress("uco", 0); + * ``` + */ + deriveAddress(service: string, index: number = 0, pathSuffix: string = ""): Uint8Array { if (!this.services[service]) { throw Error("Service doesn't exist in the keychain"); } @@ -222,14 +357,37 @@ export default class Keychain { return concatUint8Arrays(Uint8Array.from([curveID]), Uint8Array.from(hashedPublicKey)); } - toDID() { + /** + * Return a Decentralized Identity document from the keychain. (This is used in the transaction's content of the keychain tx) + * @returns {Object} The Decentralized Identity document + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * + * const keychain = await archethic.account.getKeychain(accessKeychainSeed); + * const did = keychain.toDID() + * console.log(did) + * { + * "@context": [ + * "https://www.w3.org/ns/did/v1" + * ], + * "id": "did:archethic:keychain_address", + * "authentification": servicesMaterials, //list of public keys of the services + * "verificationMethod": servicesMaterials //list of public keys of the services + * } + * ``` + */ + toDID(): DIDDocument { const address = deriveAddress(this.seed, 0); const address_hex = uint8ArrayToHex(address); - let verificationMethods = []; - let authentications = []; + const verificationMethods: DIDVerificationMethod[] = []; + const authentications = []; - for (let service in this.services) { + for (const service of Object.keys(this.services)) { const { derivationPath, curve } = this.services[service]; const purpose = derivationPath @@ -238,7 +396,7 @@ export default class Keychain { .at(1); //Only support of archethic derivation scheme for now - if (purpose == "650") { + if (purpose === "650") { const { publicKey } = deriveArchethicKeypair(this.seed, derivationPath, 0, curve); verificationMethods.push({ @@ -250,7 +408,7 @@ export default class Keychain { authentications.push(`did:archethic:${address_hex}#${service}`); } else { - throw Error("Purpose '" + purpose + "' is not yet supported"); + throw Error(`Purpose '${purpose}' is not yet supported`); } } @@ -262,7 +420,36 @@ export default class Keychain { }; } - ecEncryptServiceSeed(service: string, publicKeys: string[] | Uint8Array[], pathSuffix: string = "") { + /** + * Use ec encryption on the seed for the list of authorizedPublicKeys + * @param service Service name to identify the derivation path to use. + * @param publicKeys List of public keys to encrypt the service seed. + * @param pathSuffix Additional information to add to a service derivation path (default to empty). + * @returns {Object} The encrypted secret and the list of authorized public keys with their encrypted secret key. + * @throws {Error} If the authorized keys are not an array. + * @throws {Error} If the service doesn't exist in the keychain. + * @throws {Error} If the service derivation path has an index. + * @example + * ```ts + * import Archethic, { Keychain, Crypto } from "@archethicjs/sdk"; + * + * const archethic = new Archethic("http://testnet.archethic.net"); + * await archethic.connect(); + * + * const keychain = new Keychain(Crypto.randomSecretKey()).addService("uco", "m/650'/uco"); + * + * const storageNonce = await archethic.network.getStorageNoncePublicKey(); + * + * const { secret, authorizedPublicKeys } = keychain.ecEncryptServiceSeed("uco", [storageNonce]); + * // secret and authorizedPublicKeys can be used to create an ownership + * const tx = archethic.transaction.new().addOwnership(secret, authorizedPublicKeys); + * ``` + */ + ecEncryptServiceSeed( + service: string, + publicKeys: string[] | Uint8Array[], + pathSuffix: string = "" + ): ecEncryptServiceSeed { if (!Array.isArray(publicKeys)) { throw Error("Authorized keys must be an array"); } @@ -327,18 +514,25 @@ function deriveServiceSeed(seed: string | Uint8Array, derivationPath: string, in } function isPathWithIndex(path: string) { - let servicePath: string[] = path.split("/"); - return servicePath.length == 4 && servicePath[3] == "0"; + const servicePath: string[] = path.split("/"); + return servicePath.length === 4 && servicePath[3] === "0"; } function replaceDerivationPathIndex(path: string, suffix: string, index: number): string { - let servicePath: string[] = path.split("/").slice(0, -1); + const servicePath: string[] = path.split("/").slice(0, -1); // @ts-ignore const serviceName = servicePath.pop().concat(suffix); return servicePath.concat([serviceName, `${index}`]).join("/"); } -export function keyToJWK(publicKey: Uint8Array, keyID: string) { +/** + * Convert a public key to a JWK + * @param publicKey Public key to convert + * @param keyID Key ID to set in the JWK + * @returns {Object} The JWK + * @internal + */ +export function keyToJWK(publicKey: Uint8Array, keyID: string): object | undefined { const curveID = publicKey[0]; const key = publicKey.slice(2, publicKey.length); @@ -375,6 +569,7 @@ function readByte(binary: Uint8Array, pos: number, size: number): { byte: number pos: pos + size }; } + function readBytes(binary: Uint8Array, pos: number, size: number): { bytes: Uint8Array; pos: number } { return { bytes: binary.slice(pos, pos + size), diff --git a/src/network.ts b/src/network.ts index 1050431..65cd4e9 100644 --- a/src/network.ts +++ b/src/network.ts @@ -4,15 +4,34 @@ import { Balance, OracleData, Token } from "./types.js"; import { AddOriginKeyResponse } from "./api/types.js"; export default class Network { - private core: Archethic; + private readonly core: Archethic; + /** @hidden */ constructor(core: Archethic) { this.core = core; } + /** + * Fetch the public key of the shared storage node key + * @returns {Promise} The public key of the storage nonce + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const storageNoncePublicKey = await archethic.network.getStorageNoncePublicKey(); + * ``` + */ async getStorageNoncePublicKey(): Promise { return this.core.requestNode((endpoint) => API.getStorageNoncePublicKey(endpoint)); } + /** + * Query a node to add a new origin public to be authorized to sign transaction with the corresponding private key (see OriginSign) + * @param {string} originKey The public key to add + * @param {string} certificate The certificate that prove the public key is allowed to be added + * @returns {Promise} The transaction response + */ async addOriginKey(originKey: string, certificate: string): Promise { return this.core.rpcNode!.addOriginKey({ certificate, @@ -20,26 +39,128 @@ export default class Network { }); } - async callFunction(contractAddress: string, functionName: string, args: any[]) { + /** + * Call a Smart Contract's exported function with given args + * @param {string} contractAddress The contract address (usually latest or genesis) + * @param {string} functionName The exported function to call + * @param {any[]} args The list of arguments to call the function with + * @returns {Promise} The function response + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const response = await archethic.network.callFunction("0000AB...CD", "add", [1, 2]); + * ``` + */ + async callFunction(contractAddress: string, functionName: string, args: any[]): Promise { return this.core.rpcNode!.callFunction(contractAddress, functionName, args); } + /** + * Fetch the OracleChain data + * @param {number} timestamp The timestamp to fetch the data from + * @returns {Promise} The OracleChain data + * @example Fetch the OracleChain data + * ```ts + * import Archethic from "@archethicjs/sdk" + * + * const archethic = new Archethic("https://testnet.archethic.net") + * await archethic.connect() + * const oracleData = await archethic.network.getOracleData() + * ``` + * @example Fetch the OracleChain data at a specific timestamp + * ```ts + * import Archethic from "@archethicjs/sdk" + * + * const archethic = new Archethic("https://testnet.archethic.net") + * await archethic.connect() + * const timestamp = Date.now(); + * const oracleData = await archethic.network.getOracleData(timestamp) + * ``` + */ async getOracleData(timestamp: number | undefined = undefined): Promise { return this.core.requestNode((endpoint) => API.getOracleData(endpoint, timestamp)); } + /** + * Subscribe to get the real time updates of the OracleChain + * @param {Function} callback The callback function to call when an update is received + * @returns {Promise} The subscription response + * @example + * ```ts + * import Archethic from "@archethicjs/sdk" + * + * const archethic = new Archethic("https://testnet.archethic.net") + * await archethic.connect() + * await archethic.network.subscribeToOracleUpdates(console.log) + * ``` + */ async subscribeToOracleUpdates(callback: Function): Promise { return this.core.requestNode((endpoint) => API.subscribeToOracleUpdates(endpoint, callback)); } + /** + * Query a node to get the token definition (based on AEIP2) from an address + * @param {string} tokenAddress The token address + * @returns {Promise} The token information also genesis address and id + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const token = await archethic.network.getToken("0000AB...CD"); + * ``` + */ async getToken(tokenAddress: string): Promise { return this.core.requestNode((endpoint) => API.getToken(tokenAddress, endpoint)); } + /** + * Query a node to fetch the last balance of the given address + * @param {string} address The address to get the balance of + * @returns {Promise} The balance of the address + * @example + * ```ts + * import Archethic from "@archethicjs/sdk" + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect() + * const balance = await archethic.network.getBalance("0000AB...CD"); + * ``` + * */ async getBalance(address: string): Promise { return this.core.requestNode((endpoint) => API.getBalance(address, endpoint)); } + /** + * Query the GraphQL API of the node with a custom graphQL query that fits your needs + * @param {string} query The graphQL query + * @returns {Promise} The response of the query + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const query = ` + * query { + * transactions(page:1) { + * address + * chainLength + * data { + * code + * } + * type + * version + * } + * } + * `; + * const response = await archethic.network.rawGraphQLQuery(query); + * ``` + * */ async rawGraphQLQuery(query: string): Promise { return this.core.requestNode((endpoint) => API.rawGraphQLQuery(query, endpoint)); } diff --git a/src/transaction.ts b/src/transaction.ts index a07bb8f..f854669 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -7,54 +7,167 @@ import { Ownership, TransactionFee } from "./types.js"; import { SendTransactionResponse } from "./api/types.js"; export default class Transaction { + /** @private */ core: Archethic; + /** @private */ builder = ExtendedTransactionBuilder; + /** @hidden */ constructor(core: Archethic) { this.core = core; } + /** + * Create a new transaction instance to build and to send to the network + * @returns {ExtendedTransactionBuilder} The transaction builder + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const tx = archethic.transaction.new(); + * ``` + */ new(): ExtendedTransactionBuilder { return new this.builder(this.core); } + /** @private */ send(tx: TransactionBuilder): Promise { return this.core.rpcNode!.sendTransaction(tx); } + /** + * Get the transaction index of an address + * @param address The address to get the transaction index of + * @returns {Promise} The transaction index + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const txIndex = await archethic.transaction.getTransactionIndex("0000AB...CD"); + * ``` + */ getTransactionIndex(address: string | Uint8Array): Promise { return this.core.requestNode((endpoint) => API.getTransactionIndex(address, endpoint)); } + /** + * Get the transaction fee of a transaction + * @param tx The transaction to get the fee of + * @returns {Promise} The transaction fee + * @example + * ```ts + * import Archethic from "@archethicjs/sdk" + * + * const archethic = new Archethic("https://testnet.archethic.net") + * await archethic.connect(); + * const tx = ... + * const fee = await archethic.transaction.getTransactionFee(tx) + * ``` + */ getTransactionFee(tx: TransactionBuilder): Promise { return this.core.rpcNode!.getTransactionFee(tx); } + /** + * Query a node to find the ownerships (secrets and authorized keys) to given transaction's address + * @param address The address to get the transaction ownerships of + * @param last Get the last ownerships only (default: false) + * @returns {Promise} The transaction ownerships + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const tx = ... + * const ownerships = await archethic.transaction.getTransactionOwnerships(tx.address); + * ``` + */ getTransactionOwnerships(address: string | Uint8Array, last = false): Promise { return this.core.requestNode((endpoint) => API.getTransactionOwnerships(address, endpoint, last)); } } +/** @category transaction */ export class ExtendedTransactionBuilder extends TransactionBuilder { + /** @private */ core: Archethic; + /** @private */ sender: TransactionSender; + /** @hidden */ constructor(core: Archethic) { super(); this.core = core; this.sender = new TransactionSender(this.core); } - //Override TransactionSender.send to use the node resolution + /** + * Send a transaction to the endpoint and subscribe the node to get confirmation or validation error. + * + * When an update of the validation is received from the subscription, some events are triggered and associated function are called (see function on bellow) + * @param confirmationThreshold The number of confirmation required to consider the transaction as validated + * @param timeout The maximum time to wait for the transaction to be validated + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const tx = archethic.transaction + * .new() + * .setType("transfer") + * .addUCOTransfer("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", 0.42) + * .build("mysuperpassphraseorseed", 0) + * .originSign(privateKey) + * .on("confirmation", (nbConf, maxConf) => console.log(nbConf, maxConf)) + * .send(); + * ``` + */ send(confirmationThreshold?: number, timeout?: number): void { this.core.requestNode((endpoint) => this.sender.send(this, endpoint, confirmationThreshold, timeout)); } - //Use of composition as multi inheritance model - on(eventName: string, fun: Function): this { + /** + * Subscribe to a specific event + * @param eventName The {@link TransactionSender | event} to subscribe + * @param fun The function to call when the event is triggered + * @returns {ExtendedTransactionBuilder} The transaction builder + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const tx = archethic.transaction + * .new() + * .setType("transfer") + * .addUCOTransfer("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", 0.42) + * .build("mysuperpassphraseorseed", 0) + * .originSign(privateKey) + * .on("sent", () => console.log("transaction sent !")) + * .on("confirmation", (nbConf, maxConf) => console.log(nbConf, maxConf)) + * .on("fullConfirmation", (nbConf) => console.log(nbConf)) + * .on("requiredConfirmation", (nbConf) => console.log(nbConf)) + * .on("error", (context, reason) => console.log(context, reason)) + * .on("timeout", (nbConf) => console.log(nbConf)) + * .send(60); // confirmationThreshold: 60 + * ``` + */ + on(eventName: string, fun: Function): ExtendedTransactionBuilder { this.sender.on(eventName, fun); return this; } - unsubscribe(eventName: string): this { + /** + * Unsubscribe to a specific event or all events + * @param eventName The {@link TransactionSender | event} to unsubscribe. If not provided, all events are unsubscribed + * @returns {ExtendedTransactionBuilder} The transaction builder + */ + unsubscribe(eventName: string): ExtendedTransactionBuilder { this.sender.unsubscribe(eventName); return this; } diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index c6309af..1efee47 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -41,6 +41,8 @@ function getTransactionTypeId(type: UserTypeTransaction): number { return 5; case UserTypeTransaction.code_approval: return 6; + default: + return 0; } } @@ -56,6 +58,7 @@ export default class TransactionBuilder { /** * Create a new instance of the transaction builder + * @param {UserTypeTransaction | string} type Transaction {@link UserTypeTransaction | type} is the string defining the type of transaction to generate */ constructor(type: UserTypeTransaction | string = UserTypeTransaction.transfer) { this.version = VERSION; @@ -83,9 +86,11 @@ export default class TransactionBuilder { /** * Set the type of the transaction - * @param {String} type Transaction type + * @param {UserTypeTransaction | string} type Transaction {@link UserTypeTransaction | type} is the string defining the type of transaction to generate + * @returns {TransactionBuilder} The transaction builder instance + * @throws {Error} If the transaction type is not valid */ - setType(type: UserTypeTransaction | string) { + setType(type: UserTypeTransaction | string): this { if (!Object.keys(UserTypeTransaction).includes(type)) { throw new Error( "Transaction type must be one of " + @@ -100,18 +105,20 @@ export default class TransactionBuilder { /** * Add smart contract code to the transcation - * @param {string} code Smart contract code + * @param {string} code Smart contract code is a string defining the smart contract + * @returns {TransactionBuilder} The transaction builder instance */ - setCode(code: string) { + setCode(code: string): this { this.data.code = new TextEncoder().encode(code); return this; } /** * Add a content to the transaction - * @param {String | Uint8Array} content Hosted content + * @param {string | Uint8Array} content Hosted content + * @returns {TransactionBuilder} The transaction builder instance */ - setContent(content: string | Uint8Array) { + setContent(content: string | Uint8Array): this { if (typeof content == "string") { content = new TextEncoder().encode(content); } @@ -120,11 +127,35 @@ export default class TransactionBuilder { } /** - * Add an ownership with a secret and its authorized public keys + * Add an ownership in the data.ownerships section of the transaction with a secret and its related authorized public keys to be able to decrypt it. + * + * This aims to prove the ownership or the delegatation of some secret to a given list of public keys. * @param {string | Uint8Array} secret Secret encrypted (hexadecimal or binary buffer) - * @param {AuthorizedKeyUserInput[]} authorizedKeys List of authorized keys + * @param {AuthorizedKeyUserInput[]} authorizedKeys List of authorized keys to decrypt the secret (publicKey and encryptedSecretKey) + * @returns {TransactionBuilder} The transaction builder instance + * @throws {Error} If the authorized keys are not an array + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * import Crypto from "@archethicjs/crypto"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const seed = "myseed"; + * const genesisAddress = Crypto.deriveAddress(seed, 0); + * const index = await archethic.transaction.getTransactionIndex(genesisAddress); + * const storageNoncePublicKey = await archethic.network.getStorageNoncePublicKey(); + * const { encryptedSecret, authorizedKeys } = Crypto.encryptSecret(Crypto.randomSecretKey(), storageNoncePublicKey); + * const tx = archethic.transaction.new() + * .setType("contract") + * .setCode("...") + * .addOwnership(encryptedSecret, authorizedKeys) + * .build(seed, index) + * .originSign(Utils.originPrivateKey) + * .send(); + * ``` */ - addOwnership(secret: string | Uint8Array, authorizedKeys: AuthorizedKeyUserInput[]) { + addOwnership(secret: string | Uint8Array, authorizedKeys: AuthorizedKeyUserInput[]): this { secret = maybeStringToUint8Array(secret); if (!Array.isArray(authorizedKeys)) { @@ -158,8 +189,10 @@ export default class TransactionBuilder { * Add a UCO transfer to the transaction * @param {string | Uint8Array} to Address of the recipient (hexadecimal or binary buffer) * @param {number} amount Amount of UCO to transfer (in bigint) + * @returns {TransactionBuilder} The transaction builder instance + * @throws {Error} If the amount is not a positive number */ - addUCOTransfer(to: string | Uint8Array, amount: number) { + addUCOTransfer(to: string | Uint8Array, amount: number): this { to = maybeHexToUint8Array(to); if (isNaN(amount) || amount <= 0) { @@ -176,8 +209,16 @@ export default class TransactionBuilder { * @param {number} amount Amount of UCO to transfer (in bigint) * @param {string | Uint8Array} tokenAddress Address of token to spend (hexadecimal or binary buffer) * @param {number} tokenId ID of the token to use (default to 0) + * @returns {TransactionBuilder} The transaction builder instance + * @throws {Error} If the amount is not a positive number + * @throws {Error} If the tokenId is not a valid integer */ - addTokenTransfer(to: string | Uint8Array, amount: number, tokenAddress: string | Uint8Array, tokenId: number = 0) { + addTokenTransfer( + to: string | Uint8Array, + amount: number, + tokenAddress: string | Uint8Array, + tokenId: number = 0 + ): this { to = maybeHexToUint8Array(to); tokenAddress = maybeHexToUint8Array(tokenAddress); @@ -203,8 +244,74 @@ export default class TransactionBuilder { * @param {string | Uint8Array} to Recipient address (hexadecimal or binary buffer) * @param {string} action The named action * @param {any[]} args The arguments list for the named action (can only contain JSON valid data) + * @returns {TransactionBuilder} The transaction builder instance + * @throws {Error} If the action is not a string + * @throws {Error} If the args is not an array + * @example Smart contract example + * ``` + * @ version 1 + * condition triggered_by: transaction, on: vote(candidate), as: [ + * content: ( + * # check incoming vote + * valid_candidate? = List.in?(["Miss Scarlett", "Colonel Mustard"], candidate) + * + * # check incoming voter + * valid_voter? = !List.in?( + * State.get("voters_genesis_addresses", []), + * Chain.get_genesis_address(transaction.address) + * ) + * + * valid_candidate? && valid_voter? + * ) + * ] + * + * actions triggered_by: transaction, on: vote(candidate) do + * scarlett_votes = State.get("Miss Scarlett", 0) + * mustard_votes = State.get("Colonel Mustard", 0) + * voters = State.get("voters_genesis_addresses", []) + * + * if candidate == "Miss Scarlett" do + * scarlett_votes = scarlett_votes + 1 + * end + * if candidate == "Colonel Mustard" do + * mustard_votes = mustard_votes + 1 + * end + * + * # Add the current voter genesis address to the list + * # So he/she cannot vote twice + * voters = List.prepend(voters, Chain.get_genesis_address(transaction.address)) + * + * State.set("Miss Scarlett", scarlett_votes) + * State.set("Colonel Mustard", mustard_votes) + * State.set("voters_genesis_addresses", voters) + * end + * + * export fun get_votes() do + * [ + * scarlett: State.get("Miss Scarlett", 0), + * mustard: State.get("Colonel Mustard", 0) + * ] + * end + * ``` + * @example Usage example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const seed = "myseed"; + * const genesisAddress = Crypto.deriveAddress(seed, 0); + * const index = await archethic.transaction.getTransactionIndex(genesisAddress); + * const tx = archethic.transaction + * .new() + * .setType("transfer") + * .addRecipient("0000bc96b1a9751d3750edb9381a55b5b4e4fb104c10b0b6c9a00433ec464637bfab", "vote", ["Miss Scarlett"]) + * .build(seed, index) + * .originSign(Utils.originPrivateKey) + * .send(); + * ``` */ - addRecipient(to: string | Uint8Array, action?: string, args?: any[]) { + addRecipient(to: string | Uint8Array, action?: string, args?: any[]): this { const address = maybeHexToUint8Array(to); if (action && typeof action != "string") { @@ -227,8 +334,25 @@ export default class TransactionBuilder { * Set the transaction builder with Previous Publickey and Previous Signature * @param {string | Uint8Array} prevSign Previous Signature (hexadecimal) * @param {string | Uint8Array} prevPubKey Previous PublicKey (hexadecimal) + * @returns {TransactionBuilder} The transaction builder instance + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const tx = archethic.transaction + * .new() + * .setType("transfer") + * .addUCOTransfer("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", 0.42); + * + * const signaturePayload = tx.previousSignaturePayload(); + * const prevSign = someFunctionToGetSignature(signaturePayload); + * const prevPubKey = someFunctionToGetPubKey(); + * tx.setPreviousSignatureAndPreviousPublicKey(prevSign, prevPubKey); + * ``` */ - setPreviousSignatureAndPreviousPublicKey(prevSign: string | Uint8Array, prevPubKey: string | Uint8Array) { + setPreviousSignatureAndPreviousPublicKey(prevSign: string | Uint8Array, prevPubKey: string | Uint8Array): this { prevSign = maybeHexToUint8Array(prevSign); prevPubKey = maybeHexToUint8Array(prevPubKey); @@ -240,9 +364,23 @@ export default class TransactionBuilder { /** * Set the transaction builder with address (required for originSign) * @param {string | Uint8Array} addr Address (hexadecimal | Uint8Array) + * @returns {TransactionBuilder} The transaction builder instance + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; * + * const archethic = new Archethic("https://testnet.archethic.net"); + * await archethic.connect(); + * const tx = archethic.transaction + * .new() + * .setType("transfer") + * .addUCOTransfer("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", 0.42); + * + * const txAddress = someFunctionToGetTxAddress(); + * tx.setAddress(txAddress); + * ``` */ - setAddress(addr: string | Uint8Array) { + setAddress(addr: string | Uint8Array): this { addr = maybeHexToUint8Array(addr); this.address = addr; @@ -252,8 +390,9 @@ export default class TransactionBuilder { /** * Add a encrypted (by storage nonce public key) seed in the transaction's ownerships to allow nodes to manage smart contract * @param {boolean} generateEncryptedSeedSC Generate encrypted seed for smart contract + * @returns {TransactionBuilder} The transaction builder instance */ - setGenerateEncryptedSeedSC(generateEncryptedSeedSC: boolean) { + setGenerateEncryptedSeedSC(generateEncryptedSeedSC: boolean): this { this.generateEncryptedSeedSC = generateEncryptedSeedSC; return this; } @@ -264,13 +403,29 @@ export default class TransactionBuilder { * @param {number} index Number of transaction on the chain * @param {string} curve Elliptic curve to use for the key generation * @param {string} hashAlgo Hash algorithm to use for the address generation + * @returns {TransactionBuilder} The transaction builder instance + * @throws {Error} If the seed is not defined + * @throws {Error} If the index is not defined + * @throws {Error} If the curve is not valid + * @throws {Error} If the hash algorithm is not valid + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * const archethic = new Archethic("https://testnet.archethic.net"); + * + * const tx = archethic.transaction + * .new() + * .setType("transfer") + * .addUCOTransfer("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", 0.42) + * .build("mysuperpassphraseorseed", 0); + * ``` */ - build(seed: string | Uint8Array, index: number = 0, curve: string = "ed25519", hashAlgo: string = "sha256") { - if (seed == undefined || seed == null) { + build(seed: string | Uint8Array, index: number = 0, curve: string = "ed25519", hashAlgo: string = "sha256"): this { + if (seed === undefined || seed === null) { throw new Error("Seed must be defined"); } - if (index == undefined || index == null) { + if (index === undefined || index === null) { throw new Error("Index must be defined"); } @@ -305,8 +460,24 @@ export default class TransactionBuilder { /** * Sign the transaction with an origin private key * @param {string | Uint8Array} privateKey Origin Private Key (hexadecimal or binary buffer) + * @returns {TransactionBuilder} The transaction builder instance + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * const archethic = new Archethic("https://testnet.archethic.net"); + * + * const tx = archethic.transaction + * .new() + * .setType("transfer") + * .addUCOTransfer( + * "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", + * 0.42 + * ) + * .build("myseed", 0); + * .originSign(Utils.originPrivateKey) + * ``` */ - originSign(privateKey: string | Uint8Array) { + originSign(privateKey: string | Uint8Array): this { privateKey = maybeHexToUint8Array(privateKey); this.originSignature = sign(this.originSignaturePayload(), privateKey); @@ -316,15 +487,48 @@ export default class TransactionBuilder { /** * Set the Txn's originSignature, method called from hardware_libs * @param {string | Uint8Array} signature Origin Signature (hexadecimal or binary) + * @returns {TransactionBuilder} The transaction builder instance + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * const archethic = new Archethic("https://testnet.archethic.net"); + * + * const tx = archethic.transaction + * .new() + * .setType("transfer") + * .addUCOTransfer("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", 0.42) + * .build("mysuperpassphraseorseed", 0); + * + * const originPayload = tx.originSignaturePayload(); + * const originSignature = someFunctionToGetSignature(originPayload); + * tx.setOriginSign(originSignature); + * ``` */ - setOriginSign(signature: string | Uint8Array) { + setOriginSign(signature: string | Uint8Array): this { signature = maybeHexToUint8Array(signature); this.originSignature = signature; return this; } - originSignaturePayload() { + /** + * Get an Uint8Array payload to be signed with the origin private key of the device + * @returns {Uint8Array} The payload for the origin signature + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * const archethic = new Archethic("https://testnet.archethic.net"); + * + * const tx = archethic.transaction + * .new() + * .setType("transfer") + * .addUCOTransfer("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", 0.42) + * .build(seed, originPrivateKey); + * + * const originPayload = tx.originSignaturePayload(); + * ``` + */ + originSignaturePayload(): Uint8Array { const payloadForPreviousSignature = this.previousSignaturePayload(); return concatUint8Arrays( payloadForPreviousSignature, @@ -335,12 +539,23 @@ export default class TransactionBuilder { } /** - * Generate the payload for the previous signature by encoding address, type and data + * Generate the payload for the previous signature by encoding address, type and data + * @returns {Uint8Array} The payload for the previous signature + * @example + * import Archethic from "@archethicjs/sdk"; + * const archethic = new Archethic("https://testnet.archethic.net"); + * + * const tx = archethic.transaction + * .new() + * .setType("transfer") + * .addUCOTransfer("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", 0.42); + * + * const signaturePayload = tx.previousSignaturePayload(); */ - previousSignaturePayload() { + previousSignaturePayload(): Uint8Array { const bufCodeSize = intToUint8Array(this.data.code.length); - let contentSize = this.data.content.length; + const contentSize = this.data.content.length; const bufContentSize = intToUint8Array(contentSize); @@ -375,7 +590,7 @@ export default class TransactionBuilder { }); const recipientsBuffer = this.data.recipients.map(({ address, action, args }) => { - if (action == undefined || args == undefined) { + if (action === undefined || args === undefined) { return concatUint8Arrays( // 0 = unnamed action Uint8Array.from([0]), @@ -430,7 +645,9 @@ export default class TransactionBuilder { } /** - * JSON RPC API SEND_TRANSACTION + * Export the transaction generated into a JSON-RPC object + * @returns {TransactionRPC} The JSON-RPC representation of the transaction + * @private */ toNodeRPC(): TransactionRPC { return { @@ -487,8 +704,23 @@ export default class TransactionBuilder { } /** - * REST API (deprecated, replaced by JSON RPC API) - * content is hexadecimal + * Export the transaction generated into JSON + * @returns {string} The JSON representation of the transaction + * @example + * ```ts + * import Archethic from "@archethicjs/sdk"; + * const archethic = new Archethic("https://testnet.archethic.net"); + * + * const tx = archethic.transaction + * .new() + * .setType("transfer") + * .addUCOTransfer( + * "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", + * 0.42 + * ) + * .build("mysuperpassphraseorseed", 0); + * .toJSON(); + * ``` */ toJSON(): string { return JSON.stringify(this.toNodeRPC()); @@ -498,6 +730,8 @@ export default class TransactionBuilder { * Wallet RPC API * content is normal * only transaction payload (no address/public key/signatures) + * @returns {object} The JSON representation of the transaction for the wallet RPC API + * @private */ toWalletRPC(): object { return { diff --git a/src/transaction_sender.ts b/src/transaction_sender.ts index 3697490..c5e4bc3 100644 --- a/src/transaction_sender.ts +++ b/src/transaction_sender.ts @@ -7,19 +7,35 @@ import { RpcError } from "./api/types.js"; const senderContext = "SENDER"; +/** + * @internal + */ export default class TransactionSender { + /** @event */ onSent: Function[]; + /** @event */ onConfirmation: Function[]; + /** @event */ onFullConfirmation: Function[]; + /** @event */ onRequiredConfirmation: Function[]; + /** @event */ onError: Function[]; + /** @event */ onTimeout: Function[]; + /** @private */ confirmationNotifier: any; + /** @private */ errorNotifier: any; + /** @private */ absintheSocket: AbsintheSocket | undefined; + /** @private */ nbConfirmationReceived: number; + /** @private */ timeout: NodeJS.Timeout | undefined; + /** @private */ core: Archethic; + /** @hidden */ constructor(archethic: Archethic) { this.core = archethic; @@ -38,12 +54,8 @@ export default class TransactionSender { this.nbConfirmationReceived = 0; } - /** - * Add listener on specific event - * @param {String} event Event to subscribe - * @param {Function} func Function to call when event triggered - */ - on(event: string, func: Function) { + /** @private */ + on(event: string, func: Function): this { switch (event) { case "sent": this.onSent.push(func); @@ -70,12 +82,13 @@ export default class TransactionSender { break; default: - throw new Error("Event " + event + " is not supported"); + throw new Error(`Event ${event} is not supported`); } return this; } + /** @private */ async send( tx: TransactionBuilder, endpoint: string, @@ -94,7 +107,7 @@ export default class TransactionSender { // Create web socket const { host, protocol } = new URL(endpoint); - const ws_protocol = protocol == "https:" ? "wss" : "ws"; + const ws_protocol = protocol === "https:" ? "wss" : "ws"; this.absintheSocket = absinthe.create(`${ws_protocol}://${host}/socket`); @@ -119,6 +132,7 @@ export default class TransactionSender { return this; } + /** @private */ unsubscribe(event: string | undefined = undefined) { if (event) { switch (event) { @@ -151,7 +165,7 @@ export default class TransactionSender { break; default: - throw new Error("Event " + event + " is not supported"); + throw new Error(`Event ${event} is not supported`); } } else { absinthe.cancel(this.absintheSocket as AbsintheSocket, this.confirmationNotifier); @@ -217,7 +231,7 @@ function handleConfirmation( this.nbConfirmationReceived = nbConfirmations; // Unsubscribe to error on first confirmation - if (nbConfirmations == 1) absinthe.cancel(this.absintheSocket as AbsintheSocket, this.errorNotifier); + if (nbConfirmations === 1) absinthe.cancel(this.absintheSocket as AbsintheSocket, this.errorNotifier); this.onConfirmation.forEach((func) => func(nbConfirmations, maxConfirmations, this)); @@ -227,7 +241,7 @@ function handleConfirmation( clearTimeout(this.timeout); } - if (nbConfirmations == maxConfirmations) { + if (nbConfirmations === maxConfirmations) { clearTimeout(this.timeout); absinthe.cancel(this.absintheSocket as AbsintheSocket, this.confirmationNotifier); diff --git a/src/types.ts b/src/types.ts index fb4ec67..3e2caec 100644 --- a/src/types.ts +++ b/src/types.ts @@ -24,7 +24,7 @@ export enum UserTypeTransaction { code_approval = "code_approval" } -enum NetworkTypeTransaction { +export enum NetworkTypeTransaction { node = "node", node_shared_secrets = "node_shared_secrets", origin_shared_secrets = "origin_shared_secrets", @@ -72,7 +72,8 @@ export type Token = { type CrossValidationStamp = { nodePubliKey: string; - signature: string; // Hexadecimal + /** Hexadecimal */ + signature: string; }; export type TransactionData = { @@ -116,16 +117,19 @@ export type Recipient = { export type Ownership = { authorizedPublicKeys: AuthorizedKey[]; - secret: Uint8Array; // Hexadecimal + /** Hexadecimal */ + secret: Uint8Array; }; export type AuthorizedKey = { - encryptedSecretKey: Uint8Array; // hexadecimal + /** Hexadecimal */ + encryptedSecretKey: Uint8Array; publicKey: Uint8Array; }; export type AuthorizedKeyUserInput = { - encryptedSecretKey: string; // hexadecimal + /** Hexadecimal */ + encryptedSecretKey: string; publicKey: string; }; @@ -247,3 +251,36 @@ export type TransactionFee = { usd: number; }; }; + +export type DIDVerificationMethod = { + id: string; + type: string; + controller: string; + publicKeyJwk?: object; + publicKeyMultibase?: string; +}; + +export type DIDService = { + id: string; + type: string; + serviceEndpoint: string; +}; + +export type DIDDocument = { + "@context": string[]; + id: string; + alsoKnownAs?: string[]; + controller?: string; + verificationMethod?: DIDVerificationMethod[]; + authentication?: string[]; + assertionMethod?: string[]; + keyAgreement?: string[]; + capabilityInvocation?: string[]; + capabilityDelegation?: string[]; + service?: Service[]; +}; + +export type ecEncryptServiceSeed = { + secret: Uint8Array; + authorizedPublicKeys: AuthorizedKey[]; +}; diff --git a/src/utils.ts b/src/utils.ts index 6a5d9d6..6c4663f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,11 +1,12 @@ /** - * * Return the Initial Origin Private Key */ export const originPrivateKey = "01019280BDB84B8F8AEDBA205FE3552689964A5626EE2C60AA10E3BF22A91A036009"; /** * Convert CryptoJS.lib.WordArray to Uint8Array + * @param wordArray + * @returns {Uint8Array} Uint8Array */ export function wordArrayToUint8Array(wordArray: { sigBytes: number; words: number[] }): Uint8Array { const dataArray = new Uint8Array(wordArray.sigBytes); @@ -18,16 +19,18 @@ export function wordArrayToUint8Array(wordArray: { sigBytes: number; words: numb /** * Check if a string is a valid hex string * @param str + * @returns {boolean} True if string is a valid hex string */ -export function isHex(str: string) { +export function isHex(str: string): boolean { return /^[0-9a-fA-F]+$/.test(str); } /** * Check if given variable is an object * @param variable + * @returns {boolean} True if variable is an object */ -export function isObject(variable: any) { +export function isObject(variable: any): boolean { return typeof variable === "object" && !Array.isArray(variable) && variable !== null; } @@ -37,8 +40,7 @@ export function sortObjectKeysASC(term: any): any { if (term instanceof Map) { const sortedEntries = [...term.entries()].sort((a, b) => a[0].localeCompare(b[0])); - const sortedMap = new Map(sortedEntries.map(([key, value]) => [key, sortObjectKeysASC(value)])); - return sortedMap; + return new Map(sortedEntries.map(([key, value]) => [key, sortObjectKeysASC(value)])); } // object: sort and map over elements @@ -56,6 +58,7 @@ export function sortObjectKeysASC(term: any): any { /** * Convert a hex string to a Uint8Array * @param str + * @returns Uint8Array */ export function hexToUint8Array(str: string): Uint8Array { if (!isHex(str)) { @@ -108,6 +111,7 @@ export function intToUint8Array(int: number): Uint8Array { /** * Encode a big integer into a Uint8Array (8 bytes) * @param {Number} number Number to encode + * @returns {Uint8Array} Encoded big integer */ export function bigIntToUint8Array(number: number): Uint8Array { const buffer = new ArrayBuffer(8); @@ -119,6 +123,7 @@ export function bigIntToUint8Array(number: number): Uint8Array { /** * Decode byte array (4 bytes) into a integer * @param {Uint8Array} bytes Bytes array to decode + * @returns {number} Decoded integer */ export function uint8ArrayToInt(bytes: Uint8Array): number { let value = 0; @@ -132,6 +137,7 @@ export function uint8ArrayToInt(bytes: Uint8Array): number { * Convert any number into a big int for 10^8 decimals * @param number Number to convert * @param decimal Number of decimals + * @returns {number} Big int number */ export function toBigInt(number: number, decimal: number = 8): number { // This is a workaroud of float weird behavior @@ -148,6 +154,7 @@ export function toBigInt(number: number, decimal: number = 8): number { * Convert a big int number of 10^8 decimals into a decimal number * @param number Number to convert * @param decimal Number of decimals + * @returns {number} Decimal number */ export function fromBigInt(number: number, decimal: number = 8): number { return number / Math.pow(10, decimal); @@ -156,15 +163,16 @@ export function fromBigInt(number: number, decimal: number = 8): number { /** * Concatenate multiple Uint8Array into one * @param {Uint8Array[]} arrays + * @returns {Uint8Array} Concatenated Uint8Array */ export function concatUint8Arrays(...arrays: Uint8Array[]): Uint8Array { let totalLength = 0; - for (let arr of arrays) { + for (const arr of arrays) { totalLength += arr.length; } - let result = new Uint8Array(totalLength); + const result = new Uint8Array(totalLength); let offset = 0; - for (let arr of arrays) { + for (const arr of arrays) { result.set(arr, offset); offset += arr.length; } @@ -185,15 +193,15 @@ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" /** * Encode an Uint8Array into a base64url string - * @param arraybuffer + * @param {ArrayBuffer} arraybuffer Uint8Array + * @returns {string} base64url string */ export function base64url(arraybuffer: ArrayBuffer): string { - let bytes = new Uint8Array(arraybuffer), - i, - len = bytes.length, - base64 = ""; + const bytes = new Uint8Array(arraybuffer); + const len = bytes.length; + let base64 = ""; - for (i = 0; i < len; i += 3) { + for (let i = 0; i < len; i += 3) { base64 += chars[bytes[i] >> 2]; base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; @@ -211,6 +219,8 @@ export function base64url(arraybuffer: ArrayBuffer): string { /** * Convert any number into a byte array + * @param number Number to convert + * @returns {Uint8Array} Byte array */ export function toByteArray(number: number): Uint8Array { if (number === 0) return Uint8Array.from([0]); @@ -228,9 +238,8 @@ export function toByteArray(number: number): Uint8Array { /** * Alias of uint8ArrayToInt - * * @param bytes - * @returns the number + * @returns number */ export function fromByteArray(bytes: Uint8Array): number { return uint8ArrayToInt(bytes); @@ -240,7 +249,7 @@ export function fromByteArray(bytes: Uint8Array): number { * Return the next Uint8 from an iterator of Uint8Array * There is an assumption on success * @param iter - * @returns + * @returns the next Uint8 */ export function nextUint8(iter: IterableIterator<[number, number]>): number { return iter.next().value[1]; @@ -249,7 +258,7 @@ export function nextUint8(iter: IterableIterator<[number, number]>): number { /** * String to Uint8Array * @param str - * @returns + * @returns Uint8Array */ export function serializeString(str: string): Uint8Array { return new TextEncoder().encode(str); @@ -257,8 +266,8 @@ export function serializeString(str: string): Uint8Array { /** * Uint8Array to String - * @param str - * @returns + * @param encoded_str + * @returns string */ export function deserializeString(encoded_str: Uint8Array): string { return new TextDecoder().decode(encoded_str); From a5b5b137bde578a12f144ad782c1534735e9c954 Mon Sep 17 00:00:00 2001 From: Rudy-Perrin Date: Fri, 17 May 2024 00:33:33 +0200 Subject: [PATCH 3/5] chore: add Telegraf font import to styles.css --- src/assets/docs/styles.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/assets/docs/styles.css b/src/assets/docs/styles.css index e336db7..95e95f7 100644 --- a/src/assets/docs/styles.css +++ b/src/assets/docs/styles.css @@ -1,3 +1,5 @@ +@import url("https://fonts.cdnfonts.com/css/telegraf"); + :root { /* Archethic Neutral */ --neutral-1000: #000000; @@ -412,8 +414,7 @@ body { background: var(--color-background); background: var(--color-background-gradient); font-family: - Telegraf, - telegraf-fallback, + "Telegraf", sans-serif, -apple-system, BlinkMacSystemFont, From d0c581656d379638e5644f011dcc8d42c358d13b Mon Sep 17 00:00:00 2001 From: Rudy-Perrin Date: Fri, 17 May 2024 00:47:56 +0200 Subject: [PATCH 4/5] chore: update Archethic SDK customTitle and extraFooter --- tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index d03ea58..e44177f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -45,10 +45,10 @@ }, "coverageLabel": "document", "customCss": "src/assets/docs/styles.css", - "extraFooter": "
Coverage
", + "extraFooter": "
Coverage
", "extraFooterNoDefaultWrapper": true, "favicon": "https://github.com/archethic-foundation/archethic-assets/blob/main/assets/logos/archethic/Archethic%20-%20Logo%20crystal.png?raw=true", - "customTitle": "Archethic SDK", + "customTitle": "
Archethic Archethic SDK
", "footerLastModified": true } } From eea1f50c8f00dbeb7382ff15219d67584c32b8e7 Mon Sep 17 00:00:00 2001 From: Rudy-Perrin Date: Tue, 11 Jun 2024 22:37:36 +0200 Subject: [PATCH 5/5] Fix merge errors --- src/account.ts | 2 ++ src/index.ts | 11 ++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/account.ts b/src/account.ts index a48adfe..8c8652a 100644 --- a/src/account.ts +++ b/src/account.ts @@ -179,3 +179,5 @@ export default class Account { return keychain; } } + +export { Keychain }; diff --git a/src/index.ts b/src/index.ts index 4778029..a394199 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,17 +24,15 @@ export { export default class Archethic { /** @internal */ endpoint: Endpoint; + /** @internal */ rpcWallet: ArchethicWalletClient | undefined; + /** @internal */ rpcNode: NodeRPCClient | undefined; transaction: Transaction; account: Account; network: Network; /** @internal */ nearestEndpoints: Set; - /** @internal */ - rpcWallet: ArchethicRPCClient | undefined; - /** @internal */ - rpcNode: NodeRPCClient | undefined; /** * Create a new Archethic instance @@ -74,9 +72,8 @@ export default class Archethic { if (this.endpoint instanceof AWCEndpoint) { await this.endpoint.resolve(); } - const nodes = this.endpoint.nodeEndpoint === null ? - [] : - await Api.getNearestEndpoints(this.endpoint.nodeEndpoint.toString()); + const nodes = + this.endpoint.nodeEndpoint === null ? [] : await Api.getNearestEndpoints(this.endpoint.nodeEndpoint.toString()); const nearestEndpoints = nodes.map(({ ip, port }) => { return `http://${ip}:${port}`;